From: Hans Verkuil Date: Mon, 25 Mar 2013 08:47:48 +0000 (-0300) Subject: [media] solo6x10: prefix sources with 'solo6x10-' X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=7bce33daeaca26a3ea3f6099fdfe4e11ea46cac6;p=openwrt%2Fstaging%2Fblogic.git [media] solo6x10: prefix sources with 'solo6x10-' Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- diff --git a/drivers/staging/media/solo6x10/Makefile b/drivers/staging/media/solo6x10/Makefile index 9bbde291d6a3..7aae118947b2 100644 --- a/drivers/staging/media/solo6x10/Makefile +++ b/drivers/staging/media/solo6x10/Makefile @@ -1,3 +1,5 @@ -solo6x10-y := core.o i2c.o p2m.o v4l2.o tw28.o gpio.o disp.o enc.o v4l2-enc.o g723.o eeprom.o +solo6x10-y := solo6x10-core.o solo6x10-i2c.o solo6x10-p2m.o solo6x10-v4l2.o \ + solo6x10-tw28.o solo6x10-gpio.o solo6x10-disp.o solo6x10-enc.o \ + solo6x10-v4l2-enc.o solo6x10-g723.o solo6x10-eeprom.o obj-$(CONFIG_SOLO6X10) += solo6x10.o diff --git a/drivers/staging/media/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c deleted file mode 100644 index 36750205d23f..000000000000 --- a/drivers/staging/media/solo6x10/core.c +++ /dev/null @@ -1,709 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "solo6x10.h" -#include "solo6x10-tw28.h" - -MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver"); -MODULE_AUTHOR("Bluecherry "); -MODULE_VERSION(SOLO6X10_VERSION); -MODULE_LICENSE("GPL"); - -unsigned video_nr = -1; -module_param(video_nr, uint, 0644); -MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)"); - -static int full_eeprom; /* default is only top 64B */ -module_param(full_eeprom, uint, 0644); -MODULE_PARM_DESC(full_eeprom, "Allow access to full 128B EEPROM (dangerous)"); - - -static void solo_set_time(struct solo_dev *solo_dev) -{ - struct timespec ts; - - ktime_get_ts(&ts); - - solo_reg_write(solo_dev, SOLO_TIMER_SEC, ts.tv_sec); - solo_reg_write(solo_dev, SOLO_TIMER_USEC, ts.tv_nsec / NSEC_PER_USEC); -} - -static void solo_timer_sync(struct solo_dev *solo_dev) -{ - u32 sec, usec; - struct timespec ts; - long diff; - - if (solo_dev->type != SOLO_DEV_6110) - return; - - if (++solo_dev->time_sync < 60) - return; - - solo_dev->time_sync = 0; - - sec = solo_reg_read(solo_dev, SOLO_TIMER_SEC); - usec = solo_reg_read(solo_dev, SOLO_TIMER_USEC); - - ktime_get_ts(&ts); - - diff = (long)ts.tv_sec - (long)sec; - diff = (diff * 1000000) - + ((long)(ts.tv_nsec / NSEC_PER_USEC) - (long)usec); - - if (diff > 1000 || diff < -1000) { - solo_set_time(solo_dev); - } else if (diff) { - long usec_lsb = solo_dev->usec_lsb; - - usec_lsb -= diff / 4; - if (usec_lsb < 0) - usec_lsb = 0; - else if (usec_lsb > 255) - usec_lsb = 255; - - solo_dev->usec_lsb = usec_lsb; - solo_reg_write(solo_dev, SOLO_TIMER_USEC_LSB, - solo_dev->usec_lsb); - } -} - -static irqreturn_t solo_isr(int irq, void *data) -{ - struct solo_dev *solo_dev = data; - u32 status; - int i; - - status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); - if (!status) - return IRQ_NONE; - - if (status & ~solo_dev->irq_mask) { - solo_reg_write(solo_dev, SOLO_IRQ_STAT, - status & ~solo_dev->irq_mask); - status &= solo_dev->irq_mask; - } - - if (status & SOLO_IRQ_PCI_ERR) - solo_p2m_error_isr(solo_dev); - - for (i = 0; i < SOLO_NR_P2M; i++) - if (status & SOLO_IRQ_P2M(i)) - solo_p2m_isr(solo_dev, i); - - if (status & SOLO_IRQ_IIC) - solo_i2c_isr(solo_dev); - - if (status & SOLO_IRQ_VIDEO_IN) { - solo_video_in_isr(solo_dev); - solo_timer_sync(solo_dev); - } - - if (status & SOLO_IRQ_ENCODER) - solo_enc_v4l2_isr(solo_dev); - - if (status & SOLO_IRQ_G723) - solo_g723_isr(solo_dev); - - /* Clear all interrupts handled */ - solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); - - return IRQ_HANDLED; -} - -static void free_solo_dev(struct solo_dev *solo_dev) -{ - struct pci_dev *pdev; - - if (!solo_dev) - return; - - if (solo_dev->dev.parent) - device_unregister(&solo_dev->dev); - - pdev = solo_dev->pdev; - - /* If we never initialized the PCI device, then nothing else - * below here needs cleanup */ - if (!pdev) { - kfree(solo_dev); - return; - } - - if (solo_dev->reg_base) { - /* Bring down the sub-devices first */ - solo_g723_exit(solo_dev); - solo_enc_v4l2_exit(solo_dev); - solo_enc_exit(solo_dev); - solo_v4l2_exit(solo_dev); - solo_disp_exit(solo_dev); - solo_gpio_exit(solo_dev); - solo_p2m_exit(solo_dev); - solo_i2c_exit(solo_dev); - - /* Now cleanup the PCI device */ - solo_irq_off(solo_dev, ~0); - pci_iounmap(pdev, solo_dev->reg_base); - if (pdev->irq) - free_irq(pdev->irq, solo_dev); - } - - pci_release_regions(pdev); - pci_disable_device(pdev); - v4l2_device_unregister(&solo_dev->v4l2_dev); - pci_set_drvdata(pdev, NULL); - - kfree(solo_dev); -} - -static ssize_t eeprom_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - unsigned short *p = (unsigned short *)buf; - int i; - - if (count & 0x1) - dev_warn(dev, "EEPROM Write not aligned (truncating)\n"); - - if (!full_eeprom && count > 64) { - dev_warn(dev, "EEPROM Write truncated to 64 bytes\n"); - count = 64; - } else if (full_eeprom && count > 128) { - dev_warn(dev, "EEPROM Write truncated to 128 bytes\n"); - count = 128; - } - - solo_eeprom_ewen(solo_dev, 1); - - for (i = full_eeprom ? 0 : 32; i < min((int)(full_eeprom ? 64 : 32), - (int)(count / 2)); i++) - solo_eeprom_write(solo_dev, i, cpu_to_be16(p[i])); - - solo_eeprom_ewen(solo_dev, 0); - - return count; -} - -static ssize_t eeprom_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - unsigned short *p = (unsigned short *)buf; - int count = (full_eeprom ? 128 : 64); - int i; - - for (i = (full_eeprom ? 0 : 32); i < (count / 2); i++) - p[i] = be16_to_cpu(solo_eeprom_read(solo_dev, i)); - - return count; -} - -static ssize_t p2m_timeouts_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - - return sprintf(buf, "%d\n", solo_dev->p2m_timeouts); -} - -static ssize_t sdram_size_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - - return sprintf(buf, "%dMegs\n", solo_dev->sdram_size >> 20); -} - -static ssize_t tw28xx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - - return sprintf(buf, "tw2815[%d] tw2864[%d] tw2865[%d]\n", - hweight32(solo_dev->tw2815), - hweight32(solo_dev->tw2864), - hweight32(solo_dev->tw2865)); -} - -static ssize_t input_map_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - unsigned int val; - char *out = buf; - - val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_0); - out += sprintf(out, "Channel 0 => Input %d\n", val & 0x1f); - out += sprintf(out, "Channel 1 => Input %d\n", (val >> 5) & 0x1f); - out += sprintf(out, "Channel 2 => Input %d\n", (val >> 10) & 0x1f); - out += sprintf(out, "Channel 3 => Input %d\n", (val >> 15) & 0x1f); - out += sprintf(out, "Channel 4 => Input %d\n", (val >> 20) & 0x1f); - out += sprintf(out, "Channel 5 => Input %d\n", (val >> 25) & 0x1f); - - val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_1); - out += sprintf(out, "Channel 6 => Input %d\n", val & 0x1f); - out += sprintf(out, "Channel 7 => Input %d\n", (val >> 5) & 0x1f); - out += sprintf(out, "Channel 8 => Input %d\n", (val >> 10) & 0x1f); - out += sprintf(out, "Channel 9 => Input %d\n", (val >> 15) & 0x1f); - out += sprintf(out, "Channel 10 => Input %d\n", (val >> 20) & 0x1f); - out += sprintf(out, "Channel 11 => Input %d\n", (val >> 25) & 0x1f); - - val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_2); - out += sprintf(out, "Channel 12 => Input %d\n", val & 0x1f); - out += sprintf(out, "Channel 13 => Input %d\n", (val >> 5) & 0x1f); - out += sprintf(out, "Channel 14 => Input %d\n", (val >> 10) & 0x1f); - out += sprintf(out, "Channel 15 => Input %d\n", (val >> 15) & 0x1f); - out += sprintf(out, "Spot Output => Input %d\n", (val >> 20) & 0x1f); - - return out - buf; -} - -static ssize_t p2m_timeout_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - unsigned long ms; - - int ret = kstrtoul(buf, 10, &ms); - if (ret < 0 || ms > 200) - return -EINVAL; - solo_dev->p2m_jiffies = msecs_to_jiffies(ms); - - return count; -} - -static ssize_t p2m_timeout_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - - return sprintf(buf, "%ums\n", jiffies_to_msecs(solo_dev->p2m_jiffies)); -} - -static ssize_t intervals_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - char *out = buf; - int fps = solo_dev->fps; - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - out += sprintf(out, "Channel %d: %d/%d (0x%08x)\n", - i, solo_dev->v4l2_enc[i]->interval, fps, - solo_reg_read(solo_dev, SOLO_CAP_CH_INTV(i))); - } - - return out - buf; -} - -static ssize_t sdram_offsets_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - char *out = buf; - - out += sprintf(out, "DISP: 0x%08x @ 0x%08x\n", - SOLO_DISP_EXT_ADDR, - SOLO_DISP_EXT_SIZE); - - out += sprintf(out, "EOSD: 0x%08x @ 0x%08x (0x%08x * %d)\n", - SOLO_EOSD_EXT_ADDR, - SOLO_EOSD_EXT_AREA(solo_dev), - SOLO_EOSD_EXT_SIZE(solo_dev), - SOLO_EOSD_EXT_AREA(solo_dev) / - SOLO_EOSD_EXT_SIZE(solo_dev)); - - out += sprintf(out, "MOTI: 0x%08x @ 0x%08x\n", - SOLO_MOTION_EXT_ADDR(solo_dev), - SOLO_MOTION_EXT_SIZE); - - out += sprintf(out, "G723: 0x%08x @ 0x%08x\n", - SOLO_G723_EXT_ADDR(solo_dev), - SOLO_G723_EXT_SIZE); - - out += sprintf(out, "CAPT: 0x%08x @ 0x%08x (0x%08x * %d)\n", - SOLO_CAP_EXT_ADDR(solo_dev), - SOLO_CAP_EXT_SIZE(solo_dev), - SOLO_CAP_PAGE_SIZE, - SOLO_CAP_EXT_SIZE(solo_dev) / SOLO_CAP_PAGE_SIZE); - - out += sprintf(out, "EREF: 0x%08x @ 0x%08x (0x%08x * %d)\n", - SOLO_EREF_EXT_ADDR(solo_dev), - SOLO_EREF_EXT_AREA(solo_dev), - SOLO_EREF_EXT_SIZE, - SOLO_EREF_EXT_AREA(solo_dev) / SOLO_EREF_EXT_SIZE); - - out += sprintf(out, "MPEG: 0x%08x @ 0x%08x\n", - SOLO_MP4E_EXT_ADDR(solo_dev), - SOLO_MP4E_EXT_SIZE(solo_dev)); - - out += sprintf(out, "JPEG: 0x%08x @ 0x%08x\n", - SOLO_JPEG_EXT_ADDR(solo_dev), - SOLO_JPEG_EXT_SIZE(solo_dev)); - - return out - buf; -} - -static ssize_t sdram_show(struct file *file, struct kobject *kobj, - struct bin_attribute *a, char *buf, - loff_t off, size_t count) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - const int size = solo_dev->sdram_size; - - if (off >= size) - return 0; - - if (off + count > size) - count = size - off; - - if (solo_p2m_dma(solo_dev, 0, buf, off, count, 0, 0)) - return -EIO; - - return count; -} - -static const struct device_attribute solo_dev_attrs[] = { - __ATTR(eeprom, 0640, eeprom_show, eeprom_store), - __ATTR(p2m_timeout, 0644, p2m_timeout_show, p2m_timeout_store), - __ATTR_RO(p2m_timeouts), - __ATTR_RO(sdram_size), - __ATTR_RO(tw28xx), - __ATTR_RO(input_map), - __ATTR_RO(intervals), - __ATTR_RO(sdram_offsets), -}; - -static void solo_device_release(struct device *dev) -{ - /* Do nothing */ -} - -static int solo_sysfs_init(struct solo_dev *solo_dev) -{ - struct bin_attribute *sdram_attr = &solo_dev->sdram_attr; - struct device *dev = &solo_dev->dev; - const char *driver; - int i; - - if (solo_dev->type == SOLO_DEV_6110) - driver = "solo6110"; - else - driver = "solo6010"; - - dev->release = solo_device_release; - dev->parent = &solo_dev->pdev->dev; - set_dev_node(dev, dev_to_node(&solo_dev->pdev->dev)); - dev_set_name(dev, "%s-%d-%d", driver, solo_dev->vfd->num, - solo_dev->nr_chans); - - if (device_register(dev)) { - dev->parent = NULL; - return -ENOMEM; - } - - for (i = 0; i < ARRAY_SIZE(solo_dev_attrs); i++) { - if (device_create_file(dev, &solo_dev_attrs[i])) { - device_unregister(dev); - return -ENOMEM; - } - } - - sysfs_attr_init(&sdram_attr->attr); - sdram_attr->attr.name = "sdram"; - sdram_attr->attr.mode = 0440; - sdram_attr->read = sdram_show; - sdram_attr->size = solo_dev->sdram_size; - - if (device_create_bin_file(dev, sdram_attr)) { - device_unregister(dev); - return -ENOMEM; - } - - return 0; -} - -static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct solo_dev *solo_dev; - int ret; - u8 chip_id; - - solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); - if (solo_dev == NULL) - return -ENOMEM; - - if (id->driver_data == SOLO_DEV_6010) - dev_info(&pdev->dev, "Probing Softlogic 6010\n"); - else - dev_info(&pdev->dev, "Probing Softlogic 6110\n"); - - solo_dev->type = id->driver_data; - solo_dev->pdev = pdev; - spin_lock_init(&solo_dev->reg_io_lock); - ret = v4l2_device_register(&pdev->dev, &solo_dev->v4l2_dev); - if (ret) - goto fail_probe; - - /* Only for during init */ - solo_dev->p2m_jiffies = msecs_to_jiffies(100); - - ret = pci_enable_device(pdev); - if (ret) - goto fail_probe; - - pci_set_master(pdev); - - /* RETRY/TRDY Timeout disabled */ - pci_write_config_byte(pdev, 0x40, 0x00); - pci_write_config_byte(pdev, 0x41, 0x00); - - ret = pci_request_regions(pdev, SOLO6X10_NAME); - if (ret) - goto fail_probe; - - solo_dev->reg_base = pci_ioremap_bar(pdev, 0); - if (solo_dev->reg_base == NULL) { - ret = -ENOMEM; - goto fail_probe; - } - - chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & - SOLO_CHIP_ID_MASK; - switch (chip_id) { - case 7: - solo_dev->nr_chans = 16; - solo_dev->nr_ext = 5; - break; - case 6: - solo_dev->nr_chans = 8; - solo_dev->nr_ext = 2; - break; - default: - dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n", - chip_id); - case 5: - solo_dev->nr_chans = 4; - solo_dev->nr_ext = 1; - } - - /* Disable all interrupts to start */ - solo_irq_off(solo_dev, ~0); - - /* Initial global settings */ - if (solo_dev->type == SOLO_DEV_6010) { - solo_dev->clock_mhz = 108; - solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT - | SOLO_SYS_CFG_INPUTDIV(25) - | SOLO_SYS_CFG_FEEDBACKDIV(solo_dev->clock_mhz * 2 - 2) - | SOLO_SYS_CFG_OUTDIV(3); - solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); - } else { - u32 divq, divf; - - solo_dev->clock_mhz = 135; - - if (solo_dev->clock_mhz < 125) { - divq = 3; - divf = (solo_dev->clock_mhz * 4) / 3 - 1; - } else { - divq = 2; - divf = (solo_dev->clock_mhz * 2) / 3 - 1; - } - - solo_reg_write(solo_dev, SOLO_PLL_CONFIG, - (1 << 20) | /* PLL_RANGE */ - (8 << 15) | /* PLL_DIVR */ - (divq << 12) | - (divf << 4) | - (1 << 1) /* PLL_FSEN */); - - solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT; - } - - solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); - solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, - solo_dev->clock_mhz - 1); - - /* PLL locking time of 1ms */ - mdelay(1); - - ret = request_irq(pdev->irq, solo_isr, IRQF_SHARED, SOLO6X10_NAME, - solo_dev); - if (ret) - goto fail_probe; - - /* Handle this from the start */ - solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); - - ret = solo_i2c_init(solo_dev); - if (ret) - goto fail_probe; - - /* Setup the DMA engine */ - solo_reg_write(solo_dev, SOLO_DMA_CTRL, - SOLO_DMA_CTRL_REFRESH_CYCLE(1) | - SOLO_DMA_CTRL_SDRAM_SIZE(2) | - SOLO_DMA_CTRL_SDRAM_CLK_INVERT | - SOLO_DMA_CTRL_READ_CLK_SELECT | - SOLO_DMA_CTRL_LATENCY(1)); - - /* Undocumented crap */ - solo_reg_write(solo_dev, SOLO_DMA_CTRL1, - solo_dev->type == SOLO_DEV_6010 ? 0x100 : 0x300); - - if (solo_dev->type != SOLO_DEV_6010) { - solo_dev->usec_lsb = 0x3f; - solo_set_time(solo_dev); - } - - /* Disable watchdog */ - solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); - - /* Initialize sub components */ - - ret = solo_p2m_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_disp_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_gpio_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_tw28_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_v4l2_init(solo_dev, video_nr); - if (ret) - goto fail_probe; - - ret = solo_enc_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_enc_v4l2_init(solo_dev, video_nr); - if (ret) - goto fail_probe; - - ret = solo_g723_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_sysfs_init(solo_dev); - if (ret) - goto fail_probe; - - /* Now that init is over, set this lower */ - solo_dev->p2m_jiffies = msecs_to_jiffies(20); - - return 0; - -fail_probe: - free_solo_dev(solo_dev); - return ret; -} - -static void solo_pci_remove(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); - struct solo_dev *solo_dev = container_of(v4l2_dev, struct solo_dev, v4l2_dev); - - free_solo_dev(solo_dev); -} - -static DEFINE_PCI_DEVICE_TABLE(solo_id_table) = { - /* 6010 based cards */ - { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16), - .driver_data = SOLO_DEV_6010 }, - /* 6110 based cards */ - { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), - .driver_data = SOLO_DEV_6110 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4), - .driver_data = SOLO_DEV_6110 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8), - .driver_data = SOLO_DEV_6110 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16), - .driver_data = SOLO_DEV_6110 }, - {0,} -}; - -MODULE_DEVICE_TABLE(pci, solo_id_table); - -static struct pci_driver solo_pci_driver = { - .name = SOLO6X10_NAME, - .id_table = solo_id_table, - .probe = solo_pci_probe, - .remove = solo_pci_remove, -}; - -module_pci_driver(solo_pci_driver); diff --git a/drivers/staging/media/solo6x10/disp.c b/drivers/staging/media/solo6x10/disp.c deleted file mode 100644 index 78070c843f90..000000000000 --- a/drivers/staging/media/solo6x10/disp.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include "solo6x10.h" - -#define SOLO_VCLK_DELAY 3 -#define SOLO_PROGRESSIVE_VSIZE 1024 - -#define SOLO_MOT_THRESH_W 64 -#define SOLO_MOT_THRESH_H 64 -#define SOLO_MOT_THRESH_SIZE 8192 -#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) -#define SOLO_MOT_FLAG_SIZE 1024 -#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16) - -static void solo_vin_config(struct solo_dev *solo_dev) -{ - solo_dev->vin_hstart = 8; - solo_dev->vin_vstart = 2; - - solo_reg_write(solo_dev, SOLO_SYS_VCLK, - SOLO_VCLK_SELECT(2) | - SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_I_P, - SOLO_VI_H_START(solo_dev->vin_hstart) | - SOLO_VI_V_START(solo_dev->vin_vstart) | - SOLO_VI_V_STOP(solo_dev->vin_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_I_S, - SOLO_VI_H_START(solo_dev->vout_hstart) | - SOLO_VI_V_START(solo_dev->vout_vstart) | - SOLO_VI_V_STOP(solo_dev->vout_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_P, - SOLO_VI_H_START(0) | - SOLO_VI_V_START(1) | - SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE)); - - solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, - SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); - - /* On 6110, initialize mozaic darkness stength */ - if (solo_dev->type == SOLO_DEV_6010) - solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); - else - solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 16 << 22); - - solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2); - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, - SOLO_VI_PB_USER_MODE); - solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, - SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246)); - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, - SOLO_VI_PB_VSTART(4) | - SOLO_VI_PB_VSTOP(4 + 240)); - } else { - solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, - SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL); - solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, - SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294)); - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, - SOLO_VI_PB_VSTART(4) | - SOLO_VI_PB_VSTOP(4 + 288)); - } - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) | - SOLO_VI_PB_HSTOP(16 + 720)); -} - -static void solo_vout_config_cursor(struct solo_dev *dev) -{ - int i; - - /* Load (blank) cursor bitmap mask (2bpp) */ - for (i = 0; i < 20; i++) - solo_reg_write(dev, SOLO_VO_CURSOR_MASK(i), 0); - - solo_reg_write(dev, SOLO_VO_CURSOR_POS, 0); - - solo_reg_write(dev, SOLO_VO_CURSOR_CLR, - (0x80 << 24) | (0x80 << 16) | (0x10 << 8) | 0x80); - solo_reg_write(dev, SOLO_VO_CURSOR_CLR2, (0xe0 << 8) | 0x80); -} - -static void solo_vout_config(struct solo_dev *solo_dev) -{ - solo_dev->vout_hstart = 6; - solo_dev->vout_vstart = 8; - - solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, - solo_dev->video_type | - SOLO_VO_USER_COLOR_SET_NAV | - SOLO_VO_USER_COLOR_SET_NAH | - SOLO_VO_NA_COLOR_Y(0) | - SOLO_VO_NA_COLOR_CB(0) | - SOLO_VO_NA_COLOR_CR(0)); - - solo_reg_write(solo_dev, SOLO_VO_ACT_H, - SOLO_VO_H_START(solo_dev->vout_hstart) | - SOLO_VO_H_STOP(solo_dev->vout_hstart + - solo_dev->video_hsize)); - - solo_reg_write(solo_dev, SOLO_VO_ACT_V, - SOLO_VO_V_START(solo_dev->vout_vstart) | - SOLO_VO_V_STOP(solo_dev->vout_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VO_RANGE_HV, - SOLO_VO_H_LEN(solo_dev->video_hsize) | - SOLO_VO_V_LEN(solo_dev->video_vsize)); - - /* Border & background colors */ - solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, - (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); - solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, - (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); - solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, - (16 << 24) | (128 << 16) | (16 << 8) | 128); - - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); - - solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 0); - - solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); - solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); - - solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | - SOLO_VO_DISP_ERASE_COUNT(8) | - SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); - - - solo_vout_config_cursor(solo_dev); - - /* Enable channels we support */ - solo_reg_write(solo_dev, SOLO_VI_CH_ENA, - (1 << solo_dev->nr_chans) - 1); -} - -static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, - u16 val, int reg_size) -{ - u16 buf[64]; - int i; - int ret = 0; - - for (i = 0; i < sizeof(buf) >> 1; i++) - buf[i] = cpu_to_le16(val); - - for (i = 0; i < reg_size; i += sizeof(buf)) - ret |= solo_p2m_dma(solo_dev, 1, buf, - SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, - sizeof(buf), 0, 0); - - return ret; -} - -int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) -{ - if (ch > solo_dev->nr_chans) - return -EINVAL; - - return solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + - (ch * SOLO_MOT_THRESH_SIZE * 2), - val, SOLO_MOT_THRESH_SIZE); -} - -int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, - const struct solo_motion_thresholds *thresholds) -{ - u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; - u16 buf[SOLO_MOTION_SZ]; - int x, y; - int ret = 0; - - for (y = 0; y < SOLO_MOTION_SZ; y++) { - for (x = 0; x < SOLO_MOTION_SZ; x++) - buf[x] = cpu_to_le16(thresholds->thresholds[y][x]); - ret |= solo_p2m_dma(solo_dev, 1, buf, - SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * sizeof(buf), - sizeof(buf), 0, 0); - } - return ret; -} - -/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k - * threshold and working table for each channel. Atleast that's what the - * spec says. However, this code (taken from rdk) has some mystery 8k - * block right after the flag area, before the first thresh table. */ -static void solo_motion_config(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - /* Clear motion flag area */ - solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000, - SOLO_MOT_FLAG_SIZE); - - /* Clear working cache table */ - solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + - (i * SOLO_MOT_THRESH_SIZE * 2) + - SOLO_MOT_THRESH_SIZE, 0x0000, - SOLO_MOT_THRESH_SIZE); - - /* Set default threshold table */ - solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); - } - - /* Default motion settings */ - solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) | - (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); - solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, - SOLO_VI_MOTION_FRAME_COUNT(3) | - SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) - /* | SOLO_VI_MOTION_INTR_START_STOP */ - | SOLO_VI_MOTION_SAMPLE_COUNT(10)); - - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); -} - -int solo_disp_init(struct solo_dev *solo_dev) -{ - int i; - - solo_dev->video_hsize = 704; - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - solo_dev->video_vsize = 240; - solo_dev->fps = 30; - } else { - solo_dev->video_vsize = 288; - solo_dev->fps = 25; - } - - solo_vin_config(solo_dev); - solo_motion_config(solo_dev); - solo_vout_config(solo_dev); - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); - - return 0; -} - -void solo_disp_exit(struct solo_dev *solo_dev) -{ - int i; - - solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0); - solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); - solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), 0); - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), 0); - solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 0); - } - - /* Set default border */ - for (i = 0; i < 5; i++) - solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), 0); - - for (i = 0; i < 5; i++) - solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), 0); - - solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, 0); - solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, 0); - - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), 0); - - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), 0); -} diff --git a/drivers/staging/media/solo6x10/eeprom.c b/drivers/staging/media/solo6x10/eeprom.c deleted file mode 100644 index 9d1c9bb53d6b..000000000000 --- a/drivers/staging/media/solo6x10/eeprom.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include - -#include "solo6x10.h" - -/* Control */ -#define EE_SHIFT_CLK 0x04 -#define EE_CS 0x08 -#define EE_DATA_WRITE 0x02 -#define EE_DATA_READ 0x01 -#define EE_ENB (0x80 | EE_CS) - -#define eeprom_delay() udelay(100) -#if 0 -#define eeprom_delay() solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) -#define eeprom_delay() ({ \ - int i, ret; \ - udelay(100); \ - for (i = ret = 0; i < 1000 && !ret; i++) \ - ret = solo_eeprom_reg_read(solo_dev); \ -}) -#endif -#define ADDR_LEN 6 - -/* Commands */ -#define EE_EWEN_CMD 4 -#define EE_EWDS_CMD 4 -#define EE_WRITE_CMD 5 -#define EE_READ_CMD 6 -#define EE_ERASE_CMD 7 - -static unsigned int solo_eeprom_reg_read(struct solo_dev *solo_dev) -{ - return solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) & EE_DATA_READ; -} - -static void solo_eeprom_reg_write(struct solo_dev *solo_dev, u32 data) -{ - solo_reg_write(solo_dev, SOLO_EEPROM_CTRL, data); - eeprom_delay(); -} - -static void solo_eeprom_cmd(struct solo_dev *solo_dev, int cmd) -{ - int i; - - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ACCESS_EN); - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); - - for (i = 4 + ADDR_LEN; i >= 0; i--) { - int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0; - - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | dataval); - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | - EE_SHIFT_CLK | dataval); - } - - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); -} - -unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en) -{ - int ewen_cmd = (w_en ? 0x3f : 0) | (EE_EWEN_CMD << ADDR_LEN); - unsigned int retval = 0; - int i; - - solo_eeprom_cmd(solo_dev, ewen_cmd); - - for (i = 0; i < 16; i++) { - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | - EE_SHIFT_CLK); - retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); - retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); - } - - solo_eeprom_reg_write(solo_dev, ~EE_CS); - retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); - - return retval; -} - -unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc) -{ - int read_cmd = loc | (EE_READ_CMD << ADDR_LEN); - unsigned short retval = 0; - int i; - - solo_eeprom_cmd(solo_dev, read_cmd); - - for (i = 0; i < 16; i++) { - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | - EE_SHIFT_CLK); - retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); - } - - solo_eeprom_reg_write(solo_dev, ~EE_CS); - - return retval; -} - -int solo_eeprom_write(struct solo_dev *solo_dev, int loc, - unsigned short data) -{ - int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN); - unsigned int retval; - int i; - - solo_eeprom_cmd(solo_dev, write_cmd); - - for (i = 15; i >= 0; i--) { - unsigned int dataval = (data >> i) & 1; - - solo_eeprom_reg_write(solo_dev, EE_ENB); - solo_eeprom_reg_write(solo_dev, - EE_ENB | (dataval << 1) | EE_SHIFT_CLK); - } - - solo_eeprom_reg_write(solo_dev, EE_ENB); - solo_eeprom_reg_write(solo_dev, ~EE_CS); - solo_eeprom_reg_write(solo_dev, EE_ENB); - - for (i = retval = 0; i < 10000 && !retval; i++) - retval = solo_eeprom_reg_read(solo_dev); - - solo_eeprom_reg_write(solo_dev, ~EE_CS); - - return !retval; -} diff --git a/drivers/staging/media/solo6x10/enc.c b/drivers/staging/media/solo6x10/enc.c deleted file mode 100644 index 94d5735abf85..000000000000 --- a/drivers/staging/media/solo6x10/enc.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include "solo6x10.h" - -#define VI_PROG_HSIZE (1280 - 16) -#define VI_PROG_VSIZE (1024 - 16) - -#define IRQ_LEVEL 2 - -static void solo_capture_config(struct solo_dev *solo_dev) -{ - unsigned long height; - unsigned long width; - void *buf; - int i; - - solo_reg_write(solo_dev, SOLO_CAP_BASE, - SOLO_CAP_MAX_PAGE((SOLO_CAP_EXT_SIZE(solo_dev) - - SOLO_CAP_PAGE_SIZE) >> 16) - | SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); - - /* XXX: Undocumented bits at b17 and b24 */ - if (solo_dev->type == SOLO_DEV_6110) { - /* NOTE: Ref driver has (62 << 24) here as well, but it causes - * wacked out frame timing on 4-port 6110. */ - solo_reg_write(solo_dev, SOLO_CAP_BTW, - (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | - SOLO_CAP_MAX_BANDWIDTH(36)); - } else { - solo_reg_write(solo_dev, SOLO_CAP_BTW, - (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | - SOLO_CAP_MAX_BANDWIDTH(32)); - } - - /* Set scale 1, 9 dimension */ - width = solo_dev->video_hsize; - height = solo_dev->video_vsize; - solo_reg_write(solo_dev, SOLO_DIM_SCALE1, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 2, 10 dimension */ - width = solo_dev->video_hsize / 2; - height = solo_dev->video_vsize; - solo_reg_write(solo_dev, SOLO_DIM_SCALE2, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 3, 11 dimension */ - width = solo_dev->video_hsize / 2; - height = solo_dev->video_vsize / 2; - solo_reg_write(solo_dev, SOLO_DIM_SCALE3, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 4, 12 dimension */ - width = solo_dev->video_hsize / 3; - height = solo_dev->video_vsize / 3; - solo_reg_write(solo_dev, SOLO_DIM_SCALE4, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 5, 13 dimension */ - width = solo_dev->video_hsize / 4; - height = solo_dev->video_vsize / 2; - solo_reg_write(solo_dev, SOLO_DIM_SCALE5, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Progressive */ - width = VI_PROG_HSIZE; - height = VI_PROG_VSIZE; - solo_reg_write(solo_dev, SOLO_DIM_PROG, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 16) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Clear OSD */ - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, 0); - solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16); - solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, - 0xF0 << 16 | 0x80 << 8 | 0x80); - - if (solo_dev->type == SOLO_DEV_6010) - solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, - SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); - else - solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, SOLO_VE_OSD_V_DOUBLE - | SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); - - /* Clear OSG buffer */ - buf = kzalloc(SOLO_EOSD_EXT_SIZE(solo_dev), GFP_KERNEL); - if (!buf) - return; - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_p2m_dma(solo_dev, 1, buf, - SOLO_EOSD_EXT_ADDR + - (SOLO_EOSD_EXT_SIZE(solo_dev) * i), - SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0); - } - kfree(buf); -} - -/* Should be called with enable_lock held */ -int solo_osd_print(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - unsigned char *str = solo_enc->osd_text; - u8 *buf = solo_enc->osd_buf; - u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); - const struct font_desc *vga = find_font("VGA8x16"); - const unsigned char *vga_data; - int len; - int i, j; - - if (WARN_ON_ONCE(!vga)) - return -ENODEV; - - len = strlen(str); - - if (len == 0) { - /* Disable OSD on this channel */ - reg &= ~(1 << solo_enc->ch); - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); - return 0; - } - - memset(buf, 0, SOLO_EOSD_EXT_SIZE_MAX); - vga_data = (const unsigned char *)vga->data; - - for (i = 0; i < len; i++) { - unsigned char c = str[i]; - - for (j = 0; j < 16; j++) { - buf[(j * 2) + (i % 2) + (i / 2 * 32)] = - bitrev8(vga_data[(c * 16) + j]); - } - } - - solo_p2m_dma(solo_dev, 1, buf, - SOLO_EOSD_EXT_ADDR + - (solo_enc->ch * SOLO_EOSD_EXT_SIZE(solo_dev)), - SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0); - - /* Enable OSD on this channel */ - reg |= (1 << solo_enc->ch); - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); - - return 0; -} - -/** - * Set channel Quality Profile (0-3). - */ -void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, - unsigned int qp) -{ - unsigned long flags; - unsigned int idx, reg; - - if ((ch > 31) || (qp > 3)) - return; - - if (solo_dev->type == SOLO_DEV_6010) - return; - - if (ch < 16) { - idx = 0; - reg = SOLO_VE_JPEG_QP_CH_L; - } else { - ch -= 16; - idx = 1; - reg = SOLO_VE_JPEG_QP_CH_H; - } - ch *= 2; - - spin_lock_irqsave(&solo_dev->jpeg_qp_lock, flags); - - solo_dev->jpeg_qp[idx] &= ~(3 << ch); - solo_dev->jpeg_qp[idx] |= (qp & 3) << ch; - - solo_reg_write(solo_dev, reg, solo_dev->jpeg_qp[idx]); - - spin_unlock_irqrestore(&solo_dev->jpeg_qp_lock, flags); -} - -int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch) -{ - int idx; - - if (solo_dev->type == SOLO_DEV_6010) - return 2; - - if (WARN_ON_ONCE(ch > 31)) - return 2; - - if (ch < 16) { - idx = 0; - } else { - ch -= 16; - idx = 1; - } - ch *= 2; - - return (solo_dev->jpeg_qp[idx] >> ch) & 3; -} - -#define SOLO_QP_INIT 0xaaaaaaaa - -static void solo_jpeg_config(struct solo_dev *solo_dev) -{ - if (solo_dev->type == SOLO_DEV_6010) { - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, - (2 << 24) | (2 << 16) | (2 << 8) | 2); - } else { - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, - (4 << 24) | (3 << 16) | (2 << 8) | 1); - } - - spin_lock_init(&solo_dev->jpeg_qp_lock); - - /* Initialize Quality Profile for all channels */ - solo_dev->jpeg_qp[0] = solo_dev->jpeg_qp[1] = SOLO_QP_INIT; - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, SOLO_QP_INIT); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, SOLO_QP_INIT); - - solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG, - (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | - ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); - solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff); - if (solo_dev->type == SOLO_DEV_6110) { - solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG1, - (0 << 16) | (30 << 8) | 60); - } -} - -static void solo_mp4e_config(struct solo_dev *solo_dev) -{ - int i; - u32 cfg; - - solo_reg_write(solo_dev, SOLO_VE_CFG0, - SOLO_VE_INTR_CTRL(IRQ_LEVEL) | - SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) | - SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16)); - - - cfg = SOLO_VE_BYTE_ALIGN(2) | SOLO_VE_INSERT_INDEX - | SOLO_VE_MOTION_MODE(0); - if (solo_dev->type != SOLO_DEV_6010) { - cfg |= SOLO_VE_MPEG_SIZE_H( - (SOLO_MP4E_EXT_SIZE(solo_dev) >> 24) & 0x0f); - cfg |= SOLO_VE_JPEG_SIZE_H( - (SOLO_JPEG_EXT_SIZE(solo_dev) >> 24) & 0x0f); - } - solo_reg_write(solo_dev, SOLO_VE_CFG1, cfg); - - solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, 0); - solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0); - solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 0); - if (solo_dev->type == SOLO_DEV_6110) - solo_reg_write(solo_dev, SOLO_VE_WMRK_ENABLE, 0); - solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0); - solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0); - - solo_reg_write(solo_dev, SOLO_VE_ATTR, - SOLO_VE_LITTLE_ENDIAN | - SOLO_COMP_ATTR_FCODE(1) | - SOLO_COMP_TIME_INC(0) | - SOLO_COMP_TIME_WIDTH(15) | - SOLO_DCT_INTERVAL(solo_dev->type == SOLO_DEV_6010 ? 9 : 10)); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i), - (SOLO_EREF_EXT_ADDR(solo_dev) + - (i * SOLO_EREF_EXT_SIZE)) >> 16); - solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE_E(i), - (SOLO_EREF_EXT_ADDR(solo_dev) + - ((i + 16) * SOLO_EREF_EXT_SIZE)) >> 16); - } - - if (solo_dev->type == SOLO_DEV_6110) { - solo_reg_write(solo_dev, SOLO_VE_COMPT_MOT, 0x00040008); - } else { - for (i = 0; i < solo_dev->nr_chans; i++) - solo_reg_write(solo_dev, SOLO_VE_CH_MOT(i), 0x100); - } -} - -int solo_enc_init(struct solo_dev *solo_dev) -{ - int i; - - solo_capture_config(solo_dev); - solo_mp4e_config(solo_dev); - solo_jpeg_config(solo_dev); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); - } - - return 0; -} - -void solo_enc_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); - } -} diff --git a/drivers/staging/media/solo6x10/g723.c b/drivers/staging/media/solo6x10/g723.c deleted file mode 100644 index 1db18c7972a0..000000000000 --- a/drivers/staging/media/solo6x10/g723.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "solo6x10.h" -#include "solo6x10-tw28.h" - -#define G723_FDMA_PAGES 32 -#define G723_PERIOD_BYTES 48 -#define G723_PERIOD_BLOCK 1024 -#define G723_FRAMES_PER_PAGE 48 - -/* Sets up channels 16-19 for decoding and 0-15 for encoding */ -#define OUTMODE_MASK 0x300 - -#define SAMPLERATE 8000 -#define BITRATE 25 - -/* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page - * is broken down to 20 * 48 byte regions (one for each channel possible) - * with the rest of the page being dummy data. */ -#define G723_MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) -#define G723_INTR_ORDER 4 /* 0 - 4 */ -#define PERIODS_MIN (1 << G723_INTR_ORDER) -#define PERIODS_MAX G723_FDMA_PAGES - -struct solo_snd_pcm { - int on; - spinlock_t lock; - struct solo_dev *solo_dev; - unsigned char *g723_buf; - dma_addr_t g723_dma; -}; - -static void solo_g723_config(struct solo_dev *solo_dev) -{ - int clk_div; - - clk_div = (solo_dev->clock_mhz * 1000000) - / (SAMPLERATE * (BITRATE * 2) * 2); - - solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE, - SOLO_AUDIO_BITRATE(BITRATE) - | SOLO_AUDIO_CLK_DIV(clk_div)); - - solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, - SOLO_AUDIO_FDMA_INTERVAL(1) - | SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) - | SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); - - solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, - SOLO_AUDIO_ENABLE - | SOLO_AUDIO_I2S_MODE - | SOLO_AUDIO_I2S_MULTI(3) - | SOLO_AUDIO_MODE(OUTMODE_MASK)); -} - -void solo_g723_isr(struct solo_dev *solo_dev) -{ - struct snd_pcm_str *pstr = - &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; - struct snd_pcm_substream *ss; - struct solo_snd_pcm *solo_pcm; - - for (ss = pstr->substream; ss != NULL; ss = ss->next) { - if (snd_pcm_substream_chip(ss) == NULL) - continue; - - /* This means open() hasn't been called on this one */ - if (snd_pcm_substream_chip(ss) == solo_dev) - continue; - - /* Haven't triggered a start yet */ - solo_pcm = snd_pcm_substream_chip(ss); - if (!solo_pcm->on) - continue; - - snd_pcm_period_elapsed(ss); - } -} - -static int snd_solo_hw_params(struct snd_pcm_substream *ss, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); -} - -static int snd_solo_hw_free(struct snd_pcm_substream *ss) -{ - return snd_pcm_lib_free_pages(ss); -} - -static const struct snd_pcm_hardware snd_solo_pcm_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_U8, - .rates = SNDRV_PCM_RATE_8000, - .rate_min = SAMPLERATE, - .rate_max = SAMPLERATE, - .channels_min = 1, - .channels_max = 1, - .buffer_bytes_max = G723_MAX_BUFFER, - .period_bytes_min = G723_PERIOD_BYTES, - .period_bytes_max = G723_PERIOD_BYTES, - .periods_min = PERIODS_MIN, - .periods_max = PERIODS_MAX, -}; - -static int snd_solo_pcm_open(struct snd_pcm_substream *ss) -{ - struct solo_dev *solo_dev = snd_pcm_substream_chip(ss); - struct solo_snd_pcm *solo_pcm; - - solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); - if (solo_pcm == NULL) - goto oom; - - solo_pcm->g723_buf = pci_alloc_consistent(solo_dev->pdev, - G723_PERIOD_BYTES, - &solo_pcm->g723_dma); - if (solo_pcm->g723_buf == NULL) - goto oom; - - spin_lock_init(&solo_pcm->lock); - solo_pcm->solo_dev = solo_dev; - ss->runtime->hw = snd_solo_pcm_hw; - - snd_pcm_substream_chip(ss) = solo_pcm; - - return 0; - -oom: - kfree(solo_pcm); - return -ENOMEM; -} - -static int snd_solo_pcm_close(struct snd_pcm_substream *ss) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - - snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; - pci_free_consistent(solo_pcm->solo_dev->pdev, G723_PERIOD_BYTES, - solo_pcm->g723_buf, solo_pcm->g723_dma); - kfree(solo_pcm); - - return 0; -} - -static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo_dev *solo_dev = solo_pcm->solo_dev; - int ret = 0; - - spin_lock(&solo_pcm->lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (solo_pcm->on == 0) { - /* If this is the first user, switch on interrupts */ - if (atomic_inc_return(&solo_dev->snd_users) == 1) - solo_irq_on(solo_dev, SOLO_IRQ_G723); - solo_pcm->on = 1; - } - break; - case SNDRV_PCM_TRIGGER_STOP: - if (solo_pcm->on) { - /* If this was our last user, switch them off */ - if (atomic_dec_return(&solo_dev->snd_users) == 0) - solo_irq_off(solo_dev, SOLO_IRQ_G723); - solo_pcm->on = 0; - } - break; - default: - ret = -EINVAL; - } - - spin_unlock(&solo_pcm->lock); - - return ret; -} - -static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss) -{ - return 0; -} - -static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo_dev *solo_dev = solo_pcm->solo_dev; - snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f; - - return idx * G723_FRAMES_PER_PAGE; -} - -static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, - snd_pcm_uframes_t pos, void __user *dst, - snd_pcm_uframes_t count) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo_dev *solo_dev = solo_pcm->solo_dev; - int err, i; - - for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { - int page = (pos / G723_FRAMES_PER_PAGE) + i; - - err = solo_p2m_dma_t(solo_dev, 0, solo_pcm->g723_dma, - SOLO_G723_EXT_ADDR(solo_dev) + - (page * G723_PERIOD_BLOCK) + - (ss->number * G723_PERIOD_BYTES), - G723_PERIOD_BYTES, 0, 0); - if (err) - return err; - - err = copy_to_user(dst + (i * G723_PERIOD_BYTES), - solo_pcm->g723_buf, G723_PERIOD_BYTES); - - if (err) - return -EFAULT; - } - - return 0; -} - -static struct snd_pcm_ops snd_solo_pcm_ops = { - .open = snd_solo_pcm_open, - .close = snd_solo_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_solo_hw_params, - .hw_free = snd_solo_hw_free, - .prepare = snd_solo_pcm_prepare, - .trigger = snd_solo_pcm_trigger, - .pointer = snd_solo_pcm_pointer, - .copy = snd_solo_pcm_copy, -}; - -static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *info) -{ - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 1; - info->value.integer.min = 0; - info->value.integer.max = 15; - info->value.integer.step = 1; - - return 0; -} - -static int snd_solo_capture_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); - u8 ch = value->id.numid - 1; - - value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch); - - return 0; -} - -static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); - u8 ch = value->id.numid - 1; - u8 old_val; - - old_val = tw28_get_audio_gain(solo_dev, ch); - if (old_val == value->value.integer.value[0]) - return 0; - - tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]); - - return 1; -} - -static struct snd_kcontrol_new snd_solo_capture_volume = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Volume", - .info = snd_solo_capture_volume_info, - .get = snd_solo_capture_volume_get, - .put = snd_solo_capture_volume_put, -}; - -static int solo_snd_pcm_init(struct solo_dev *solo_dev) -{ - struct snd_card *card = solo_dev->snd_card; - struct snd_pcm *pcm; - struct snd_pcm_substream *ss; - int ret; - int i; - - ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans, - &pcm); - if (ret < 0) - return ret; - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_solo_pcm_ops); - - snd_pcm_chip(pcm) = solo_dev; - pcm->info_flags = 0; - strcpy(pcm->name, card->shortname); - - for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - ss; ss = ss->next, i++) - sprintf(ss->name, "Camera #%d Audio", i); - - ret = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - G723_MAX_BUFFER, G723_MAX_BUFFER); - if (ret < 0) - return ret; - - solo_dev->snd_pcm = pcm; - - return 0; -} - -int solo_g723_init(struct solo_dev *solo_dev) -{ - static struct snd_device_ops ops = { NULL }; - struct snd_card *card; - struct snd_kcontrol_new kctl; - char name[32]; - int ret; - - atomic_set(&solo_dev->snd_users, 0); - - /* Allows for easier mapping between video and audio */ - sprintf(name, "Softlogic%d", solo_dev->vfd->num); - - ret = snd_card_create(SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, - &solo_dev->snd_card); - if (ret < 0) - return ret; - - card = solo_dev->snd_card; - - strcpy(card->driver, SOLO6X10_NAME); - strcpy(card->shortname, "SOLO-6x10 Audio"); - sprintf(card->longname, "%s on %s IRQ %d", card->shortname, - pci_name(solo_dev->pdev), solo_dev->pdev->irq); - snd_card_set_dev(card, &solo_dev->pdev->dev); - - ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops); - if (ret < 0) - goto snd_error; - - /* Mixer controls */ - strcpy(card->mixername, "SOLO-6x10"); - kctl = snd_solo_capture_volume; - kctl.count = solo_dev->nr_chans; - - ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); - if (ret < 0) - return ret; - - ret = solo_snd_pcm_init(solo_dev); - if (ret < 0) - goto snd_error; - - ret = snd_card_register(card); - if (ret < 0) - goto snd_error; - - solo_g723_config(solo_dev); - - dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name); - - return 0; - -snd_error: - snd_card_free(card); - return ret; -} - -void solo_g723_exit(struct solo_dev *solo_dev) -{ - if (!solo_dev->snd_card) - return; - - solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0); - solo_irq_off(solo_dev, SOLO_IRQ_G723); - - snd_card_free(solo_dev->snd_card); - solo_dev->snd_card = NULL; -} diff --git a/drivers/staging/media/solo6x10/gpio.c b/drivers/staging/media/solo6x10/gpio.c deleted file mode 100644 index 73276dc92875..000000000000 --- a/drivers/staging/media/solo6x10/gpio.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include "solo6x10.h" - -static void solo_gpio_mode(struct solo_dev *solo_dev, - unsigned int port_mask, unsigned int mode) -{ - int port; - unsigned int ret; - - ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0); - - /* To set gpio */ - for (port = 0; port < 16; port++) { - if (!((1 << port) & port_mask)) - continue; - - ret &= (~(3 << (port << 1))); - ret |= ((mode & 3) << (port << 1)); - } - - solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret); - - /* To set extended gpio - sensor */ - ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1); - - for (port = 0; port < 16; port++) { - if (!((1 << (port + 16)) & port_mask)) - continue; - - if (!mode) - ret &= ~(1 << port); - else - ret |= 1 << port; - } - - solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret); -} - -static void solo_gpio_set(struct solo_dev *solo_dev, unsigned int value) -{ - solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, - solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value); -} - -static void solo_gpio_clear(struct solo_dev *solo_dev, unsigned int value) -{ - solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, - solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value); -} - -static void solo_gpio_config(struct solo_dev *solo_dev) -{ - /* Video reset */ - solo_gpio_mode(solo_dev, 0x30, 1); - solo_gpio_clear(solo_dev, 0x30); - udelay(100); - solo_gpio_set(solo_dev, 0x30); - udelay(100); - - /* Warning: Don't touch the next line unless you're sure of what - * you're doing: first four gpio [0-3] are used for video. */ - solo_gpio_mode(solo_dev, 0x0f, 2); - - /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */ - solo_gpio_mode(solo_dev, 0xff00, 1); - - /* Initially set relay status to 0 */ - solo_gpio_clear(solo_dev, 0xff00); -} - -int solo_gpio_init(struct solo_dev *solo_dev) -{ - solo_gpio_config(solo_dev); - return 0; -} - -void solo_gpio_exit(struct solo_dev *solo_dev) -{ - solo_gpio_clear(solo_dev, 0x30); - solo_gpio_config(solo_dev); -} diff --git a/drivers/staging/media/solo6x10/i2c.c b/drivers/staging/media/solo6x10/i2c.c deleted file mode 100644 index 01aa417c9258..000000000000 --- a/drivers/staging/media/solo6x10/i2c.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c - * channel. The bus can only handle one i2c event at a time. The below handles - * this all wrong. We should be using the status registers to see if the bus - * is in use, and have a global lock to check the status register. Also, - * the bulk of the work should be handled out-of-interrupt. The ugly loops - * that occur during interrupt scare me. The ISR should merely signal - * thread context, ACK the interrupt, and move on. -- BenC */ - -#include - -#include "solo6x10.h" - -u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) -{ - struct i2c_msg msgs[2]; - u8 data; - - msgs[0].flags = 0; - msgs[0].addr = addr; - msgs[0].len = 1; - msgs[0].buf = &off; - - msgs[1].flags = I2C_M_RD; - msgs[1].addr = addr; - msgs[1].len = 1; - msgs[1].buf = &data; - - i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); - - return data; -} - -void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, - u8 off, u8 data) -{ - struct i2c_msg msgs; - u8 buf[2]; - - buf[0] = off; - buf[1] = data; - msgs.flags = 0; - msgs.addr = addr; - msgs.len = 2; - msgs.buf = buf; - - i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); -} - -static void solo_i2c_flush(struct solo_dev *solo_dev, int wr) -{ - u32 ctrl; - - ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); - - if (solo_dev->i2c_state == IIC_STATE_START) - ctrl |= SOLO_IIC_START; - - if (wr) { - ctrl |= SOLO_IIC_WRITE; - } else { - ctrl |= SOLO_IIC_READ; - if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) - ctrl |= SOLO_IIC_ACK_EN; - } - - if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) - ctrl |= SOLO_IIC_STOP; - - solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); -} - -static void solo_i2c_start(struct solo_dev *solo_dev) -{ - u32 addr = solo_dev->i2c_msg->addr << 1; - - if (solo_dev->i2c_msg->flags & I2C_M_RD) - addr |= 1; - - solo_dev->i2c_state = IIC_STATE_START; - solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); - solo_i2c_flush(solo_dev, 1); -} - -static void solo_i2c_stop(struct solo_dev *solo_dev) -{ - solo_irq_off(solo_dev, SOLO_IRQ_IIC); - solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); - solo_dev->i2c_state = IIC_STATE_STOP; - wake_up(&solo_dev->i2c_wait); -} - -static int solo_i2c_handle_read(struct solo_dev *solo_dev) -{ -prepare_read: - if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { - solo_i2c_flush(solo_dev, 0); - return 0; - } - - solo_dev->i2c_msg_ptr = 0; - solo_dev->i2c_msg++; - solo_dev->i2c_msg_num--; - - if (solo_dev->i2c_msg_num == 0) { - solo_i2c_stop(solo_dev); - return 0; - } - - if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { - solo_i2c_start(solo_dev); - } else { - if (solo_dev->i2c_msg->flags & I2C_M_RD) - goto prepare_read; - else - solo_i2c_stop(solo_dev); - } - - return 0; -} - -static int solo_i2c_handle_write(struct solo_dev *solo_dev) -{ -retry_write: - if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { - solo_reg_write(solo_dev, SOLO_IIC_TXD, - solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); - solo_dev->i2c_msg_ptr++; - solo_i2c_flush(solo_dev, 1); - return 0; - } - - solo_dev->i2c_msg_ptr = 0; - solo_dev->i2c_msg++; - solo_dev->i2c_msg_num--; - - if (solo_dev->i2c_msg_num == 0) { - solo_i2c_stop(solo_dev); - return 0; - } - - if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { - solo_i2c_start(solo_dev); - } else { - if (solo_dev->i2c_msg->flags & I2C_M_RD) - solo_i2c_stop(solo_dev); - else - goto retry_write; - } - - return 0; -} - -int solo_i2c_isr(struct solo_dev *solo_dev) -{ - u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); - int ret = -EINVAL; - - - if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) - || solo_dev->i2c_id < 0) { - solo_i2c_stop(solo_dev); - return -ENXIO; - } - - switch (solo_dev->i2c_state) { - case IIC_STATE_START: - if (solo_dev->i2c_msg->flags & I2C_M_RD) { - solo_dev->i2c_state = IIC_STATE_READ; - ret = solo_i2c_handle_read(solo_dev); - break; - } - - solo_dev->i2c_state = IIC_STATE_WRITE; - case IIC_STATE_WRITE: - ret = solo_i2c_handle_write(solo_dev); - break; - - case IIC_STATE_READ: - solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = - solo_reg_read(solo_dev, SOLO_IIC_RXD); - solo_dev->i2c_msg_ptr++; - - ret = solo_i2c_handle_read(solo_dev); - break; - - default: - solo_i2c_stop(solo_dev); - } - - return ret; -} - -static int solo_i2c_master_xfer(struct i2c_adapter *adap, - struct i2c_msg msgs[], int num) -{ - struct solo_dev *solo_dev = adap->algo_data; - unsigned long timeout; - int ret; - int i; - DEFINE_WAIT(wait); - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (&solo_dev->i2c_adap[i] == adap) - break; - } - - if (i == SOLO_I2C_ADAPTERS) - return num; /* XXX Right return value for failure? */ - - mutex_lock(&solo_dev->i2c_mutex); - solo_dev->i2c_id = i; - solo_dev->i2c_msg = msgs; - solo_dev->i2c_msg_num = num; - solo_dev->i2c_msg_ptr = 0; - - solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); - solo_irq_on(solo_dev, SOLO_IRQ_IIC); - solo_i2c_start(solo_dev); - - timeout = HZ / 2; - - for (;;) { - prepare_to_wait(&solo_dev->i2c_wait, &wait, - TASK_INTERRUPTIBLE); - - if (solo_dev->i2c_state == IIC_STATE_STOP) - break; - - timeout = schedule_timeout(timeout); - if (!timeout) - break; - - if (signal_pending(current)) - break; - } - - finish_wait(&solo_dev->i2c_wait, &wait); - ret = num - solo_dev->i2c_msg_num; - solo_dev->i2c_state = IIC_STATE_IDLE; - solo_dev->i2c_id = -1; - - mutex_unlock(&solo_dev->i2c_mutex); - - return ret; -} - -static u32 solo_i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C; -} - -static const struct i2c_algorithm solo_i2c_algo = { - .master_xfer = solo_i2c_master_xfer, - .functionality = solo_i2c_functionality, -}; - -int solo_i2c_init(struct solo_dev *solo_dev) -{ - int i; - int ret; - - solo_reg_write(solo_dev, SOLO_IIC_CFG, - SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); - - solo_dev->i2c_id = -1; - solo_dev->i2c_state = IIC_STATE_IDLE; - init_waitqueue_head(&solo_dev->i2c_wait); - mutex_init(&solo_dev->i2c_mutex); - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; - - snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", - SOLO6X10_NAME, i); - adap->algo = &solo_i2c_algo; - adap->algo_data = solo_dev; - adap->retries = 1; - adap->dev.parent = &solo_dev->pdev->dev; - - ret = i2c_add_adapter(adap); - if (ret) { - adap->algo_data = NULL; - break; - } - } - - if (ret) { - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (!solo_dev->i2c_adap[i].algo_data) - break; - i2c_del_adapter(&solo_dev->i2c_adap[i]); - solo_dev->i2c_adap[i].algo_data = NULL; - } - return ret; - } - - return 0; -} - -void solo_i2c_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (!solo_dev->i2c_adap[i].algo_data) - continue; - i2c_del_adapter(&solo_dev->i2c_adap[i]); - solo_dev->i2c_adap[i].algo_data = NULL; - } -} diff --git a/drivers/staging/media/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c deleted file mode 100644 index 333594189b81..000000000000 --- a/drivers/staging/media/solo6x10/p2m.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include "solo6x10.h" - -static int multi_p2m; -module_param(multi_p2m, uint, 0644); -MODULE_PARM_DESC(multi_p2m, - "Use multiple P2M DMA channels (default: no, 6010-only)"); - -static int desc_mode; -module_param(desc_mode, uint, 0644); -MODULE_PARM_DESC(desc_mode, - "Allow use of descriptor mode DMA (default: no, 6010-only)"); - -int solo_p2m_dma(struct solo_dev *solo_dev, int wr, - void *sys_addr, u32 ext_addr, u32 size, - int repeat, u32 ext_size) -{ - dma_addr_t dma_addr; - int ret; - - if (WARN_ON_ONCE((unsigned long)sys_addr & 0x03)) - return -EINVAL; - if (WARN_ON_ONCE(!size)) - return -EINVAL; - - dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(solo_dev->pdev, dma_addr)) - return -ENOMEM; - - ret = solo_p2m_dma_t(solo_dev, wr, dma_addr, ext_addr, size, - repeat, ext_size); - - pci_unmap_single(solo_dev->pdev, dma_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - - return ret; -} - -/* Mutex must be held for p2m_id before calling this!! */ -int solo_p2m_dma_desc(struct solo_dev *solo_dev, - struct solo_p2m_desc *desc, dma_addr_t desc_dma, - int desc_cnt) -{ - struct solo_p2m_dev *p2m_dev; - unsigned int timeout; - unsigned int config = 0; - int ret = 0; - int p2m_id = 0; - - /* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */ - if (solo_dev->type != SOLO_DEV_6110 && multi_p2m) { - p2m_id = atomic_inc_return(&solo_dev->p2m_count) % SOLO_NR_P2M; - if (p2m_id < 0) - p2m_id = -p2m_id; - } - - p2m_dev = &solo_dev->p2m_dev[p2m_id]; - - if (mutex_lock_interruptible(&p2m_dev->mutex)) - return -EINTR; - - INIT_COMPLETION(p2m_dev->completion); - p2m_dev->error = 0; - - if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && desc_mode) { - /* For 6010 with more than one desc, we can do a one-shot */ - p2m_dev->desc_count = p2m_dev->desc_idx = 0; - config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(p2m_id)); - - solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(p2m_id), desc_dma); - solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(p2m_id), desc_cnt); - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config | - SOLO_P2M_DESC_MODE); - } else { - /* For single descriptors and 6110, we need to run each desc */ - p2m_dev->desc_count = desc_cnt; - p2m_dev->desc_idx = 1; - p2m_dev->descs = desc; - - solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(p2m_id), - desc[1].dma_addr); - solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(p2m_id), - desc[1].ext_addr); - solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(p2m_id), - desc[1].cfg); - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), - desc[1].ctrl); - } - - timeout = wait_for_completion_timeout(&p2m_dev->completion, - solo_dev->p2m_jiffies); - - if (WARN_ON_ONCE(p2m_dev->error)) - ret = -EIO; - else if (timeout == 0) { - solo_dev->p2m_timeouts++; - ret = -EAGAIN; - } - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), 0); - - /* Don't write here for the no_desc_mode case, because config is 0. - * We can't test no_desc_mode again, it might race. */ - if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && config) - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config); - - mutex_unlock(&p2m_dev->mutex); - - return ret; -} - -void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size, - int repeat, u32 ext_size) -{ - WARN_ON_ONCE(dma_addr & 0x03); - WARN_ON_ONCE(!size); - - desc->cfg = SOLO_P2M_COPY_SIZE(size >> 2); - desc->ctrl = SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | - (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON; - - if (repeat) { - desc->cfg |= SOLO_P2M_EXT_INC(ext_size >> 2); - desc->ctrl |= SOLO_P2M_PCI_INC(size >> 2) | - SOLO_P2M_REPEAT(repeat); - } - - desc->dma_addr = dma_addr; - desc->ext_addr = ext_addr; -} - -int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size, - int repeat, u32 ext_size) -{ - struct solo_p2m_desc desc[2]; - - solo_p2m_fill_desc(&desc[1], wr, dma_addr, ext_addr, size, repeat, - ext_size); - - /* No need for desc_dma since we know it is a single-shot */ - return solo_p2m_dma_desc(solo_dev, desc, 0, 1); -} - -void solo_p2m_isr(struct solo_dev *solo_dev, int id) -{ - struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id]; - struct solo_p2m_desc *desc; - - if (p2m_dev->desc_count <= p2m_dev->desc_idx) { - complete(&p2m_dev->completion); - return; - } - - /* Setup next descriptor */ - p2m_dev->desc_idx++; - desc = &p2m_dev->descs[p2m_dev->desc_idx]; - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); - solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), desc->dma_addr); - solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), desc->ext_addr); - solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), desc->cfg); - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), desc->ctrl); -} - -void solo_p2m_error_isr(struct solo_dev *solo_dev) -{ - unsigned int err = solo_reg_read(solo_dev, SOLO_PCI_ERR); - struct solo_p2m_dev *p2m_dev; - int i; - - if (!(err & (SOLO_PCI_ERR_P2M | SOLO_PCI_ERR_P2M_DESC))) - return; - - for (i = 0; i < SOLO_NR_P2M; i++) { - p2m_dev = &solo_dev->p2m_dev[i]; - p2m_dev->error = 1; - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); - complete(&p2m_dev->completion); - } -} - -void solo_p2m_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < SOLO_NR_P2M; i++) - solo_irq_off(solo_dev, SOLO_IRQ_P2M(i)); -} - -static int solo_p2m_test(struct solo_dev *solo_dev, int base, int size) -{ - u32 *wr_buf; - u32 *rd_buf; - int i; - int ret = -EIO; - int order = get_order(size); - - wr_buf = (u32 *)__get_free_pages(GFP_KERNEL, order); - if (wr_buf == NULL) - return -1; - - rd_buf = (u32 *)__get_free_pages(GFP_KERNEL, order); - if (rd_buf == NULL) { - free_pages((unsigned long)wr_buf, order); - return -1; - } - - for (i = 0; i < (size >> 3); i++) - *(wr_buf + i) = (i << 16) | (i + 1); - - for (i = (size >> 3); i < (size >> 2); i++) - *(wr_buf + i) = ~((i << 16) | (i + 1)); - - memset(rd_buf, 0x55, size); - - if (solo_p2m_dma(solo_dev, 1, wr_buf, base, size, 0, 0)) - goto test_fail; - - if (solo_p2m_dma(solo_dev, 0, rd_buf, base, size, 0, 0)) - goto test_fail; - - for (i = 0; i < (size >> 2); i++) { - if (*(wr_buf + i) != *(rd_buf + i)) - goto test_fail; - } - - ret = 0; - -test_fail: - free_pages((unsigned long)wr_buf, order); - free_pages((unsigned long)rd_buf, order); - - return ret; -} - -int solo_p2m_init(struct solo_dev *solo_dev) -{ - struct solo_p2m_dev *p2m_dev; - int i; - - for (i = 0; i < SOLO_NR_P2M; i++) { - p2m_dev = &solo_dev->p2m_dev[i]; - - mutex_init(&p2m_dev->mutex); - init_completion(&p2m_dev->completion); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), - SOLO_P2M_CSC_16BIT_565 | - SOLO_P2M_DESC_INTR_OPT | - SOLO_P2M_DMA_INTERVAL(0) | - SOLO_P2M_PCI_MASTER_MODE); - solo_irq_on(solo_dev, SOLO_IRQ_P2M(i)); - } - - /* Find correct SDRAM size */ - for (solo_dev->sdram_size = 0, i = 2; i >= 0; i--) { - solo_reg_write(solo_dev, SOLO_DMA_CTRL, - SOLO_DMA_CTRL_REFRESH_CYCLE(1) | - SOLO_DMA_CTRL_SDRAM_SIZE(i) | - SOLO_DMA_CTRL_SDRAM_CLK_INVERT | - SOLO_DMA_CTRL_READ_CLK_SELECT | - SOLO_DMA_CTRL_LATENCY(1)); - - solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config | - SOLO_SYS_CFG_RESET); - solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); - - switch (i) { - case 2: - if (solo_p2m_test(solo_dev, 0x07ff0000, 0x00010000) || - solo_p2m_test(solo_dev, 0x05ff0000, 0x00010000)) - continue; - break; - - case 1: - if (solo_p2m_test(solo_dev, 0x03ff0000, 0x00010000)) - continue; - break; - - default: - if (solo_p2m_test(solo_dev, 0x01ff0000, 0x00010000)) - continue; - } - - solo_dev->sdram_size = (32 << 20) << i; - break; - } - - if (!solo_dev->sdram_size) { - dev_err(&solo_dev->pdev->dev, "Error detecting SDRAM size\n"); - return -EIO; - } - - if (SOLO_SDRAM_END(solo_dev) > solo_dev->sdram_size) { - dev_err(&solo_dev->pdev->dev, - "SDRAM is not large enough (%u < %u)\n", - solo_dev->sdram_size, SOLO_SDRAM_END(solo_dev)); - return -EIO; - } - - return 0; -} diff --git a/drivers/staging/media/solo6x10/solo6x10-core.c b/drivers/staging/media/solo6x10/solo6x10-core.c new file mode 100644 index 000000000000..36750205d23f --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-core.c @@ -0,0 +1,709 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "solo6x10.h" +#include "solo6x10-tw28.h" + +MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver"); +MODULE_AUTHOR("Bluecherry "); +MODULE_VERSION(SOLO6X10_VERSION); +MODULE_LICENSE("GPL"); + +unsigned video_nr = -1; +module_param(video_nr, uint, 0644); +MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)"); + +static int full_eeprom; /* default is only top 64B */ +module_param(full_eeprom, uint, 0644); +MODULE_PARM_DESC(full_eeprom, "Allow access to full 128B EEPROM (dangerous)"); + + +static void solo_set_time(struct solo_dev *solo_dev) +{ + struct timespec ts; + + ktime_get_ts(&ts); + + solo_reg_write(solo_dev, SOLO_TIMER_SEC, ts.tv_sec); + solo_reg_write(solo_dev, SOLO_TIMER_USEC, ts.tv_nsec / NSEC_PER_USEC); +} + +static void solo_timer_sync(struct solo_dev *solo_dev) +{ + u32 sec, usec; + struct timespec ts; + long diff; + + if (solo_dev->type != SOLO_DEV_6110) + return; + + if (++solo_dev->time_sync < 60) + return; + + solo_dev->time_sync = 0; + + sec = solo_reg_read(solo_dev, SOLO_TIMER_SEC); + usec = solo_reg_read(solo_dev, SOLO_TIMER_USEC); + + ktime_get_ts(&ts); + + diff = (long)ts.tv_sec - (long)sec; + diff = (diff * 1000000) + + ((long)(ts.tv_nsec / NSEC_PER_USEC) - (long)usec); + + if (diff > 1000 || diff < -1000) { + solo_set_time(solo_dev); + } else if (diff) { + long usec_lsb = solo_dev->usec_lsb; + + usec_lsb -= diff / 4; + if (usec_lsb < 0) + usec_lsb = 0; + else if (usec_lsb > 255) + usec_lsb = 255; + + solo_dev->usec_lsb = usec_lsb; + solo_reg_write(solo_dev, SOLO_TIMER_USEC_LSB, + solo_dev->usec_lsb); + } +} + +static irqreturn_t solo_isr(int irq, void *data) +{ + struct solo_dev *solo_dev = data; + u32 status; + int i; + + status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); + if (!status) + return IRQ_NONE; + + if (status & ~solo_dev->irq_mask) { + solo_reg_write(solo_dev, SOLO_IRQ_STAT, + status & ~solo_dev->irq_mask); + status &= solo_dev->irq_mask; + } + + if (status & SOLO_IRQ_PCI_ERR) + solo_p2m_error_isr(solo_dev); + + for (i = 0; i < SOLO_NR_P2M; i++) + if (status & SOLO_IRQ_P2M(i)) + solo_p2m_isr(solo_dev, i); + + if (status & SOLO_IRQ_IIC) + solo_i2c_isr(solo_dev); + + if (status & SOLO_IRQ_VIDEO_IN) { + solo_video_in_isr(solo_dev); + solo_timer_sync(solo_dev); + } + + if (status & SOLO_IRQ_ENCODER) + solo_enc_v4l2_isr(solo_dev); + + if (status & SOLO_IRQ_G723) + solo_g723_isr(solo_dev); + + /* Clear all interrupts handled */ + solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); + + return IRQ_HANDLED; +} + +static void free_solo_dev(struct solo_dev *solo_dev) +{ + struct pci_dev *pdev; + + if (!solo_dev) + return; + + if (solo_dev->dev.parent) + device_unregister(&solo_dev->dev); + + pdev = solo_dev->pdev; + + /* If we never initialized the PCI device, then nothing else + * below here needs cleanup */ + if (!pdev) { + kfree(solo_dev); + return; + } + + if (solo_dev->reg_base) { + /* Bring down the sub-devices first */ + solo_g723_exit(solo_dev); + solo_enc_v4l2_exit(solo_dev); + solo_enc_exit(solo_dev); + solo_v4l2_exit(solo_dev); + solo_disp_exit(solo_dev); + solo_gpio_exit(solo_dev); + solo_p2m_exit(solo_dev); + solo_i2c_exit(solo_dev); + + /* Now cleanup the PCI device */ + solo_irq_off(solo_dev, ~0); + pci_iounmap(pdev, solo_dev->reg_base); + if (pdev->irq) + free_irq(pdev->irq, solo_dev); + } + + pci_release_regions(pdev); + pci_disable_device(pdev); + v4l2_device_unregister(&solo_dev->v4l2_dev); + pci_set_drvdata(pdev, NULL); + + kfree(solo_dev); +} + +static ssize_t eeprom_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned short *p = (unsigned short *)buf; + int i; + + if (count & 0x1) + dev_warn(dev, "EEPROM Write not aligned (truncating)\n"); + + if (!full_eeprom && count > 64) { + dev_warn(dev, "EEPROM Write truncated to 64 bytes\n"); + count = 64; + } else if (full_eeprom && count > 128) { + dev_warn(dev, "EEPROM Write truncated to 128 bytes\n"); + count = 128; + } + + solo_eeprom_ewen(solo_dev, 1); + + for (i = full_eeprom ? 0 : 32; i < min((int)(full_eeprom ? 64 : 32), + (int)(count / 2)); i++) + solo_eeprom_write(solo_dev, i, cpu_to_be16(p[i])); + + solo_eeprom_ewen(solo_dev, 0); + + return count; +} + +static ssize_t eeprom_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned short *p = (unsigned short *)buf; + int count = (full_eeprom ? 128 : 64); + int i; + + for (i = (full_eeprom ? 0 : 32); i < (count / 2); i++) + p[i] = be16_to_cpu(solo_eeprom_read(solo_dev, i)); + + return count; +} + +static ssize_t p2m_timeouts_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "%d\n", solo_dev->p2m_timeouts); +} + +static ssize_t sdram_size_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "%dMegs\n", solo_dev->sdram_size >> 20); +} + +static ssize_t tw28xx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "tw2815[%d] tw2864[%d] tw2865[%d]\n", + hweight32(solo_dev->tw2815), + hweight32(solo_dev->tw2864), + hweight32(solo_dev->tw2865)); +} + +static ssize_t input_map_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned int val; + char *out = buf; + + val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_0); + out += sprintf(out, "Channel 0 => Input %d\n", val & 0x1f); + out += sprintf(out, "Channel 1 => Input %d\n", (val >> 5) & 0x1f); + out += sprintf(out, "Channel 2 => Input %d\n", (val >> 10) & 0x1f); + out += sprintf(out, "Channel 3 => Input %d\n", (val >> 15) & 0x1f); + out += sprintf(out, "Channel 4 => Input %d\n", (val >> 20) & 0x1f); + out += sprintf(out, "Channel 5 => Input %d\n", (val >> 25) & 0x1f); + + val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_1); + out += sprintf(out, "Channel 6 => Input %d\n", val & 0x1f); + out += sprintf(out, "Channel 7 => Input %d\n", (val >> 5) & 0x1f); + out += sprintf(out, "Channel 8 => Input %d\n", (val >> 10) & 0x1f); + out += sprintf(out, "Channel 9 => Input %d\n", (val >> 15) & 0x1f); + out += sprintf(out, "Channel 10 => Input %d\n", (val >> 20) & 0x1f); + out += sprintf(out, "Channel 11 => Input %d\n", (val >> 25) & 0x1f); + + val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_2); + out += sprintf(out, "Channel 12 => Input %d\n", val & 0x1f); + out += sprintf(out, "Channel 13 => Input %d\n", (val >> 5) & 0x1f); + out += sprintf(out, "Channel 14 => Input %d\n", (val >> 10) & 0x1f); + out += sprintf(out, "Channel 15 => Input %d\n", (val >> 15) & 0x1f); + out += sprintf(out, "Spot Output => Input %d\n", (val >> 20) & 0x1f); + + return out - buf; +} + +static ssize_t p2m_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned long ms; + + int ret = kstrtoul(buf, 10, &ms); + if (ret < 0 || ms > 200) + return -EINVAL; + solo_dev->p2m_jiffies = msecs_to_jiffies(ms); + + return count; +} + +static ssize_t p2m_timeout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "%ums\n", jiffies_to_msecs(solo_dev->p2m_jiffies)); +} + +static ssize_t intervals_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + char *out = buf; + int fps = solo_dev->fps; + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + out += sprintf(out, "Channel %d: %d/%d (0x%08x)\n", + i, solo_dev->v4l2_enc[i]->interval, fps, + solo_reg_read(solo_dev, SOLO_CAP_CH_INTV(i))); + } + + return out - buf; +} + +static ssize_t sdram_offsets_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + char *out = buf; + + out += sprintf(out, "DISP: 0x%08x @ 0x%08x\n", + SOLO_DISP_EXT_ADDR, + SOLO_DISP_EXT_SIZE); + + out += sprintf(out, "EOSD: 0x%08x @ 0x%08x (0x%08x * %d)\n", + SOLO_EOSD_EXT_ADDR, + SOLO_EOSD_EXT_AREA(solo_dev), + SOLO_EOSD_EXT_SIZE(solo_dev), + SOLO_EOSD_EXT_AREA(solo_dev) / + SOLO_EOSD_EXT_SIZE(solo_dev)); + + out += sprintf(out, "MOTI: 0x%08x @ 0x%08x\n", + SOLO_MOTION_EXT_ADDR(solo_dev), + SOLO_MOTION_EXT_SIZE); + + out += sprintf(out, "G723: 0x%08x @ 0x%08x\n", + SOLO_G723_EXT_ADDR(solo_dev), + SOLO_G723_EXT_SIZE); + + out += sprintf(out, "CAPT: 0x%08x @ 0x%08x (0x%08x * %d)\n", + SOLO_CAP_EXT_ADDR(solo_dev), + SOLO_CAP_EXT_SIZE(solo_dev), + SOLO_CAP_PAGE_SIZE, + SOLO_CAP_EXT_SIZE(solo_dev) / SOLO_CAP_PAGE_SIZE); + + out += sprintf(out, "EREF: 0x%08x @ 0x%08x (0x%08x * %d)\n", + SOLO_EREF_EXT_ADDR(solo_dev), + SOLO_EREF_EXT_AREA(solo_dev), + SOLO_EREF_EXT_SIZE, + SOLO_EREF_EXT_AREA(solo_dev) / SOLO_EREF_EXT_SIZE); + + out += sprintf(out, "MPEG: 0x%08x @ 0x%08x\n", + SOLO_MP4E_EXT_ADDR(solo_dev), + SOLO_MP4E_EXT_SIZE(solo_dev)); + + out += sprintf(out, "JPEG: 0x%08x @ 0x%08x\n", + SOLO_JPEG_EXT_ADDR(solo_dev), + SOLO_JPEG_EXT_SIZE(solo_dev)); + + return out - buf; +} + +static ssize_t sdram_show(struct file *file, struct kobject *kobj, + struct bin_attribute *a, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + const int size = solo_dev->sdram_size; + + if (off >= size) + return 0; + + if (off + count > size) + count = size - off; + + if (solo_p2m_dma(solo_dev, 0, buf, off, count, 0, 0)) + return -EIO; + + return count; +} + +static const struct device_attribute solo_dev_attrs[] = { + __ATTR(eeprom, 0640, eeprom_show, eeprom_store), + __ATTR(p2m_timeout, 0644, p2m_timeout_show, p2m_timeout_store), + __ATTR_RO(p2m_timeouts), + __ATTR_RO(sdram_size), + __ATTR_RO(tw28xx), + __ATTR_RO(input_map), + __ATTR_RO(intervals), + __ATTR_RO(sdram_offsets), +}; + +static void solo_device_release(struct device *dev) +{ + /* Do nothing */ +} + +static int solo_sysfs_init(struct solo_dev *solo_dev) +{ + struct bin_attribute *sdram_attr = &solo_dev->sdram_attr; + struct device *dev = &solo_dev->dev; + const char *driver; + int i; + + if (solo_dev->type == SOLO_DEV_6110) + driver = "solo6110"; + else + driver = "solo6010"; + + dev->release = solo_device_release; + dev->parent = &solo_dev->pdev->dev; + set_dev_node(dev, dev_to_node(&solo_dev->pdev->dev)); + dev_set_name(dev, "%s-%d-%d", driver, solo_dev->vfd->num, + solo_dev->nr_chans); + + if (device_register(dev)) { + dev->parent = NULL; + return -ENOMEM; + } + + for (i = 0; i < ARRAY_SIZE(solo_dev_attrs); i++) { + if (device_create_file(dev, &solo_dev_attrs[i])) { + device_unregister(dev); + return -ENOMEM; + } + } + + sysfs_attr_init(&sdram_attr->attr); + sdram_attr->attr.name = "sdram"; + sdram_attr->attr.mode = 0440; + sdram_attr->read = sdram_show; + sdram_attr->size = solo_dev->sdram_size; + + if (device_create_bin_file(dev, sdram_attr)) { + device_unregister(dev); + return -ENOMEM; + } + + return 0; +} + +static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct solo_dev *solo_dev; + int ret; + u8 chip_id; + + solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); + if (solo_dev == NULL) + return -ENOMEM; + + if (id->driver_data == SOLO_DEV_6010) + dev_info(&pdev->dev, "Probing Softlogic 6010\n"); + else + dev_info(&pdev->dev, "Probing Softlogic 6110\n"); + + solo_dev->type = id->driver_data; + solo_dev->pdev = pdev; + spin_lock_init(&solo_dev->reg_io_lock); + ret = v4l2_device_register(&pdev->dev, &solo_dev->v4l2_dev); + if (ret) + goto fail_probe; + + /* Only for during init */ + solo_dev->p2m_jiffies = msecs_to_jiffies(100); + + ret = pci_enable_device(pdev); + if (ret) + goto fail_probe; + + pci_set_master(pdev); + + /* RETRY/TRDY Timeout disabled */ + pci_write_config_byte(pdev, 0x40, 0x00); + pci_write_config_byte(pdev, 0x41, 0x00); + + ret = pci_request_regions(pdev, SOLO6X10_NAME); + if (ret) + goto fail_probe; + + solo_dev->reg_base = pci_ioremap_bar(pdev, 0); + if (solo_dev->reg_base == NULL) { + ret = -ENOMEM; + goto fail_probe; + } + + chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & + SOLO_CHIP_ID_MASK; + switch (chip_id) { + case 7: + solo_dev->nr_chans = 16; + solo_dev->nr_ext = 5; + break; + case 6: + solo_dev->nr_chans = 8; + solo_dev->nr_ext = 2; + break; + default: + dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n", + chip_id); + case 5: + solo_dev->nr_chans = 4; + solo_dev->nr_ext = 1; + } + + /* Disable all interrupts to start */ + solo_irq_off(solo_dev, ~0); + + /* Initial global settings */ + if (solo_dev->type == SOLO_DEV_6010) { + solo_dev->clock_mhz = 108; + solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT + | SOLO_SYS_CFG_INPUTDIV(25) + | SOLO_SYS_CFG_FEEDBACKDIV(solo_dev->clock_mhz * 2 - 2) + | SOLO_SYS_CFG_OUTDIV(3); + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); + } else { + u32 divq, divf; + + solo_dev->clock_mhz = 135; + + if (solo_dev->clock_mhz < 125) { + divq = 3; + divf = (solo_dev->clock_mhz * 4) / 3 - 1; + } else { + divq = 2; + divf = (solo_dev->clock_mhz * 2) / 3 - 1; + } + + solo_reg_write(solo_dev, SOLO_PLL_CONFIG, + (1 << 20) | /* PLL_RANGE */ + (8 << 15) | /* PLL_DIVR */ + (divq << 12) | + (divf << 4) | + (1 << 1) /* PLL_FSEN */); + + solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT; + } + + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); + solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, + solo_dev->clock_mhz - 1); + + /* PLL locking time of 1ms */ + mdelay(1); + + ret = request_irq(pdev->irq, solo_isr, IRQF_SHARED, SOLO6X10_NAME, + solo_dev); + if (ret) + goto fail_probe; + + /* Handle this from the start */ + solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); + + ret = solo_i2c_init(solo_dev); + if (ret) + goto fail_probe; + + /* Setup the DMA engine */ + solo_reg_write(solo_dev, SOLO_DMA_CTRL, + SOLO_DMA_CTRL_REFRESH_CYCLE(1) | + SOLO_DMA_CTRL_SDRAM_SIZE(2) | + SOLO_DMA_CTRL_SDRAM_CLK_INVERT | + SOLO_DMA_CTRL_READ_CLK_SELECT | + SOLO_DMA_CTRL_LATENCY(1)); + + /* Undocumented crap */ + solo_reg_write(solo_dev, SOLO_DMA_CTRL1, + solo_dev->type == SOLO_DEV_6010 ? 0x100 : 0x300); + + if (solo_dev->type != SOLO_DEV_6010) { + solo_dev->usec_lsb = 0x3f; + solo_set_time(solo_dev); + } + + /* Disable watchdog */ + solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); + + /* Initialize sub components */ + + ret = solo_p2m_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_disp_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_gpio_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_tw28_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_v4l2_init(solo_dev, video_nr); + if (ret) + goto fail_probe; + + ret = solo_enc_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_enc_v4l2_init(solo_dev, video_nr); + if (ret) + goto fail_probe; + + ret = solo_g723_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_sysfs_init(solo_dev); + if (ret) + goto fail_probe; + + /* Now that init is over, set this lower */ + solo_dev->p2m_jiffies = msecs_to_jiffies(20); + + return 0; + +fail_probe: + free_solo_dev(solo_dev); + return ret; +} + +static void solo_pci_remove(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct solo_dev *solo_dev = container_of(v4l2_dev, struct solo_dev, v4l2_dev); + + free_solo_dev(solo_dev); +} + +static DEFINE_PCI_DEVICE_TABLE(solo_id_table) = { + /* 6010 based cards */ + { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16), + .driver_data = SOLO_DEV_6010 }, + /* 6110 based cards */ + { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), + .driver_data = SOLO_DEV_6110 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4), + .driver_data = SOLO_DEV_6110 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8), + .driver_data = SOLO_DEV_6110 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16), + .driver_data = SOLO_DEV_6110 }, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, solo_id_table); + +static struct pci_driver solo_pci_driver = { + .name = SOLO6X10_NAME, + .id_table = solo_id_table, + .probe = solo_pci_probe, + .remove = solo_pci_remove, +}; + +module_pci_driver(solo_pci_driver); diff --git a/drivers/staging/media/solo6x10/solo6x10-disp.c b/drivers/staging/media/solo6x10/solo6x10-disp.c new file mode 100644 index 000000000000..78070c843f90 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-disp.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "solo6x10.h" + +#define SOLO_VCLK_DELAY 3 +#define SOLO_PROGRESSIVE_VSIZE 1024 + +#define SOLO_MOT_THRESH_W 64 +#define SOLO_MOT_THRESH_H 64 +#define SOLO_MOT_THRESH_SIZE 8192 +#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) +#define SOLO_MOT_FLAG_SIZE 1024 +#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16) + +static void solo_vin_config(struct solo_dev *solo_dev) +{ + solo_dev->vin_hstart = 8; + solo_dev->vin_vstart = 2; + + solo_reg_write(solo_dev, SOLO_SYS_VCLK, + SOLO_VCLK_SELECT(2) | + SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_I_P, + SOLO_VI_H_START(solo_dev->vin_hstart) | + SOLO_VI_V_START(solo_dev->vin_vstart) | + SOLO_VI_V_STOP(solo_dev->vin_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_I_S, + SOLO_VI_H_START(solo_dev->vout_hstart) | + SOLO_VI_V_START(solo_dev->vout_vstart) | + SOLO_VI_V_STOP(solo_dev->vout_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_P, + SOLO_VI_H_START(0) | + SOLO_VI_V_START(1) | + SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE)); + + solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, + SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); + + /* On 6110, initialize mozaic darkness stength */ + if (solo_dev->type == SOLO_DEV_6010) + solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); + else + solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 16 << 22); + + solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2); + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, + SOLO_VI_PB_USER_MODE); + solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, + SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246)); + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, + SOLO_VI_PB_VSTART(4) | + SOLO_VI_PB_VSTOP(4 + 240)); + } else { + solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, + SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL); + solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, + SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294)); + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, + SOLO_VI_PB_VSTART(4) | + SOLO_VI_PB_VSTOP(4 + 288)); + } + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) | + SOLO_VI_PB_HSTOP(16 + 720)); +} + +static void solo_vout_config_cursor(struct solo_dev *dev) +{ + int i; + + /* Load (blank) cursor bitmap mask (2bpp) */ + for (i = 0; i < 20; i++) + solo_reg_write(dev, SOLO_VO_CURSOR_MASK(i), 0); + + solo_reg_write(dev, SOLO_VO_CURSOR_POS, 0); + + solo_reg_write(dev, SOLO_VO_CURSOR_CLR, + (0x80 << 24) | (0x80 << 16) | (0x10 << 8) | 0x80); + solo_reg_write(dev, SOLO_VO_CURSOR_CLR2, (0xe0 << 8) | 0x80); +} + +static void solo_vout_config(struct solo_dev *solo_dev) +{ + solo_dev->vout_hstart = 6; + solo_dev->vout_vstart = 8; + + solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, + solo_dev->video_type | + SOLO_VO_USER_COLOR_SET_NAV | + SOLO_VO_USER_COLOR_SET_NAH | + SOLO_VO_NA_COLOR_Y(0) | + SOLO_VO_NA_COLOR_CB(0) | + SOLO_VO_NA_COLOR_CR(0)); + + solo_reg_write(solo_dev, SOLO_VO_ACT_H, + SOLO_VO_H_START(solo_dev->vout_hstart) | + SOLO_VO_H_STOP(solo_dev->vout_hstart + + solo_dev->video_hsize)); + + solo_reg_write(solo_dev, SOLO_VO_ACT_V, + SOLO_VO_V_START(solo_dev->vout_vstart) | + SOLO_VO_V_STOP(solo_dev->vout_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VO_RANGE_HV, + SOLO_VO_H_LEN(solo_dev->video_hsize) | + SOLO_VO_V_LEN(solo_dev->video_vsize)); + + /* Border & background colors */ + solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, + (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); + solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, + (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); + solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, + (16 << 24) | (128 << 16) | (16 << 8) | 128); + + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); + + solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 0); + + solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); + + solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | + SOLO_VO_DISP_ERASE_COUNT(8) | + SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); + + + solo_vout_config_cursor(solo_dev); + + /* Enable channels we support */ + solo_reg_write(solo_dev, SOLO_VI_CH_ENA, + (1 << solo_dev->nr_chans) - 1); +} + +static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, + u16 val, int reg_size) +{ + u16 buf[64]; + int i; + int ret = 0; + + for (i = 0; i < sizeof(buf) >> 1; i++) + buf[i] = cpu_to_le16(val); + + for (i = 0; i < reg_size; i += sizeof(buf)) + ret |= solo_p2m_dma(solo_dev, 1, buf, + SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, + sizeof(buf), 0, 0); + + return ret; +} + +int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) +{ + if (ch > solo_dev->nr_chans) + return -EINVAL; + + return solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + + (ch * SOLO_MOT_THRESH_SIZE * 2), + val, SOLO_MOT_THRESH_SIZE); +} + +int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, + const struct solo_motion_thresholds *thresholds) +{ + u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; + u16 buf[SOLO_MOTION_SZ]; + int x, y; + int ret = 0; + + for (y = 0; y < SOLO_MOTION_SZ; y++) { + for (x = 0; x < SOLO_MOTION_SZ; x++) + buf[x] = cpu_to_le16(thresholds->thresholds[y][x]); + ret |= solo_p2m_dma(solo_dev, 1, buf, + SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * sizeof(buf), + sizeof(buf), 0, 0); + } + return ret; +} + +/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k + * threshold and working table for each channel. Atleast that's what the + * spec says. However, this code (taken from rdk) has some mystery 8k + * block right after the flag area, before the first thresh table. */ +static void solo_motion_config(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + /* Clear motion flag area */ + solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000, + SOLO_MOT_FLAG_SIZE); + + /* Clear working cache table */ + solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + + (i * SOLO_MOT_THRESH_SIZE * 2) + + SOLO_MOT_THRESH_SIZE, 0x0000, + SOLO_MOT_THRESH_SIZE); + + /* Set default threshold table */ + solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); + } + + /* Default motion settings */ + solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) | + (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); + solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, + SOLO_VI_MOTION_FRAME_COUNT(3) | + SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) + /* | SOLO_VI_MOTION_INTR_START_STOP */ + | SOLO_VI_MOTION_SAMPLE_COUNT(10)); + + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); +} + +int solo_disp_init(struct solo_dev *solo_dev) +{ + int i; + + solo_dev->video_hsize = 704; + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + solo_dev->video_vsize = 240; + solo_dev->fps = 30; + } else { + solo_dev->video_vsize = 288; + solo_dev->fps = 25; + } + + solo_vin_config(solo_dev); + solo_motion_config(solo_dev); + solo_vout_config(solo_dev); + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); + + return 0; +} + +void solo_disp_exit(struct solo_dev *solo_dev) +{ + int i; + + solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), 0); + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), 0); + solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 0); + } + + /* Set default border */ + for (i = 0; i < 5; i++) + solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), 0); + + for (i = 0; i < 5; i++) + solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), 0); + + solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, 0); + solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, 0); + + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), 0); + + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), 0); +} diff --git a/drivers/staging/media/solo6x10/solo6x10-eeprom.c b/drivers/staging/media/solo6x10/solo6x10-eeprom.c new file mode 100644 index 000000000000..9d1c9bb53d6b --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-eeprom.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "solo6x10.h" + +/* Control */ +#define EE_SHIFT_CLK 0x04 +#define EE_CS 0x08 +#define EE_DATA_WRITE 0x02 +#define EE_DATA_READ 0x01 +#define EE_ENB (0x80 | EE_CS) + +#define eeprom_delay() udelay(100) +#if 0 +#define eeprom_delay() solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) +#define eeprom_delay() ({ \ + int i, ret; \ + udelay(100); \ + for (i = ret = 0; i < 1000 && !ret; i++) \ + ret = solo_eeprom_reg_read(solo_dev); \ +}) +#endif +#define ADDR_LEN 6 + +/* Commands */ +#define EE_EWEN_CMD 4 +#define EE_EWDS_CMD 4 +#define EE_WRITE_CMD 5 +#define EE_READ_CMD 6 +#define EE_ERASE_CMD 7 + +static unsigned int solo_eeprom_reg_read(struct solo_dev *solo_dev) +{ + return solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) & EE_DATA_READ; +} + +static void solo_eeprom_reg_write(struct solo_dev *solo_dev, u32 data) +{ + solo_reg_write(solo_dev, SOLO_EEPROM_CTRL, data); + eeprom_delay(); +} + +static void solo_eeprom_cmd(struct solo_dev *solo_dev, int cmd) +{ + int i; + + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ACCESS_EN); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); + + for (i = 4 + ADDR_LEN; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0; + + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | dataval); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | + EE_SHIFT_CLK | dataval); + } + + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); +} + +unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en) +{ + int ewen_cmd = (w_en ? 0x3f : 0) | (EE_EWEN_CMD << ADDR_LEN); + unsigned int retval = 0; + int i; + + solo_eeprom_cmd(solo_dev, ewen_cmd); + + for (i = 0; i < 16; i++) { + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | + EE_SHIFT_CLK); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + } + + solo_eeprom_reg_write(solo_dev, ~EE_CS); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + + return retval; +} + +unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc) +{ + int read_cmd = loc | (EE_READ_CMD << ADDR_LEN); + unsigned short retval = 0; + int i; + + solo_eeprom_cmd(solo_dev, read_cmd); + + for (i = 0; i < 16; i++) { + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | + EE_SHIFT_CLK); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); + } + + solo_eeprom_reg_write(solo_dev, ~EE_CS); + + return retval; +} + +int solo_eeprom_write(struct solo_dev *solo_dev, int loc, + unsigned short data) +{ + int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN); + unsigned int retval; + int i; + + solo_eeprom_cmd(solo_dev, write_cmd); + + for (i = 15; i >= 0; i--) { + unsigned int dataval = (data >> i) & 1; + + solo_eeprom_reg_write(solo_dev, EE_ENB); + solo_eeprom_reg_write(solo_dev, + EE_ENB | (dataval << 1) | EE_SHIFT_CLK); + } + + solo_eeprom_reg_write(solo_dev, EE_ENB); + solo_eeprom_reg_write(solo_dev, ~EE_CS); + solo_eeprom_reg_write(solo_dev, EE_ENB); + + for (i = retval = 0; i < 10000 && !retval; i++) + retval = solo_eeprom_reg_read(solo_dev); + + solo_eeprom_reg_write(solo_dev, ~EE_CS); + + return !retval; +} diff --git a/drivers/staging/media/solo6x10/solo6x10-enc.c b/drivers/staging/media/solo6x10/solo6x10-enc.c new file mode 100644 index 000000000000..94d5735abf85 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-enc.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "solo6x10.h" + +#define VI_PROG_HSIZE (1280 - 16) +#define VI_PROG_VSIZE (1024 - 16) + +#define IRQ_LEVEL 2 + +static void solo_capture_config(struct solo_dev *solo_dev) +{ + unsigned long height; + unsigned long width; + void *buf; + int i; + + solo_reg_write(solo_dev, SOLO_CAP_BASE, + SOLO_CAP_MAX_PAGE((SOLO_CAP_EXT_SIZE(solo_dev) + - SOLO_CAP_PAGE_SIZE) >> 16) + | SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); + + /* XXX: Undocumented bits at b17 and b24 */ + if (solo_dev->type == SOLO_DEV_6110) { + /* NOTE: Ref driver has (62 << 24) here as well, but it causes + * wacked out frame timing on 4-port 6110. */ + solo_reg_write(solo_dev, SOLO_CAP_BTW, + (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | + SOLO_CAP_MAX_BANDWIDTH(36)); + } else { + solo_reg_write(solo_dev, SOLO_CAP_BTW, + (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | + SOLO_CAP_MAX_BANDWIDTH(32)); + } + + /* Set scale 1, 9 dimension */ + width = solo_dev->video_hsize; + height = solo_dev->video_vsize; + solo_reg_write(solo_dev, SOLO_DIM_SCALE1, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 2, 10 dimension */ + width = solo_dev->video_hsize / 2; + height = solo_dev->video_vsize; + solo_reg_write(solo_dev, SOLO_DIM_SCALE2, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 3, 11 dimension */ + width = solo_dev->video_hsize / 2; + height = solo_dev->video_vsize / 2; + solo_reg_write(solo_dev, SOLO_DIM_SCALE3, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 4, 12 dimension */ + width = solo_dev->video_hsize / 3; + height = solo_dev->video_vsize / 3; + solo_reg_write(solo_dev, SOLO_DIM_SCALE4, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 5, 13 dimension */ + width = solo_dev->video_hsize / 4; + height = solo_dev->video_vsize / 2; + solo_reg_write(solo_dev, SOLO_DIM_SCALE5, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Progressive */ + width = VI_PROG_HSIZE; + height = VI_PROG_VSIZE; + solo_reg_write(solo_dev, SOLO_DIM_PROG, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 16) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Clear OSD */ + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, 0); + solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16); + solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, + 0xF0 << 16 | 0x80 << 8 | 0x80); + + if (solo_dev->type == SOLO_DEV_6010) + solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, + SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); + else + solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, SOLO_VE_OSD_V_DOUBLE + | SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); + + /* Clear OSG buffer */ + buf = kzalloc(SOLO_EOSD_EXT_SIZE(solo_dev), GFP_KERNEL); + if (!buf) + return; + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_p2m_dma(solo_dev, 1, buf, + SOLO_EOSD_EXT_ADDR + + (SOLO_EOSD_EXT_SIZE(solo_dev) * i), + SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0); + } + kfree(buf); +} + +/* Should be called with enable_lock held */ +int solo_osd_print(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + unsigned char *str = solo_enc->osd_text; + u8 *buf = solo_enc->osd_buf; + u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); + const struct font_desc *vga = find_font("VGA8x16"); + const unsigned char *vga_data; + int len; + int i, j; + + if (WARN_ON_ONCE(!vga)) + return -ENODEV; + + len = strlen(str); + + if (len == 0) { + /* Disable OSD on this channel */ + reg &= ~(1 << solo_enc->ch); + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); + return 0; + } + + memset(buf, 0, SOLO_EOSD_EXT_SIZE_MAX); + vga_data = (const unsigned char *)vga->data; + + for (i = 0; i < len; i++) { + unsigned char c = str[i]; + + for (j = 0; j < 16; j++) { + buf[(j * 2) + (i % 2) + (i / 2 * 32)] = + bitrev8(vga_data[(c * 16) + j]); + } + } + + solo_p2m_dma(solo_dev, 1, buf, + SOLO_EOSD_EXT_ADDR + + (solo_enc->ch * SOLO_EOSD_EXT_SIZE(solo_dev)), + SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0); + + /* Enable OSD on this channel */ + reg |= (1 << solo_enc->ch); + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); + + return 0; +} + +/** + * Set channel Quality Profile (0-3). + */ +void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, + unsigned int qp) +{ + unsigned long flags; + unsigned int idx, reg; + + if ((ch > 31) || (qp > 3)) + return; + + if (solo_dev->type == SOLO_DEV_6010) + return; + + if (ch < 16) { + idx = 0; + reg = SOLO_VE_JPEG_QP_CH_L; + } else { + ch -= 16; + idx = 1; + reg = SOLO_VE_JPEG_QP_CH_H; + } + ch *= 2; + + spin_lock_irqsave(&solo_dev->jpeg_qp_lock, flags); + + solo_dev->jpeg_qp[idx] &= ~(3 << ch); + solo_dev->jpeg_qp[idx] |= (qp & 3) << ch; + + solo_reg_write(solo_dev, reg, solo_dev->jpeg_qp[idx]); + + spin_unlock_irqrestore(&solo_dev->jpeg_qp_lock, flags); +} + +int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch) +{ + int idx; + + if (solo_dev->type == SOLO_DEV_6010) + return 2; + + if (WARN_ON_ONCE(ch > 31)) + return 2; + + if (ch < 16) { + idx = 0; + } else { + ch -= 16; + idx = 1; + } + ch *= 2; + + return (solo_dev->jpeg_qp[idx] >> ch) & 3; +} + +#define SOLO_QP_INIT 0xaaaaaaaa + +static void solo_jpeg_config(struct solo_dev *solo_dev) +{ + if (solo_dev->type == SOLO_DEV_6010) { + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, + (2 << 24) | (2 << 16) | (2 << 8) | 2); + } else { + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, + (4 << 24) | (3 << 16) | (2 << 8) | 1); + } + + spin_lock_init(&solo_dev->jpeg_qp_lock); + + /* Initialize Quality Profile for all channels */ + solo_dev->jpeg_qp[0] = solo_dev->jpeg_qp[1] = SOLO_QP_INIT; + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, SOLO_QP_INIT); + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, SOLO_QP_INIT); + + solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG, + (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | + ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); + solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff); + if (solo_dev->type == SOLO_DEV_6110) { + solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG1, + (0 << 16) | (30 << 8) | 60); + } +} + +static void solo_mp4e_config(struct solo_dev *solo_dev) +{ + int i; + u32 cfg; + + solo_reg_write(solo_dev, SOLO_VE_CFG0, + SOLO_VE_INTR_CTRL(IRQ_LEVEL) | + SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) | + SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16)); + + + cfg = SOLO_VE_BYTE_ALIGN(2) | SOLO_VE_INSERT_INDEX + | SOLO_VE_MOTION_MODE(0); + if (solo_dev->type != SOLO_DEV_6010) { + cfg |= SOLO_VE_MPEG_SIZE_H( + (SOLO_MP4E_EXT_SIZE(solo_dev) >> 24) & 0x0f); + cfg |= SOLO_VE_JPEG_SIZE_H( + (SOLO_JPEG_EXT_SIZE(solo_dev) >> 24) & 0x0f); + } + solo_reg_write(solo_dev, SOLO_VE_CFG1, cfg); + + solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, 0); + solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0); + solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 0); + if (solo_dev->type == SOLO_DEV_6110) + solo_reg_write(solo_dev, SOLO_VE_WMRK_ENABLE, 0); + solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0); + solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0); + + solo_reg_write(solo_dev, SOLO_VE_ATTR, + SOLO_VE_LITTLE_ENDIAN | + SOLO_COMP_ATTR_FCODE(1) | + SOLO_COMP_TIME_INC(0) | + SOLO_COMP_TIME_WIDTH(15) | + SOLO_DCT_INTERVAL(solo_dev->type == SOLO_DEV_6010 ? 9 : 10)); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i), + (SOLO_EREF_EXT_ADDR(solo_dev) + + (i * SOLO_EREF_EXT_SIZE)) >> 16); + solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE_E(i), + (SOLO_EREF_EXT_ADDR(solo_dev) + + ((i + 16) * SOLO_EREF_EXT_SIZE)) >> 16); + } + + if (solo_dev->type == SOLO_DEV_6110) { + solo_reg_write(solo_dev, SOLO_VE_COMPT_MOT, 0x00040008); + } else { + for (i = 0; i < solo_dev->nr_chans; i++) + solo_reg_write(solo_dev, SOLO_VE_CH_MOT(i), 0x100); + } +} + +int solo_enc_init(struct solo_dev *solo_dev) +{ + int i; + + solo_capture_config(solo_dev); + solo_mp4e_config(solo_dev); + solo_jpeg_config(solo_dev); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); + } + + return 0; +} + +void solo_enc_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); + } +} diff --git a/drivers/staging/media/solo6x10/solo6x10-g723.c b/drivers/staging/media/solo6x10/solo6x10-g723.c new file mode 100644 index 000000000000..1db18c7972a0 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-g723.c @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "solo6x10.h" +#include "solo6x10-tw28.h" + +#define G723_FDMA_PAGES 32 +#define G723_PERIOD_BYTES 48 +#define G723_PERIOD_BLOCK 1024 +#define G723_FRAMES_PER_PAGE 48 + +/* Sets up channels 16-19 for decoding and 0-15 for encoding */ +#define OUTMODE_MASK 0x300 + +#define SAMPLERATE 8000 +#define BITRATE 25 + +/* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page + * is broken down to 20 * 48 byte regions (one for each channel possible) + * with the rest of the page being dummy data. */ +#define G723_MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) +#define G723_INTR_ORDER 4 /* 0 - 4 */ +#define PERIODS_MIN (1 << G723_INTR_ORDER) +#define PERIODS_MAX G723_FDMA_PAGES + +struct solo_snd_pcm { + int on; + spinlock_t lock; + struct solo_dev *solo_dev; + unsigned char *g723_buf; + dma_addr_t g723_dma; +}; + +static void solo_g723_config(struct solo_dev *solo_dev) +{ + int clk_div; + + clk_div = (solo_dev->clock_mhz * 1000000) + / (SAMPLERATE * (BITRATE * 2) * 2); + + solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE, + SOLO_AUDIO_BITRATE(BITRATE) + | SOLO_AUDIO_CLK_DIV(clk_div)); + + solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, + SOLO_AUDIO_FDMA_INTERVAL(1) + | SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) + | SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); + + solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, + SOLO_AUDIO_ENABLE + | SOLO_AUDIO_I2S_MODE + | SOLO_AUDIO_I2S_MULTI(3) + | SOLO_AUDIO_MODE(OUTMODE_MASK)); +} + +void solo_g723_isr(struct solo_dev *solo_dev) +{ + struct snd_pcm_str *pstr = + &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; + struct snd_pcm_substream *ss; + struct solo_snd_pcm *solo_pcm; + + for (ss = pstr->substream; ss != NULL; ss = ss->next) { + if (snd_pcm_substream_chip(ss) == NULL) + continue; + + /* This means open() hasn't been called on this one */ + if (snd_pcm_substream_chip(ss) == solo_dev) + continue; + + /* Haven't triggered a start yet */ + solo_pcm = snd_pcm_substream_chip(ss); + if (!solo_pcm->on) + continue; + + snd_pcm_period_elapsed(ss); + } +} + +static int snd_solo_hw_params(struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); +} + +static int snd_solo_hw_free(struct snd_pcm_substream *ss) +{ + return snd_pcm_lib_free_pages(ss); +} + +static const struct snd_pcm_hardware snd_solo_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8, + .rates = SNDRV_PCM_RATE_8000, + .rate_min = SAMPLERATE, + .rate_max = SAMPLERATE, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = G723_MAX_BUFFER, + .period_bytes_min = G723_PERIOD_BYTES, + .period_bytes_max = G723_PERIOD_BYTES, + .periods_min = PERIODS_MIN, + .periods_max = PERIODS_MAX, +}; + +static int snd_solo_pcm_open(struct snd_pcm_substream *ss) +{ + struct solo_dev *solo_dev = snd_pcm_substream_chip(ss); + struct solo_snd_pcm *solo_pcm; + + solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); + if (solo_pcm == NULL) + goto oom; + + solo_pcm->g723_buf = pci_alloc_consistent(solo_dev->pdev, + G723_PERIOD_BYTES, + &solo_pcm->g723_dma); + if (solo_pcm->g723_buf == NULL) + goto oom; + + spin_lock_init(&solo_pcm->lock); + solo_pcm->solo_dev = solo_dev; + ss->runtime->hw = snd_solo_pcm_hw; + + snd_pcm_substream_chip(ss) = solo_pcm; + + return 0; + +oom: + kfree(solo_pcm); + return -ENOMEM; +} + +static int snd_solo_pcm_close(struct snd_pcm_substream *ss) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + + snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; + pci_free_consistent(solo_pcm->solo_dev->pdev, G723_PERIOD_BYTES, + solo_pcm->g723_buf, solo_pcm->g723_dma); + kfree(solo_pcm); + + return 0; +} + +static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo_dev *solo_dev = solo_pcm->solo_dev; + int ret = 0; + + spin_lock(&solo_pcm->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (solo_pcm->on == 0) { + /* If this is the first user, switch on interrupts */ + if (atomic_inc_return(&solo_dev->snd_users) == 1) + solo_irq_on(solo_dev, SOLO_IRQ_G723); + solo_pcm->on = 1; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (solo_pcm->on) { + /* If this was our last user, switch them off */ + if (atomic_dec_return(&solo_dev->snd_users) == 0) + solo_irq_off(solo_dev, SOLO_IRQ_G723); + solo_pcm->on = 0; + } + break; + default: + ret = -EINVAL; + } + + spin_unlock(&solo_pcm->lock); + + return ret; +} + +static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss) +{ + return 0; +} + +static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo_dev *solo_dev = solo_pcm->solo_dev; + snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f; + + return idx * G723_FRAMES_PER_PAGE; +} + +static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, + snd_pcm_uframes_t pos, void __user *dst, + snd_pcm_uframes_t count) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo_dev *solo_dev = solo_pcm->solo_dev; + int err, i; + + for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { + int page = (pos / G723_FRAMES_PER_PAGE) + i; + + err = solo_p2m_dma_t(solo_dev, 0, solo_pcm->g723_dma, + SOLO_G723_EXT_ADDR(solo_dev) + + (page * G723_PERIOD_BLOCK) + + (ss->number * G723_PERIOD_BYTES), + G723_PERIOD_BYTES, 0, 0); + if (err) + return err; + + err = copy_to_user(dst + (i * G723_PERIOD_BYTES), + solo_pcm->g723_buf, G723_PERIOD_BYTES); + + if (err) + return -EFAULT; + } + + return 0; +} + +static struct snd_pcm_ops snd_solo_pcm_ops = { + .open = snd_solo_pcm_open, + .close = snd_solo_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_solo_hw_params, + .hw_free = snd_solo_hw_free, + .prepare = snd_solo_pcm_prepare, + .trigger = snd_solo_pcm_trigger, + .pointer = snd_solo_pcm_pointer, + .copy = snd_solo_pcm_copy, +}; + +static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 1; + info->value.integer.min = 0; + info->value.integer.max = 15; + info->value.integer.step = 1; + + return 0; +} + +static int snd_solo_capture_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); + u8 ch = value->id.numid - 1; + + value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch); + + return 0; +} + +static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); + u8 ch = value->id.numid - 1; + u8 old_val; + + old_val = tw28_get_audio_gain(solo_dev, ch); + if (old_val == value->value.integer.value[0]) + return 0; + + tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]); + + return 1; +} + +static struct snd_kcontrol_new snd_solo_capture_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .info = snd_solo_capture_volume_info, + .get = snd_solo_capture_volume_get, + .put = snd_solo_capture_volume_put, +}; + +static int solo_snd_pcm_init(struct solo_dev *solo_dev) +{ + struct snd_card *card = solo_dev->snd_card; + struct snd_pcm *pcm; + struct snd_pcm_substream *ss; + int ret; + int i; + + ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans, + &pcm); + if (ret < 0) + return ret; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_solo_pcm_ops); + + snd_pcm_chip(pcm) = solo_dev; + pcm->info_flags = 0; + strcpy(pcm->name, card->shortname); + + for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + ss; ss = ss->next, i++) + sprintf(ss->name, "Camera #%d Audio", i); + + ret = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + G723_MAX_BUFFER, G723_MAX_BUFFER); + if (ret < 0) + return ret; + + solo_dev->snd_pcm = pcm; + + return 0; +} + +int solo_g723_init(struct solo_dev *solo_dev) +{ + static struct snd_device_ops ops = { NULL }; + struct snd_card *card; + struct snd_kcontrol_new kctl; + char name[32]; + int ret; + + atomic_set(&solo_dev->snd_users, 0); + + /* Allows for easier mapping between video and audio */ + sprintf(name, "Softlogic%d", solo_dev->vfd->num); + + ret = snd_card_create(SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, + &solo_dev->snd_card); + if (ret < 0) + return ret; + + card = solo_dev->snd_card; + + strcpy(card->driver, SOLO6X10_NAME); + strcpy(card->shortname, "SOLO-6x10 Audio"); + sprintf(card->longname, "%s on %s IRQ %d", card->shortname, + pci_name(solo_dev->pdev), solo_dev->pdev->irq); + snd_card_set_dev(card, &solo_dev->pdev->dev); + + ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops); + if (ret < 0) + goto snd_error; + + /* Mixer controls */ + strcpy(card->mixername, "SOLO-6x10"); + kctl = snd_solo_capture_volume; + kctl.count = solo_dev->nr_chans; + + ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); + if (ret < 0) + return ret; + + ret = solo_snd_pcm_init(solo_dev); + if (ret < 0) + goto snd_error; + + ret = snd_card_register(card); + if (ret < 0) + goto snd_error; + + solo_g723_config(solo_dev); + + dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name); + + return 0; + +snd_error: + snd_card_free(card); + return ret; +} + +void solo_g723_exit(struct solo_dev *solo_dev) +{ + if (!solo_dev->snd_card) + return; + + solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0); + solo_irq_off(solo_dev, SOLO_IRQ_G723); + + snd_card_free(solo_dev->snd_card); + solo_dev->snd_card = NULL; +} diff --git a/drivers/staging/media/solo6x10/solo6x10-gpio.c b/drivers/staging/media/solo6x10/solo6x10-gpio.c new file mode 100644 index 000000000000..73276dc92875 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-gpio.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "solo6x10.h" + +static void solo_gpio_mode(struct solo_dev *solo_dev, + unsigned int port_mask, unsigned int mode) +{ + int port; + unsigned int ret; + + ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0); + + /* To set gpio */ + for (port = 0; port < 16; port++) { + if (!((1 << port) & port_mask)) + continue; + + ret &= (~(3 << (port << 1))); + ret |= ((mode & 3) << (port << 1)); + } + + solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret); + + /* To set extended gpio - sensor */ + ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1); + + for (port = 0; port < 16; port++) { + if (!((1 << (port + 16)) & port_mask)) + continue; + + if (!mode) + ret &= ~(1 << port); + else + ret |= 1 << port; + } + + solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret); +} + +static void solo_gpio_set(struct solo_dev *solo_dev, unsigned int value) +{ + solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, + solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value); +} + +static void solo_gpio_clear(struct solo_dev *solo_dev, unsigned int value) +{ + solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, + solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value); +} + +static void solo_gpio_config(struct solo_dev *solo_dev) +{ + /* Video reset */ + solo_gpio_mode(solo_dev, 0x30, 1); + solo_gpio_clear(solo_dev, 0x30); + udelay(100); + solo_gpio_set(solo_dev, 0x30); + udelay(100); + + /* Warning: Don't touch the next line unless you're sure of what + * you're doing: first four gpio [0-3] are used for video. */ + solo_gpio_mode(solo_dev, 0x0f, 2); + + /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */ + solo_gpio_mode(solo_dev, 0xff00, 1); + + /* Initially set relay status to 0 */ + solo_gpio_clear(solo_dev, 0xff00); +} + +int solo_gpio_init(struct solo_dev *solo_dev) +{ + solo_gpio_config(solo_dev); + return 0; +} + +void solo_gpio_exit(struct solo_dev *solo_dev) +{ + solo_gpio_clear(solo_dev, 0x30); + solo_gpio_config(solo_dev); +} diff --git a/drivers/staging/media/solo6x10/solo6x10-i2c.c b/drivers/staging/media/solo6x10/solo6x10-i2c.c new file mode 100644 index 000000000000..01aa417c9258 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-i2c.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c + * channel. The bus can only handle one i2c event at a time. The below handles + * this all wrong. We should be using the status registers to see if the bus + * is in use, and have a global lock to check the status register. Also, + * the bulk of the work should be handled out-of-interrupt. The ugly loops + * that occur during interrupt scare me. The ISR should merely signal + * thread context, ACK the interrupt, and move on. -- BenC */ + +#include + +#include "solo6x10.h" + +u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) +{ + struct i2c_msg msgs[2]; + u8 data; + + msgs[0].flags = 0; + msgs[0].addr = addr; + msgs[0].len = 1; + msgs[0].buf = &off; + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = addr; + msgs[1].len = 1; + msgs[1].buf = &data; + + i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); + + return data; +} + +void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, + u8 off, u8 data) +{ + struct i2c_msg msgs; + u8 buf[2]; + + buf[0] = off; + buf[1] = data; + msgs.flags = 0; + msgs.addr = addr; + msgs.len = 2; + msgs.buf = buf; + + i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); +} + +static void solo_i2c_flush(struct solo_dev *solo_dev, int wr) +{ + u32 ctrl; + + ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); + + if (solo_dev->i2c_state == IIC_STATE_START) + ctrl |= SOLO_IIC_START; + + if (wr) { + ctrl |= SOLO_IIC_WRITE; + } else { + ctrl |= SOLO_IIC_READ; + if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) + ctrl |= SOLO_IIC_ACK_EN; + } + + if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) + ctrl |= SOLO_IIC_STOP; + + solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); +} + +static void solo_i2c_start(struct solo_dev *solo_dev) +{ + u32 addr = solo_dev->i2c_msg->addr << 1; + + if (solo_dev->i2c_msg->flags & I2C_M_RD) + addr |= 1; + + solo_dev->i2c_state = IIC_STATE_START; + solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); + solo_i2c_flush(solo_dev, 1); +} + +static void solo_i2c_stop(struct solo_dev *solo_dev) +{ + solo_irq_off(solo_dev, SOLO_IRQ_IIC); + solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); + solo_dev->i2c_state = IIC_STATE_STOP; + wake_up(&solo_dev->i2c_wait); +} + +static int solo_i2c_handle_read(struct solo_dev *solo_dev) +{ +prepare_read: + if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { + solo_i2c_flush(solo_dev, 0); + return 0; + } + + solo_dev->i2c_msg_ptr = 0; + solo_dev->i2c_msg++; + solo_dev->i2c_msg_num--; + + if (solo_dev->i2c_msg_num == 0) { + solo_i2c_stop(solo_dev); + return 0; + } + + if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { + solo_i2c_start(solo_dev); + } else { + if (solo_dev->i2c_msg->flags & I2C_M_RD) + goto prepare_read; + else + solo_i2c_stop(solo_dev); + } + + return 0; +} + +static int solo_i2c_handle_write(struct solo_dev *solo_dev) +{ +retry_write: + if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { + solo_reg_write(solo_dev, SOLO_IIC_TXD, + solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); + solo_dev->i2c_msg_ptr++; + solo_i2c_flush(solo_dev, 1); + return 0; + } + + solo_dev->i2c_msg_ptr = 0; + solo_dev->i2c_msg++; + solo_dev->i2c_msg_num--; + + if (solo_dev->i2c_msg_num == 0) { + solo_i2c_stop(solo_dev); + return 0; + } + + if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { + solo_i2c_start(solo_dev); + } else { + if (solo_dev->i2c_msg->flags & I2C_M_RD) + solo_i2c_stop(solo_dev); + else + goto retry_write; + } + + return 0; +} + +int solo_i2c_isr(struct solo_dev *solo_dev) +{ + u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); + int ret = -EINVAL; + + + if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) + || solo_dev->i2c_id < 0) { + solo_i2c_stop(solo_dev); + return -ENXIO; + } + + switch (solo_dev->i2c_state) { + case IIC_STATE_START: + if (solo_dev->i2c_msg->flags & I2C_M_RD) { + solo_dev->i2c_state = IIC_STATE_READ; + ret = solo_i2c_handle_read(solo_dev); + break; + } + + solo_dev->i2c_state = IIC_STATE_WRITE; + case IIC_STATE_WRITE: + ret = solo_i2c_handle_write(solo_dev); + break; + + case IIC_STATE_READ: + solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = + solo_reg_read(solo_dev, SOLO_IIC_RXD); + solo_dev->i2c_msg_ptr++; + + ret = solo_i2c_handle_read(solo_dev); + break; + + default: + solo_i2c_stop(solo_dev); + } + + return ret; +} + +static int solo_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct solo_dev *solo_dev = adap->algo_data; + unsigned long timeout; + int ret; + int i; + DEFINE_WAIT(wait); + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (&solo_dev->i2c_adap[i] == adap) + break; + } + + if (i == SOLO_I2C_ADAPTERS) + return num; /* XXX Right return value for failure? */ + + mutex_lock(&solo_dev->i2c_mutex); + solo_dev->i2c_id = i; + solo_dev->i2c_msg = msgs; + solo_dev->i2c_msg_num = num; + solo_dev->i2c_msg_ptr = 0; + + solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); + solo_irq_on(solo_dev, SOLO_IRQ_IIC); + solo_i2c_start(solo_dev); + + timeout = HZ / 2; + + for (;;) { + prepare_to_wait(&solo_dev->i2c_wait, &wait, + TASK_INTERRUPTIBLE); + + if (solo_dev->i2c_state == IIC_STATE_STOP) + break; + + timeout = schedule_timeout(timeout); + if (!timeout) + break; + + if (signal_pending(current)) + break; + } + + finish_wait(&solo_dev->i2c_wait, &wait); + ret = num - solo_dev->i2c_msg_num; + solo_dev->i2c_state = IIC_STATE_IDLE; + solo_dev->i2c_id = -1; + + mutex_unlock(&solo_dev->i2c_mutex); + + return ret; +} + +static u32 solo_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm solo_i2c_algo = { + .master_xfer = solo_i2c_master_xfer, + .functionality = solo_i2c_functionality, +}; + +int solo_i2c_init(struct solo_dev *solo_dev) +{ + int i; + int ret; + + solo_reg_write(solo_dev, SOLO_IIC_CFG, + SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); + + solo_dev->i2c_id = -1; + solo_dev->i2c_state = IIC_STATE_IDLE; + init_waitqueue_head(&solo_dev->i2c_wait); + mutex_init(&solo_dev->i2c_mutex); + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; + + snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", + SOLO6X10_NAME, i); + adap->algo = &solo_i2c_algo; + adap->algo_data = solo_dev; + adap->retries = 1; + adap->dev.parent = &solo_dev->pdev->dev; + + ret = i2c_add_adapter(adap); + if (ret) { + adap->algo_data = NULL; + break; + } + } + + if (ret) { + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (!solo_dev->i2c_adap[i].algo_data) + break; + i2c_del_adapter(&solo_dev->i2c_adap[i]); + solo_dev->i2c_adap[i].algo_data = NULL; + } + return ret; + } + + return 0; +} + +void solo_i2c_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (!solo_dev->i2c_adap[i].algo_data) + continue; + i2c_del_adapter(&solo_dev->i2c_adap[i]); + solo_dev->i2c_adap[i].algo_data = NULL; + } +} diff --git a/drivers/staging/media/solo6x10/solo6x10-p2m.c b/drivers/staging/media/solo6x10/solo6x10-p2m.c new file mode 100644 index 000000000000..333594189b81 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-p2m.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "solo6x10.h" + +static int multi_p2m; +module_param(multi_p2m, uint, 0644); +MODULE_PARM_DESC(multi_p2m, + "Use multiple P2M DMA channels (default: no, 6010-only)"); + +static int desc_mode; +module_param(desc_mode, uint, 0644); +MODULE_PARM_DESC(desc_mode, + "Allow use of descriptor mode DMA (default: no, 6010-only)"); + +int solo_p2m_dma(struct solo_dev *solo_dev, int wr, + void *sys_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size) +{ + dma_addr_t dma_addr; + int ret; + + if (WARN_ON_ONCE((unsigned long)sys_addr & 0x03)) + return -EINVAL; + if (WARN_ON_ONCE(!size)) + return -EINVAL; + + dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, + wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(solo_dev->pdev, dma_addr)) + return -ENOMEM; + + ret = solo_p2m_dma_t(solo_dev, wr, dma_addr, ext_addr, size, + repeat, ext_size); + + pci_unmap_single(solo_dev->pdev, dma_addr, size, + wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + return ret; +} + +/* Mutex must be held for p2m_id before calling this!! */ +int solo_p2m_dma_desc(struct solo_dev *solo_dev, + struct solo_p2m_desc *desc, dma_addr_t desc_dma, + int desc_cnt) +{ + struct solo_p2m_dev *p2m_dev; + unsigned int timeout; + unsigned int config = 0; + int ret = 0; + int p2m_id = 0; + + /* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */ + if (solo_dev->type != SOLO_DEV_6110 && multi_p2m) { + p2m_id = atomic_inc_return(&solo_dev->p2m_count) % SOLO_NR_P2M; + if (p2m_id < 0) + p2m_id = -p2m_id; + } + + p2m_dev = &solo_dev->p2m_dev[p2m_id]; + + if (mutex_lock_interruptible(&p2m_dev->mutex)) + return -EINTR; + + INIT_COMPLETION(p2m_dev->completion); + p2m_dev->error = 0; + + if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && desc_mode) { + /* For 6010 with more than one desc, we can do a one-shot */ + p2m_dev->desc_count = p2m_dev->desc_idx = 0; + config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(p2m_id)); + + solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(p2m_id), desc_dma); + solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(p2m_id), desc_cnt); + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config | + SOLO_P2M_DESC_MODE); + } else { + /* For single descriptors and 6110, we need to run each desc */ + p2m_dev->desc_count = desc_cnt; + p2m_dev->desc_idx = 1; + p2m_dev->descs = desc; + + solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(p2m_id), + desc[1].dma_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(p2m_id), + desc[1].ext_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(p2m_id), + desc[1].cfg); + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), + desc[1].ctrl); + } + + timeout = wait_for_completion_timeout(&p2m_dev->completion, + solo_dev->p2m_jiffies); + + if (WARN_ON_ONCE(p2m_dev->error)) + ret = -EIO; + else if (timeout == 0) { + solo_dev->p2m_timeouts++; + ret = -EAGAIN; + } + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), 0); + + /* Don't write here for the no_desc_mode case, because config is 0. + * We can't test no_desc_mode again, it might race. */ + if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && config) + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config); + + mutex_unlock(&p2m_dev->mutex); + + return ret; +} + +void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size) +{ + WARN_ON_ONCE(dma_addr & 0x03); + WARN_ON_ONCE(!size); + + desc->cfg = SOLO_P2M_COPY_SIZE(size >> 2); + desc->ctrl = SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | + (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON; + + if (repeat) { + desc->cfg |= SOLO_P2M_EXT_INC(ext_size >> 2); + desc->ctrl |= SOLO_P2M_PCI_INC(size >> 2) | + SOLO_P2M_REPEAT(repeat); + } + + desc->dma_addr = dma_addr; + desc->ext_addr = ext_addr; +} + +int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size) +{ + struct solo_p2m_desc desc[2]; + + solo_p2m_fill_desc(&desc[1], wr, dma_addr, ext_addr, size, repeat, + ext_size); + + /* No need for desc_dma since we know it is a single-shot */ + return solo_p2m_dma_desc(solo_dev, desc, 0, 1); +} + +void solo_p2m_isr(struct solo_dev *solo_dev, int id) +{ + struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id]; + struct solo_p2m_desc *desc; + + if (p2m_dev->desc_count <= p2m_dev->desc_idx) { + complete(&p2m_dev->completion); + return; + } + + /* Setup next descriptor */ + p2m_dev->desc_idx++; + desc = &p2m_dev->descs[p2m_dev->desc_idx]; + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); + solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), desc->dma_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), desc->ext_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), desc->cfg); + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), desc->ctrl); +} + +void solo_p2m_error_isr(struct solo_dev *solo_dev) +{ + unsigned int err = solo_reg_read(solo_dev, SOLO_PCI_ERR); + struct solo_p2m_dev *p2m_dev; + int i; + + if (!(err & (SOLO_PCI_ERR_P2M | SOLO_PCI_ERR_P2M_DESC))) + return; + + for (i = 0; i < SOLO_NR_P2M; i++) { + p2m_dev = &solo_dev->p2m_dev[i]; + p2m_dev->error = 1; + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); + complete(&p2m_dev->completion); + } +} + +void solo_p2m_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < SOLO_NR_P2M; i++) + solo_irq_off(solo_dev, SOLO_IRQ_P2M(i)); +} + +static int solo_p2m_test(struct solo_dev *solo_dev, int base, int size) +{ + u32 *wr_buf; + u32 *rd_buf; + int i; + int ret = -EIO; + int order = get_order(size); + + wr_buf = (u32 *)__get_free_pages(GFP_KERNEL, order); + if (wr_buf == NULL) + return -1; + + rd_buf = (u32 *)__get_free_pages(GFP_KERNEL, order); + if (rd_buf == NULL) { + free_pages((unsigned long)wr_buf, order); + return -1; + } + + for (i = 0; i < (size >> 3); i++) + *(wr_buf + i) = (i << 16) | (i + 1); + + for (i = (size >> 3); i < (size >> 2); i++) + *(wr_buf + i) = ~((i << 16) | (i + 1)); + + memset(rd_buf, 0x55, size); + + if (solo_p2m_dma(solo_dev, 1, wr_buf, base, size, 0, 0)) + goto test_fail; + + if (solo_p2m_dma(solo_dev, 0, rd_buf, base, size, 0, 0)) + goto test_fail; + + for (i = 0; i < (size >> 2); i++) { + if (*(wr_buf + i) != *(rd_buf + i)) + goto test_fail; + } + + ret = 0; + +test_fail: + free_pages((unsigned long)wr_buf, order); + free_pages((unsigned long)rd_buf, order); + + return ret; +} + +int solo_p2m_init(struct solo_dev *solo_dev) +{ + struct solo_p2m_dev *p2m_dev; + int i; + + for (i = 0; i < SOLO_NR_P2M; i++) { + p2m_dev = &solo_dev->p2m_dev[i]; + + mutex_init(&p2m_dev->mutex); + init_completion(&p2m_dev->completion); + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), + SOLO_P2M_CSC_16BIT_565 | + SOLO_P2M_DESC_INTR_OPT | + SOLO_P2M_DMA_INTERVAL(0) | + SOLO_P2M_PCI_MASTER_MODE); + solo_irq_on(solo_dev, SOLO_IRQ_P2M(i)); + } + + /* Find correct SDRAM size */ + for (solo_dev->sdram_size = 0, i = 2; i >= 0; i--) { + solo_reg_write(solo_dev, SOLO_DMA_CTRL, + SOLO_DMA_CTRL_REFRESH_CYCLE(1) | + SOLO_DMA_CTRL_SDRAM_SIZE(i) | + SOLO_DMA_CTRL_SDRAM_CLK_INVERT | + SOLO_DMA_CTRL_READ_CLK_SELECT | + SOLO_DMA_CTRL_LATENCY(1)); + + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config | + SOLO_SYS_CFG_RESET); + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); + + switch (i) { + case 2: + if (solo_p2m_test(solo_dev, 0x07ff0000, 0x00010000) || + solo_p2m_test(solo_dev, 0x05ff0000, 0x00010000)) + continue; + break; + + case 1: + if (solo_p2m_test(solo_dev, 0x03ff0000, 0x00010000)) + continue; + break; + + default: + if (solo_p2m_test(solo_dev, 0x01ff0000, 0x00010000)) + continue; + } + + solo_dev->sdram_size = (32 << 20) << i; + break; + } + + if (!solo_dev->sdram_size) { + dev_err(&solo_dev->pdev->dev, "Error detecting SDRAM size\n"); + return -EIO; + } + + if (SOLO_SDRAM_END(solo_dev) > solo_dev->sdram_size) { + dev_err(&solo_dev->pdev->dev, + "SDRAM is not large enough (%u < %u)\n", + solo_dev->sdram_size, SOLO_SDRAM_END(solo_dev)); + return -EIO; + } + + return 0; +} diff --git a/drivers/staging/media/solo6x10/solo6x10-tw28.c b/drivers/staging/media/solo6x10/solo6x10-tw28.c new file mode 100644 index 000000000000..ad00e2b60323 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-tw28.c @@ -0,0 +1,854 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "solo6x10.h" +#include "solo6x10-tw28.h" + +#define DEFAULT_HDELAY_NTSC (32 - 8) +#define DEFAULT_HACTIVE_NTSC (720 + 16) +#define DEFAULT_VDELAY_NTSC (7 - 2) +#define DEFAULT_VACTIVE_NTSC (240 + 4) + +#define DEFAULT_HDELAY_PAL (32 + 4) +#define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL) +#define DEFAULT_VDELAY_PAL (6) +#define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL) + + +static const u8 tbl_tw2864_ntsc_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, + 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01, + 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, + 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, + 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81, + 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, +}; + +static const u8 tbl_tw2864_pal_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, + 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01, + 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, + 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, + 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81, + 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, +}; + +static const u8 tbl_tw2865_ntsc_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */ + 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, + 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ + 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, + 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, + 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, + 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ + 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8, + 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, + 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, + 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, +}; + +static const u8 tbl_tw2865_pal_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */ + 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, + 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ + 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, + 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, + 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, + 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ + 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8, + 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, + 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, + 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, +}; + +#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id))) + +static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off, + u8 tw_off) +{ + if (is_tw286x(solo_dev, chip_id)) + return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw6x_off); + else + return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw_off); +} + +static void tw_writebyte(struct solo_dev *solo_dev, int chip_id, + u8 tw6x_off, u8 tw_off, u8 val) +{ + if (is_tw286x(solo_dev, chip_id)) + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw6x_off, val); + else + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw_off, val); +} + +static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off, + u8 val) +{ + int i; + + for (i = 0; i < 5; i++) { + u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off); + if (rval == val) + return; + + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val); + msleep_interruptible(1); + } + +/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */ +/* addr, off, val); */ +} + +static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_tw2865_common[256]; + int i; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) + memcpy(tbl_tw2865_common, tbl_tw2865_pal_template, + sizeof(tbl_tw2865_common)); + else + memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template, + sizeof(tbl_tw2865_common)); + + /* ALINK Mode */ + if (solo_dev->nr_chans == 4) { + tbl_tw2865_common[0xd2] = 0x01; + tbl_tw2865_common[0xcf] = 0x00; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2865_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2865_common[0xcf] = 0x80; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2865_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2865_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2865_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2865_common[0xcf] = 0x80; + } + + for (i = 0; i < 0xff; i++) { + /* Skip read only registers */ + switch (i) { + case 0xb8 ... 0xc1: + case 0xc4 ... 0xc7: + case 0xfd: + continue; + } + switch (i & ~0x30) { + case 0x00: + case 0x0c ... 0x0d: + continue; + } + + tw_write_and_verify(solo_dev, dev_addr, i, + tbl_tw2865_common[i]); + } + + return 0; +} + +static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_tw2864_common[256]; + int i; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) + memcpy(tbl_tw2864_common, tbl_tw2864_pal_template, + sizeof(tbl_tw2864_common)); + else + memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template, + sizeof(tbl_tw2864_common)); + + if (solo_dev->tw2865 == 0) { + /* IRQ Mode */ + if (solo_dev->nr_chans == 4) { + tbl_tw2864_common[0xd2] = 0x01; + tbl_tw2864_common[0xcf] = 0x00; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2864_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x40; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2864_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2864_common[0xcf] = 0x40; + } + } else { + /* ALINK Mode. Assumes that the first tw28xx is a + * 2865 and these are in cascade. */ + for (i = 0; i <= 4; i++) + tbl_tw2864_common[0x08 | i << 4] = 0x12; + + if (solo_dev->nr_chans == 8) { + tbl_tw2864_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x80; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2864_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2864_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2864_common[0xcf] = 0x80; + } + } + + for (i = 0; i < 0xff; i++) { + /* Skip read only registers */ + switch (i) { + case 0xb8 ... 0xc1: + case 0xfd: + continue; + } + switch (i & ~0x30) { + case 0x00: + case 0x0c: + case 0x0d: + continue; + } + + tw_write_and_verify(solo_dev, dev_addr, i, + tbl_tw2864_common[i]); + } + + return 0; +} + +static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_ntsc_tw2815_common[] = { + 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80, + 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11, + }; + + u8 tbl_pal_tw2815_common[] = { + 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80, + 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11, + }; + + u8 tbl_tw2815_sfr[] = { + 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */ + 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */ + 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00, + 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */ + 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88, + 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */ + }; + u8 *tbl_tw2815_common; + int i; + int ch; + + tbl_ntsc_tw2815_common[0x06] = 0; + + /* Horizontal Delay Control */ + tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8); + + /* Horizontal Active Control */ + tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2); + + /* Vertical Delay Control */ + tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4); + + /* Vertical Active Control */ + tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5); + + tbl_pal_tw2815_common[0x06] = 0; + + /* Horizontal Delay Control */ + tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8); + + /* Horizontal Active Control */ + tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2); + + /* Vertical Delay Control */ + tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4); + + /* Vertical Active Control */ + tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5); + + tbl_tw2815_common = + (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ? + tbl_ntsc_tw2815_common : tbl_pal_tw2815_common; + + /* Dual ITU-R BT.656 format */ + tbl_tw2815_common[0x0d] |= 0x04; + + /* Audio configuration */ + tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6); + + if (solo_dev->nr_chans == 4) { + tbl_tw2815_sfr[0x63 - 0x40] |= 1; + tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2815_sfr[0x63 - 0x40] |= 2; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2815_sfr[0x63 - 0x40] |= 3; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; + } + + /* Output mode of R_ADATM pin (0 mixing, 1 record) */ + /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */ + + /* 8KHz, used to be 16KHz, but changed for remote client compat */ + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2; + tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2; + + /* Playback of right channel */ + tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5; + + /* Reserved value (XXX ??) */ + tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5; + + /* Analog output gain and mix ratio playback on full */ + tbl_tw2815_sfr[0x70 - 0x40] |= 0xff; + /* Select playback audio and mute all except */ + tbl_tw2815_sfr[0x71 - 0x40] |= 0x10; + tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f; + + /* End of audio configuration */ + + for (ch = 0; ch < 4; ch++) { + tbl_tw2815_common[0x0d] &= ~3; + switch (ch) { + case 0: + tbl_tw2815_common[0x0d] |= 0x21; + break; + case 1: + tbl_tw2815_common[0x0d] |= 0x20; + break; + case 2: + tbl_tw2815_common[0x0d] |= 0x23; + break; + case 3: + tbl_tw2815_common[0x0d] |= 0x22; + break; + } + + for (i = 0; i < 0x0f; i++) { + if (i == 0x00) + continue; /* read-only */ + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + dev_addr, (ch * 0x10) + i, + tbl_tw2815_common[i]); + } + } + + for (i = 0x40; i < 0x76; i++) { + /* Skip read-only and nop registers */ + if (i == 0x40 || i == 0x59 || i == 0x5a || + i == 0x5d || i == 0x5e || i == 0x5f) + continue; + + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i, + tbl_tw2815_sfr[i - 0x40]); + } + + return 0; +} + +#define FIRST_ACTIVE_LINE 0x0008 +#define LAST_ACTIVE_LINE 0x0102 + +static void saa7128_setup(struct solo_dev *solo_dev) +{ + int i; + unsigned char regs[128] = { + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00, + 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, + 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18, + 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f, + 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06, + 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e, + 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77, + 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00, + 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff, + }; + + regs[0x7A] = FIRST_ACTIVE_LINE & 0xff; + regs[0x7B] = LAST_ACTIVE_LINE & 0xff; + regs[0x7C] = ((1 << 7) | + (((LAST_ACTIVE_LINE >> 8) & 1) << 6) | + (((FIRST_ACTIVE_LINE >> 8) & 1) << 4)); + + /* PAL: XXX: We could do a second set of regs to avoid this */ + if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) { + regs[0x28] = 0xE1; + + regs[0x5A] = 0x0F; + regs[0x61] = 0x02; + regs[0x62] = 0x35; + regs[0x63] = 0xCB; + regs[0x64] = 0x8A; + regs[0x65] = 0x09; + regs[0x66] = 0x2A; + + regs[0x6C] = 0xf1; + regs[0x6E] = 0x20; + + regs[0x7A] = 0x06 + 12; + regs[0x7b] = 0x24 + 12; + regs[0x7c] |= 1 << 6; + } + + /* First 0x25 bytes are read-only? */ + for (i = 0x26; i < 128; i++) { + if (i == 0x60 || i == 0x7D) + continue; + solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]); + } + + return; +} + +int solo_tw28_init(struct solo_dev *solo_dev) +{ + int i; + u8 value; + + solo_dev->tw28_cnt = 0; + + /* Detect techwell chip type(s) */ + for (i = 0; i < solo_dev->nr_chans / 4; i++) { + value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(i), 0xFF); + + switch (value >> 3) { + case 0x18: + solo_dev->tw2865 |= 1 << i; + solo_dev->tw28_cnt++; + break; + case 0x0c: + solo_dev->tw2864 |= 1 << i; + solo_dev->tw28_cnt++; + break; + default: + value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(i), + 0x59); + if ((value >> 3) == 0x04) { + solo_dev->tw2815 |= 1 << i; + solo_dev->tw28_cnt++; + } + } + } + + if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) { + dev_err(&solo_dev->pdev->dev, + "Could not initialize any techwell chips\n"); + return -EINVAL; + } + + saa7128_setup(solo_dev); + + for (i = 0; i < solo_dev->tw28_cnt; i++) { + if ((solo_dev->tw2865 & (1 << i))) + tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + else if ((solo_dev->tw2864 & (1 << i))) + tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + else + tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + } + + return 0; +} + +/* + * We accessed the video status signal in the Techwell chip through + * iic/i2c because the video status reported by register REG_VI_STATUS1 + * (address 0x012C) of the SOLO6010 chip doesn't give the correct video + * status signal values. + */ +int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch) +{ + u8 val, chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR, + TW_AV_STAT_ADDR) & 0x0f; + + return val & (1 << ch) ? 1 : 0; +} + +#if 0 +/* Status of audio from up to 4 techwell chips are combined into 1 variable. + * See techwell datasheet for details. */ +u16 tw28_get_audio_status(struct solo_dev *solo_dev) +{ + u8 val; + u16 status = 0; + int i; + + for (i = 0; i < solo_dev->tw28_cnt; i++) { + val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR, + TW_AV_STAT_ADDR) & 0xf0) >> 4; + status |= val << (i * 4); + } + + return status; +} +#endif + +bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch) +{ + return is_tw286x(solo_dev, ch / 4); +} + +int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, + s32 val) +{ + char sval; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + if (val > 255 || val < 0) + return -ERANGE; + + switch (ctrl) { + case V4L2_CID_SHARPNESS: + /* Only 286x has sharpness */ + if (is_tw286x(solo_dev, chip_num)) { + u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num)); + v &= 0xf0; + v |= val; + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num), v); + } else { + return -EINVAL; + } + break; + + case V4L2_CID_HUE: + if (is_tw286x(solo_dev, chip_num)) + sval = val - 128; + else + sval = (char)val; + tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), + TW_HUE_ADDR(ch), sval); + + break; + + case V4L2_CID_SATURATION: + /* 286x chips have a U and V component for saturation */ + if (is_tw286x(solo_dev, chip_num)) { + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SATURATIONU_ADDR(ch), val); + } + tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch), + TW_SATURATION_ADDR(ch), val); + + break; + + case V4L2_CID_CONTRAST: + tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch), + TW_CONTRAST_ADDR(ch), val); + break; + + case V4L2_CID_BRIGHTNESS: + if (is_tw286x(solo_dev, chip_num)) + sval = val - 128; + else + sval = (char)val; + tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch), + TW_BRIGHTNESS_ADDR(ch), sval); + + break; + default: + return -EINVAL; + } + + return 0; +} + +int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, + s32 *val) +{ + u8 rval, chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + switch (ctrl) { + case V4L2_CID_SHARPNESS: + /* Only 286x has sharpness */ + if (is_tw286x(solo_dev, chip_num)) { + rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num)); + *val = rval & 0x0f; + } else + *val = 0; + break; + case V4L2_CID_HUE: + rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), + TW_HUE_ADDR(ch)); + if (is_tw286x(solo_dev, chip_num)) + *val = (s32)((char)rval) + 128; + else + *val = rval; + break; + case V4L2_CID_SATURATION: + *val = tw_readbyte(solo_dev, chip_num, + TW286x_SATURATIONU_ADDR(ch), + TW_SATURATION_ADDR(ch)); + break; + case V4L2_CID_CONTRAST: + *val = tw_readbyte(solo_dev, chip_num, + TW286x_CONTRAST_ADDR(ch), + TW_CONTRAST_ADDR(ch)); + break; + case V4L2_CID_BRIGHTNESS: + rval = tw_readbyte(solo_dev, chip_num, + TW286x_BRIGHTNESS_ADDR(ch), + TW_BRIGHTNESS_ADDR(ch)); + if (is_tw286x(solo_dev, chip_num)) + *val = (s32)((char)rval) + 128; + else + *val = rval; + break; + default: + return -EINVAL; + } + + return 0; +} + +#if 0 +/* + * For audio output volume, the output channel is only 1. In this case we + * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used + * is the base address of the techwell chip. + */ +void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val) +{ + unsigned int val; + unsigned int chip_num; + + chip_num = (solo_dev->nr_chans - 1) / 4; + + val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, + TW_AUDIO_OUTPUT_VOL_ADDR); + + u_val = (val & 0x0f) | (u_val << 4); + + tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, + TW_AUDIO_OUTPUT_VOL_ADDR, u_val); +} +#endif + +u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch) +{ + u8 val; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + val = tw_readbyte(solo_dev, chip_num, + TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch)); + + return (ch % 2) ? (val >> 4) : (val & 0x0f); +} + +void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val) +{ + u8 old_val; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + old_val = tw_readbyte(solo_dev, chip_num, + TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch)); + + val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) | + ((ch % 2) ? (val << 4) : val); + + tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch), val); +} diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c new file mode 100644 index 000000000000..39a8fb2802e4 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -0,0 +1,1363 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "solo6x10.h" +#include "solo6x10-tw28.h" +#include "solo6x10-jpeg.h" + +#define MIN_VID_BUFFERS 2 +#define FRAME_BUF_SIZE (196 * 1024) +#define MP4_QS 16 +#define DMA_ALIGN 4096 + +/* 6010 M4V */ +static unsigned char vop_6010_ntsc_d1[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x58, 0x10, 0xf0, 0x71, 0x18, 0x3f, +}; + +static unsigned char vop_6010_ntsc_cif[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x2c, 0x10, 0x78, 0x51, 0x18, 0x3f, +}; + +static unsigned char vop_6010_pal_d1[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x58, 0x11, 0x20, 0x71, 0x18, 0x3f, +}; + +static unsigned char vop_6010_pal_cif[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x2c, 0x10, 0x90, 0x51, 0x18, 0x3f, +}; + +/* 6110 h.264 */ +static unsigned char vop_6110_ntsc_d1[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x05, 0x81, 0xec, 0x80, 0x00, 0x00, + 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, +}; + +static unsigned char vop_6110_ntsc_cif[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x0b, 0x0f, 0xc8, 0x00, 0x00, 0x00, + 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, +}; + +static unsigned char vop_6110_pal_d1[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x05, 0x80, 0x93, 0x20, 0x00, 0x00, + 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, +}; + +static unsigned char vop_6110_pal_cif[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x0b, 0x04, 0xb2, 0x00, 0x00, 0x00, + 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, +}; + +struct vop_header { + /* VE_STATUS0 */ + u32 mpeg_size:20, sad_motion_flag:1, video_motion_flag:1, vop_type:2, + channel:5, source_fl:1, interlace:1, progressive:1; + + /* VE_STATUS1 */ + u32 vsize:8, hsize:8, last_queue:4, nop0:8, scale:4; + + /* VE_STATUS2 */ + u32 mpeg_off; + + /* VE_STATUS3 */ + u32 jpeg_off; + + /* VE_STATUS4 */ + u32 jpeg_size:20, interval:10, nop1:2; + + /* VE_STATUS5/6 */ + u32 sec, usec; + + /* VE_STATUS7/8/9 */ + u32 nop2[3]; + + /* VE_STATUS10 */ + u32 mpeg_size_alt:20, nop3:12; + + u32 end_nops[5]; +} __packed; + +struct solo_enc_buf { + enum solo_enc_types type; + struct vop_header *vh; + int motion; +}; + +static int solo_is_motion_on(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + + return (solo_dev->motion_mask >> solo_enc->ch) & 1; +} + +static int solo_motion_detected(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + unsigned long flags; + u32 ch_mask = 1 << solo_enc->ch; + int ret = 0; + + spin_lock_irqsave(&solo_enc->motion_lock, flags); + if (solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS) & ch_mask) { + solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, ch_mask); + ret = 1; + } + spin_unlock_irqrestore(&solo_enc->motion_lock, flags); + + return ret; +} + +static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + u32 mask = 1 << solo_enc->ch; + unsigned long flags; + + spin_lock_irqsave(&solo_enc->motion_lock, flags); + + if (on) + solo_dev->motion_mask |= mask; + else + solo_dev->motion_mask &= ~mask; + + solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, mask); + + solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, + SOLO_VI_MOTION_EN(solo_dev->motion_mask) | + (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); + + spin_unlock_irqrestore(&solo_enc->motion_lock, flags); +} + +void solo_update_mode(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + int vop_len; + unsigned char *vop; + + solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; + solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); + + if (solo_enc->mode == SOLO_ENC_MODE_CIF) { + solo_enc->width = solo_dev->video_hsize >> 1; + solo_enc->height = solo_dev->video_vsize; + if (solo_dev->type == SOLO_DEV_6110) { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6110_ntsc_cif; + vop_len = sizeof(vop_6110_ntsc_cif); + } else { + vop = vop_6110_pal_cif; + vop_len = sizeof(vop_6110_pal_cif); + } + } else { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6010_ntsc_cif; + vop_len = sizeof(vop_6010_ntsc_cif); + } else { + vop = vop_6010_pal_cif; + vop_len = sizeof(vop_6010_pal_cif); + } + } + } else { + solo_enc->width = solo_dev->video_hsize; + solo_enc->height = solo_dev->video_vsize << 1; + solo_enc->bw_weight <<= 2; + if (solo_dev->type == SOLO_DEV_6110) { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6110_ntsc_d1; + vop_len = sizeof(vop_6110_ntsc_d1); + } else { + vop = vop_6110_pal_d1; + vop_len = sizeof(vop_6110_pal_d1); + } + } else { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6010_ntsc_d1; + vop_len = sizeof(vop_6010_ntsc_d1); + } else { + vop = vop_6010_pal_d1; + vop_len = sizeof(vop_6010_pal_d1); + } + } + } + + memcpy(solo_enc->vop, vop, vop_len); + + /* Some fixups for 6010/M4V */ + if (solo_dev->type == SOLO_DEV_6010) { + u16 fps = solo_dev->fps * 1000; + u16 interval = solo_enc->interval * 1000; + + vop = solo_enc->vop; + + /* Frame rate and interval */ + vop[22] = fps >> 4; + vop[23] = ((fps << 4) & 0xf0) | 0x0c + | ((interval >> 13) & 0x3); + vop[24] = (interval >> 5) & 0xff; + vop[25] = ((interval << 3) & 0xf8) | 0x04; + } + + solo_enc->vop_len = vop_len; + + /* Now handle the jpeg header */ + vop = solo_enc->jpeg_header; + vop[SOF0_START + 5] = 0xff & (solo_enc->height >> 8); + vop[SOF0_START + 6] = 0xff & solo_enc->height; + vop[SOF0_START + 7] = 0xff & (solo_enc->width >> 8); + vop[SOF0_START + 8] = 0xff & solo_enc->width; + + memcpy(vop + DQT_START, + jpeg_dqt[solo_g_jpeg_qp(solo_dev, solo_enc->ch)], DQT_LEN); +} + +static int solo_enc_on(struct solo_enc_dev *solo_enc) +{ + u8 ch = solo_enc->ch; + struct solo_dev *solo_dev = solo_enc->solo_dev; + u8 interval; + + solo_update_mode(solo_enc); + + /* Make sure to do a bandwidth check */ + if (solo_enc->bw_weight > solo_dev->enc_bw_remain) + return -EBUSY; + solo_enc->sequence = 0; + solo_dev->enc_bw_remain -= solo_enc->bw_weight; + + if (solo_enc->type == SOLO_ENC_TYPE_EXT) + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); + + /* Disable all encoding for this channel */ + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); + + /* Common for both std and ext encoding */ + solo_reg_write(solo_dev, SOLO_VE_CH_INTL(ch), + solo_enc->interlaced ? 1 : 0); + + if (solo_enc->interlaced) + interval = solo_enc->interval - 1; + else + interval = solo_enc->interval; + + /* Standard encoding only */ + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_QP(ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_CAP_CH_INTV(ch), interval); + + /* Extended encoding only */ + solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_CAP_CH_INTV_E(ch), interval); + + /* Enables the standard encoder */ + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); + + return 0; +} + +static void solo_enc_off(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + + solo_dev->enc_bw_remain += solo_enc->bw_weight; + + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0); +} + +static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma, + unsigned int off, unsigned int size) +{ + int ret; + + if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) + return -EINVAL; + + /* Single shot */ + if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { + return solo_p2m_dma_t(solo_dev, 0, dma, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, size, + 0, 0); + } + + /* Buffer wrap */ + ret = solo_p2m_dma_t(solo_dev, 0, dma, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, + SOLO_MP4E_EXT_SIZE(solo_dev) - off, 0, 0); + + if (!ret) { + ret = solo_p2m_dma_t(solo_dev, 0, + dma + SOLO_MP4E_EXT_SIZE(solo_dev) - off, + SOLO_MP4E_EXT_ADDR(solo_dev), + size + off - SOLO_MP4E_EXT_SIZE(solo_dev), 0, 0); + } + + return ret; +} + +/* Build a descriptor queue out of an SG list and send it to the P2M for + * processing. */ +static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip, + struct vb2_dma_sg_desc *vbuf, int off, int size, + unsigned int base, unsigned int base_size) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct scatterlist *sg; + int i; + int ret; + + if (WARN_ON_ONCE(size > FRAME_BUF_SIZE)) + return -EINVAL; + + solo_enc->desc_count = 1; + + for_each_sg(vbuf->sglist, sg, vbuf->num_pages, i) { + struct solo_p2m_desc *desc; + dma_addr_t dma; + int len; + int left = base_size - off; + + desc = &solo_enc->desc_items[solo_enc->desc_count++]; + dma = sg_dma_address(sg); + len = sg_dma_len(sg); + + /* We assume this is smaller than the scatter size */ + BUG_ON(skip >= len); + if (skip) { + len -= skip; + dma += skip; + size -= skip; + skip = 0; + } + + len = min(len, size); + + if (len <= left) { + /* Single descriptor */ + solo_p2m_fill_desc(desc, 0, dma, base + off, + len, 0, 0); + } else { + /* Buffer wrap */ + /* XXX: Do these as separate DMA requests, to avoid + timeout errors triggered by awkwardly sized + descriptors. See + + */ + ret = solo_p2m_dma_t(solo_dev, 0, dma, base + off, + left, 0, 0); + if (ret) + return ret; + + ret = solo_p2m_dma_t(solo_dev, 0, dma + left, base, + len - left, 0, 0); + if (ret) + return ret; + + solo_enc->desc_count--; + } + + size -= len; + if (size <= 0) + break; + + off += len; + if (off >= base_size) + off -= base_size; + + /* Because we may use two descriptors per loop */ + if (solo_enc->desc_count >= (solo_enc->desc_nelts - 1)) { + ret = solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, + solo_enc->desc_dma, + solo_enc->desc_count - 1); + if (ret) + return ret; + solo_enc->desc_count = 1; + } + } + + if (solo_enc->desc_count <= 1) + return 0; + + return solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, solo_enc->desc_dma, + solo_enc->desc_count - 1); +} + +static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, + struct vb2_buffer *vb, struct vop_header *vh) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct vb2_dma_sg_desc *vbuf = vb2_dma_sg_plane_desc(vb, 0); + int frame_size; + int ret; + + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + + if (vb2_plane_size(vb, 0) < vh->jpeg_size + solo_enc->jpeg_len) + return -EIO; + + sg_copy_from_buffer(vbuf->sglist, vbuf->num_pages, + solo_enc->jpeg_header, + solo_enc->jpeg_len); + + frame_size = (vh->jpeg_size + solo_enc->jpeg_len + (DMA_ALIGN - 1)) + & ~(DMA_ALIGN - 1); + vb2_set_plane_payload(vb, 0, vh->jpeg_size + solo_enc->jpeg_len); + + dma_map_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, + DMA_FROM_DEVICE); + ret = solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, vh->jpeg_off, + frame_size, SOLO_JPEG_EXT_ADDR(solo_dev), + SOLO_JPEG_EXT_SIZE(solo_dev)); + dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, + DMA_FROM_DEVICE); + return ret; +} + +static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, + struct vb2_buffer *vb, struct vop_header *vh) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct vb2_dma_sg_desc *vbuf = vb2_dma_sg_plane_desc(vb, 0); + int frame_off, frame_size; + int skip = 0; + int ret; + + if (vb2_plane_size(vb, 0) < vh->mpeg_size) + return -EIO; + + /* If this is a key frame, add extra header */ + if (!vh->vop_type) { + sg_copy_from_buffer(vbuf->sglist, vbuf->num_pages, + solo_enc->vop, + solo_enc->vop_len); + + skip = solo_enc->vop_len; + + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + vb2_set_plane_payload(vb, 0, vh->mpeg_size + solo_enc->vop_len); + } else { + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; + vb2_set_plane_payload(vb, 0, vh->mpeg_size); + } + + /* Now get the actual mpeg payload */ + frame_off = (vh->mpeg_off + sizeof(*vh)) + % SOLO_MP4E_EXT_SIZE(solo_dev); + frame_size = (vh->mpeg_size + skip + (DMA_ALIGN - 1)) + & ~(DMA_ALIGN - 1); + + dma_map_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, + DMA_FROM_DEVICE); + ret = solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size, + SOLO_MP4E_EXT_ADDR(solo_dev), + SOLO_MP4E_EXT_SIZE(solo_dev)); + dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, + DMA_FROM_DEVICE); + return ret; +} + +static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, + struct vb2_buffer *vb, struct solo_enc_buf *enc_buf) +{ + struct vop_header *vh = enc_buf->vh; + int ret; + + /* Check for motion flags */ + vb->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_MOTION_ON | + V4L2_BUF_FLAG_MOTION_DETECTED); + if (solo_is_motion_on(solo_enc)) { + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_ON; + if (enc_buf->motion) + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_DETECTED; + } + + if (solo_enc->fmt == V4L2_PIX_FMT_MPEG4) + ret = solo_fill_mpeg(solo_enc, vb, vh); + else + ret = solo_fill_jpeg(solo_enc, vb, vh); + + if (!ret) { + vb->v4l2_buf.sequence = solo_enc->sequence++; + vb->v4l2_buf.timestamp.tv_sec = vh->sec; + vb->v4l2_buf.timestamp.tv_usec = vh->usec; + } + + vb2_buffer_done(vb, ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + + return ret; +} + +static void solo_enc_handle_one(struct solo_enc_dev *solo_enc, + struct solo_enc_buf *enc_buf) +{ + struct solo_vb2_buf *vb; + unsigned long flags; + + mutex_lock(&solo_enc->lock); + if (solo_enc->type != enc_buf->type) + goto unlock; + + spin_lock_irqsave(&solo_enc->av_lock, flags); + if (list_empty(&solo_enc->vidq_active)) { + spin_unlock_irqrestore(&solo_enc->av_lock, flags); + goto unlock; + } + vb = list_first_entry(&solo_enc->vidq_active, struct solo_vb2_buf, list); + list_del(&vb->list); + spin_unlock_irqrestore(&solo_enc->av_lock, flags); + + solo_enc_fillbuf(solo_enc, &vb->vb, enc_buf); +unlock: + mutex_unlock(&solo_enc->lock); +} + +void solo_enc_v4l2_isr(struct solo_dev *solo_dev) +{ + wake_up_interruptible_all(&solo_dev->ring_thread_wait); +} + +static void solo_handle_ring(struct solo_dev *solo_dev) +{ + for (;;) { + struct solo_enc_dev *solo_enc; + struct solo_enc_buf enc_buf; + u32 mpeg_current, off; + u8 ch; + u8 cur_q; + + /* Check if the hardware has any new ones in the queue */ + cur_q = solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xff; + if (cur_q == solo_dev->enc_idx) + break; + + mpeg_current = solo_reg_read(solo_dev, + SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); + solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS; + + ch = (mpeg_current >> 24) & 0x1f; + off = mpeg_current & 0x00ffffff; + + if (ch >= SOLO_MAX_CHANNELS) { + ch -= SOLO_MAX_CHANNELS; + enc_buf.type = SOLO_ENC_TYPE_EXT; + } else + enc_buf.type = SOLO_ENC_TYPE_STD; + + solo_enc = solo_dev->v4l2_enc[ch]; + if (solo_enc == NULL) { + dev_err(&solo_dev->pdev->dev, + "Got spurious packet for channel %d\n", ch); + continue; + } + + /* FAIL... */ + if (enc_get_mpeg_dma(solo_dev, solo_dev->vh_dma, off, + sizeof(struct vop_header))) + continue; + + enc_buf.vh = (struct vop_header *)solo_dev->vh_buf; + enc_buf.vh->mpeg_off -= SOLO_MP4E_EXT_ADDR(solo_dev); + enc_buf.vh->jpeg_off -= SOLO_JPEG_EXT_ADDR(solo_dev); + + /* Sanity check */ + if (enc_buf.vh->mpeg_off != off) + continue; + + if (solo_motion_detected(solo_enc)) + enc_buf.motion = 1; + else + enc_buf.motion = 0; + + solo_enc_handle_one(solo_enc, &enc_buf); + } +} + +static int solo_ring_thread(void *data) +{ + struct solo_dev *solo_dev = data; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&solo_dev->ring_thread_wait, &wait); + + for (;;) { + long timeout = schedule_timeout_interruptible(HZ); + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); + solo_handle_ring(solo_dev); + solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); + try_to_freeze(); + } + + remove_wait_queue(&solo_dev->ring_thread_wait, &wait); + + return 0; +} + +static int solo_enc_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + sizes[0] = FRAME_BUF_SIZE; + *num_planes = 1; + + if (*num_buffers < MIN_VID_BUFFERS) + *num_buffers = MIN_VID_BUFFERS; + + return 0; +} + +static void solo_enc_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct solo_enc_dev *solo_enc = vb2_get_drv_priv(vq); + struct solo_vb2_buf *solo_vb = + container_of(vb, struct solo_vb2_buf, vb); + + spin_lock(&solo_enc->av_lock); + list_add_tail(&solo_vb->list, &solo_enc->vidq_active); + spin_unlock(&solo_enc->av_lock); +} + +static int solo_ring_start(struct solo_dev *solo_dev) +{ + solo_dev->ring_thread = kthread_run(solo_ring_thread, solo_dev, + SOLO6X10_NAME "_ring"); + if (IS_ERR(solo_dev->ring_thread)) { + int err = PTR_ERR(solo_dev->ring_thread); + solo_dev->ring_thread = NULL; + return err; + } + + solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); + + return 0; +} + +static void solo_ring_stop(struct solo_dev *solo_dev) +{ + if (solo_dev->ring_thread) { + kthread_stop(solo_dev->ring_thread); + solo_dev->ring_thread = NULL; + } + + solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); +} + +static int solo_enc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); + int ret; + + ret = solo_enc_on(solo_enc); + if (ret) + return ret; + return solo_ring_start(solo_enc->solo_dev); +} + +static int solo_enc_stop_streaming(struct vb2_queue *q) +{ + struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); + + solo_enc_off(solo_enc); + INIT_LIST_HEAD(&solo_enc->vidq_active); + solo_ring_stop(solo_enc->solo_dev); + return 0; +} + +static struct vb2_ops solo_enc_video_qops = { + .queue_setup = solo_enc_queue_setup, + .buf_queue = solo_enc_buf_queue, + .start_streaming = solo_enc_start_streaming, + .stop_streaming = solo_enc_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int solo_enc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + strcpy(cap->driver, SOLO6X10_NAME); + snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d", + solo_enc->ch); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + pci_name(solo_dev->pdev)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int solo_enc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (input->index) + return -EINVAL; + + snprintf(input->name, sizeof(input->name), "Encoder %d", + solo_enc->ch + 1); + input->type = V4L2_INPUT_TYPE_CAMERA; + input->std = solo_enc->vfd->tvnorms; + + if (!tw28_get_video_status(solo_dev, solo_enc->ch)) + input->status = V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int solo_enc_set_input(struct file *file, void *priv, + unsigned int index) +{ + if (index) + return -EINVAL; + + return 0; +} + +static int solo_enc_get_input(struct file *file, void *priv, + unsigned int *index) +{ + *index = 0; + + return 0; +} + +static int solo_enc_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + switch (f->index) { + case 0: + f->pixelformat = V4L2_PIX_FMT_MPEG4; + strcpy(f->description, "MPEG-4 AVC"); + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_MJPEG; + strcpy(f->description, "MJPEG"); + break; + default: + return -EINVAL; + } + + f->flags = V4L2_FMT_FLAG_COMPRESSED; + + return 0; +} + +static int solo_enc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (pix->pixelformat != V4L2_PIX_FMT_MPEG4 && + pix->pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + if (pix->width < solo_dev->video_hsize || + pix->height < solo_dev->video_vsize << 1) { + /* Default to CIF 1/2 size */ + pix->width = solo_dev->video_hsize >> 1; + pix->height = solo_dev->video_vsize; + } else { + /* Full frame */ + pix->width = solo_dev->video_hsize; + pix->height = solo_dev->video_vsize << 1; + } + + switch (pix->field) { + case V4L2_FIELD_NONE: + case V4L2_FIELD_INTERLACED: + break; + case V4L2_FIELD_ANY: + default: + pix->field = V4L2_FIELD_INTERLACED; + break; + } + + /* Just set these */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->sizeimage = FRAME_BUF_SIZE; + pix->bytesperline = 0; + pix->priv = 0; + + return 0; +} + +static int solo_enc_set_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + if (vb2_is_busy(&solo_enc->vidq)) + return -EBUSY; + + ret = solo_enc_try_fmt_cap(file, priv, f); + if (ret) + return ret; + + if (pix->width == solo_dev->video_hsize) + solo_enc->mode = SOLO_ENC_MODE_D1; + else + solo_enc->mode = SOLO_ENC_MODE_CIF; + + /* This does not change the encoder at all */ + solo_enc->fmt = pix->pixelformat; + + /* + * More information is needed about these 'extended' types. As far + * as I can tell these are basically additional video streams with + * different MPEG encoding attributes that can run in parallel with + * the main stream. If so, then this should be implemented as a + * second video node. Abusing priv like this is certainly not the + * right approach. + if (pix->priv) + solo_enc->type = SOLO_ENC_TYPE_EXT; + */ + return 0; +} + +static int solo_enc_get_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = solo_enc->width; + pix->height = solo_enc->height; + pix->pixelformat = solo_enc->fmt; + pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED : + V4L2_FIELD_NONE; + pix->sizeimage = FRAME_BUF_SIZE; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->priv = 0; + + return 0; +} + +static int solo_enc_g_std(struct file *file, void *priv, v4l2_std_id *i) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + *i = V4L2_STD_NTSC_M; + else + *i = V4L2_STD_PAL; + return 0; +} + +static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id std) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + + return solo_set_video_type(solo_enc->solo_dev, std & V4L2_STD_PAL); +} + +static int solo_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (fsize->pixel_format != V4L2_PIX_FMT_MPEG4 && + fsize->pixel_format != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + switch (fsize->index) { + case 0: + fsize->discrete.width = solo_dev->video_hsize >> 1; + fsize->discrete.height = solo_dev->video_vsize; + break; + case 1: + fsize->discrete.width = solo_dev->video_hsize; + fsize->discrete.height = solo_dev->video_vsize << 1; + break; + default: + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + + return 0; +} + +static int solo_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fintv) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (fintv->pixel_format != V4L2_PIX_FMT_MPEG4 && + fintv->pixel_format != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + if (fintv->index) + return -EINVAL; + if ((fintv->width != solo_dev->video_hsize >> 1 || + fintv->height != solo_dev->video_vsize) && + (fintv->width != solo_dev->video_hsize || + fintv->height != solo_dev->video_vsize << 1)) + return -EINVAL; + + fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; + + fintv->stepwise.min.numerator = 1; + fintv->stepwise.min.denominator = solo_dev->fps; + + fintv->stepwise.max.numerator = 15; + fintv->stepwise.max.denominator = solo_dev->fps; + + fintv->stepwise.step.numerator = 1; + fintv->stepwise.step.denominator = solo_dev->fps; + + return 0; +} + +static int solo_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_captureparm *cp = &sp->parm.capture; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = solo_enc->interval; + cp->timeperframe.denominator = solo_dev->fps; + cp->capturemode = 0; + /* XXX: Shouldn't we be able to get/set this from videobuf? */ + cp->readbuffers = 2; + + return 0; +} + +static int solo_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_captureparm *cp = &sp->parm.capture; + + if (vb2_is_streaming(&solo_enc->vidq)) + return -EBUSY; + + if ((cp->timeperframe.numerator == 0) || + (cp->timeperframe.denominator == 0)) { + /* reset framerate */ + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = solo_dev->fps; + } + + if (cp->timeperframe.denominator != solo_dev->fps) + cp->timeperframe.denominator = solo_dev->fps; + + if (cp->timeperframe.numerator > 15) + cp->timeperframe.numerator = 15; + + solo_enc->interval = cp->timeperframe.numerator; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->readbuffers = 2; + + solo_update_mode(solo_enc); + return 0; +} + +static long solo_enc_default(struct file *file, void *fh, + bool valid_prio, int cmd, void *arg) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct solo_motion_thresholds *thresholds = arg; + + switch (cmd) { + case SOLO_IOC_G_MOTION_THRESHOLDS: + *thresholds = solo_enc->motion_thresholds; + return 0; + + case SOLO_IOC_S_MOTION_THRESHOLDS: + if (!valid_prio) + return -EBUSY; + solo_enc->motion_thresholds = *thresholds; + if (solo_enc->motion_enabled && !solo_enc->motion_global) + return solo_set_motion_block(solo_dev, solo_enc->ch, + &solo_enc->motion_thresholds); + return 0; + default: + return -ENOTTY; + } +} + +static int solo_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct solo_enc_dev *solo_enc = + container_of(ctrl->handler, struct solo_enc_dev, hdl); + struct solo_dev *solo_dev = solo_enc->solo_dev; + int err; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_SHARPNESS: + return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, + ctrl->val); + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + solo_enc->gop = ctrl->val; + return 0; + case V4L2_CID_MOTION_THRESHOLD: + solo_enc->motion_thresh = ctrl->val; + if (!solo_enc->motion_global || !solo_enc->motion_enabled) + return 0; + return solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->val); + case V4L2_CID_MOTION_MODE: + solo_enc->motion_global = ctrl->val == 1; + solo_enc->motion_enabled = ctrl->val > 0; + if (ctrl->val) { + if (solo_enc->motion_global) + solo_set_motion_threshold(solo_dev, solo_enc->ch, + solo_enc->motion_thresh); + else + solo_set_motion_block(solo_dev, solo_enc->ch, + &solo_enc->motion_thresholds); + } + solo_motion_toggle(solo_enc, ctrl->val); + return 0; + case V4L2_CID_OSD_TEXT: + strcpy(solo_enc->osd_text, ctrl->string); + err = solo_osd_print(solo_enc); + return err; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_file_operations solo_enc_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { + .vidioc_querycap = solo_enc_querycap, + .vidioc_s_std = solo_enc_s_std, + .vidioc_g_std = solo_enc_g_std, + /* Input callbacks */ + .vidioc_enum_input = solo_enc_enum_input, + .vidioc_s_input = solo_enc_set_input, + .vidioc_g_input = solo_enc_get_input, + /* Video capture format callbacks */ + .vidioc_enum_fmt_vid_cap = solo_enc_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = solo_enc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap, + .vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap, + /* Streaming I/O */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + /* Frame size and interval */ + .vidioc_enum_framesizes = solo_enum_framesizes, + .vidioc_enum_frameintervals = solo_enum_frameintervals, + /* Video capture parameters */ + .vidioc_s_parm = solo_s_parm, + .vidioc_g_parm = solo_g_parm, + /* Logging and events */ + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_default = solo_enc_default, +}; + +static const struct video_device solo_enc_template = { + .name = SOLO6X10_NAME, + .fops = &solo_enc_fops, + .ioctl_ops = &solo_enc_ioctl_ops, + .minor = -1, + .release = video_device_release, + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, +}; + +static const struct v4l2_ctrl_ops solo_ctrl_ops = { + .s_ctrl = solo_s_ctrl, +}; + +static const struct v4l2_ctrl_config solo_motion_threshold_ctrl = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_MOTION_THRESHOLD, + .name = "Motion Detection Threshold", + .type = V4L2_CTRL_TYPE_INTEGER, + .max = 0xffff, + .def = SOLO_DEF_MOT_THRESH, + .step = 1, + .flags = V4L2_CTRL_FLAG_SLIDER, +}; + +static const char * const solo_motion_mode_menu[] = { + "Disabled", + "Global Threshold", + "Regional Threshold", + NULL +}; + +static const struct v4l2_ctrl_config solo_motion_enable_ctrl = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_MOTION_MODE, + .name = "Motion Detection Mode", + .type = V4L2_CTRL_TYPE_MENU, + .qmenu = solo_motion_mode_menu, + .max = 2, +}; + +static const struct v4l2_ctrl_config solo_osd_text_ctrl = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_OSD_TEXT, + .name = "OSD Text", + .type = V4L2_CTRL_TYPE_STRING, + .max = OSD_TEXT_MAX, + .step = 1, +}; + +static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, + u8 ch, unsigned nr) +{ + struct solo_enc_dev *solo_enc; + struct v4l2_ctrl_handler *hdl; + int ret; + int x, y; + + solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); + if (!solo_enc) + return ERR_PTR(-ENOMEM); + + hdl = &solo_enc->hdl; + v4l2_ctrl_handler_init(hdl, 10); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, 128); + if (tw28_has_sharpness(solo_dev, ch)) + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 15, 1, 0); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, solo_dev->fps); + v4l2_ctrl_new_custom(hdl, &solo_motion_threshold_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &solo_motion_enable_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &solo_osd_text_ctrl, NULL); + if (hdl->error) { + ret = hdl->error; + goto hdl_free; + } + + solo_enc->solo_dev = solo_dev; + solo_enc->ch = ch; + mutex_init(&solo_enc->lock); + spin_lock_init(&solo_enc->av_lock); + INIT_LIST_HEAD(&solo_enc->vidq_active); + solo_enc->fmt = V4L2_PIX_FMT_MPEG4; + solo_enc->type = SOLO_ENC_TYPE_STD; + + solo_enc->qp = SOLO_DEFAULT_QP; + solo_enc->gop = solo_dev->fps; + solo_enc->interval = 1; + solo_enc->mode = SOLO_ENC_MODE_CIF; + solo_enc->motion_global = true; + solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; + for (y = 0; y < SOLO_MOTION_SZ; y++) + for (x = 0; x < SOLO_MOTION_SZ; x++) + solo_enc->motion_thresholds.thresholds[y][x] = + SOLO_DEF_MOT_THRESH; + + solo_enc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + solo_enc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + solo_enc->vidq.ops = &solo_enc_video_qops; + solo_enc->vidq.mem_ops = &vb2_dma_sg_memops; + solo_enc->vidq.drv_priv = solo_enc; + solo_enc->vidq.gfp_flags = __GFP_DMA32; + solo_enc->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + solo_enc->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); + solo_enc->vidq.lock = &solo_enc->lock; + ret = vb2_queue_init(&solo_enc->vidq); + if (ret) + goto hdl_free; + solo_update_mode(solo_enc); + + spin_lock_init(&solo_enc->motion_lock); + + /* Initialize this per encoder */ + solo_enc->jpeg_len = sizeof(jpeg_header); + memcpy(solo_enc->jpeg_header, jpeg_header, solo_enc->jpeg_len); + + solo_enc->desc_nelts = 32; + solo_enc->desc_items = pci_alloc_consistent(solo_dev->pdev, + sizeof(struct solo_p2m_desc) * + solo_enc->desc_nelts, &solo_enc->desc_dma); + ret = -ENOMEM; + if (solo_enc->desc_items == NULL) + goto hdl_free; + + solo_enc->vfd = video_device_alloc(); + if (!solo_enc->vfd) + goto pci_free; + + *solo_enc->vfd = solo_enc_template; + solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev; + solo_enc->vfd->ctrl_handler = hdl; + solo_enc->vfd->queue = &solo_enc->vidq; + solo_enc->vfd->lock = &solo_enc->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &solo_enc->vfd->flags); + video_set_drvdata(solo_enc->vfd, solo_enc); + ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr); + if (ret < 0) + goto vdev_release; + + snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name), + "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num, + solo_enc->vfd->num); + + return solo_enc; + +vdev_release: + video_device_release(solo_enc->vfd); +pci_free: + pci_free_consistent(solo_enc->solo_dev->pdev, + sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts, + solo_enc->desc_items, solo_enc->desc_dma); +hdl_free: + v4l2_ctrl_handler_free(hdl); + kfree(solo_enc); + return ERR_PTR(ret); +} + +static void solo_enc_free(struct solo_enc_dev *solo_enc) +{ + if (solo_enc == NULL) + return; + + video_unregister_device(solo_enc->vfd); + v4l2_ctrl_handler_free(&solo_enc->hdl); + kfree(solo_enc); +} + +int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr) +{ + int i; + + init_waitqueue_head(&solo_dev->ring_thread_wait); + + solo_dev->vh_size = sizeof(struct vop_header); + solo_dev->vh_buf = pci_alloc_consistent(solo_dev->pdev, + solo_dev->vh_size, + &solo_dev->vh_dma); + if (solo_dev->vh_buf == NULL) + return -ENOMEM; + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i, nr); + if (IS_ERR(solo_dev->v4l2_enc[i])) + break; + } + + if (i != solo_dev->nr_chans) { + int ret = PTR_ERR(solo_dev->v4l2_enc[i]); + while (i--) + solo_enc_free(solo_dev->v4l2_enc[i]); + pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, + solo_dev->vh_buf, solo_dev->vh_dma); + solo_dev->vh_buf = NULL; + return ret; + } + + if (solo_dev->type == SOLO_DEV_6010) + solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; + else + solo_dev->enc_bw_remain = solo_dev->fps * 4 * 5; + + dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n", + solo_dev->v4l2_enc[0]->vfd->num, + solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num); + + return 0; +} + +void solo_enc_v4l2_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_enc_free(solo_dev->v4l2_enc[i]); + + if (solo_dev->vh_buf) + pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, + solo_dev->vh_buf, solo_dev->vh_dma); +} diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2.c b/drivers/staging/media/solo6x10/solo6x10-v4l2.c new file mode 100644 index 000000000000..7b26de3488da --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2.c @@ -0,0 +1,734 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "solo6x10.h" +#include "solo6x10-tw28.h" + +/* Image size is two fields, SOLO_HW_BPL is one horizontal line in hardware */ +#define SOLO_HW_BPL 2048 +#define solo_vlines(__solo) (__solo->video_vsize * 2) +#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ + solo_vlines(__solo)) +#define solo_bytesperline(__solo) (__solo->video_hsize * 2) + +#define MIN_VID_BUFFERS 2 + +static inline void erase_on(struct solo_dev *solo_dev) +{ + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); + solo_dev->erasing = 1; + solo_dev->frame_blank = 0; +} + +static inline int erase_off(struct solo_dev *solo_dev) +{ + if (!solo_dev->erasing) + return 0; + + /* First time around, assert erase off */ + if (!solo_dev->frame_blank) + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0); + /* Keep the erasing flag on for 8 frames minimum */ + if (solo_dev->frame_blank++ >= 8) + solo_dev->erasing = 0; + + return 1; +} + +void solo_video_in_isr(struct solo_dev *solo_dev) +{ + wake_up_interruptible_all(&solo_dev->disp_thread_wait); +} + +static void solo_win_setup(struct solo_dev *solo_dev, u8 ch, + int sx, int sy, int ex, int ey, int scale) +{ + if (ch >= solo_dev->nr_chans) + return; + + /* Here, we just keep window/channel the same */ + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch), + SOLO_VI_WIN_CHANNEL(ch) | + SOLO_VI_WIN_SX(sx) | + SOLO_VI_WIN_EX(ex) | + SOLO_VI_WIN_SCALE(scale)); + + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch), + SOLO_VI_WIN_SY(sy) | + SOLO_VI_WIN_EY(ey)); +} + +static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on) +{ + u8 ch = idx * 4; + + if (ch >= solo_dev->nr_chans) + return -EINVAL; + + if (!on) { + u8 i; + for (i = ch; i < ch + 4; i++) + solo_win_setup(solo_dev, i, solo_dev->video_hsize, + solo_vlines(solo_dev), + solo_dev->video_hsize, + solo_vlines(solo_dev), 0); + return 0; + } + + /* Row 1 */ + solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2, + solo_vlines(solo_dev) / 2, 3); + solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0, + solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3); + /* Row 2 */ + solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2, + solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3); + solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2, + solo_vlines(solo_dev) / 2, solo_dev->video_hsize, + solo_vlines(solo_dev), 3); + + return 0; +} + +static int solo_v4l2_ch_ext_16up(struct solo_dev *solo_dev, int on) +{ + int sy, ysize, hsize, i; + + if (!on) { + for (i = 0; i < 16; i++) + solo_win_setup(solo_dev, i, solo_dev->video_hsize, + solo_vlines(solo_dev), + solo_dev->video_hsize, + solo_vlines(solo_dev), 0); + return 0; + } + + ysize = solo_vlines(solo_dev) / 4; + hsize = solo_dev->video_hsize / 4; + + for (sy = 0, i = 0; i < 4; i++, sy += ysize) { + solo_win_setup(solo_dev, i * 4, 0, sy, hsize, + sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy, + hsize * 2, sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy, + hsize * 3, sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy, + solo_dev->video_hsize, sy + ysize, 5); + } + + return 0; +} + +static int solo_v4l2_ch(struct solo_dev *solo_dev, u8 ch, int on) +{ + u8 ext_ch; + + if (ch < solo_dev->nr_chans) { + solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize, + on ? 0 : solo_vlines(solo_dev), + solo_dev->video_hsize, solo_vlines(solo_dev), + on ? 1 : 0); + return 0; + } + + if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) + return -EINVAL; + + ext_ch = ch - solo_dev->nr_chans; + + /* 4up's first */ + if (ext_ch < 4) + return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on); + + /* Remaining case is 16up for 16-port */ + return solo_v4l2_ch_ext_16up(solo_dev, on); +} + +static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch) +{ + if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) + return -EINVAL; + + erase_on(solo_dev); + + solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0); + solo_v4l2_ch(solo_dev, ch, 1); + + solo_dev->cur_disp_ch = ch; + + return 0; +} + +static void solo_fillbuf(struct solo_dev *solo_dev, + struct vb2_buffer *vb) +{ + dma_addr_t vbuf; + unsigned int fdma_addr; + int error = -1; + int i; + + vbuf = vb2_dma_contig_plane_dma_addr(vb, 0); + if (!vbuf) + goto finish_buf; + + if (erase_off(solo_dev)) { + void *p = vb2_plane_vaddr(vb, 0); + int image_size = solo_image_size(solo_dev); + for (i = 0; i < image_size; i += 2) { + ((u8 *)p)[i] = 0x80; + ((u8 *)p)[i + 1] = 0x00; + } + error = 0; + } else { + fdma_addr = SOLO_DISP_EXT_ADDR + (solo_dev->old_write * + (SOLO_HW_BPL * solo_vlines(solo_dev))); + + error = solo_p2m_dma_t(solo_dev, 0, vbuf, fdma_addr, + solo_bytesperline(solo_dev), + solo_vlines(solo_dev), SOLO_HW_BPL); + } + +finish_buf: + if (!error) { + vb2_set_plane_payload(vb, 0, + solo_vlines(solo_dev) * solo_bytesperline(solo_dev)); + vb->v4l2_buf.sequence = solo_dev->sequence++; + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); + } + + vb2_buffer_done(vb, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); +} + +static void solo_thread_try(struct solo_dev *solo_dev) +{ + struct solo_vb2_buf *vb; + + /* Only "break" from this loop if slock is held, otherwise + * just return. */ + for (;;) { + unsigned int cur_write; + + cur_write = SOLO_VI_STATUS0_PAGE( + solo_reg_read(solo_dev, SOLO_VI_STATUS0)); + if (cur_write == solo_dev->old_write) + return; + + spin_lock(&solo_dev->slock); + + if (list_empty(&solo_dev->vidq_active)) + break; + + vb = list_first_entry(&solo_dev->vidq_active, struct solo_vb2_buf, + list); + + solo_dev->old_write = cur_write; + list_del(&vb->list); + + spin_unlock(&solo_dev->slock); + + solo_fillbuf(solo_dev, &vb->vb); + } + + assert_spin_locked(&solo_dev->slock); + spin_unlock(&solo_dev->slock); +} + +static int solo_thread(void *data) +{ + struct solo_dev *solo_dev = data; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&solo_dev->disp_thread_wait, &wait); + + for (;;) { + long timeout = schedule_timeout_interruptible(HZ); + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + solo_thread_try(solo_dev); + try_to_freeze(); + } + + remove_wait_queue(&solo_dev->disp_thread_wait, &wait); + + return 0; +} + +static int solo_start_thread(struct solo_dev *solo_dev) +{ + int ret = 0; + + solo_dev->kthread = kthread_run(solo_thread, solo_dev, SOLO6X10_NAME "_disp"); + + if (IS_ERR(solo_dev->kthread)) { + ret = PTR_ERR(solo_dev->kthread); + solo_dev->kthread = NULL; + return ret; + } + solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN); + + return ret; +} + +static void solo_stop_thread(struct solo_dev *solo_dev) +{ + if (!solo_dev->kthread) + return; + + solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); + kthread_stop(solo_dev->kthread); + solo_dev->kthread = NULL; +} + +static int solo_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct solo_dev *solo_dev = vb2_get_drv_priv(q); + + sizes[0] = solo_image_size(solo_dev); + alloc_ctxs[0] = solo_dev->alloc_ctx; + *num_planes = 1; + + if (*num_buffers < MIN_VID_BUFFERS) + *num_buffers = MIN_VID_BUFFERS; + + return 0; +} + +static int solo_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct solo_dev *solo_dev = vb2_get_drv_priv(q); + + solo_dev->sequence = 0; + return solo_start_thread(solo_dev); +} + +static int solo_stop_streaming(struct vb2_queue *q) +{ + struct solo_dev *solo_dev = vb2_get_drv_priv(q); + + solo_stop_thread(solo_dev); + INIT_LIST_HEAD(&solo_dev->vidq_active); + return 0; +} + +static void solo_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct solo_dev *solo_dev = vb2_get_drv_priv(vq); + struct solo_vb2_buf *solo_vb = + container_of(vb, struct solo_vb2_buf, vb); + + spin_lock(&solo_dev->slock); + list_add_tail(&solo_vb->list, &solo_dev->vidq_active); + spin_unlock(&solo_dev->slock); + wake_up_interruptible(&solo_dev->disp_thread_wait); +} + +static const struct vb2_ops solo_video_qops = { + .queue_setup = solo_queue_setup, + .buf_queue = solo_buf_queue, + .start_streaming = solo_start_streaming, + .stop_streaming = solo_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int solo_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + strcpy(cap->driver, SOLO6X10_NAME); + strcpy(cap->card, "Softlogic 6x10"); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + pci_name(solo_dev->pdev)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int solo_enum_ext_input(struct solo_dev *solo_dev, + struct v4l2_input *input) +{ + static const char * const dispnames_1[] = { "4UP" }; + static const char * const dispnames_2[] = { "4UP-1", "4UP-2" }; + static const char * const dispnames_5[] = { + "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" + }; + const char * const *dispnames; + + if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) + return -EINVAL; + + if (solo_dev->nr_ext == 5) + dispnames = dispnames_5; + else if (solo_dev->nr_ext == 2) + dispnames = dispnames_2; + else + dispnames = dispnames_1; + + snprintf(input->name, sizeof(input->name), "Multi %s", + dispnames[input->index - solo_dev->nr_chans]); + + return 0; +} + +static int solo_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + if (input->index >= solo_dev->nr_chans) { + int ret = solo_enum_ext_input(solo_dev, input); + if (ret < 0) + return ret; + } else { + snprintf(input->name, sizeof(input->name), "Camera %d", + input->index + 1); + + /* We can only check this for normal inputs */ + if (!tw28_get_video_status(solo_dev, input->index)) + input->status = V4L2_IN_ST_NO_SIGNAL; + } + + input->type = V4L2_INPUT_TYPE_CAMERA; + input->std = solo_dev->vfd->tvnorms; + return 0; +} + +static int solo_set_input(struct file *file, void *priv, unsigned int index) +{ + struct solo_dev *solo_dev = video_drvdata(file); + int ret = solo_v4l2_set_ch(solo_dev, index); + + if (!ret) { + while (erase_off(solo_dev)) + /* Do nothing */; + } + + return ret; +} + +static int solo_get_input(struct file *file, void *priv, unsigned int *index) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + *index = solo_dev->cur_disp_ch; + + return 0; +} + +static int solo_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_PIX_FMT_UYVY; + strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description)); + + return 0; +} + +static int solo_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_dev *solo_dev = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + int image_size = solo_image_size(solo_dev); + + if (pix->pixelformat != V4L2_PIX_FMT_UYVY) + return -EINVAL; + + pix->width = solo_dev->video_hsize; + pix->height = solo_vlines(solo_dev); + pix->sizeimage = image_size; + pix->field = V4L2_FIELD_INTERLACED; + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->priv = 0; + return 0; +} + +static int solo_set_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + if (vb2_is_busy(&solo_dev->vidq)) + return -EBUSY; + + /* For right now, if it doesn't match our running config, + * then fail */ + return solo_try_fmt_cap(file, priv, f); +} + +static int solo_get_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_dev *solo_dev = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = solo_dev->video_hsize; + pix->height = solo_vlines(solo_dev); + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->field = V4L2_FIELD_INTERLACED; + pix->sizeimage = solo_image_size(solo_dev); + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->bytesperline = solo_bytesperline(solo_dev); + pix->priv = 0; + + return 0; +} + +static int solo_g_std(struct file *file, void *priv, v4l2_std_id *i) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + *i = V4L2_STD_NTSC_M; + else + *i = V4L2_STD_PAL; + return 0; +} + +int solo_set_video_type(struct solo_dev *solo_dev, bool type) +{ + int i; + + /* Make sure all video nodes are idle */ + if (vb2_is_busy(&solo_dev->vidq)) + return -EBUSY; + for (i = 0; i < solo_dev->nr_chans; i++) + if (vb2_is_busy(&solo_dev->v4l2_enc[i]->vidq)) + return -EBUSY; + solo_dev->video_type = type; + /* Reconfigure for the new standard */ + solo_disp_init(solo_dev); + solo_enc_init(solo_dev); + solo_tw28_init(solo_dev); + for (i = 0; i < solo_dev->nr_chans; i++) + solo_update_mode(solo_dev->v4l2_enc[i]); + return solo_v4l2_set_ch(solo_dev, solo_dev->cur_disp_ch); +} + +static int solo_s_std(struct file *file, void *priv, v4l2_std_id std) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + return solo_set_video_type(solo_dev, std & V4L2_STD_PAL); +} + +static int solo_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct solo_dev *solo_dev = + container_of(ctrl->handler, struct solo_dev, disp_hdl); + + switch (ctrl->id) { + case V4L2_CID_MOTION_TRACE: + if (ctrl->val) { + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, + SOLO_VI_MOTION_Y_ADD | + SOLO_VI_MOTION_Y_VALUE(0x20) | + SOLO_VI_MOTION_CB_VALUE(0x10) | + SOLO_VI_MOTION_CR_VALUE(0x10)); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, + SOLO_VI_MOTION_CR_ADD | + SOLO_VI_MOTION_Y_VALUE(0x10) | + SOLO_VI_MOTION_CB_VALUE(0x80) | + SOLO_VI_MOTION_CR_VALUE(0x10)); + } else { + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); + } + return 0; + default: + break; + } + return -EINVAL; +} + +static const struct v4l2_file_operations solo_v4l2_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { + .vidioc_querycap = solo_querycap, + .vidioc_s_std = solo_s_std, + .vidioc_g_std = solo_g_std, + /* Input callbacks */ + .vidioc_enum_input = solo_enum_input, + .vidioc_s_input = solo_set_input, + .vidioc_g_input = solo_get_input, + /* Video capture format callbacks */ + .vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = solo_try_fmt_cap, + .vidioc_s_fmt_vid_cap = solo_set_fmt_cap, + .vidioc_g_fmt_vid_cap = solo_get_fmt_cap, + /* Streaming I/O */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + /* Logging and events */ + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device solo_v4l2_template = { + .name = SOLO6X10_NAME, + .fops = &solo_v4l2_fops, + .ioctl_ops = &solo_v4l2_ioctl_ops, + .minor = -1, + .release = video_device_release, + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, +}; + +static const struct v4l2_ctrl_ops solo_ctrl_ops = { + .s_ctrl = solo_s_ctrl, +}; + +static const struct v4l2_ctrl_config solo_motion_trace_ctrl = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_MOTION_TRACE, + .name = "Motion Detection Trace", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) +{ + int ret; + int i; + + init_waitqueue_head(&solo_dev->disp_thread_wait); + spin_lock_init(&solo_dev->slock); + mutex_init(&solo_dev->lock); + INIT_LIST_HEAD(&solo_dev->vidq_active); + + solo_dev->vfd = video_device_alloc(); + if (!solo_dev->vfd) + return -ENOMEM; + + *solo_dev->vfd = solo_v4l2_template; + solo_dev->vfd->v4l2_dev = &solo_dev->v4l2_dev; + solo_dev->vfd->queue = &solo_dev->vidq; + solo_dev->vfd->lock = &solo_dev->lock; + v4l2_ctrl_handler_init(&solo_dev->disp_hdl, 1); + v4l2_ctrl_new_custom(&solo_dev->disp_hdl, &solo_motion_trace_ctrl, NULL); + if (solo_dev->disp_hdl.error) { + ret = solo_dev->disp_hdl.error; + goto fail; + } + solo_dev->vfd->ctrl_handler = &solo_dev->disp_hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &solo_dev->vfd->flags); + + video_set_drvdata(solo_dev->vfd, solo_dev); + + solo_dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + solo_dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + solo_dev->vidq.ops = &solo_video_qops; + solo_dev->vidq.mem_ops = &vb2_dma_contig_memops; + solo_dev->vidq.drv_priv = solo_dev; + solo_dev->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + solo_dev->vidq.gfp_flags = __GFP_DMA32; + solo_dev->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); + solo_dev->vidq.lock = &solo_dev->lock; + ret = vb2_queue_init(&solo_dev->vidq); + if (ret < 0) + goto fail; + + solo_dev->alloc_ctx = vb2_dma_contig_init_ctx(&solo_dev->pdev->dev); + if (IS_ERR(solo_dev->alloc_ctx)) { + dev_err(&solo_dev->pdev->dev, "Can't allocate buffer context"); + return PTR_ERR(solo_dev->alloc_ctx); + } + + /* Cycle all the channels and clear */ + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_v4l2_set_ch(solo_dev, i); + while (erase_off(solo_dev)) + /* Do nothing */; + } + + /* Set the default display channel */ + solo_v4l2_set_ch(solo_dev, 0); + while (erase_off(solo_dev)) + /* Do nothing */; + + ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr); + if (ret < 0) + goto fail; + + snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)", + SOLO6X10_NAME, solo_dev->vfd->num); + + dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with " + "%d inputs (%d extended)\n", solo_dev->vfd->num, + solo_dev->nr_chans, solo_dev->nr_ext); + + return 0; + +fail: + video_device_release(solo_dev->vfd); + vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); + v4l2_ctrl_handler_free(&solo_dev->disp_hdl); + solo_dev->vfd = NULL; + return ret; +} + +void solo_v4l2_exit(struct solo_dev *solo_dev) +{ + if (solo_dev->vfd == NULL) + return; + + video_unregister_device(solo_dev->vfd); + vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); + v4l2_ctrl_handler_free(&solo_dev->disp_hdl); + solo_dev->vfd = NULL; +} diff --git a/drivers/staging/media/solo6x10/tw28.c b/drivers/staging/media/solo6x10/tw28.c deleted file mode 100644 index ad00e2b60323..000000000000 --- a/drivers/staging/media/solo6x10/tw28.c +++ /dev/null @@ -1,854 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include - -#include "solo6x10.h" -#include "solo6x10-tw28.h" - -#define DEFAULT_HDELAY_NTSC (32 - 8) -#define DEFAULT_HACTIVE_NTSC (720 + 16) -#define DEFAULT_VDELAY_NTSC (7 - 2) -#define DEFAULT_VACTIVE_NTSC (240 + 4) - -#define DEFAULT_HDELAY_PAL (32 + 4) -#define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL) -#define DEFAULT_VDELAY_PAL (6) -#define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL) - - -static const u8 tbl_tw2864_ntsc_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ - 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ - 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ - 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ - 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, - 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01, - 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, - 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, - 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81, - 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, -}; - -static const u8 tbl_tw2864_pal_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ - 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ - 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ - 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ - 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, - 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01, - 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, - 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, - 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81, - 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, -}; - -static const u8 tbl_tw2865_ntsc_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */ - 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, - 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ - 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, - 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, - 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, - 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ - 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8, - 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, - 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, -}; - -static const u8 tbl_tw2865_pal_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */ - 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, - 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ - 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, - 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, - 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, - 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ - 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8, - 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, - 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, -}; - -#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id))) - -static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off, - u8 tw_off) -{ - if (is_tw286x(solo_dev, chip_id)) - return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw6x_off); - else - return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw_off); -} - -static void tw_writebyte(struct solo_dev *solo_dev, int chip_id, - u8 tw6x_off, u8 tw_off, u8 val) -{ - if (is_tw286x(solo_dev, chip_id)) - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw6x_off, val); - else - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw_off, val); -} - -static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off, - u8 val) -{ - int i; - - for (i = 0; i < 5; i++) { - u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off); - if (rval == val) - return; - - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val); - msleep_interruptible(1); - } - -/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */ -/* addr, off, val); */ -} - -static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_tw2865_common[256]; - int i; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) - memcpy(tbl_tw2865_common, tbl_tw2865_pal_template, - sizeof(tbl_tw2865_common)); - else - memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template, - sizeof(tbl_tw2865_common)); - - /* ALINK Mode */ - if (solo_dev->nr_chans == 4) { - tbl_tw2865_common[0xd2] = 0x01; - tbl_tw2865_common[0xcf] = 0x00; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2865_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2865_common[0xcf] = 0x80; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2865_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2865_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2865_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2865_common[0xcf] = 0x80; - } - - for (i = 0; i < 0xff; i++) { - /* Skip read only registers */ - switch (i) { - case 0xb8 ... 0xc1: - case 0xc4 ... 0xc7: - case 0xfd: - continue; - } - switch (i & ~0x30) { - case 0x00: - case 0x0c ... 0x0d: - continue; - } - - tw_write_and_verify(solo_dev, dev_addr, i, - tbl_tw2865_common[i]); - } - - return 0; -} - -static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_tw2864_common[256]; - int i; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) - memcpy(tbl_tw2864_common, tbl_tw2864_pal_template, - sizeof(tbl_tw2864_common)); - else - memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template, - sizeof(tbl_tw2864_common)); - - if (solo_dev->tw2865 == 0) { - /* IRQ Mode */ - if (solo_dev->nr_chans == 4) { - tbl_tw2864_common[0xd2] = 0x01; - tbl_tw2864_common[0xcf] = 0x00; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2864_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x40; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2864_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2864_common[0xcf] = 0x40; - } - } else { - /* ALINK Mode. Assumes that the first tw28xx is a - * 2865 and these are in cascade. */ - for (i = 0; i <= 4; i++) - tbl_tw2864_common[0x08 | i << 4] = 0x12; - - if (solo_dev->nr_chans == 8) { - tbl_tw2864_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x80; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2864_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2864_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2864_common[0xcf] = 0x80; - } - } - - for (i = 0; i < 0xff; i++) { - /* Skip read only registers */ - switch (i) { - case 0xb8 ... 0xc1: - case 0xfd: - continue; - } - switch (i & ~0x30) { - case 0x00: - case 0x0c: - case 0x0d: - continue; - } - - tw_write_and_verify(solo_dev, dev_addr, i, - tbl_tw2864_common[i]); - } - - return 0; -} - -static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_ntsc_tw2815_common[] = { - 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80, - 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11, - }; - - u8 tbl_pal_tw2815_common[] = { - 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80, - 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11, - }; - - u8 tbl_tw2815_sfr[] = { - 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */ - 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */ - 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00, - 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */ - 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88, - 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */ - }; - u8 *tbl_tw2815_common; - int i; - int ch; - - tbl_ntsc_tw2815_common[0x06] = 0; - - /* Horizontal Delay Control */ - tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8); - - /* Horizontal Active Control */ - tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2); - - /* Vertical Delay Control */ - tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4); - - /* Vertical Active Control */ - tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5); - - tbl_pal_tw2815_common[0x06] = 0; - - /* Horizontal Delay Control */ - tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8); - - /* Horizontal Active Control */ - tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2); - - /* Vertical Delay Control */ - tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4); - - /* Vertical Active Control */ - tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5); - - tbl_tw2815_common = - (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ? - tbl_ntsc_tw2815_common : tbl_pal_tw2815_common; - - /* Dual ITU-R BT.656 format */ - tbl_tw2815_common[0x0d] |= 0x04; - - /* Audio configuration */ - tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6); - - if (solo_dev->nr_chans == 4) { - tbl_tw2815_sfr[0x63 - 0x40] |= 1; - tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2815_sfr[0x63 - 0x40] |= 2; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2815_sfr[0x63 - 0x40] |= 3; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; - } - - /* Output mode of R_ADATM pin (0 mixing, 1 record) */ - /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */ - - /* 8KHz, used to be 16KHz, but changed for remote client compat */ - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2; - tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2; - - /* Playback of right channel */ - tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5; - - /* Reserved value (XXX ??) */ - tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5; - - /* Analog output gain and mix ratio playback on full */ - tbl_tw2815_sfr[0x70 - 0x40] |= 0xff; - /* Select playback audio and mute all except */ - tbl_tw2815_sfr[0x71 - 0x40] |= 0x10; - tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f; - - /* End of audio configuration */ - - for (ch = 0; ch < 4; ch++) { - tbl_tw2815_common[0x0d] &= ~3; - switch (ch) { - case 0: - tbl_tw2815_common[0x0d] |= 0x21; - break; - case 1: - tbl_tw2815_common[0x0d] |= 0x20; - break; - case 2: - tbl_tw2815_common[0x0d] |= 0x23; - break; - case 3: - tbl_tw2815_common[0x0d] |= 0x22; - break; - } - - for (i = 0; i < 0x0f; i++) { - if (i == 0x00) - continue; /* read-only */ - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - dev_addr, (ch * 0x10) + i, - tbl_tw2815_common[i]); - } - } - - for (i = 0x40; i < 0x76; i++) { - /* Skip read-only and nop registers */ - if (i == 0x40 || i == 0x59 || i == 0x5a || - i == 0x5d || i == 0x5e || i == 0x5f) - continue; - - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i, - tbl_tw2815_sfr[i - 0x40]); - } - - return 0; -} - -#define FIRST_ACTIVE_LINE 0x0008 -#define LAST_ACTIVE_LINE 0x0102 - -static void saa7128_setup(struct solo_dev *solo_dev) -{ - int i; - unsigned char regs[128] = { - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, - 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00, - 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, - 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18, - 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f, - 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06, - 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e, - 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77, - 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00, - 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff, - }; - - regs[0x7A] = FIRST_ACTIVE_LINE & 0xff; - regs[0x7B] = LAST_ACTIVE_LINE & 0xff; - regs[0x7C] = ((1 << 7) | - (((LAST_ACTIVE_LINE >> 8) & 1) << 6) | - (((FIRST_ACTIVE_LINE >> 8) & 1) << 4)); - - /* PAL: XXX: We could do a second set of regs to avoid this */ - if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) { - regs[0x28] = 0xE1; - - regs[0x5A] = 0x0F; - regs[0x61] = 0x02; - regs[0x62] = 0x35; - regs[0x63] = 0xCB; - regs[0x64] = 0x8A; - regs[0x65] = 0x09; - regs[0x66] = 0x2A; - - regs[0x6C] = 0xf1; - regs[0x6E] = 0x20; - - regs[0x7A] = 0x06 + 12; - regs[0x7b] = 0x24 + 12; - regs[0x7c] |= 1 << 6; - } - - /* First 0x25 bytes are read-only? */ - for (i = 0x26; i < 128; i++) { - if (i == 0x60 || i == 0x7D) - continue; - solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]); - } - - return; -} - -int solo_tw28_init(struct solo_dev *solo_dev) -{ - int i; - u8 value; - - solo_dev->tw28_cnt = 0; - - /* Detect techwell chip type(s) */ - for (i = 0; i < solo_dev->nr_chans / 4; i++) { - value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(i), 0xFF); - - switch (value >> 3) { - case 0x18: - solo_dev->tw2865 |= 1 << i; - solo_dev->tw28_cnt++; - break; - case 0x0c: - solo_dev->tw2864 |= 1 << i; - solo_dev->tw28_cnt++; - break; - default: - value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(i), - 0x59); - if ((value >> 3) == 0x04) { - solo_dev->tw2815 |= 1 << i; - solo_dev->tw28_cnt++; - } - } - } - - if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) { - dev_err(&solo_dev->pdev->dev, - "Could not initialize any techwell chips\n"); - return -EINVAL; - } - - saa7128_setup(solo_dev); - - for (i = 0; i < solo_dev->tw28_cnt; i++) { - if ((solo_dev->tw2865 & (1 << i))) - tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - else if ((solo_dev->tw2864 & (1 << i))) - tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - else - tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - } - - return 0; -} - -/* - * We accessed the video status signal in the Techwell chip through - * iic/i2c because the video status reported by register REG_VI_STATUS1 - * (address 0x012C) of the SOLO6010 chip doesn't give the correct video - * status signal values. - */ -int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch) -{ - u8 val, chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR, - TW_AV_STAT_ADDR) & 0x0f; - - return val & (1 << ch) ? 1 : 0; -} - -#if 0 -/* Status of audio from up to 4 techwell chips are combined into 1 variable. - * See techwell datasheet for details. */ -u16 tw28_get_audio_status(struct solo_dev *solo_dev) -{ - u8 val; - u16 status = 0; - int i; - - for (i = 0; i < solo_dev->tw28_cnt; i++) { - val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR, - TW_AV_STAT_ADDR) & 0xf0) >> 4; - status |= val << (i * 4); - } - - return status; -} -#endif - -bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch) -{ - return is_tw286x(solo_dev, ch / 4); -} - -int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, - s32 val) -{ - char sval; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - if (val > 255 || val < 0) - return -ERANGE; - - switch (ctrl) { - case V4L2_CID_SHARPNESS: - /* Only 286x has sharpness */ - if (is_tw286x(solo_dev, chip_num)) { - u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num)); - v &= 0xf0; - v |= val; - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num), v); - } else { - return -EINVAL; - } - break; - - case V4L2_CID_HUE: - if (is_tw286x(solo_dev, chip_num)) - sval = val - 128; - else - sval = (char)val; - tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), - TW_HUE_ADDR(ch), sval); - - break; - - case V4L2_CID_SATURATION: - /* 286x chips have a U and V component for saturation */ - if (is_tw286x(solo_dev, chip_num)) { - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SATURATIONU_ADDR(ch), val); - } - tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch), - TW_SATURATION_ADDR(ch), val); - - break; - - case V4L2_CID_CONTRAST: - tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch), - TW_CONTRAST_ADDR(ch), val); - break; - - case V4L2_CID_BRIGHTNESS: - if (is_tw286x(solo_dev, chip_num)) - sval = val - 128; - else - sval = (char)val; - tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch), - TW_BRIGHTNESS_ADDR(ch), sval); - - break; - default: - return -EINVAL; - } - - return 0; -} - -int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, - s32 *val) -{ - u8 rval, chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - switch (ctrl) { - case V4L2_CID_SHARPNESS: - /* Only 286x has sharpness */ - if (is_tw286x(solo_dev, chip_num)) { - rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num)); - *val = rval & 0x0f; - } else - *val = 0; - break; - case V4L2_CID_HUE: - rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), - TW_HUE_ADDR(ch)); - if (is_tw286x(solo_dev, chip_num)) - *val = (s32)((char)rval) + 128; - else - *val = rval; - break; - case V4L2_CID_SATURATION: - *val = tw_readbyte(solo_dev, chip_num, - TW286x_SATURATIONU_ADDR(ch), - TW_SATURATION_ADDR(ch)); - break; - case V4L2_CID_CONTRAST: - *val = tw_readbyte(solo_dev, chip_num, - TW286x_CONTRAST_ADDR(ch), - TW_CONTRAST_ADDR(ch)); - break; - case V4L2_CID_BRIGHTNESS: - rval = tw_readbyte(solo_dev, chip_num, - TW286x_BRIGHTNESS_ADDR(ch), - TW_BRIGHTNESS_ADDR(ch)); - if (is_tw286x(solo_dev, chip_num)) - *val = (s32)((char)rval) + 128; - else - *val = rval; - break; - default: - return -EINVAL; - } - - return 0; -} - -#if 0 -/* - * For audio output volume, the output channel is only 1. In this case we - * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used - * is the base address of the techwell chip. - */ -void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val) -{ - unsigned int val; - unsigned int chip_num; - - chip_num = (solo_dev->nr_chans - 1) / 4; - - val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, - TW_AUDIO_OUTPUT_VOL_ADDR); - - u_val = (val & 0x0f) | (u_val << 4); - - tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, - TW_AUDIO_OUTPUT_VOL_ADDR, u_val); -} -#endif - -u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch) -{ - u8 val; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - val = tw_readbyte(solo_dev, chip_num, - TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch)); - - return (ch % 2) ? (val >> 4) : (val & 0x0f); -} - -void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val) -{ - u8 old_val; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - old_val = tw_readbyte(solo_dev, chip_num, - TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch)); - - val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) | - ((ch % 2) ? (val << 4) : val); - - tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch), val); -} diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c deleted file mode 100644 index 39a8fb2802e4..000000000000 --- a/drivers/staging/media/solo6x10/v4l2-enc.c +++ /dev/null @@ -1,1363 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "solo6x10.h" -#include "solo6x10-tw28.h" -#include "solo6x10-jpeg.h" - -#define MIN_VID_BUFFERS 2 -#define FRAME_BUF_SIZE (196 * 1024) -#define MP4_QS 16 -#define DMA_ALIGN 4096 - -/* 6010 M4V */ -static unsigned char vop_6010_ntsc_d1[] = { - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, - 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, - 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, - 0x1f, 0x4c, 0x58, 0x10, 0xf0, 0x71, 0x18, 0x3f, -}; - -static unsigned char vop_6010_ntsc_cif[] = { - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, - 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, - 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, - 0x1f, 0x4c, 0x2c, 0x10, 0x78, 0x51, 0x18, 0x3f, -}; - -static unsigned char vop_6010_pal_d1[] = { - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, - 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, - 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, - 0x1f, 0x4c, 0x58, 0x11, 0x20, 0x71, 0x18, 0x3f, -}; - -static unsigned char vop_6010_pal_cif[] = { - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, - 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, - 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, - 0x1f, 0x4c, 0x2c, 0x10, 0x90, 0x51, 0x18, 0x3f, -}; - -/* 6110 h.264 */ -static unsigned char vop_6110_ntsc_d1[] = { - 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, - 0x9a, 0x74, 0x05, 0x81, 0xec, 0x80, 0x00, 0x00, - 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, -}; - -static unsigned char vop_6110_ntsc_cif[] = { - 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, - 0x9a, 0x74, 0x0b, 0x0f, 0xc8, 0x00, 0x00, 0x00, - 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, -}; - -static unsigned char vop_6110_pal_d1[] = { - 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, - 0x9a, 0x74, 0x05, 0x80, 0x93, 0x20, 0x00, 0x00, - 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, -}; - -static unsigned char vop_6110_pal_cif[] = { - 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, - 0x9a, 0x74, 0x0b, 0x04, 0xb2, 0x00, 0x00, 0x00, - 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, -}; - -struct vop_header { - /* VE_STATUS0 */ - u32 mpeg_size:20, sad_motion_flag:1, video_motion_flag:1, vop_type:2, - channel:5, source_fl:1, interlace:1, progressive:1; - - /* VE_STATUS1 */ - u32 vsize:8, hsize:8, last_queue:4, nop0:8, scale:4; - - /* VE_STATUS2 */ - u32 mpeg_off; - - /* VE_STATUS3 */ - u32 jpeg_off; - - /* VE_STATUS4 */ - u32 jpeg_size:20, interval:10, nop1:2; - - /* VE_STATUS5/6 */ - u32 sec, usec; - - /* VE_STATUS7/8/9 */ - u32 nop2[3]; - - /* VE_STATUS10 */ - u32 mpeg_size_alt:20, nop3:12; - - u32 end_nops[5]; -} __packed; - -struct solo_enc_buf { - enum solo_enc_types type; - struct vop_header *vh; - int motion; -}; - -static int solo_is_motion_on(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - - return (solo_dev->motion_mask >> solo_enc->ch) & 1; -} - -static int solo_motion_detected(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - unsigned long flags; - u32 ch_mask = 1 << solo_enc->ch; - int ret = 0; - - spin_lock_irqsave(&solo_enc->motion_lock, flags); - if (solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS) & ch_mask) { - solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, ch_mask); - ret = 1; - } - spin_unlock_irqrestore(&solo_enc->motion_lock, flags); - - return ret; -} - -static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - u32 mask = 1 << solo_enc->ch; - unsigned long flags; - - spin_lock_irqsave(&solo_enc->motion_lock, flags); - - if (on) - solo_dev->motion_mask |= mask; - else - solo_dev->motion_mask &= ~mask; - - solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, mask); - - solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, - SOLO_VI_MOTION_EN(solo_dev->motion_mask) | - (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); - - spin_unlock_irqrestore(&solo_enc->motion_lock, flags); -} - -void solo_update_mode(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - int vop_len; - unsigned char *vop; - - solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; - solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); - - if (solo_enc->mode == SOLO_ENC_MODE_CIF) { - solo_enc->width = solo_dev->video_hsize >> 1; - solo_enc->height = solo_dev->video_vsize; - if (solo_dev->type == SOLO_DEV_6110) { - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - vop = vop_6110_ntsc_cif; - vop_len = sizeof(vop_6110_ntsc_cif); - } else { - vop = vop_6110_pal_cif; - vop_len = sizeof(vop_6110_pal_cif); - } - } else { - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - vop = vop_6010_ntsc_cif; - vop_len = sizeof(vop_6010_ntsc_cif); - } else { - vop = vop_6010_pal_cif; - vop_len = sizeof(vop_6010_pal_cif); - } - } - } else { - solo_enc->width = solo_dev->video_hsize; - solo_enc->height = solo_dev->video_vsize << 1; - solo_enc->bw_weight <<= 2; - if (solo_dev->type == SOLO_DEV_6110) { - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - vop = vop_6110_ntsc_d1; - vop_len = sizeof(vop_6110_ntsc_d1); - } else { - vop = vop_6110_pal_d1; - vop_len = sizeof(vop_6110_pal_d1); - } - } else { - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - vop = vop_6010_ntsc_d1; - vop_len = sizeof(vop_6010_ntsc_d1); - } else { - vop = vop_6010_pal_d1; - vop_len = sizeof(vop_6010_pal_d1); - } - } - } - - memcpy(solo_enc->vop, vop, vop_len); - - /* Some fixups for 6010/M4V */ - if (solo_dev->type == SOLO_DEV_6010) { - u16 fps = solo_dev->fps * 1000; - u16 interval = solo_enc->interval * 1000; - - vop = solo_enc->vop; - - /* Frame rate and interval */ - vop[22] = fps >> 4; - vop[23] = ((fps << 4) & 0xf0) | 0x0c - | ((interval >> 13) & 0x3); - vop[24] = (interval >> 5) & 0xff; - vop[25] = ((interval << 3) & 0xf8) | 0x04; - } - - solo_enc->vop_len = vop_len; - - /* Now handle the jpeg header */ - vop = solo_enc->jpeg_header; - vop[SOF0_START + 5] = 0xff & (solo_enc->height >> 8); - vop[SOF0_START + 6] = 0xff & solo_enc->height; - vop[SOF0_START + 7] = 0xff & (solo_enc->width >> 8); - vop[SOF0_START + 8] = 0xff & solo_enc->width; - - memcpy(vop + DQT_START, - jpeg_dqt[solo_g_jpeg_qp(solo_dev, solo_enc->ch)], DQT_LEN); -} - -static int solo_enc_on(struct solo_enc_dev *solo_enc) -{ - u8 ch = solo_enc->ch; - struct solo_dev *solo_dev = solo_enc->solo_dev; - u8 interval; - - solo_update_mode(solo_enc); - - /* Make sure to do a bandwidth check */ - if (solo_enc->bw_weight > solo_dev->enc_bw_remain) - return -EBUSY; - solo_enc->sequence = 0; - solo_dev->enc_bw_remain -= solo_enc->bw_weight; - - if (solo_enc->type == SOLO_ENC_TYPE_EXT) - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); - - /* Disable all encoding for this channel */ - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); - - /* Common for both std and ext encoding */ - solo_reg_write(solo_dev, SOLO_VE_CH_INTL(ch), - solo_enc->interlaced ? 1 : 0); - - if (solo_enc->interlaced) - interval = solo_enc->interval - 1; - else - interval = solo_enc->interval; - - /* Standard encoding only */ - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_QP(ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_CAP_CH_INTV(ch), interval); - - /* Extended encoding only */ - solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_CAP_CH_INTV_E(ch), interval); - - /* Enables the standard encoder */ - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); - - return 0; -} - -static void solo_enc_off(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - - solo_dev->enc_bw_remain += solo_enc->bw_weight; - - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0); -} - -static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma, - unsigned int off, unsigned int size) -{ - int ret; - - if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) - return -EINVAL; - - /* Single shot */ - if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_t(solo_dev, 0, dma, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, size, - 0, 0); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_t(solo_dev, 0, dma, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, - SOLO_MP4E_EXT_SIZE(solo_dev) - off, 0, 0); - - if (!ret) { - ret = solo_p2m_dma_t(solo_dev, 0, - dma + SOLO_MP4E_EXT_SIZE(solo_dev) - off, - SOLO_MP4E_EXT_ADDR(solo_dev), - size + off - SOLO_MP4E_EXT_SIZE(solo_dev), 0, 0); - } - - return ret; -} - -/* Build a descriptor queue out of an SG list and send it to the P2M for - * processing. */ -static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip, - struct vb2_dma_sg_desc *vbuf, int off, int size, - unsigned int base, unsigned int base_size) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct scatterlist *sg; - int i; - int ret; - - if (WARN_ON_ONCE(size > FRAME_BUF_SIZE)) - return -EINVAL; - - solo_enc->desc_count = 1; - - for_each_sg(vbuf->sglist, sg, vbuf->num_pages, i) { - struct solo_p2m_desc *desc; - dma_addr_t dma; - int len; - int left = base_size - off; - - desc = &solo_enc->desc_items[solo_enc->desc_count++]; - dma = sg_dma_address(sg); - len = sg_dma_len(sg); - - /* We assume this is smaller than the scatter size */ - BUG_ON(skip >= len); - if (skip) { - len -= skip; - dma += skip; - size -= skip; - skip = 0; - } - - len = min(len, size); - - if (len <= left) { - /* Single descriptor */ - solo_p2m_fill_desc(desc, 0, dma, base + off, - len, 0, 0); - } else { - /* Buffer wrap */ - /* XXX: Do these as separate DMA requests, to avoid - timeout errors triggered by awkwardly sized - descriptors. See - - */ - ret = solo_p2m_dma_t(solo_dev, 0, dma, base + off, - left, 0, 0); - if (ret) - return ret; - - ret = solo_p2m_dma_t(solo_dev, 0, dma + left, base, - len - left, 0, 0); - if (ret) - return ret; - - solo_enc->desc_count--; - } - - size -= len; - if (size <= 0) - break; - - off += len; - if (off >= base_size) - off -= base_size; - - /* Because we may use two descriptors per loop */ - if (solo_enc->desc_count >= (solo_enc->desc_nelts - 1)) { - ret = solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, - solo_enc->desc_dma, - solo_enc->desc_count - 1); - if (ret) - return ret; - solo_enc->desc_count = 1; - } - } - - if (solo_enc->desc_count <= 1) - return 0; - - return solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, solo_enc->desc_dma, - solo_enc->desc_count - 1); -} - -static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, - struct vb2_buffer *vb, struct vop_header *vh) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct vb2_dma_sg_desc *vbuf = vb2_dma_sg_plane_desc(vb, 0); - int frame_size; - int ret; - - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; - - if (vb2_plane_size(vb, 0) < vh->jpeg_size + solo_enc->jpeg_len) - return -EIO; - - sg_copy_from_buffer(vbuf->sglist, vbuf->num_pages, - solo_enc->jpeg_header, - solo_enc->jpeg_len); - - frame_size = (vh->jpeg_size + solo_enc->jpeg_len + (DMA_ALIGN - 1)) - & ~(DMA_ALIGN - 1); - vb2_set_plane_payload(vb, 0, vh->jpeg_size + solo_enc->jpeg_len); - - dma_map_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, - DMA_FROM_DEVICE); - ret = solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, vh->jpeg_off, - frame_size, SOLO_JPEG_EXT_ADDR(solo_dev), - SOLO_JPEG_EXT_SIZE(solo_dev)); - dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, - DMA_FROM_DEVICE); - return ret; -} - -static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, - struct vb2_buffer *vb, struct vop_header *vh) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct vb2_dma_sg_desc *vbuf = vb2_dma_sg_plane_desc(vb, 0); - int frame_off, frame_size; - int skip = 0; - int ret; - - if (vb2_plane_size(vb, 0) < vh->mpeg_size) - return -EIO; - - /* If this is a key frame, add extra header */ - if (!vh->vop_type) { - sg_copy_from_buffer(vbuf->sglist, vbuf->num_pages, - solo_enc->vop, - solo_enc->vop_len); - - skip = solo_enc->vop_len; - - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; - vb2_set_plane_payload(vb, 0, vh->mpeg_size + solo_enc->vop_len); - } else { - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; - vb2_set_plane_payload(vb, 0, vh->mpeg_size); - } - - /* Now get the actual mpeg payload */ - frame_off = (vh->mpeg_off + sizeof(*vh)) - % SOLO_MP4E_EXT_SIZE(solo_dev); - frame_size = (vh->mpeg_size + skip + (DMA_ALIGN - 1)) - & ~(DMA_ALIGN - 1); - - dma_map_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, - DMA_FROM_DEVICE); - ret = solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size, - SOLO_MP4E_EXT_ADDR(solo_dev), - SOLO_MP4E_EXT_SIZE(solo_dev)); - dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, - DMA_FROM_DEVICE); - return ret; -} - -static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, - struct vb2_buffer *vb, struct solo_enc_buf *enc_buf) -{ - struct vop_header *vh = enc_buf->vh; - int ret; - - /* Check for motion flags */ - vb->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_MOTION_ON | - V4L2_BUF_FLAG_MOTION_DETECTED); - if (solo_is_motion_on(solo_enc)) { - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_ON; - if (enc_buf->motion) - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_DETECTED; - } - - if (solo_enc->fmt == V4L2_PIX_FMT_MPEG4) - ret = solo_fill_mpeg(solo_enc, vb, vh); - else - ret = solo_fill_jpeg(solo_enc, vb, vh); - - if (!ret) { - vb->v4l2_buf.sequence = solo_enc->sequence++; - vb->v4l2_buf.timestamp.tv_sec = vh->sec; - vb->v4l2_buf.timestamp.tv_usec = vh->usec; - } - - vb2_buffer_done(vb, ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - - return ret; -} - -static void solo_enc_handle_one(struct solo_enc_dev *solo_enc, - struct solo_enc_buf *enc_buf) -{ - struct solo_vb2_buf *vb; - unsigned long flags; - - mutex_lock(&solo_enc->lock); - if (solo_enc->type != enc_buf->type) - goto unlock; - - spin_lock_irqsave(&solo_enc->av_lock, flags); - if (list_empty(&solo_enc->vidq_active)) { - spin_unlock_irqrestore(&solo_enc->av_lock, flags); - goto unlock; - } - vb = list_first_entry(&solo_enc->vidq_active, struct solo_vb2_buf, list); - list_del(&vb->list); - spin_unlock_irqrestore(&solo_enc->av_lock, flags); - - solo_enc_fillbuf(solo_enc, &vb->vb, enc_buf); -unlock: - mutex_unlock(&solo_enc->lock); -} - -void solo_enc_v4l2_isr(struct solo_dev *solo_dev) -{ - wake_up_interruptible_all(&solo_dev->ring_thread_wait); -} - -static void solo_handle_ring(struct solo_dev *solo_dev) -{ - for (;;) { - struct solo_enc_dev *solo_enc; - struct solo_enc_buf enc_buf; - u32 mpeg_current, off; - u8 ch; - u8 cur_q; - - /* Check if the hardware has any new ones in the queue */ - cur_q = solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xff; - if (cur_q == solo_dev->enc_idx) - break; - - mpeg_current = solo_reg_read(solo_dev, - SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); - solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS; - - ch = (mpeg_current >> 24) & 0x1f; - off = mpeg_current & 0x00ffffff; - - if (ch >= SOLO_MAX_CHANNELS) { - ch -= SOLO_MAX_CHANNELS; - enc_buf.type = SOLO_ENC_TYPE_EXT; - } else - enc_buf.type = SOLO_ENC_TYPE_STD; - - solo_enc = solo_dev->v4l2_enc[ch]; - if (solo_enc == NULL) { - dev_err(&solo_dev->pdev->dev, - "Got spurious packet for channel %d\n", ch); - continue; - } - - /* FAIL... */ - if (enc_get_mpeg_dma(solo_dev, solo_dev->vh_dma, off, - sizeof(struct vop_header))) - continue; - - enc_buf.vh = (struct vop_header *)solo_dev->vh_buf; - enc_buf.vh->mpeg_off -= SOLO_MP4E_EXT_ADDR(solo_dev); - enc_buf.vh->jpeg_off -= SOLO_JPEG_EXT_ADDR(solo_dev); - - /* Sanity check */ - if (enc_buf.vh->mpeg_off != off) - continue; - - if (solo_motion_detected(solo_enc)) - enc_buf.motion = 1; - else - enc_buf.motion = 0; - - solo_enc_handle_one(solo_enc, &enc_buf); - } -} - -static int solo_ring_thread(void *data) -{ - struct solo_dev *solo_dev = data; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&solo_dev->ring_thread_wait, &wait); - - for (;;) { - long timeout = schedule_timeout_interruptible(HZ); - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); - solo_handle_ring(solo_dev); - solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); - try_to_freeze(); - } - - remove_wait_queue(&solo_dev->ring_thread_wait, &wait); - - return 0; -} - -static int solo_enc_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, - unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - sizes[0] = FRAME_BUF_SIZE; - *num_planes = 1; - - if (*num_buffers < MIN_VID_BUFFERS) - *num_buffers = MIN_VID_BUFFERS; - - return 0; -} - -static void solo_enc_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct solo_enc_dev *solo_enc = vb2_get_drv_priv(vq); - struct solo_vb2_buf *solo_vb = - container_of(vb, struct solo_vb2_buf, vb); - - spin_lock(&solo_enc->av_lock); - list_add_tail(&solo_vb->list, &solo_enc->vidq_active); - spin_unlock(&solo_enc->av_lock); -} - -static int solo_ring_start(struct solo_dev *solo_dev) -{ - solo_dev->ring_thread = kthread_run(solo_ring_thread, solo_dev, - SOLO6X10_NAME "_ring"); - if (IS_ERR(solo_dev->ring_thread)) { - int err = PTR_ERR(solo_dev->ring_thread); - solo_dev->ring_thread = NULL; - return err; - } - - solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); - - return 0; -} - -static void solo_ring_stop(struct solo_dev *solo_dev) -{ - if (solo_dev->ring_thread) { - kthread_stop(solo_dev->ring_thread); - solo_dev->ring_thread = NULL; - } - - solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); -} - -static int solo_enc_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); - int ret; - - ret = solo_enc_on(solo_enc); - if (ret) - return ret; - return solo_ring_start(solo_enc->solo_dev); -} - -static int solo_enc_stop_streaming(struct vb2_queue *q) -{ - struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); - - solo_enc_off(solo_enc); - INIT_LIST_HEAD(&solo_enc->vidq_active); - solo_ring_stop(solo_enc->solo_dev); - return 0; -} - -static struct vb2_ops solo_enc_video_qops = { - .queue_setup = solo_enc_queue_setup, - .buf_queue = solo_enc_buf_queue, - .start_streaming = solo_enc_start_streaming, - .stop_streaming = solo_enc_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int solo_enc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - - strcpy(cap->driver, SOLO6X10_NAME); - snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d", - solo_enc->ch); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", - pci_name(solo_dev->pdev)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int solo_enc_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (input->index) - return -EINVAL; - - snprintf(input->name, sizeof(input->name), "Encoder %d", - solo_enc->ch + 1); - input->type = V4L2_INPUT_TYPE_CAMERA; - input->std = solo_enc->vfd->tvnorms; - - if (!tw28_get_video_status(solo_dev, solo_enc->ch)) - input->status = V4L2_IN_ST_NO_SIGNAL; - - return 0; -} - -static int solo_enc_set_input(struct file *file, void *priv, - unsigned int index) -{ - if (index) - return -EINVAL; - - return 0; -} - -static int solo_enc_get_input(struct file *file, void *priv, - unsigned int *index) -{ - *index = 0; - - return 0; -} - -static int solo_enc_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - switch (f->index) { - case 0: - f->pixelformat = V4L2_PIX_FMT_MPEG4; - strcpy(f->description, "MPEG-4 AVC"); - break; - case 1: - f->pixelformat = V4L2_PIX_FMT_MJPEG; - strcpy(f->description, "MJPEG"); - break; - default: - return -EINVAL; - } - - f->flags = V4L2_FMT_FLAG_COMPRESSED; - - return 0; -} - -static int solo_enc_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - - if (pix->pixelformat != V4L2_PIX_FMT_MPEG4 && - pix->pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - if (pix->width < solo_dev->video_hsize || - pix->height < solo_dev->video_vsize << 1) { - /* Default to CIF 1/2 size */ - pix->width = solo_dev->video_hsize >> 1; - pix->height = solo_dev->video_vsize; - } else { - /* Full frame */ - pix->width = solo_dev->video_hsize; - pix->height = solo_dev->video_vsize << 1; - } - - switch (pix->field) { - case V4L2_FIELD_NONE: - case V4L2_FIELD_INTERLACED: - break; - case V4L2_FIELD_ANY: - default: - pix->field = V4L2_FIELD_INTERLACED; - break; - } - - /* Just set these */ - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->sizeimage = FRAME_BUF_SIZE; - pix->bytesperline = 0; - pix->priv = 0; - - return 0; -} - -static int solo_enc_set_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - if (vb2_is_busy(&solo_enc->vidq)) - return -EBUSY; - - ret = solo_enc_try_fmt_cap(file, priv, f); - if (ret) - return ret; - - if (pix->width == solo_dev->video_hsize) - solo_enc->mode = SOLO_ENC_MODE_D1; - else - solo_enc->mode = SOLO_ENC_MODE_CIF; - - /* This does not change the encoder at all */ - solo_enc->fmt = pix->pixelformat; - - /* - * More information is needed about these 'extended' types. As far - * as I can tell these are basically additional video streams with - * different MPEG encoding attributes that can run in parallel with - * the main stream. If so, then this should be implemented as a - * second video node. Abusing priv like this is certainly not the - * right approach. - if (pix->priv) - solo_enc->type = SOLO_ENC_TYPE_EXT; - */ - return 0; -} - -static int solo_enc_get_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct v4l2_pix_format *pix = &f->fmt.pix; - - pix->width = solo_enc->width; - pix->height = solo_enc->height; - pix->pixelformat = solo_enc->fmt; - pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED : - V4L2_FIELD_NONE; - pix->sizeimage = FRAME_BUF_SIZE; - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->priv = 0; - - return 0; -} - -static int solo_enc_g_std(struct file *file, void *priv, v4l2_std_id *i) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - *i = V4L2_STD_NTSC_M; - else - *i = V4L2_STD_PAL; - return 0; -} - -static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - - return solo_set_video_type(solo_enc->solo_dev, std & V4L2_STD_PAL); -} - -static int solo_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (fsize->pixel_format != V4L2_PIX_FMT_MPEG4 && - fsize->pixel_format != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - switch (fsize->index) { - case 0: - fsize->discrete.width = solo_dev->video_hsize >> 1; - fsize->discrete.height = solo_dev->video_vsize; - break; - case 1: - fsize->discrete.width = solo_dev->video_hsize; - fsize->discrete.height = solo_dev->video_vsize << 1; - break; - default: - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - - return 0; -} - -static int solo_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fintv) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (fintv->pixel_format != V4L2_PIX_FMT_MPEG4 && - fintv->pixel_format != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - if (fintv->index) - return -EINVAL; - if ((fintv->width != solo_dev->video_hsize >> 1 || - fintv->height != solo_dev->video_vsize) && - (fintv->width != solo_dev->video_hsize || - fintv->height != solo_dev->video_vsize << 1)) - return -EINVAL; - - fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; - - fintv->stepwise.min.numerator = 1; - fintv->stepwise.min.denominator = solo_dev->fps; - - fintv->stepwise.max.numerator = 15; - fintv->stepwise.max.denominator = solo_dev->fps; - - fintv->stepwise.step.numerator = 1; - fintv->stepwise.step.denominator = solo_dev->fps; - - return 0; -} - -static int solo_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_captureparm *cp = &sp->parm.capture; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = solo_enc->interval; - cp->timeperframe.denominator = solo_dev->fps; - cp->capturemode = 0; - /* XXX: Shouldn't we be able to get/set this from videobuf? */ - cp->readbuffers = 2; - - return 0; -} - -static int solo_s_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_captureparm *cp = &sp->parm.capture; - - if (vb2_is_streaming(&solo_enc->vidq)) - return -EBUSY; - - if ((cp->timeperframe.numerator == 0) || - (cp->timeperframe.denominator == 0)) { - /* reset framerate */ - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = solo_dev->fps; - } - - if (cp->timeperframe.denominator != solo_dev->fps) - cp->timeperframe.denominator = solo_dev->fps; - - if (cp->timeperframe.numerator > 15) - cp->timeperframe.numerator = 15; - - solo_enc->interval = cp->timeperframe.numerator; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->readbuffers = 2; - - solo_update_mode(solo_enc); - return 0; -} - -static long solo_enc_default(struct file *file, void *fh, - bool valid_prio, int cmd, void *arg) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct solo_motion_thresholds *thresholds = arg; - - switch (cmd) { - case SOLO_IOC_G_MOTION_THRESHOLDS: - *thresholds = solo_enc->motion_thresholds; - return 0; - - case SOLO_IOC_S_MOTION_THRESHOLDS: - if (!valid_prio) - return -EBUSY; - solo_enc->motion_thresholds = *thresholds; - if (solo_enc->motion_enabled && !solo_enc->motion_global) - return solo_set_motion_block(solo_dev, solo_enc->ch, - &solo_enc->motion_thresholds); - return 0; - default: - return -ENOTTY; - } -} - -static int solo_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct solo_enc_dev *solo_enc = - container_of(ctrl->handler, struct solo_enc_dev, hdl); - struct solo_dev *solo_dev = solo_enc->solo_dev; - int err; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - case V4L2_CID_SHARPNESS: - return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, - ctrl->val); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - solo_enc->gop = ctrl->val; - return 0; - case V4L2_CID_MOTION_THRESHOLD: - solo_enc->motion_thresh = ctrl->val; - if (!solo_enc->motion_global || !solo_enc->motion_enabled) - return 0; - return solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->val); - case V4L2_CID_MOTION_MODE: - solo_enc->motion_global = ctrl->val == 1; - solo_enc->motion_enabled = ctrl->val > 0; - if (ctrl->val) { - if (solo_enc->motion_global) - solo_set_motion_threshold(solo_dev, solo_enc->ch, - solo_enc->motion_thresh); - else - solo_set_motion_block(solo_dev, solo_enc->ch, - &solo_enc->motion_thresholds); - } - solo_motion_toggle(solo_enc, ctrl->val); - return 0; - case V4L2_CID_OSD_TEXT: - strcpy(solo_enc->osd_text, ctrl->string); - err = solo_osd_print(solo_enc); - return err; - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_file_operations solo_enc_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { - .vidioc_querycap = solo_enc_querycap, - .vidioc_s_std = solo_enc_s_std, - .vidioc_g_std = solo_enc_g_std, - /* Input callbacks */ - .vidioc_enum_input = solo_enc_enum_input, - .vidioc_s_input = solo_enc_set_input, - .vidioc_g_input = solo_enc_get_input, - /* Video capture format callbacks */ - .vidioc_enum_fmt_vid_cap = solo_enc_enum_fmt_cap, - .vidioc_try_fmt_vid_cap = solo_enc_try_fmt_cap, - .vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap, - .vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap, - /* Streaming I/O */ - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - /* Frame size and interval */ - .vidioc_enum_framesizes = solo_enum_framesizes, - .vidioc_enum_frameintervals = solo_enum_frameintervals, - /* Video capture parameters */ - .vidioc_s_parm = solo_s_parm, - .vidioc_g_parm = solo_g_parm, - /* Logging and events */ - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_default = solo_enc_default, -}; - -static const struct video_device solo_enc_template = { - .name = SOLO6X10_NAME, - .fops = &solo_enc_fops, - .ioctl_ops = &solo_enc_ioctl_ops, - .minor = -1, - .release = video_device_release, - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, -}; - -static const struct v4l2_ctrl_ops solo_ctrl_ops = { - .s_ctrl = solo_s_ctrl, -}; - -static const struct v4l2_ctrl_config solo_motion_threshold_ctrl = { - .ops = &solo_ctrl_ops, - .id = V4L2_CID_MOTION_THRESHOLD, - .name = "Motion Detection Threshold", - .type = V4L2_CTRL_TYPE_INTEGER, - .max = 0xffff, - .def = SOLO_DEF_MOT_THRESH, - .step = 1, - .flags = V4L2_CTRL_FLAG_SLIDER, -}; - -static const char * const solo_motion_mode_menu[] = { - "Disabled", - "Global Threshold", - "Regional Threshold", - NULL -}; - -static const struct v4l2_ctrl_config solo_motion_enable_ctrl = { - .ops = &solo_ctrl_ops, - .id = V4L2_CID_MOTION_MODE, - .name = "Motion Detection Mode", - .type = V4L2_CTRL_TYPE_MENU, - .qmenu = solo_motion_mode_menu, - .max = 2, -}; - -static const struct v4l2_ctrl_config solo_osd_text_ctrl = { - .ops = &solo_ctrl_ops, - .id = V4L2_CID_OSD_TEXT, - .name = "OSD Text", - .type = V4L2_CTRL_TYPE_STRING, - .max = OSD_TEXT_MAX, - .step = 1, -}; - -static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, - u8 ch, unsigned nr) -{ - struct solo_enc_dev *solo_enc; - struct v4l2_ctrl_handler *hdl; - int ret; - int x, y; - - solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); - if (!solo_enc) - return ERR_PTR(-ENOMEM); - - hdl = &solo_enc->hdl; - v4l2_ctrl_handler_init(hdl, 10); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_HUE, 0, 255, 1, 128); - if (tw28_has_sharpness(solo_dev, ch)) - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 15, 1, 0); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, solo_dev->fps); - v4l2_ctrl_new_custom(hdl, &solo_motion_threshold_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &solo_motion_enable_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &solo_osd_text_ctrl, NULL); - if (hdl->error) { - ret = hdl->error; - goto hdl_free; - } - - solo_enc->solo_dev = solo_dev; - solo_enc->ch = ch; - mutex_init(&solo_enc->lock); - spin_lock_init(&solo_enc->av_lock); - INIT_LIST_HEAD(&solo_enc->vidq_active); - solo_enc->fmt = V4L2_PIX_FMT_MPEG4; - solo_enc->type = SOLO_ENC_TYPE_STD; - - solo_enc->qp = SOLO_DEFAULT_QP; - solo_enc->gop = solo_dev->fps; - solo_enc->interval = 1; - solo_enc->mode = SOLO_ENC_MODE_CIF; - solo_enc->motion_global = true; - solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; - for (y = 0; y < SOLO_MOTION_SZ; y++) - for (x = 0; x < SOLO_MOTION_SZ; x++) - solo_enc->motion_thresholds.thresholds[y][x] = - SOLO_DEF_MOT_THRESH; - - solo_enc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - solo_enc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - solo_enc->vidq.ops = &solo_enc_video_qops; - solo_enc->vidq.mem_ops = &vb2_dma_sg_memops; - solo_enc->vidq.drv_priv = solo_enc; - solo_enc->vidq.gfp_flags = __GFP_DMA32; - solo_enc->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - solo_enc->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); - solo_enc->vidq.lock = &solo_enc->lock; - ret = vb2_queue_init(&solo_enc->vidq); - if (ret) - goto hdl_free; - solo_update_mode(solo_enc); - - spin_lock_init(&solo_enc->motion_lock); - - /* Initialize this per encoder */ - solo_enc->jpeg_len = sizeof(jpeg_header); - memcpy(solo_enc->jpeg_header, jpeg_header, solo_enc->jpeg_len); - - solo_enc->desc_nelts = 32; - solo_enc->desc_items = pci_alloc_consistent(solo_dev->pdev, - sizeof(struct solo_p2m_desc) * - solo_enc->desc_nelts, &solo_enc->desc_dma); - ret = -ENOMEM; - if (solo_enc->desc_items == NULL) - goto hdl_free; - - solo_enc->vfd = video_device_alloc(); - if (!solo_enc->vfd) - goto pci_free; - - *solo_enc->vfd = solo_enc_template; - solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev; - solo_enc->vfd->ctrl_handler = hdl; - solo_enc->vfd->queue = &solo_enc->vidq; - solo_enc->vfd->lock = &solo_enc->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &solo_enc->vfd->flags); - video_set_drvdata(solo_enc->vfd, solo_enc); - ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr); - if (ret < 0) - goto vdev_release; - - snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name), - "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num, - solo_enc->vfd->num); - - return solo_enc; - -vdev_release: - video_device_release(solo_enc->vfd); -pci_free: - pci_free_consistent(solo_enc->solo_dev->pdev, - sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts, - solo_enc->desc_items, solo_enc->desc_dma); -hdl_free: - v4l2_ctrl_handler_free(hdl); - kfree(solo_enc); - return ERR_PTR(ret); -} - -static void solo_enc_free(struct solo_enc_dev *solo_enc) -{ - if (solo_enc == NULL) - return; - - video_unregister_device(solo_enc->vfd); - v4l2_ctrl_handler_free(&solo_enc->hdl); - kfree(solo_enc); -} - -int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr) -{ - int i; - - init_waitqueue_head(&solo_dev->ring_thread_wait); - - solo_dev->vh_size = sizeof(struct vop_header); - solo_dev->vh_buf = pci_alloc_consistent(solo_dev->pdev, - solo_dev->vh_size, - &solo_dev->vh_dma); - if (solo_dev->vh_buf == NULL) - return -ENOMEM; - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i, nr); - if (IS_ERR(solo_dev->v4l2_enc[i])) - break; - } - - if (i != solo_dev->nr_chans) { - int ret = PTR_ERR(solo_dev->v4l2_enc[i]); - while (i--) - solo_enc_free(solo_dev->v4l2_enc[i]); - pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, - solo_dev->vh_buf, solo_dev->vh_dma); - solo_dev->vh_buf = NULL; - return ret; - } - - if (solo_dev->type == SOLO_DEV_6010) - solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; - else - solo_dev->enc_bw_remain = solo_dev->fps * 4 * 5; - - dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n", - solo_dev->v4l2_enc[0]->vfd->num, - solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num); - - return 0; -} - -void solo_enc_v4l2_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_enc_free(solo_dev->v4l2_enc[i]); - - if (solo_dev->vh_buf) - pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, - solo_dev->vh_buf, solo_dev->vh_dma); -} diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c deleted file mode 100644 index 7b26de3488da..000000000000 --- a/drivers/staging/media/solo6x10/v4l2.c +++ /dev/null @@ -1,734 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "solo6x10.h" -#include "solo6x10-tw28.h" - -/* Image size is two fields, SOLO_HW_BPL is one horizontal line in hardware */ -#define SOLO_HW_BPL 2048 -#define solo_vlines(__solo) (__solo->video_vsize * 2) -#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ - solo_vlines(__solo)) -#define solo_bytesperline(__solo) (__solo->video_hsize * 2) - -#define MIN_VID_BUFFERS 2 - -static inline void erase_on(struct solo_dev *solo_dev) -{ - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); - solo_dev->erasing = 1; - solo_dev->frame_blank = 0; -} - -static inline int erase_off(struct solo_dev *solo_dev) -{ - if (!solo_dev->erasing) - return 0; - - /* First time around, assert erase off */ - if (!solo_dev->frame_blank) - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0); - /* Keep the erasing flag on for 8 frames minimum */ - if (solo_dev->frame_blank++ >= 8) - solo_dev->erasing = 0; - - return 1; -} - -void solo_video_in_isr(struct solo_dev *solo_dev) -{ - wake_up_interruptible_all(&solo_dev->disp_thread_wait); -} - -static void solo_win_setup(struct solo_dev *solo_dev, u8 ch, - int sx, int sy, int ex, int ey, int scale) -{ - if (ch >= solo_dev->nr_chans) - return; - - /* Here, we just keep window/channel the same */ - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch), - SOLO_VI_WIN_CHANNEL(ch) | - SOLO_VI_WIN_SX(sx) | - SOLO_VI_WIN_EX(ex) | - SOLO_VI_WIN_SCALE(scale)); - - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch), - SOLO_VI_WIN_SY(sy) | - SOLO_VI_WIN_EY(ey)); -} - -static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on) -{ - u8 ch = idx * 4; - - if (ch >= solo_dev->nr_chans) - return -EINVAL; - - if (!on) { - u8 i; - for (i = ch; i < ch + 4; i++) - solo_win_setup(solo_dev, i, solo_dev->video_hsize, - solo_vlines(solo_dev), - solo_dev->video_hsize, - solo_vlines(solo_dev), 0); - return 0; - } - - /* Row 1 */ - solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2, - solo_vlines(solo_dev) / 2, 3); - solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0, - solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3); - /* Row 2 */ - solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2, - solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3); - solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2, - solo_vlines(solo_dev) / 2, solo_dev->video_hsize, - solo_vlines(solo_dev), 3); - - return 0; -} - -static int solo_v4l2_ch_ext_16up(struct solo_dev *solo_dev, int on) -{ - int sy, ysize, hsize, i; - - if (!on) { - for (i = 0; i < 16; i++) - solo_win_setup(solo_dev, i, solo_dev->video_hsize, - solo_vlines(solo_dev), - solo_dev->video_hsize, - solo_vlines(solo_dev), 0); - return 0; - } - - ysize = solo_vlines(solo_dev) / 4; - hsize = solo_dev->video_hsize / 4; - - for (sy = 0, i = 0; i < 4; i++, sy += ysize) { - solo_win_setup(solo_dev, i * 4, 0, sy, hsize, - sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy, - hsize * 2, sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy, - hsize * 3, sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy, - solo_dev->video_hsize, sy + ysize, 5); - } - - return 0; -} - -static int solo_v4l2_ch(struct solo_dev *solo_dev, u8 ch, int on) -{ - u8 ext_ch; - - if (ch < solo_dev->nr_chans) { - solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize, - on ? 0 : solo_vlines(solo_dev), - solo_dev->video_hsize, solo_vlines(solo_dev), - on ? 1 : 0); - return 0; - } - - if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) - return -EINVAL; - - ext_ch = ch - solo_dev->nr_chans; - - /* 4up's first */ - if (ext_ch < 4) - return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on); - - /* Remaining case is 16up for 16-port */ - return solo_v4l2_ch_ext_16up(solo_dev, on); -} - -static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch) -{ - if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) - return -EINVAL; - - erase_on(solo_dev); - - solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0); - solo_v4l2_ch(solo_dev, ch, 1); - - solo_dev->cur_disp_ch = ch; - - return 0; -} - -static void solo_fillbuf(struct solo_dev *solo_dev, - struct vb2_buffer *vb) -{ - dma_addr_t vbuf; - unsigned int fdma_addr; - int error = -1; - int i; - - vbuf = vb2_dma_contig_plane_dma_addr(vb, 0); - if (!vbuf) - goto finish_buf; - - if (erase_off(solo_dev)) { - void *p = vb2_plane_vaddr(vb, 0); - int image_size = solo_image_size(solo_dev); - for (i = 0; i < image_size; i += 2) { - ((u8 *)p)[i] = 0x80; - ((u8 *)p)[i + 1] = 0x00; - } - error = 0; - } else { - fdma_addr = SOLO_DISP_EXT_ADDR + (solo_dev->old_write * - (SOLO_HW_BPL * solo_vlines(solo_dev))); - - error = solo_p2m_dma_t(solo_dev, 0, vbuf, fdma_addr, - solo_bytesperline(solo_dev), - solo_vlines(solo_dev), SOLO_HW_BPL); - } - -finish_buf: - if (!error) { - vb2_set_plane_payload(vb, 0, - solo_vlines(solo_dev) * solo_bytesperline(solo_dev)); - vb->v4l2_buf.sequence = solo_dev->sequence++; - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - } - - vb2_buffer_done(vb, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); -} - -static void solo_thread_try(struct solo_dev *solo_dev) -{ - struct solo_vb2_buf *vb; - - /* Only "break" from this loop if slock is held, otherwise - * just return. */ - for (;;) { - unsigned int cur_write; - - cur_write = SOLO_VI_STATUS0_PAGE( - solo_reg_read(solo_dev, SOLO_VI_STATUS0)); - if (cur_write == solo_dev->old_write) - return; - - spin_lock(&solo_dev->slock); - - if (list_empty(&solo_dev->vidq_active)) - break; - - vb = list_first_entry(&solo_dev->vidq_active, struct solo_vb2_buf, - list); - - solo_dev->old_write = cur_write; - list_del(&vb->list); - - spin_unlock(&solo_dev->slock); - - solo_fillbuf(solo_dev, &vb->vb); - } - - assert_spin_locked(&solo_dev->slock); - spin_unlock(&solo_dev->slock); -} - -static int solo_thread(void *data) -{ - struct solo_dev *solo_dev = data; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&solo_dev->disp_thread_wait, &wait); - - for (;;) { - long timeout = schedule_timeout_interruptible(HZ); - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - solo_thread_try(solo_dev); - try_to_freeze(); - } - - remove_wait_queue(&solo_dev->disp_thread_wait, &wait); - - return 0; -} - -static int solo_start_thread(struct solo_dev *solo_dev) -{ - int ret = 0; - - solo_dev->kthread = kthread_run(solo_thread, solo_dev, SOLO6X10_NAME "_disp"); - - if (IS_ERR(solo_dev->kthread)) { - ret = PTR_ERR(solo_dev->kthread); - solo_dev->kthread = NULL; - return ret; - } - solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN); - - return ret; -} - -static void solo_stop_thread(struct solo_dev *solo_dev) -{ - if (!solo_dev->kthread) - return; - - solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); - kthread_stop(solo_dev->kthread); - solo_dev->kthread = NULL; -} - -static int solo_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, - unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct solo_dev *solo_dev = vb2_get_drv_priv(q); - - sizes[0] = solo_image_size(solo_dev); - alloc_ctxs[0] = solo_dev->alloc_ctx; - *num_planes = 1; - - if (*num_buffers < MIN_VID_BUFFERS) - *num_buffers = MIN_VID_BUFFERS; - - return 0; -} - -static int solo_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct solo_dev *solo_dev = vb2_get_drv_priv(q); - - solo_dev->sequence = 0; - return solo_start_thread(solo_dev); -} - -static int solo_stop_streaming(struct vb2_queue *q) -{ - struct solo_dev *solo_dev = vb2_get_drv_priv(q); - - solo_stop_thread(solo_dev); - INIT_LIST_HEAD(&solo_dev->vidq_active); - return 0; -} - -static void solo_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct solo_dev *solo_dev = vb2_get_drv_priv(vq); - struct solo_vb2_buf *solo_vb = - container_of(vb, struct solo_vb2_buf, vb); - - spin_lock(&solo_dev->slock); - list_add_tail(&solo_vb->list, &solo_dev->vidq_active); - spin_unlock(&solo_dev->slock); - wake_up_interruptible(&solo_dev->disp_thread_wait); -} - -static const struct vb2_ops solo_video_qops = { - .queue_setup = solo_queue_setup, - .buf_queue = solo_buf_queue, - .start_streaming = solo_start_streaming, - .stop_streaming = solo_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int solo_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - strcpy(cap->driver, SOLO6X10_NAME); - strcpy(cap->card, "Softlogic 6x10"); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", - pci_name(solo_dev->pdev)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int solo_enum_ext_input(struct solo_dev *solo_dev, - struct v4l2_input *input) -{ - static const char * const dispnames_1[] = { "4UP" }; - static const char * const dispnames_2[] = { "4UP-1", "4UP-2" }; - static const char * const dispnames_5[] = { - "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" - }; - const char * const *dispnames; - - if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) - return -EINVAL; - - if (solo_dev->nr_ext == 5) - dispnames = dispnames_5; - else if (solo_dev->nr_ext == 2) - dispnames = dispnames_2; - else - dispnames = dispnames_1; - - snprintf(input->name, sizeof(input->name), "Multi %s", - dispnames[input->index - solo_dev->nr_chans]); - - return 0; -} - -static int solo_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - if (input->index >= solo_dev->nr_chans) { - int ret = solo_enum_ext_input(solo_dev, input); - if (ret < 0) - return ret; - } else { - snprintf(input->name, sizeof(input->name), "Camera %d", - input->index + 1); - - /* We can only check this for normal inputs */ - if (!tw28_get_video_status(solo_dev, input->index)) - input->status = V4L2_IN_ST_NO_SIGNAL; - } - - input->type = V4L2_INPUT_TYPE_CAMERA; - input->std = solo_dev->vfd->tvnorms; - return 0; -} - -static int solo_set_input(struct file *file, void *priv, unsigned int index) -{ - struct solo_dev *solo_dev = video_drvdata(file); - int ret = solo_v4l2_set_ch(solo_dev, index); - - if (!ret) { - while (erase_off(solo_dev)) - /* Do nothing */; - } - - return ret; -} - -static int solo_get_input(struct file *file, void *priv, unsigned int *index) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - *index = solo_dev->cur_disp_ch; - - return 0; -} - -static int solo_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index) - return -EINVAL; - - f->pixelformat = V4L2_PIX_FMT_UYVY; - strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description)); - - return 0; -} - -static int solo_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_dev *solo_dev = video_drvdata(file); - struct v4l2_pix_format *pix = &f->fmt.pix; - int image_size = solo_image_size(solo_dev); - - if (pix->pixelformat != V4L2_PIX_FMT_UYVY) - return -EINVAL; - - pix->width = solo_dev->video_hsize; - pix->height = solo_vlines(solo_dev); - pix->sizeimage = image_size; - pix->field = V4L2_FIELD_INTERLACED; - pix->pixelformat = V4L2_PIX_FMT_UYVY; - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->priv = 0; - return 0; -} - -static int solo_set_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - if (vb2_is_busy(&solo_dev->vidq)) - return -EBUSY; - - /* For right now, if it doesn't match our running config, - * then fail */ - return solo_try_fmt_cap(file, priv, f); -} - -static int solo_get_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_dev *solo_dev = video_drvdata(file); - struct v4l2_pix_format *pix = &f->fmt.pix; - - pix->width = solo_dev->video_hsize; - pix->height = solo_vlines(solo_dev); - pix->pixelformat = V4L2_PIX_FMT_UYVY; - pix->field = V4L2_FIELD_INTERLACED; - pix->sizeimage = solo_image_size(solo_dev); - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->bytesperline = solo_bytesperline(solo_dev); - pix->priv = 0; - - return 0; -} - -static int solo_g_std(struct file *file, void *priv, v4l2_std_id *i) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - *i = V4L2_STD_NTSC_M; - else - *i = V4L2_STD_PAL; - return 0; -} - -int solo_set_video_type(struct solo_dev *solo_dev, bool type) -{ - int i; - - /* Make sure all video nodes are idle */ - if (vb2_is_busy(&solo_dev->vidq)) - return -EBUSY; - for (i = 0; i < solo_dev->nr_chans; i++) - if (vb2_is_busy(&solo_dev->v4l2_enc[i]->vidq)) - return -EBUSY; - solo_dev->video_type = type; - /* Reconfigure for the new standard */ - solo_disp_init(solo_dev); - solo_enc_init(solo_dev); - solo_tw28_init(solo_dev); - for (i = 0; i < solo_dev->nr_chans; i++) - solo_update_mode(solo_dev->v4l2_enc[i]); - return solo_v4l2_set_ch(solo_dev, solo_dev->cur_disp_ch); -} - -static int solo_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - return solo_set_video_type(solo_dev, std & V4L2_STD_PAL); -} - -static int solo_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct solo_dev *solo_dev = - container_of(ctrl->handler, struct solo_dev, disp_hdl); - - switch (ctrl->id) { - case V4L2_CID_MOTION_TRACE: - if (ctrl->val) { - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, - SOLO_VI_MOTION_Y_ADD | - SOLO_VI_MOTION_Y_VALUE(0x20) | - SOLO_VI_MOTION_CB_VALUE(0x10) | - SOLO_VI_MOTION_CR_VALUE(0x10)); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, - SOLO_VI_MOTION_CR_ADD | - SOLO_VI_MOTION_Y_VALUE(0x10) | - SOLO_VI_MOTION_CB_VALUE(0x80) | - SOLO_VI_MOTION_CR_VALUE(0x10)); - } else { - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); - } - return 0; - default: - break; - } - return -EINVAL; -} - -static const struct v4l2_file_operations solo_v4l2_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { - .vidioc_querycap = solo_querycap, - .vidioc_s_std = solo_s_std, - .vidioc_g_std = solo_g_std, - /* Input callbacks */ - .vidioc_enum_input = solo_enum_input, - .vidioc_s_input = solo_set_input, - .vidioc_g_input = solo_get_input, - /* Video capture format callbacks */ - .vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap, - .vidioc_try_fmt_vid_cap = solo_try_fmt_cap, - .vidioc_s_fmt_vid_cap = solo_set_fmt_cap, - .vidioc_g_fmt_vid_cap = solo_get_fmt_cap, - /* Streaming I/O */ - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - /* Logging and events */ - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static struct video_device solo_v4l2_template = { - .name = SOLO6X10_NAME, - .fops = &solo_v4l2_fops, - .ioctl_ops = &solo_v4l2_ioctl_ops, - .minor = -1, - .release = video_device_release, - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, -}; - -static const struct v4l2_ctrl_ops solo_ctrl_ops = { - .s_ctrl = solo_s_ctrl, -}; - -static const struct v4l2_ctrl_config solo_motion_trace_ctrl = { - .ops = &solo_ctrl_ops, - .id = V4L2_CID_MOTION_TRACE, - .name = "Motion Detection Trace", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .step = 1, -}; - -int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) -{ - int ret; - int i; - - init_waitqueue_head(&solo_dev->disp_thread_wait); - spin_lock_init(&solo_dev->slock); - mutex_init(&solo_dev->lock); - INIT_LIST_HEAD(&solo_dev->vidq_active); - - solo_dev->vfd = video_device_alloc(); - if (!solo_dev->vfd) - return -ENOMEM; - - *solo_dev->vfd = solo_v4l2_template; - solo_dev->vfd->v4l2_dev = &solo_dev->v4l2_dev; - solo_dev->vfd->queue = &solo_dev->vidq; - solo_dev->vfd->lock = &solo_dev->lock; - v4l2_ctrl_handler_init(&solo_dev->disp_hdl, 1); - v4l2_ctrl_new_custom(&solo_dev->disp_hdl, &solo_motion_trace_ctrl, NULL); - if (solo_dev->disp_hdl.error) { - ret = solo_dev->disp_hdl.error; - goto fail; - } - solo_dev->vfd->ctrl_handler = &solo_dev->disp_hdl; - set_bit(V4L2_FL_USE_FH_PRIO, &solo_dev->vfd->flags); - - video_set_drvdata(solo_dev->vfd, solo_dev); - - solo_dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - solo_dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - solo_dev->vidq.ops = &solo_video_qops; - solo_dev->vidq.mem_ops = &vb2_dma_contig_memops; - solo_dev->vidq.drv_priv = solo_dev; - solo_dev->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - solo_dev->vidq.gfp_flags = __GFP_DMA32; - solo_dev->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); - solo_dev->vidq.lock = &solo_dev->lock; - ret = vb2_queue_init(&solo_dev->vidq); - if (ret < 0) - goto fail; - - solo_dev->alloc_ctx = vb2_dma_contig_init_ctx(&solo_dev->pdev->dev); - if (IS_ERR(solo_dev->alloc_ctx)) { - dev_err(&solo_dev->pdev->dev, "Can't allocate buffer context"); - return PTR_ERR(solo_dev->alloc_ctx); - } - - /* Cycle all the channels and clear */ - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_v4l2_set_ch(solo_dev, i); - while (erase_off(solo_dev)) - /* Do nothing */; - } - - /* Set the default display channel */ - solo_v4l2_set_ch(solo_dev, 0); - while (erase_off(solo_dev)) - /* Do nothing */; - - ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr); - if (ret < 0) - goto fail; - - snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)", - SOLO6X10_NAME, solo_dev->vfd->num); - - dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with " - "%d inputs (%d extended)\n", solo_dev->vfd->num, - solo_dev->nr_chans, solo_dev->nr_ext); - - return 0; - -fail: - video_device_release(solo_dev->vfd); - vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); - v4l2_ctrl_handler_free(&solo_dev->disp_hdl); - solo_dev->vfd = NULL; - return ret; -} - -void solo_v4l2_exit(struct solo_dev *solo_dev) -{ - if (solo_dev->vfd == NULL) - return; - - video_unregister_device(solo_dev->vfd); - vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); - v4l2_ctrl_handler_free(&solo_dev->disp_hdl); - solo_dev->vfd = NULL; -}