[media] solo6x10: prefix sources with 'solo6x10-'
authorHans Verkuil <hans.verkuil@cisco.com>
Mon, 25 Mar 2013 08:47:48 +0000 (05:47 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 25 Mar 2013 11:54:05 +0000 (08:54 -0300)
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
23 files changed:
drivers/staging/media/solo6x10/Makefile
drivers/staging/media/solo6x10/core.c [deleted file]
drivers/staging/media/solo6x10/disp.c [deleted file]
drivers/staging/media/solo6x10/eeprom.c [deleted file]
drivers/staging/media/solo6x10/enc.c [deleted file]
drivers/staging/media/solo6x10/g723.c [deleted file]
drivers/staging/media/solo6x10/gpio.c [deleted file]
drivers/staging/media/solo6x10/i2c.c [deleted file]
drivers/staging/media/solo6x10/p2m.c [deleted file]
drivers/staging/media/solo6x10/solo6x10-core.c [new file with mode: 0644]
drivers/staging/media/solo6x10/solo6x10-disp.c [new file with mode: 0644]
drivers/staging/media/solo6x10/solo6x10-eeprom.c [new file with mode: 0644]
drivers/staging/media/solo6x10/solo6x10-enc.c [new file with mode: 0644]
drivers/staging/media/solo6x10/solo6x10-g723.c [new file with mode: 0644]
drivers/staging/media/solo6x10/solo6x10-gpio.c [new file with mode: 0644]
drivers/staging/media/solo6x10/solo6x10-i2c.c [new file with mode: 0644]
drivers/staging/media/solo6x10/solo6x10-p2m.c [new file with mode: 0644]
drivers/staging/media/solo6x10/solo6x10-tw28.c [new file with mode: 0644]
drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c [new file with mode: 0644]
drivers/staging/media/solo6x10/solo6x10-v4l2.c [new file with mode: 0644]
drivers/staging/media/solo6x10/tw28.c [deleted file]
drivers/staging/media/solo6x10/v4l2-enc.c [deleted file]
drivers/staging/media/solo6x10/v4l2.c [deleted file]

index 9bbde29..7aae118 100644 (file)
@@ -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 (file)
index 3675020..0000000
+++ /dev/null
@@ -1,709 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/videodev2.h>
-#include <linux/delay.h>
-#include <linux/sysfs.h>
-#include <linux/ktime.h>
-#include <linux/slab.h>
-
-#include "solo6x10.h"
-#include "solo6x10-tw28.h"
-
-MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver");
-MODULE_AUTHOR("Bluecherry <maintainers@bluecherrydvr.com>");
-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 (file)
index 78070c8..0000000
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-ioctl.h>
-
-#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 (file)
index 9d1c9bb..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-#include <linux/delay.h>
-
-#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 (file)
index 94d5735..0000000
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-#include <linux/font.h>
-#include <linux/bitrev.h>
-#include <linux/slab.h>
-
-#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 (file)
index 1db18c7..0000000
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-#include <linux/mempool.h>
-#include <linux/poll.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/control.h>
-
-#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 (file)
index 73276dc..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/uaccess.h>
-
-#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 (file)
index 01aa417..0000000
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-
-#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 (file)
index 3335941..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#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 (file)
index 0000000..3675020
--- /dev/null
@@ -0,0 +1,709 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+
+#include "solo6x10.h"
+#include "solo6x10-tw28.h"
+
+MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver");
+MODULE_AUTHOR("Bluecherry <maintainers@bluecherrydvr.com>");
+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 (file)
index 0000000..78070c8
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ioctl.h>
+
+#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 (file)
index 0000000..9d1c9bb
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/delay.h>
+
+#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 (file)
index 0000000..94d5735
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/font.h>
+#include <linux/bitrev.h>
+#include <linux/slab.h>
+
+#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 (file)
index 0000000..1db18c7
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/mempool.h>
+#include <linux/poll.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+
+#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 (file)
index 0000000..73276dc
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
+#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 (file)
index 0000000..01aa417
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+
+#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 (file)
index 0000000..3335941
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#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 (file)
index 0000000..ad00e2b
--- /dev/null
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/delay.h>
+
+#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 (file)
index 0000000..39a8fb2
--- /dev/null
@@ -0,0 +1,1363 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-sg.h>
+
+#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
+                          <https://github.com/bluecherrydvr/solo6x10/issues/8>
+                        */
+                       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 (file)
index 0000000..7b26de3
--- /dev/null
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-contig.h>
+
+#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 (file)
index ad00e2b..0000000
+++ /dev/null
@@ -1,854 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-#include <linux/delay.h>
-
-#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 (file)
index 39a8fb2..0000000
+++ /dev/null
@@ -1,1363 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-event.h>
-#include <media/videobuf2-dma-sg.h>
-
-#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
-                          <https://github.com/bluecherrydvr/solo6x10/issues/8>
-                        */
-                       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 (file)
index 7b26de3..0000000
+++ /dev/null
@@ -1,734 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
- *
- * Original author:
- * Ben Collins <bcollins@ubuntu.com>
- *
- * Additional work by:
- * John Brooks <john.brooks@bluecherry.net>
- *
- * 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 <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-event.h>
-#include <media/videobuf2-dma-contig.h>
-
-#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;
-}