This driver supports both Solo-6010 and Solo-6110 chips anyway.
Renamed solo6010.h -> solo6x10.h.
Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-solo6x10-y := solo6010-core.o solo6010-i2c.o solo6010-p2m.o \
- solo6010-v4l2.o solo6010-tw28.o solo6010-gpio.o \
- solo6010-disp.o solo6010-enc.o solo6010-v4l2-enc.o \
- solo6010-g723.o
+solo6x10-y := core.o i2c.o p2m.o v4l2.o tw28.o gpio.o disp.o enc.o v4l2-enc.o g723.o
obj-$(CONFIG_SOLO6X10) := solo6x10.o
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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 "solo6x10.h"
+#include "tw28.h"
+
+MODULE_DESCRIPTION("Softlogic 6010 MP4 Encoder/Decoder V4L2/ALSA Driver");
+MODULE_AUTHOR("Ben Collins <bcollins@bluecherry.net>");
+MODULE_VERSION(SOLO6010_VERSION);
+MODULE_LICENSE("GPL");
+
+void solo6010_irq_on(struct solo6010_dev *solo_dev, u32 mask)
+{
+ solo_dev->irq_mask |= mask;
+ solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask);
+}
+
+void solo6010_irq_off(struct solo6010_dev *solo_dev, u32 mask)
+{
+ solo_dev->irq_mask &= ~mask;
+ solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask);
+}
+
+/* XXX We should check the return value of the sub-device ISR's */
+static irqreturn_t solo6010_isr(int irq, void *data)
+{
+ struct solo6010_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) {
+ u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR);
+ solo_p2m_error_isr(solo_dev, err);
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR);
+ }
+
+ 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);
+
+ /* Call this first so enc gets detected flag set */
+ if (status & SOLO_IRQ_MOTION)
+ solo_motion_isr(solo_dev);
+
+ if (status & SOLO_IRQ_ENCODER)
+ solo_enc_v4l2_isr(solo_dev);
+
+ if (status & SOLO_IRQ_G723)
+ solo_g723_isr(solo_dev);
+
+ return IRQ_HANDLED;
+}
+
+static void free_solo_dev(struct solo6010_dev *solo_dev)
+{
+ struct pci_dev *pdev;
+
+ if (!solo_dev)
+ return;
+
+ pdev = solo_dev->pdev;
+
+ /* If we never initialized the PCI device, then nothing else
+ * below here needs cleanup */
+ if (!pdev) {
+ kfree(solo_dev);
+ return;
+ }
+
+ /* 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 */
+ if (solo_dev->reg_base) {
+ solo6010_irq_off(solo_dev, ~0);
+ pci_iounmap(pdev, solo_dev->reg_base);
+ free_irq(pdev->irq, solo_dev);
+ }
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+
+ kfree(solo_dev);
+}
+
+static int __devinit solo6010_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct solo6010_dev *solo_dev;
+ int ret;
+ int sdram;
+ u8 chip_id;
+ u32 reg;
+
+ solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL);
+ if (solo_dev == NULL)
+ return -ENOMEM;
+
+ solo_dev->pdev = pdev;
+ spin_lock_init(&solo_dev->reg_io_lock);
+ pci_set_drvdata(pdev, solo_dev);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto fail_probe;
+
+ pci_set_master(pdev);
+
+ ret = pci_request_regions(pdev, SOLO6010_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, "
+ "defaulting to 4 channels\n",
+ chip_id);
+ case 5:
+ solo_dev->nr_chans = 4;
+ solo_dev->nr_ext = 1;
+ }
+
+ solo_dev->flags = id->driver_data;
+
+ /* Disable all interrupts to start */
+ solo6010_irq_off(solo_dev, ~0);
+
+ reg = SOLO_SYS_CFG_SDRAM64BIT;
+ /* Initial global settings */
+ if (!(solo_dev->flags & FLAGS_6110))
+ reg |= SOLO6010_SYS_CFG_INPUTDIV(25) |
+ SOLO6010_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) |
+ SOLO6010_SYS_CFG_OUTDIV(3);
+ solo_reg_write(solo_dev, SOLO_SYS_CFG, reg);
+
+ if (solo_dev->flags & FLAGS_6110) {
+ u32 sys_clock_MHz = SOLO_CLOCK_MHZ;
+ u32 pll_DIVQ;
+ u32 pll_DIVF;
+
+ if (sys_clock_MHz < 125) {
+ pll_DIVQ = 3;
+ pll_DIVF = (sys_clock_MHz * 4) / 3;
+ } else {
+ pll_DIVQ = 2;
+ pll_DIVF = (sys_clock_MHz * 2) / 3;
+ }
+
+ solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG,
+ SOLO6110_PLL_RANGE_5_10MHZ |
+ SOLO6110_PLL_DIVR(9) |
+ SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) |
+ SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN);
+ mdelay(1); // PLL Locking time (1ms)
+
+ solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */
+ } else
+ solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */
+
+ solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1);
+
+ /* PLL locking time of 1ms */
+ mdelay(1);
+
+ ret = request_irq(pdev->irq, solo6010_isr, IRQF_SHARED, SOLO6010_NAME,
+ solo_dev);
+ if (ret)
+ goto fail_probe;
+
+ /* Handle this from the start */
+ solo6010_irq_on(solo_dev, SOLO_IRQ_PCI_ERR);
+
+ ret = solo_i2c_init(solo_dev);
+ if (ret)
+ goto fail_probe;
+
+ /* Setup the DMA engine */
+ sdram = (solo_dev->nr_chans >= 8) ? 2 : 1;
+ solo_reg_write(solo_dev, SOLO_DMA_CTRL,
+ SOLO_DMA_CTRL_REFRESH_CYCLE(1) |
+ SOLO_DMA_CTRL_SDRAM_SIZE(sdram) |
+ SOLO_DMA_CTRL_SDRAM_CLK_INVERT |
+ SOLO_DMA_CTRL_READ_CLK_SELECT |
+ SOLO_DMA_CTRL_LATENCY(1));
+
+ 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);
+ if (ret)
+ goto fail_probe;
+
+ ret = solo_enc_init(solo_dev);
+ if (ret)
+ goto fail_probe;
+
+ ret = solo_enc_v4l2_init(solo_dev);
+ if (ret)
+ goto fail_probe;
+
+ ret = solo_g723_init(solo_dev);
+ if (ret)
+ goto fail_probe;
+
+ return 0;
+
+fail_probe:
+ free_solo_dev(solo_dev);
+ return ret;
+}
+
+static void __devexit solo6010_pci_remove(struct pci_dev *pdev)
+{
+ struct solo6010_dev *solo_dev = pci_get_drvdata(pdev);
+
+ free_solo_dev(solo_dev);
+}
+
+static struct pci_device_id solo6010_id_table[] = {
+ /* 6010 based cards */
+ {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)},
+ {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110),
+ .driver_data = FLAGS_6110},
+ {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16)},
+ /* 6110 based cards */
+ {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16)},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, solo6010_id_table);
+
+static struct pci_driver solo6010_pci_driver = {
+ .name = SOLO6010_NAME,
+ .id_table = solo6010_id_table,
+ .probe = solo6010_pci_probe,
+ .remove = solo6010_pci_remove,
+};
+
+static int __init solo6010_module_init(void)
+{
+ return pci_register_driver(&solo6010_pci_driver);
+}
+
+static void __exit solo6010_module_exit(void)
+{
+ pci_unregister_driver(&solo6010_pci_driver);
+}
+
+module_init(solo6010_module_init);
+module_exit(solo6010_module_exit);
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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 512
+#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 32)
+
+static unsigned video_type;
+module_param(video_type, uint, 0644);
+MODULE_PARM_DESC(video_type, "video_type (0 = NTSC/Default, 1 = PAL)");
+
+static void solo_vin_config(struct solo6010_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));
+
+ solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0);
+ 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_disp_config(struct solo6010_dev *solo_dev)
+{
+ solo_dev->vout_hstart = 6;
+ solo_dev->vout_vstart = 8;
+
+ 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_FMT_ENC,
+ solo_dev->video_type |
+ SOLO_VO_USER_COLOR_SET_NAV |
+ 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));
+
+ solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 5);
+
+ 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_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
+
+ /* Enable channels we support */
+ solo_reg_write(solo_dev, SOLO_VI_CH_ENA, (1 << solo_dev->nr_chans) - 1);
+
+ /* Disable the watchdog */
+ solo_reg_write(solo_dev, SOLO_WATCHDOG, 0);
+}
+
+static int solo_dma_vin_region(struct solo6010_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] = val;
+
+ for (i = 0; i < reg_size; i += sizeof(buf))
+ ret |= solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_VIN, 1, buf,
+ SOLO_MOTION_EXT_ADDR(solo_dev) + off + i,
+ sizeof(buf));
+
+ return ret;
+}
+
+void solo_set_motion_threshold(struct solo6010_dev *solo_dev, u8 ch, u16 val)
+{
+ if (ch > solo_dev->nr_chans)
+ return;
+
+ solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +
+ (ch * SOLO_MOT_THRESH_SIZE * 2),
+ val, SOLO_MOT_THRESH_REAL);
+}
+
+/* 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 (take from rdk) has some mystery 8k
+ * block right after the flag area, before the first thresh table. */
+static void solo_motion_config(struct solo6010_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 +
+ SOLO_MOT_THRESH_SIZE +
+ (i * SOLO_MOT_THRESH_SIZE * 2),
+ 0x0000, SOLO_MOT_THRESH_REAL);
+
+ /* 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 solo6010_dev *solo_dev)
+{
+ int i;
+
+ solo_dev->video_hsize = 704;
+ if (video_type == 0) {
+ solo_dev->video_type = SOLO_VO_FMT_TYPE_NTSC;
+ solo_dev->video_vsize = 240;
+ solo_dev->fps = 30;
+ } else {
+ solo_dev->video_type = SOLO_VO_FMT_TYPE_PAL;
+ solo_dev->video_vsize = 288;
+ solo_dev->fps = 25;
+ }
+
+ solo_vin_config(solo_dev);
+ solo_motion_config(solo_dev);
+ solo_disp_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 solo6010_dev *solo_dev)
+{
+ int i;
+
+ solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION);
+
+ 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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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 "solo6x10.h"
+#include "osd-font.h"
+
+#define CAPTURE_MAX_BANDWIDTH 32 /* D1 4channel (D1 == 4) */
+#define OSG_BUFFER_SIZE 1024
+
+#define VI_PROG_HSIZE (1280 - 16)
+#define VI_PROG_VSIZE (1024 - 16)
+
+static void solo_capture_config(struct solo6010_dev *solo_dev)
+{
+ int i, j;
+ unsigned long height;
+ unsigned long width;
+ unsigned char *buf;
+
+ solo_reg_write(solo_dev, SOLO_CAP_BASE,
+ SOLO_CAP_MAX_PAGE(SOLO_CAP_EXT_MAX_PAGE *
+ solo_dev->nr_chans) |
+ SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16));
+ solo_reg_write(solo_dev, SOLO_CAP_BTW,
+ (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) |
+ SOLO_CAP_MAX_BANDWIDTH(CAPTURE_MAX_BANDWIDTH));
+
+ /* 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);
+ solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, 0);
+
+ /* Clear OSG buffer */
+ buf = kzalloc(OSG_BUFFER_SIZE, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ for (i = 0; i < solo_dev->nr_chans; i++) {
+ for (j = 0; j < SOLO_EOSD_EXT_SIZE; j += OSG_BUFFER_SIZE) {
+ solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_MP4E, 1, buf,
+ SOLO_EOSD_EXT_ADDR +
+ (i * SOLO_EOSD_EXT_SIZE) + j,
+ OSG_BUFFER_SIZE);
+ }
+ }
+ kfree(buf);
+}
+
+int solo_osd_print(struct solo_enc_dev *solo_enc)
+{
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+ char *str = solo_enc->osd_text;
+ u8 *buf;
+ u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH);
+ int len = strlen(str);
+ int i, j;
+ int x = 1, y = 1;
+
+ if (len == 0) {
+ reg &= ~(1 << solo_enc->ch);
+ solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
+ return 0;
+ }
+
+ buf = kzalloc(SOLO_EOSD_EXT_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < len; i++) {
+ for (j = 0; j < 16; j++) {
+ buf[(j*2) + (i%2) + ((x + (i/2)) * 32) + (y * 2048)] =
+ (solo_osd_font[(str[i] * 4) + (j / 4)]
+ >> ((3 - (j % 4)) * 8)) & 0xff;
+ }
+ }
+
+ solo_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR +
+ (solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE);
+ reg |= (1 << solo_enc->ch);
+ solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
+
+ kfree(buf);
+
+ return 0;
+}
+
+static void solo_jpeg_config(struct solo6010_dev *solo_dev)
+{
+ u32 reg;
+ if (solo_dev->flags & FLAGS_6110)
+ reg = (4 << 24) | (3 << 16) | (2 << 8) | (1 << 0);
+ else
+ reg = (2 << 24) | (2 << 16) | (2 << 8) | (2 << 0);
+ solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, reg);
+ solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, 0);
+ solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, 0);
+ 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);
+ /* que limit, samp limit, pos limit */
+ solo_reg_write(solo_dev, 0x0688, (0 << 16) | (30 << 8) | 60);
+}
+
+static void solo_mp4e_config(struct solo6010_dev *solo_dev)
+{
+ int i;
+ u32 reg;
+
+ /* We can only use VE_INTR_CTRL(0) if we want to support mjpeg */
+ solo_reg_write(solo_dev, SOLO_VE_CFG0,
+ SOLO_VE_INTR_CTRL(0) |
+ SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) |
+ SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16));
+
+ solo_reg_write(solo_dev, SOLO_VE_CFG1,
+ SOLO_VE_INSERT_INDEX | SOLO_VE_MOTION_MODE(0));
+
+ 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);
+ solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0);
+ solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0);
+
+ reg = SOLO_VE_LITTLE_ENDIAN | SOLO_COMP_ATTR_FCODE(1) |
+ SOLO_COMP_TIME_INC(0) | SOLO_COMP_TIME_WIDTH(15);
+ if (solo_dev->flags & FLAGS_6110)
+ reg |= SOLO_DCT_INTERVAL(10);
+ else
+ reg |= SOLO_DCT_INTERVAL(36 / 4);
+ solo_reg_write(solo_dev, SOLO_VE_ATTR, reg);
+
+ 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);
+
+ if (solo_dev->flags & FLAGS_6110)
+ solo_reg_write(solo_dev, 0x0634, 0x00040008); /* ? */
+}
+
+int solo_enc_init(struct solo6010_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);
+ }
+
+ solo6010_irq_on(solo_dev, SOLO_IRQ_ENCODER);
+
+ return 0;
+}
+
+void solo_enc_exit(struct solo6010_dev *solo_dev)
+{
+ int i;
+
+ solo6010_irq_off(solo_dev, SOLO_IRQ_ENCODER);
+
+ 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);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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 <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include "solo6x10.h"
+#include "tw28.h"
+
+#define G723_INTR_ORDER 0
+#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 MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX)
+#define IRQ_PAGES 4 /* 0 - 4 */
+#define PERIODS_MIN (1 << IRQ_PAGES)
+#define PERIODS_MAX G723_FDMA_PAGES
+
+struct solo_snd_pcm {
+ int on;
+ spinlock_t lock;
+ struct solo6010_dev *solo_dev;
+ unsigned char g723_buf[G723_PERIOD_BYTES];
+};
+
+static void solo_g723_config(struct solo6010_dev *solo_dev)
+{
+ int clk_div;
+
+ clk_div = SOLO_CLOCK_MHZ / (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(IRQ_PAGES) |
+ 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 solo6010_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;
+
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_G723);
+
+ 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 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 = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 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 solo6010_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)
+ return -ENOMEM;
+
+ 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;
+}
+
+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;
+ 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 solo6010_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)
+ solo6010_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)
+ solo6010_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 solo6010_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 solo6010_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(solo_dev, SOLO_P2M_DMA_ID_G723E, 0,
+ solo_pcm->g723_buf,
+ SOLO_G723_EXT_ADDR(solo_dev) +
+ (page * G723_PERIOD_BLOCK) +
+ (ss->number * G723_PERIOD_BYTES),
+ G723_PERIOD_BYTES);
+ 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 solo6010_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 solo6010_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 solo6010_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),
+ MAX_BUFFER, MAX_BUFFER);
+ if (ret < 0)
+ return ret;
+
+ solo_dev->snd_pcm = pcm;
+
+ return 0;
+}
+
+int solo_g723_init(struct solo6010_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, SOLO6010_NAME);
+ strcpy(card->shortname, "SOLO-6010 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-6010");
+ 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 solo6010_dev *solo_dev)
+{
+ solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0);
+ solo6010_irq_off(solo_dev, SOLO_IRQ_G723);
+
+ snd_card_free(solo_dev->snd_card);
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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 <asm/uaccess.h>
+#include "solo6x10.h"
+
+static void solo_gpio_mode(struct solo6010_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 solo6010_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 solo6010_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 solo6010_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 solo6010_dev *solo_dev)
+{
+ solo_gpio_config(solo_dev);
+ return 0;
+}
+
+void solo_gpio_exit(struct solo6010_dev *solo_dev)
+{
+ solo_gpio_clear(solo_dev, 0x30);
+ solo_gpio_config(solo_dev);
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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 SOLO6010 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 solo6010_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 solo6010_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 solo6010_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 solo6010_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 solo6010_dev *solo_dev)
+{
+ solo6010_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 solo6010_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 solo6010_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 solo6010_dev *solo_dev)
+{
+ u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL);
+ int ret = -EINVAL;
+
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC);
+
+ if (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 solo6010_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);
+ solo6010_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 struct i2c_algorithm solo_i2c_algo = {
+ .master_xfer = solo_i2c_master_xfer,
+ .functionality = solo_i2c_functionality,
+};
+
+int solo_i2c_init(struct solo6010_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",
+ SOLO6010_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;
+ }
+
+ dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n",
+ SOLO_I2C_ADAPTERS);
+
+ return 0;
+}
+
+void solo_i2c_exit(struct solo6010_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;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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.
+ */
+
+#ifndef __SOLO6010_JPEG_H
+#define __SOLO6010_JPEG_H
+
+static unsigned char jpeg_header[] = {
+ 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c,
+ 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79,
+ 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16,
+ 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c,
+ 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30,
+ 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, 0x3a, 0x50,
+ 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, 0x70, 0x6e,
+ 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a,
+ 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4,
+ 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, 0xf2, 0xe0,
+ 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0xff, 0xdb,
+ 0x00, 0x43, 0x01, 0x22, 0x24, 0x24, 0x30, 0x2a,
+ 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6, 0x84, 0x70,
+ 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xff, 0xc4, 0x01, 0xa2, 0x00,
+ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01,
+ 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04,
+ 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03,
+ 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41,
+ 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14,
+ 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1,
+ 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62,
+ 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19,
+ 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74,
+ 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84,
+ 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
+ 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
+ 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+ 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+ 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5,
+ 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
+ 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01,
+ 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01,
+ 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04,
+ 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02,
+ 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12,
+ 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32,
+ 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1,
+ 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72,
+ 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1,
+ 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29,
+ 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73,
+ 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82,
+ 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+ 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,
+ 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,
+ 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5,
+ 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4,
+ 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3,
+ 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff,
+ 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x02, 0xc0,
+ 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03,
+ 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01,
+ 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+};
+
+/* This is the byte marker for the start of SOF0: 0xffc0 marker */
+#define SOF0_START 575
+
+#endif /* __SOLO6010_JPEG_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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.
+ */
+
+#ifndef __SOLO6010_OFFSETS_H
+#define __SOLO6010_OFFSETS_H
+
+/* Offsets and sizes of the external address */
+#define SOLO_DISP_EXT_ADDR 0x00000000
+#define SOLO_DISP_EXT_SIZE 0x00480000
+
+#define SOLO_DEC2LIVE_EXT_ADDR (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE)
+#define SOLO_DEC2LIVE_EXT_SIZE 0x00240000
+
+#define SOLO_OSG_EXT_ADDR (SOLO_DEC2LIVE_EXT_ADDR + SOLO_DEC2LIVE_EXT_SIZE)
+#define SOLO_OSG_EXT_SIZE 0x00120000
+
+#define SOLO_EOSD_EXT_ADDR (SOLO_OSG_EXT_ADDR + SOLO_OSG_EXT_SIZE)
+#define SOLO_EOSD_EXT_SIZE 0x00010000
+
+#define SOLO_MOTION_EXT_ADDR(__solo) (SOLO_EOSD_EXT_ADDR + \
+ (SOLO_EOSD_EXT_SIZE * __solo->nr_chans))
+#define SOLO_MOTION_EXT_SIZE 0x00080000
+
+#define SOLO_G723_EXT_ADDR(__solo) \
+ (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE)
+#define SOLO_G723_EXT_SIZE 0x00010000
+
+#define SOLO_CAP_EXT_ADDR(__solo) \
+ (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE)
+#define SOLO_CAP_EXT_MAX_PAGE (18 + 15)
+#define SOLO_CAP_EXT_SIZE (SOLO_CAP_EXT_MAX_PAGE * 65536)
+
+/* This +1 is very important -- Why?! -- BenC */
+#define SOLO_EREF_EXT_ADDR(__solo) \
+ (SOLO_CAP_EXT_ADDR(__solo) + \
+ (SOLO_CAP_EXT_SIZE * (__solo->nr_chans + 1)))
+#define SOLO_EREF_EXT_SIZE 0x00140000
+
+#define SOLO_MP4E_EXT_ADDR(__solo) \
+ (SOLO_EREF_EXT_ADDR(__solo) + \
+ (SOLO_EREF_EXT_SIZE * __solo->nr_chans))
+#define SOLO_MP4E_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans)
+
+#define SOLO_DREF_EXT_ADDR(__solo) \
+ (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo))
+#define SOLO_DREF_EXT_SIZE 0x00140000
+
+#define SOLO_MP4D_EXT_ADDR(__solo) \
+ (SOLO_DREF_EXT_ADDR(__solo) + \
+ (SOLO_DREF_EXT_SIZE * __solo->nr_chans))
+#define SOLO_MP4D_EXT_SIZE 0x00080000
+
+#define SOLO_JPEG_EXT_ADDR(__solo) \
+ (SOLO_MP4D_EXT_ADDR(__solo) + \
+ (SOLO_MP4D_EXT_SIZE * __solo->nr_chans))
+#define SOLO_JPEG_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans)
+
+#endif /* __SOLO6010_OFFSETS_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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.
+ */
+
+#ifndef __SOLO6010_OSD_FONT_H
+#define __SOLO6010_OSD_FONT_H
+
+static const unsigned int solo_osd_font[] = {
+ 0x00000000, 0x0000c0c8, 0xccfefe0c, 0x08000000,
+ 0x00000000, 0x10103838, 0x7c7cfefe, 0x00000000, /* 0 */
+ 0x00000000, 0xfefe7c7c, 0x38381010, 0x10000000,
+ 0x00000000, 0x7c82fefe, 0xfefefe7c, 0x00000000,
+ 0x00000000, 0x00001038, 0x10000000, 0x00000000,
+ 0x00000000, 0x0010387c, 0xfe7c3810, 0x00000000,
+ 0x00000000, 0x00384444, 0x44380000, 0x00000000,
+ 0x00000000, 0x38448282, 0x82443800, 0x00000000,
+ 0x00000000, 0x007c7c7c, 0x7c7c0000, 0x00000000,
+ 0x00000000, 0x6c6c6c6c, 0x6c6c6c6c, 0x00000000,
+ 0x00000000, 0x061e7efe, 0xfe7e1e06, 0x00000000,
+ 0x00000000, 0xc0f0fcfe, 0xfefcf0c0, 0x00000000,
+ 0x00000000, 0xc6cedefe, 0xfedecec6, 0x00000000,
+ 0x00000000, 0xc6e6f6fe, 0xfef6e6c6, 0x00000000,
+ 0x00000000, 0x12367efe, 0xfe7e3612, 0x00000000,
+ 0x00000000, 0x90d8fcfe, 0xfefcd890, 0x00000000,
+ 0x00000038, 0x7cc692ba, 0x92c67c38, 0x00000000,
+ 0x00000038, 0x7cc6aa92, 0xaac67c38, 0x00000000,
+ 0x00000038, 0x7830107c, 0xbaa8680c, 0x00000000,
+ 0x00000038, 0x3c18127c, 0xb8382c60, 0x00000000,
+ 0x00000044, 0xaa6c8254, 0x38eec67c, 0x00000000,
+ 0x00000082, 0x44288244, 0x38c6827c, 0x00000000,
+ 0x00000038, 0x444444fe, 0xfeeec6fe, 0x00000000,
+ 0x00000018, 0x78187818, 0x3c7e7e3c, 0x00000000,
+ 0x00000000, 0x3854929a, 0x82443800, 0x00000000,
+ 0x00000000, 0x00c0c8cc, 0xfefe0c08, 0x00000000,
+ 0x0000e0a0, 0xe040e00e, 0x8a0ea40e, 0x00000000,
+ 0x0000e0a0, 0xe040e00e, 0x0a8e440e, 0x00000000,
+ 0x0000007c, 0x82829292, 0x929282fe, 0x00000000,
+ 0x000000f8, 0xfc046494, 0x946404fc, 0x00000000,
+ 0x0000003f, 0x7f404c52, 0x524c407f, 0x00000000,
+ 0x0000007c, 0x82ba82ba, 0x82ba82fe, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x183c3c3c, 0x18180018, 0x18000000, /* 32 ! */
+ 0x00000066, 0x66240000, 0x00000000, 0x00000000,
+ 0x00000000, 0x6c6cfe6c, 0x6c6cfe6c, 0x6c000000, /* 34 " # */
+ 0x00001010, 0x7cd6d616, 0x7cd0d6d6, 0x7c101000,
+ 0x00000000, 0x0086c660, 0x30180cc6, 0xc2000000, /* 36 $ % */
+ 0x00000000, 0x386c6c38, 0xdc766666, 0xdc000000,
+ 0x0000000c, 0x0c0c0600, 0x00000000, 0x00000000, /* 38 & ' */
+ 0x00000000, 0x30180c0c, 0x0c0c0c18, 0x30000000,
+ 0x00000000, 0x0c183030, 0x30303018, 0x0c000000, /* 40 ( ) */
+ 0x00000000, 0x0000663c, 0xff3c6600, 0x00000000,
+ 0x00000000, 0x00001818, 0x7e181800, 0x00000000, /* 42 * + */
+ 0x00000000, 0x00000000, 0x00000e0e, 0x0c060000,
+ 0x00000000, 0x00000000, 0x7e000000, 0x00000000, /* 44 , - */
+ 0x00000000, 0x00000000, 0x00000006, 0x06000000,
+ 0x00000000, 0x80c06030, 0x180c0602, 0x00000000, /* 46 . / */
+ 0x0000007c, 0xc6e6f6de, 0xcec6c67c, 0x00000000,
+ 0x00000030, 0x383c3030, 0x303030fc, 0x00000000, /* 48 0 1 */
+ 0x0000007c, 0xc6c06030, 0x180cc6fe, 0x00000000,
+ 0x0000007c, 0xc6c0c07c, 0xc0c0c67c, 0x00000000, /* 50 2 3 */
+ 0x00000060, 0x70786c66, 0xfe6060f0, 0x00000000,
+ 0x000000fe, 0x0606067e, 0xc0c0c67c, 0x00000000, /* 52 4 5 */
+ 0x00000038, 0x0c06067e, 0xc6c6c67c, 0x00000000,
+ 0x000000fe, 0xc6c06030, 0x18181818, 0x00000000, /* 54 6 7 */
+ 0x0000007c, 0xc6c6c67c, 0xc6c6c67c, 0x00000000,
+ 0x0000007c, 0xc6c6c6fc, 0xc0c06038, 0x00000000, /* 56 8 9 */
+ 0x00000000, 0x18180000, 0x00181800, 0x00000000,
+ 0x00000000, 0x18180000, 0x0018180c, 0x00000000, /* 58 : ; */
+ 0x00000060, 0x30180c06, 0x0c183060, 0x00000000,
+ 0x00000000, 0x007e0000, 0x007e0000, 0x00000000,
+ 0x00000006, 0x0c183060, 0x30180c06, 0x00000000,
+ 0x0000007c, 0xc6c66030, 0x30003030, 0x00000000,
+ 0x0000007c, 0xc6f6d6d6, 0x7606067c, 0x00000000,
+ 0x00000010, 0x386cc6c6, 0xfec6c6c6, 0x00000000, /* 64 @ A */
+ 0x0000007e, 0xc6c6c67e, 0xc6c6c67e, 0x00000000,
+ 0x00000078, 0xcc060606, 0x0606cc78, 0x00000000, /* 66 */
+ 0x0000003e, 0x66c6c6c6, 0xc6c6663e, 0x00000000,
+ 0x000000fe, 0x0606063e, 0x060606fe, 0x00000000, /* 68 */
+ 0x000000fe, 0x0606063e, 0x06060606, 0x00000000,
+ 0x00000078, 0xcc060606, 0xf6c6ccb8, 0x00000000, /* 70 */
+ 0x000000c6, 0xc6c6c6fe, 0xc6c6c6c6, 0x00000000,
+ 0x0000003c, 0x18181818, 0x1818183c, 0x00000000, /* 72 */
+ 0x00000060, 0x60606060, 0x6066663c, 0x00000000,
+ 0x000000c6, 0xc666361e, 0x3666c6c6, 0x00000000, /* 74 */
+ 0x00000006, 0x06060606, 0x060606fe, 0x00000000,
+ 0x000000c6, 0xeefed6c6, 0xc6c6c6c6, 0x00000000, /* 76 */
+ 0x000000c6, 0xcedefef6, 0xe6c6c6c6, 0x00000000,
+ 0x00000038, 0x6cc6c6c6, 0xc6c66c38, 0x00000000, /* 78 */
+ 0x0000007e, 0xc6c6c67e, 0x06060606, 0x00000000,
+ 0x00000038, 0x6cc6c6c6, 0xc6d67c38, 0x60000000, /* 80 */
+ 0x0000007e, 0xc6c6c67e, 0x66c6c6c6, 0x00000000,
+ 0x0000007c, 0xc6c60c38, 0x60c6c67c, 0x00000000, /* 82 */
+ 0x0000007e, 0x18181818, 0x18181818, 0x00000000,
+ 0x000000c6, 0xc6c6c6c6, 0xc6c6c67c, 0x00000000, /* 84 */
+ 0x000000c6, 0xc6c6c6c6, 0xc66c3810, 0x00000000,
+ 0x000000c6, 0xc6c6c6c6, 0xd6d6fe6c, 0x00000000, /* 86 */
+ 0x000000c6, 0xc6c66c38, 0x6cc6c6c6, 0x00000000,
+ 0x00000066, 0x66666666, 0x3c181818, 0x00000000, /* 88 */
+ 0x000000fe, 0xc0603018, 0x0c0606fe, 0x00000000,
+ 0x0000003c, 0x0c0c0c0c, 0x0c0c0c3c, 0x00000000, /* 90 */
+ 0x00000002, 0x060c1830, 0x60c08000, 0x00000000,
+ 0x0000003c, 0x30303030, 0x3030303c, 0x00000000, /* 92 */
+ 0x00001038, 0x6cc60000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00fe0000,
+ 0x00001818, 0x30000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00003c60, 0x7c66667c, 0x00000000,
+ 0x0000000c, 0x0c0c7ccc, 0xcccccc7c, 0x00000000,
+ 0x00000000, 0x00007cc6, 0x0606c67c, 0x00000000,
+ 0x00000060, 0x60607c66, 0x6666667c, 0x00000000,
+ 0x00000000, 0x00007cc6, 0xfe06c67c, 0x00000000,
+ 0x00000078, 0x0c0c0c3e, 0x0c0c0c0c, 0x00000000,
+ 0x00000000, 0x00007c66, 0x6666667c, 0x60603e00,
+ 0x0000000c, 0x0c0c7ccc, 0xcccccccc, 0x00000000,
+ 0x00000030, 0x30003830, 0x30303078, 0x00000000,
+ 0x00000030, 0x30003c30, 0x30303030, 0x30301f00,
+ 0x0000000c, 0x0c0ccc6c, 0x3c6ccccc, 0x00000000,
+ 0x00000030, 0x30303030, 0x30303030, 0x00000000,
+ 0x00000000, 0x000066fe, 0xd6d6d6d6, 0x00000000,
+ 0x00000000, 0x000078cc, 0xcccccccc, 0x00000000,
+ 0x00000000, 0x00007cc6, 0xc6c6c67c, 0x00000000,
+ 0x00000000, 0x00007ccc, 0xcccccc7c, 0x0c0c0c00,
+ 0x00000000, 0x00007c66, 0x6666667c, 0x60606000,
+ 0x00000000, 0x000076dc, 0x0c0c0c0c, 0x00000000,
+ 0x00000000, 0x00007cc6, 0x1c70c67c, 0x00000000,
+ 0x00000000, 0x1818fe18, 0x18181870, 0x00000000,
+ 0x00000000, 0x00006666, 0x6666663c, 0x00000000,
+ 0x00000000, 0x0000c6c6, 0xc66c3810, 0x00000000,
+ 0x00000000, 0x0000c6d6, 0xd6d6fe6c, 0x00000000,
+ 0x00000000, 0x0000c66c, 0x38386cc6, 0x00000000,
+ 0x00000000, 0x00006666, 0x6666667c, 0x60603e00,
+ 0x00000000, 0x0000fe60, 0x30180cfe, 0x00000000,
+ 0x00000070, 0x1818180e, 0x18181870, 0x00000000,
+ 0x00000018, 0x18181800, 0x18181818, 0x00000000,
+ 0x0000000e, 0x18181870, 0x1818180e, 0x00000000,
+ 0x000000dc, 0x76000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x0010386c, 0xc6c6fe00, 0x00000000
+};
+
+#endif /* __SOLO6010_OSD_FONT_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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/scatterlist.h>
+#include "solo6x10.h"
+
+/* #define SOLO_TEST_P2M */
+
+int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr,
+ void *sys_addr, u32 ext_addr, u32 size)
+{
+ dma_addr_t dma_addr;
+ int ret;
+
+ WARN_ON(!size);
+ BUG_ON(id >= SOLO_NR_P2M);
+
+ if (!size)
+ return -EINVAL;
+
+ dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size,
+ wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+
+ ret = solo_p2m_dma_t(solo_dev, id, wr, dma_addr, ext_addr, size);
+
+ pci_unmap_single(solo_dev->pdev, dma_addr, size,
+ wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+
+ return ret;
+}
+
+int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr,
+ dma_addr_t dma_addr, u32 ext_addr, u32 size)
+{
+ struct p2m_desc *desc = kzalloc(sizeof(*desc) * 2, GFP_DMA);
+ int ret;
+
+ if (desc == NULL)
+ return -ENOMEM;
+
+ solo_p2m_push_desc(&desc[1], wr, dma_addr, ext_addr, size, 0, 0);
+ ret = solo_p2m_dma_desc(solo_dev, id, desc, 2);
+ kfree(desc);
+
+ return ret;
+}
+
+void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr,
+ u32 ext_addr, u32 size, int repeat, u32 ext_size)
+{
+ desc->ta = cpu_to_le32(dma_addr);
+ desc->fa = cpu_to_le32(ext_addr);
+
+ desc->ext = cpu_to_le32(SOLO_P2M_COPY_SIZE(size >> 2));
+ desc->ctrl = cpu_to_le32(SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) |
+ (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON);
+
+ /* Ext size only matters when we're repeating */
+ if (repeat) {
+ desc->ext |= cpu_to_le32(SOLO_P2M_EXT_INC(ext_size >> 2));
+ desc->ctrl |= cpu_to_le32(SOLO_P2M_PCI_INC(size >> 2) |
+ SOLO_P2M_REPEAT(repeat));
+ }
+}
+
+int solo_p2m_dma_desc(struct solo6010_dev *solo_dev, u8 id,
+ struct p2m_desc *desc, int desc_count)
+{
+ struct solo_p2m_dev *p2m_dev;
+ unsigned int timeout;
+ int ret = 0;
+ u32 config = 0;
+ dma_addr_t desc_dma = 0;
+
+ BUG_ON(id >= SOLO_NR_P2M);
+ BUG_ON(!desc_count || desc_count > SOLO_NR_P2M_DESC);
+
+ p2m_dev = &solo_dev->p2m_dev[id];
+
+ mutex_lock(&p2m_dev->mutex);
+
+ solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
+
+ INIT_COMPLETION(p2m_dev->completion);
+ p2m_dev->error = 0;
+
+ /* Enable the descriptors */
+ config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(id));
+ desc_dma = pci_map_single(solo_dev->pdev, desc,
+ desc_count * sizeof(*desc),
+ PCI_DMA_TODEVICE);
+ solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), desc_dma);
+ solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), desc_count - 1);
+ solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config |
+ SOLO_P2M_DESC_MODE);
+
+ /* Should have all descriptors completed from one interrupt */
+ timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ);
+
+ solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
+
+ /* Reset back to non-descriptor mode */
+ solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config);
+ solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), 0);
+ solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), 0);
+ pci_unmap_single(solo_dev->pdev, desc_dma,
+ desc_count * sizeof(*desc),
+ PCI_DMA_TODEVICE);
+
+ if (p2m_dev->error)
+ ret = -EIO;
+ else if (timeout == 0)
+ ret = -EAGAIN;
+
+ mutex_unlock(&p2m_dev->mutex);
+
+ WARN_ON_ONCE(ret);
+
+ return ret;
+}
+
+int solo_p2m_dma_sg(struct solo6010_dev *solo_dev, u8 id,
+ struct p2m_desc *pdesc, int wr,
+ struct scatterlist *sg, u32 sg_off,
+ u32 ext_addr, u32 size)
+{
+ int i;
+ int idx;
+
+ BUG_ON(id >= SOLO_NR_P2M);
+
+ if (WARN_ON_ONCE(!size))
+ return -EINVAL;
+
+ memset(pdesc, 0, sizeof(*pdesc));
+
+ /* Should rewrite this to handle > SOLO_NR_P2M_DESC transactions */
+ for (i = 0, idx = 1; idx < SOLO_NR_P2M_DESC && sg && size > 0;
+ i++, sg = sg_next(sg)) {
+ struct p2m_desc *desc = &pdesc[idx];
+ u32 sg_len = sg_dma_len(sg);
+ u32 len;
+
+ if (sg_off >= sg_len) {
+ sg_off -= sg_len;
+ continue;
+ }
+
+ sg_len -= sg_off;
+ len = min(sg_len, size);
+
+ solo_p2m_push_desc(desc, wr, sg_dma_address(sg) + sg_off,
+ ext_addr, len, 0, 0);
+
+ size -= len;
+ ext_addr += len;
+ idx++;
+
+ sg_off = 0;
+ }
+
+ WARN_ON_ONCE(size || i >= SOLO_NR_P2M_DESC);
+
+ return solo_p2m_dma_desc(solo_dev, id, pdesc, idx);
+}
+
+#ifdef SOLO_TEST_P2M
+
+#define P2M_TEST_CHAR 0xbe
+
+static unsigned long long p2m_test(struct solo6010_dev *solo_dev, u8 id,
+ u32 base, int size)
+{
+ u8 *wr_buf;
+ u8 *rd_buf;
+ int i;
+ unsigned long long err_cnt = 0;
+
+ wr_buf = kmalloc(size, GFP_KERNEL);
+ if (!wr_buf) {
+ printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n");
+ return size;
+ }
+
+ rd_buf = kmalloc(size, GFP_KERNEL);
+ if (!rd_buf) {
+ printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n");
+ kfree(wr_buf);
+ return size;
+ }
+
+ memset(wr_buf, P2M_TEST_CHAR, size);
+ memset(rd_buf, P2M_TEST_CHAR + 1, size);
+
+ solo_p2m_dma(solo_dev, id, 1, wr_buf, base, size);
+ solo_p2m_dma(solo_dev, id, 0, rd_buf, base, size);
+
+ for (i = 0; i < size; i++)
+ if (wr_buf[i] != rd_buf[i])
+ err_cnt++;
+
+ kfree(wr_buf);
+ kfree(rd_buf);
+
+ return err_cnt;
+}
+
+#define TEST_CHUNK_SIZE (8 * 1024)
+
+static void run_p2m_test(struct solo6010_dev *solo_dev)
+{
+ unsigned long long errs = 0;
+ u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev);
+ int i, d;
+
+ printk(KERN_WARNING "%s: Testing %u bytes of external ram\n",
+ SOLO6010_NAME, size);
+
+ for (i = 0; i < size; i += TEST_CHUNK_SIZE)
+ for (d = 0; d < 4; d++)
+ errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE);
+
+ printk(KERN_WARNING "%s: Found %llu errors during p2m test\n",
+ SOLO6010_NAME, errs);
+
+ return;
+}
+#else
+#define run_p2m_test(__solo) do {} while (0)
+#endif
+
+void solo_p2m_isr(struct solo6010_dev *solo_dev, int id)
+{
+ struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id];
+
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id));
+
+ complete(&p2m_dev->completion);
+}
+
+void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status)
+{
+ struct solo_p2m_dev *p2m_dev;
+ int i;
+
+ if (!(status & SOLO_PCI_ERR_P2M))
+ 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 solo6010_dev *solo_dev)
+{
+ int i;
+
+ for (i = 0; i < SOLO_NR_P2M; i++)
+ solo6010_irq_off(solo_dev, SOLO_IRQ_P2M(i));
+}
+
+int solo_p2m_init(struct solo6010_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_DMA_INTERVAL(3) |
+ SOLO_P2M_DESC_INTR_OPT |
+ SOLO_P2M_PCI_MASTER_MODE);
+ solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i));
+ }
+
+ run_p2m_test(solo_dev);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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.
+ */
+
+#ifndef __SOLO6010_REGISTERS_H
+#define __SOLO6010_REGISTERS_H
+
+#include "offsets.h"
+
+/* Global 6010 system configuration */
+#define SOLO_SYS_CFG 0x0000
+#define SOLO6010_SYS_CFG_FOUT_EN 0x00000001 /* 6010 only */
+#define SOLO6010_SYS_CFG_PLL_BYPASS 0x00000002 /* 6010 only */
+#define SOLO6010_SYS_CFG_PLL_PWDN 0x00000004 /* 6010 only */
+#define SOLO6010_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) /* 6010 only */
+#define SOLO6010_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) /* 6010 only */
+#define SOLO6010_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) /* 6010 only */
+#define SOLO_SYS_CFG_CLOCK_DIV 0x00080000
+#define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24)
+#define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26)
+#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 /* 6110: must be set */
+#define SOLO_SYS_CFG_RESET 0x80000000
+
+#define SOLO_DMA_CTRL 0x0004
+#define SOLO_DMA_CTRL_REFRESH_CYCLE(n) ((n)<<8)
+/* 0=16/32MB, 1=32/64MB, 2=64/128MB, 3=128/256MB */
+#define SOLO_DMA_CTRL_SDRAM_SIZE(n) ((n)<<6)
+#define SOLO_DMA_CTRL_SDRAM_CLK_INVERT (1<<5)
+#define SOLO_DMA_CTRL_STROBE_SELECT (1<<4)
+#define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3)
+#define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2)
+#define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0)
+#define SOLO_DMA_CTRL1 0x0008
+
+#define SOLO_SYS_VCLK 0x000C
+#define SOLO_VCLK_INVERT (1<<22)
+/* 0=sys_clk/4, 1=sys_clk/2, 2=clk_in/2 of system input */
+#define SOLO_VCLK_SELECT(n) ((n)<<20)
+#define SOLO_VCLK_VIN1415_DELAY(n) ((n)<<14)
+#define SOLO_VCLK_VIN1213_DELAY(n) ((n)<<12)
+#define SOLO_VCLK_VIN1011_DELAY(n) ((n)<<10)
+#define SOLO_VCLK_VIN0809_DELAY(n) ((n)<<8)
+#define SOLO_VCLK_VIN0607_DELAY(n) ((n)<<6)
+#define SOLO_VCLK_VIN0405_DELAY(n) ((n)<<4)
+#define SOLO_VCLK_VIN0203_DELAY(n) ((n)<<2)
+#define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0)
+
+#define SOLO_IRQ_STAT 0x0010
+#define SOLO_IRQ_ENABLE 0x0014
+#define SOLO_IRQ_P2M(n) (1<<((n)+17))
+#define SOLO_IRQ_GPIO (1<<16)
+#define SOLO_IRQ_VIDEO_LOSS (1<<15)
+#define SOLO_IRQ_VIDEO_IN (1<<14)
+#define SOLO_IRQ_MOTION (1<<13)
+#define SOLO_IRQ_ATA_CMD (1<<12)
+#define SOLO_IRQ_ATA_DIR (1<<11)
+#define SOLO_IRQ_PCI_ERR (1<<10)
+#define SOLO_IRQ_PS2_1 (1<<9)
+#define SOLO_IRQ_PS2_0 (1<<8)
+#define SOLO_IRQ_SPI (1<<7)
+#define SOLO_IRQ_IIC (1<<6)
+#define SOLO_IRQ_UART(n) (1<<((n) + 4))
+#define SOLO_IRQ_G723 (1<<3)
+#define SOLO_IRQ_DECODER (1<<1)
+#define SOLO_IRQ_ENCODER (1<<0)
+
+#define SOLO_CHIP_OPTION 0x001C
+#define SOLO_CHIP_ID_MASK 0x00000007
+
+#define SOLO6110_PLL_CONFIG 0x0020
+#define SOLO6110_PLL_RANGE_BYPASS (0 << 20)
+#define SOLO6110_PLL_RANGE_5_10MHZ (1 << 20)
+#define SOLO6110_PLL_RANGE_8_16MHZ (2 << 20)
+#define SOLO6110_PLL_RANGE_13_26MHZ (3 << 20)
+#define SOLO6110_PLL_RANGE_21_42MHZ (4 << 20)
+#define SOLO6110_PLL_RANGE_34_68MHZ (5 << 20)
+#define SOLO6110_PLL_RANGE_54_108MHZ (6 << 20)
+#define SOLO6110_PLL_RANGE_88_200MHZ (7 << 20)
+#define SOLO6110_PLL_DIVR(x) (((x) - 1) << 15)
+#define SOLO6110_PLL_DIVQ_EXP(x) ((x) << 12)
+#define SOLO6110_PLL_DIVF(x) (((x) - 1) << 4)
+#define SOLO6110_PLL_RESET (1 << 3)
+#define SOLO6110_PLL_BYPASS (1 << 2)
+#define SOLO6110_PLL_FSEN (1 << 1)
+#define SOLO6110_PLL_FB (1 << 0)
+
+#define SOLO_EEPROM_CTRL 0x0060
+#define SOLO_EEPROM_ACCESS_EN (1<<7)
+#define SOLO_EEPROM_CS (1<<3)
+#define SOLO_EEPROM_CLK (1<<2)
+#define SOLO_EEPROM_DO (1<<1)
+#define SOLO_EEPROM_DI (1<<0)
+#define SOLO_EEPROM_ENABLE (EEPROM_ACCESS_EN | EEPROM_CS)
+
+#define SOLO_PCI_ERR 0x0070
+#define SOLO_PCI_ERR_FATAL 0x00000001
+#define SOLO_PCI_ERR_PARITY 0x00000002
+#define SOLO_PCI_ERR_TARGET 0x00000004
+#define SOLO_PCI_ERR_TIMEOUT 0x00000008
+#define SOLO_PCI_ERR_P2M 0x00000010
+#define SOLO_PCI_ERR_ATA 0x00000020
+#define SOLO_PCI_ERR_P2M_DESC 0x00000040
+#define SOLO_PCI_ERR_FSM0(__s) (((__s) >> 16) & 0x0f)
+#define SOLO_PCI_ERR_FSM1(__s) (((__s) >> 20) & 0x0f)
+#define SOLO_PCI_ERR_FSM2(__s) (((__s) >> 24) & 0x1f)
+
+#define SOLO_P2M_BASE 0x0080
+
+#define SOLO_P2M_CONFIG(n) (0x0080 + ((n)*0x20))
+#define SOLO_P2M_DMA_INTERVAL(n) ((n)<<6)/* N*32 clocks */
+#define SOLO_P2M_CSC_BYTE_REORDER (1<<5) /* BGR -> RGB */
+/* 0:r=[14:10] g=[9:5] b=[4:0], 1:r=[15:11] g=[10:5] b=[4:0] */
+#define SOLO_P2M_CSC_16BIT_565 (1<<4)
+#define SOLO_P2M_UV_SWAP (1<<3)
+#define SOLO_P2M_PCI_MASTER_MODE (1<<2)
+#define SOLO_P2M_DESC_INTR_OPT (1<<1) /* 1:Empty, 0:Each */
+#define SOLO_P2M_DESC_MODE (1<<0)
+
+#define SOLO_P2M_DES_ADR(n) (0x0084 + ((n)*0x20))
+
+#define SOLO_P2M_DESC_ID(n) (0x0088 + ((n)*0x20))
+#define SOLO_P2M_UPDATE_ID(n) ((n)<<0)
+
+#define SOLO_P2M_STATUS(n) (0x008C + ((n)*0x20))
+#define SOLO_P2M_COMMAND_DONE (1<<8)
+#define SOLO_P2M_CURRENT_ID(stat) (0xff & (stat))
+
+#define SOLO_P2M_CONTROL(n) (0x0090 + ((n)*0x20))
+#define SOLO_P2M_PCI_INC(n) ((n)<<20)
+#define SOLO_P2M_REPEAT(n) ((n)<<10)
+/* 0:512, 1:256, 2:128, 3:64, 4:32, 5:128(2page) */
+#define SOLO_P2M_BURST_SIZE(n) ((n)<<7)
+#define SOLO_P2M_BURST_512 0
+#define SOLO_P2M_BURST_256 1
+#define SOLO_P2M_BURST_128 2
+#define SOLO_P2M_BURST_64 3
+#define SOLO_P2M_BURST_32 4
+#define SOLO_P2M_CSC_16BIT (1<<6) /* 0:24bit, 1:16bit */
+/* 0:Y[0]<-0(OFF), 1:Y[0]<-1(ON), 2:Y[0]<-G[0], 3:Y[0]<-Bit[15] */
+#define SOLO_P2M_ALPHA_MODE(n) ((n)<<4)
+#define SOLO_P2M_CSC_ON (1<<3)
+#define SOLO_P2M_INTERRUPT_REQ (1<<2)
+#define SOLO_P2M_WRITE (1<<1)
+#define SOLO_P2M_TRANS_ON (1<<0)
+
+#define SOLO_P2M_EXT_CFG(n) (0x0094 + ((n)*0x20))
+#define SOLO_P2M_EXT_INC(n) ((n)<<20)
+#define SOLO_P2M_COPY_SIZE(n) ((n)<<0)
+
+#define SOLO_P2M_TAR_ADR(n) (0x0098 + ((n)*0x20))
+
+#define SOLO_P2M_EXT_ADR(n) (0x009C + ((n)*0x20))
+
+#define SOLO_P2M_BUFFER(i) (0x2000 + ((i)*4))
+
+#define SOLO_VI_CH_SWITCH_0 0x0100
+#define SOLO_VI_CH_SWITCH_1 0x0104
+#define SOLO_VI_CH_SWITCH_2 0x0108
+
+#define SOLO_VI_CH_ENA 0x010C
+#define SOLO_VI_CH_FORMAT 0x0110
+#define SOLO_VI_FD_SEL_MASK(n) ((n)<<16)
+#define SOLO_VI_PROG_MASK(n) ((n)<<0)
+
+#define SOLO_VI_FMT_CFG 0x0114
+#define SOLO_VI_FMT_CHECK_VCOUNT (1<<31)
+#define SOLO_VI_FMT_CHECK_HCOUNT (1<<30)
+#define SOLO_VI_FMT_TEST_SIGNAL (1<<28)
+
+#define SOLO_VI_PAGE_SW 0x0118
+#define SOLO_FI_INV_DISP_LIVE(n) ((n)<<8)
+#define SOLO_FI_INV_DISP_OUT(n) ((n)<<7)
+#define SOLO_DISP_SYNC_FI(n) ((n)<<6)
+#define SOLO_PIP_PAGE_ADD(n) ((n)<<3)
+#define SOLO_NORMAL_PAGE_ADD(n) ((n)<<0)
+
+#define SOLO_VI_ACT_I_P 0x011C
+#define SOLO_VI_ACT_I_S 0x0120
+#define SOLO_VI_ACT_P 0x0124
+#define SOLO_VI_FI_INVERT (1<<31)
+#define SOLO_VI_H_START(n) ((n)<<21)
+#define SOLO_VI_V_START(n) ((n)<<11)
+#define SOLO_VI_V_STOP(n) ((n)<<0)
+
+#define SOLO_VI_STATUS0 0x0128
+#define SOLO_VI_STATUS0_PAGE(__n) ((__n) & 0x07)
+#define SOLO_VI_STATUS1 0x012C
+
+/* XXX: Might be better off in kernel level disp.h */
+#define DISP_PAGE(stat) ((stat) & 0x07)
+
+#define SOLO_VI_PB_CONFIG 0x0130
+#define SOLO_VI_PB_USER_MODE (1<<1)
+#define SOLO_VI_PB_PAL (1<<0)
+#define SOLO_VI_PB_RANGE_HV 0x0134
+#define SOLO_VI_PB_HSIZE(h) ((h)<<12)
+#define SOLO_VI_PB_VSIZE(v) ((v)<<0)
+#define SOLO_VI_PB_ACT_H 0x0138
+#define SOLO_VI_PB_HSTART(n) ((n)<<12)
+#define SOLO_VI_PB_HSTOP(n) ((n)<<0)
+#define SOLO_VI_PB_ACT_V 0x013C
+#define SOLO_VI_PB_VSTART(n) ((n)<<12)
+#define SOLO_VI_PB_VSTOP(n) ((n)<<0)
+
+#define SOLO_VI_MOSAIC(ch) (0x0140 + ((ch)*4))
+#define SOLO_VI_MOSAIC_SX(x) ((x)<<24)
+#define SOLO_VI_MOSAIC_EX(x) ((x)<<16)
+#define SOLO_VI_MOSAIC_SY(x) ((x)<<8)
+#define SOLO_VI_MOSAIC_EY(x) ((x)<<0)
+
+#define SOLO_VI_WIN_CTRL0(ch) (0x0180 + ((ch)*4))
+#define SOLO_VI_WIN_CTRL1(ch) (0x01C0 + ((ch)*4))
+
+#define SOLO_VI_WIN_CHANNEL(n) ((n)<<28)
+
+#define SOLO_VI_WIN_PIP(n) ((n)<<27)
+#define SOLO_VI_WIN_SCALE(n) ((n)<<24)
+
+#define SOLO_VI_WIN_SX(x) ((x)<<12)
+#define SOLO_VI_WIN_EX(x) ((x)<<0)
+
+#define SOLO_VI_WIN_SY(x) ((x)<<12)
+#define SOLO_VI_WIN_EY(x) ((x)<<0)
+
+#define SOLO_VI_WIN_ON(ch) (0x0200 + ((ch)*4))
+
+#define SOLO_VI_WIN_SW 0x0240
+#define SOLO_VI_WIN_LIVE_AUTO_MUTE 0x0244
+
+#define SOLO_VI_MOT_ADR 0x0260
+#define SOLO_VI_MOTION_EN(mask) ((mask)<<16)
+#define SOLO_VI_MOT_CTRL 0x0264
+#define SOLO_VI_MOTION_FRAME_COUNT(n) ((n)<<24)
+#define SOLO_VI_MOTION_SAMPLE_LENGTH(n) ((n)<<16)
+#define SOLO_VI_MOTION_INTR_START_STOP (1<<15)
+#define SOLO_VI_MOTION_FREEZE_DATA (1<<14)
+#define SOLO_VI_MOTION_SAMPLE_COUNT(n) ((n)<<0)
+#define SOLO_VI_MOT_CLEAR 0x0268
+#define SOLO_VI_MOT_STATUS 0x026C
+#define SOLO_VI_MOTION_CNT(n) ((n)<<0)
+#define SOLO_VI_MOTION_BORDER 0x0270
+#define SOLO_VI_MOTION_BAR 0x0274
+#define SOLO_VI_MOTION_Y_SET (1<<29)
+#define SOLO_VI_MOTION_Y_ADD (1<<28)
+#define SOLO_VI_MOTION_CB_SET (1<<27)
+#define SOLO_VI_MOTION_CB_ADD (1<<26)
+#define SOLO_VI_MOTION_CR_SET (1<<25)
+#define SOLO_VI_MOTION_CR_ADD (1<<24)
+#define SOLO_VI_MOTION_Y_VALUE(v) ((v)<<16)
+#define SOLO_VI_MOTION_CB_VALUE(v) ((v)<<8)
+#define SOLO_VI_MOTION_CR_VALUE(v) ((v)<<0)
+
+#define SOLO_VO_FMT_ENC 0x0300
+#define SOLO_VO_SCAN_MODE_PROGRESSIVE (1<<31)
+#define SOLO_VO_FMT_TYPE_PAL (1<<30)
+#define SOLO_VO_FMT_TYPE_NTSC 0
+#define SOLO_VO_USER_SET (1<<29)
+
+#define SOLO_VO_FI_CHANGE (1<<20)
+#define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19)
+#define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18)
+#define SOLO_VO_USER_COLOR_SET_NAV (1<<17)
+#define SOLO_VO_USER_COLOR_SET_NAH (1<<16)
+#define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8)
+#define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4)
+#define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0)
+
+#define SOLO_VO_ACT_H 0x0304
+#define SOLO_VO_H_BLANK(n) ((n)<<22)
+#define SOLO_VO_H_START(n) ((n)<<11)
+#define SOLO_VO_H_STOP(n) ((n)<<0)
+
+#define SOLO_VO_ACT_V 0x0308
+#define SOLO_VO_V_BLANK(n) ((n)<<22)
+#define SOLO_VO_V_START(n) ((n)<<11)
+#define SOLO_VO_V_STOP(n) ((n)<<0)
+
+#define SOLO_VO_RANGE_HV 0x030C
+#define SOLO_VO_SYNC_INVERT (1<<24)
+#define SOLO_VO_HSYNC_INVERT (1<<23)
+#define SOLO_VO_VSYNC_INVERT (1<<22)
+#define SOLO_VO_H_LEN(n) ((n)<<11)
+#define SOLO_VO_V_LEN(n) ((n)<<0)
+
+#define SOLO_VO_DISP_CTRL 0x0310
+#define SOLO_VO_DISP_ON (1<<31)
+#define SOLO_VO_DISP_ERASE_COUNT(n) ((n&0xf)<<24)
+#define SOLO_VO_DISP_DOUBLE_SCAN (1<<22)
+#define SOLO_VO_DISP_SINGLE_PAGE (1<<21)
+#define SOLO_VO_DISP_BASE(n) (((n)>>16) & 0xffff)
+
+#define SOLO_VO_DISP_ERASE 0x0314
+#define SOLO_VO_DISP_ERASE_ON (1<<0)
+
+#define SOLO_VO_ZOOM_CTRL 0x0318
+#define SOLO_VO_ZOOM_VER_ON (1<<24)
+#define SOLO_VO_ZOOM_HOR_ON (1<<23)
+#define SOLO_VO_ZOOM_V_COMP (1<<22)
+#define SOLO_VO_ZOOM_SX(h) (((h)/2)<<11)
+#define SOLO_VO_ZOOM_SY(v) (((v)/2)<<0)
+
+#define SOLO_VO_FREEZE_CTRL 0x031C
+#define SOLO_VO_FREEZE_ON (1<<1)
+#define SOLO_VO_FREEZE_INTERPOLATION (1<<0)
+
+#define SOLO_VO_BKG_COLOR 0x0320
+#define SOLO_BG_Y(y) ((y)<<16)
+#define SOLO_BG_U(u) ((u)<<8)
+#define SOLO_BG_V(v) ((v)<<0)
+
+#define SOLO_VO_DEINTERLACE 0x0324
+#define SOLO_VO_DEINTERLACE_THRESHOLD(n) ((n)<<8)
+#define SOLO_VO_DEINTERLACE_EDGE_VALUE(n) ((n)<<0)
+
+#define SOLO_VO_BORDER_LINE_COLOR 0x0330
+#define SOLO_VO_BORDER_FILL_COLOR 0x0334
+#define SOLO_VO_BORDER_LINE_MASK 0x0338
+#define SOLO_VO_BORDER_FILL_MASK 0x033c
+
+#define SOLO_VO_BORDER_X(n) (0x0340+((n)*4))
+#define SOLO_VO_BORDER_Y(n) (0x0354+((n)*4))
+
+#define SOLO_VO_CELL_EXT_SET 0x0368
+#define SOLO_VO_CELL_EXT_START 0x036c
+#define SOLO_VO_CELL_EXT_STOP 0x0370
+
+#define SOLO_VO_CELL_EXT_SET2 0x0374
+#define SOLO_VO_CELL_EXT_START2 0x0378
+#define SOLO_VO_CELL_EXT_STOP2 0x037c
+
+#define SOLO_VO_RECTANGLE_CTRL(n) (0x0368+((n)*12))
+#define SOLO_VO_RECTANGLE_START(n) (0x036c+((n)*12))
+#define SOLO_VO_RECTANGLE_STOP(n) (0x0370+((n)*12))
+
+#define SOLO_VO_CURSOR_POS (0x0380)
+#define SOLO_VO_CURSOR_CLR (0x0384)
+#define SOLO_VO_CURSOR_CLR2 (0x0388)
+#define SOLO_VO_CURSOR_MASK(id) (0x0390+((id)*4))
+
+#define SOLO_VO_EXPANSION(id) (0x0250+((id)*4))
+
+#define SOLO_OSG_CONFIG 0x03E0
+#define SOLO_VO_OSG_ON (1<<31)
+#define SOLO_VO_OSG_COLOR_MUTE (1<<28)
+#define SOLO_VO_OSG_ALPHA_RATE(n) ((n)<<22)
+#define SOLO_VO_OSG_ALPHA_BG_RATE(n) ((n)<<16)
+#define SOLO_VO_OSG_BASE(offset) (((offset)>>16)&0xffff)
+
+#define SOLO_OSG_ERASE 0x03E4
+#define SOLO_OSG_ERASE_ON (0x80)
+#define SOLO_OSG_ERASE_OFF (0x00)
+
+#define SOLO_VO_OSG_BLINK 0x03E8
+#define SOLO_VO_OSG_BLINK_ON (1<<1)
+#define SOLO_VO_OSG_BLINK_INTREVAL18 (1<<0)
+
+#define SOLO_CAP_BASE 0x0400
+#define SOLO_CAP_MAX_PAGE(n) ((n)<<16)
+#define SOLO_CAP_BASE_ADDR(n) ((n)<<0)
+#define SOLO_CAP_BTW 0x0404
+#define SOLO_CAP_PROG_BANDWIDTH(n) ((n)<<8)
+#define SOLO_CAP_MAX_BANDWIDTH(n) ((n)<<0)
+
+#define SOLO_DIM_SCALE1 0x0408
+#define SOLO_DIM_SCALE2 0x040C
+#define SOLO_DIM_SCALE3 0x0410
+#define SOLO_DIM_SCALE4 0x0414
+#define SOLO_DIM_SCALE5 0x0418
+#define SOLO_DIM_V_MB_NUM_FRAME(n) ((n)<<16)
+#define SOLO_DIM_V_MB_NUM_FIELD(n) ((n)<<8)
+#define SOLO_DIM_H_MB_NUM(n) ((n)<<0)
+
+#define SOLO_DIM_PROG 0x041C
+#define SOLO_CAP_STATUS 0x0420
+
+#define SOLO_CAP_CH_SCALE(ch) (0x0440+((ch)*4))
+#define SOLO_CAP_CH_COMP_ENA_E(ch) (0x0480+((ch)*4))
+#define SOLO_CAP_CH_INTV(ch) (0x04C0+((ch)*4))
+#define SOLO_CAP_CH_INTV_E(ch) (0x0500+((ch)*4))
+
+
+#define SOLO_VE_CFG0 0x0610
+#define SOLO_VE_TWO_PAGE_MODE (1<<31)
+#define SOLO_VE_INTR_CTRL(n) ((n)<<24)
+#define SOLO_VE_BLOCK_SIZE(n) ((n)<<16)
+#define SOLO_VE_BLOCK_BASE(n) ((n)<<0)
+
+#define SOLO_VE_CFG1 0x0614
+#define SOLO6110_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 only */
+#define SOLO6010_VE_BYTE_ALIGN(n) ((n)<<24) /* 6010 only */
+#define SOLO6110_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 only */
+#define SOLO_VE_INSERT_INDEX (1<<18)
+#define SOLO_VE_MOTION_MODE(n) ((n)<<16)
+#define SOLO_VE_MOTION_BASE(n) ((n)<<0)
+
+#define SOLO_VE_WMRK_POLY 0x061C
+#define SOLO_VE_VMRK_INIT_KEY 0x0620
+#define SOLO_VE_WMRK_STRL 0x0624
+#define SOLO_VE_ENCRYP_POLY 0x0628
+#define SOLO_VE_ENCRYP_INIT 0x062C
+#define SOLO_VE_ATTR 0x0630
+#define SOLO_VE_LITTLE_ENDIAN (1<<31)
+#define SOLO_COMP_ATTR_RN (1<<30)
+#define SOLO_COMP_ATTR_FCODE(n) ((n)<<27)
+#define SOLO_COMP_TIME_INC(n) ((n)<<25)
+#define SOLO_COMP_TIME_WIDTH(n) ((n)<<21)
+#define SOLO_DCT_INTERVAL(n) ((n)<<16)
+
+#define SOLO_VE_STATE(n) (0x0640+((n)*4))
+
+#define SOLO_VE_JPEG_QP_TBL 0x0670
+#define SOLO_VE_JPEG_QP_CH_L 0x0674
+#define SOLO_VE_JPEG_QP_CH_H 0x0678
+#define SOLO_VE_JPEG_CFG 0x067C
+#define SOLO_VE_JPEG_CTRL 0x0680
+
+#define SOLO_VE_OSD_CH 0x0690
+#define SOLO_VE_OSD_BASE 0x0694
+#define SOLO_VE_OSD_CLR 0x0698
+#define SOLO_VE_OSD_OPT 0x069C
+
+#define SOLO_VE_CH_INTL(ch) (0x0700+((ch)*4))
+#define SOLO6010_VE_CH_MOT(ch) (0x0740+((ch)*4)) /* 6010 only */
+#define SOLO_VE_CH_QP(ch) (0x0780+((ch)*4))
+#define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4))
+#define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4))
+#define SOLO_VE_CH_GOP_E(ch) (0x0840+((ch)*4))
+#define SOLO_VE_CH_REF_BASE(ch) (0x0880+((ch)*4))
+#define SOLO_VE_CH_REF_BASE_E(ch) (0x08C0+((ch)*4))
+
+#define SOLO_VE_MPEG4_QUE(n) (0x0A00+((n)*8))
+#define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8))
+
+#define SOLO_VD_CFG0 0x0900
+#define SOLO6010_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) /* 6010 only */
+#define SOLO_VD_CFG_BUSY_WIAT_CODE (1<<23)
+#define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22)
+#define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21)
+#define SOLO_VD_CFG_BUSY_WIAT_MS (1<<20)
+#define SOLO_VD_CFG_SINGLE_MODE (1<<18)
+#define SOLO_VD_CFG_SCAL_MANUAL (1<<17)
+#define SOLO_VD_CFG_USER_PAGE_CTRL (1<<16)
+#define SOLO_VD_CFG_LITTLE_ENDIAN (1<<15)
+#define SOLO_VD_CFG_START_FI (1<<14)
+#define SOLO_VD_CFG_ERR_LOCK (1<<13)
+#define SOLO_VD_CFG_ERR_INT_ENA (1<<12)
+#define SOLO_VD_CFG_TIME_WIDTH(n) ((n)<<8)
+#define SOLO_VD_CFG_DCT_INTERVAL(n) ((n)<<0)
+
+#define SOLO_VD_CFG1 0x0904
+
+#define SOLO_VD_DEINTERLACE 0x0908
+#define SOLO_VD_DEINTERLACE_THRESHOLD(n) ((n)<<8)
+#define SOLO_VD_DEINTERLACE_EDGE_VALUE(n) ((n)<<0)
+
+#define SOLO_VD_CODE_ADR 0x090C
+
+#define SOLO_VD_CTRL 0x0910
+#define SOLO_VD_OPER_ON (1<<31)
+#define SOLO_VD_MAX_ITEM(n) ((n)<<0)
+
+#define SOLO_VD_STATUS0 0x0920
+#define SOLO_VD_STATUS0_INTR_ACK (1<<22)
+#define SOLO_VD_STATUS0_INTR_EMPTY (1<<21)
+#define SOLO_VD_STATUS0_INTR_ERR (1<<20)
+
+#define SOLO_VD_STATUS1 0x0924
+
+#define SOLO_VD_IDX0 0x0930
+#define SOLO_VD_IDX_INTERLACE (1<<30)
+#define SOLO_VD_IDX_CHANNEL(n) ((n)<<24)
+#define SOLO_VD_IDX_SIZE(n) ((n)<<0)
+
+#define SOLO_VD_IDX1 0x0934
+#define SOLO_VD_IDX_SRC_SCALE(n) ((n)<<28)
+#define SOLO_VD_IDX_WINDOW(n) ((n)<<24)
+#define SOLO_VD_IDX_DEINTERLACE (1<<16)
+#define SOLO_VD_IDX_H_BLOCK(n) ((n)<<8)
+#define SOLO_VD_IDX_V_BLOCK(n) ((n)<<0)
+
+#define SOLO_VD_IDX2 0x0938
+#define SOLO_VD_IDX_REF_BASE_SIDE (1<<31)
+#define SOLO_VD_IDX_REF_BASE(n) (((n)>>16)&0xffff)
+
+#define SOLO_VD_IDX3 0x093C
+#define SOLO_VD_IDX_DISP_SCALE(n) ((n)<<28)
+#define SOLO_VD_IDX_INTERLACE_WR (1<<27)
+#define SOLO_VD_IDX_INTERPOL (1<<26)
+#define SOLO_VD_IDX_HOR2X (1<<25)
+#define SOLO_VD_IDX_OFFSET_X(n) ((n)<<12)
+#define SOLO_VD_IDX_OFFSET_Y(n) ((n)<<0)
+
+#define SOLO_VD_IDX4 0x0940
+#define SOLO_VD_IDX_DEC_WR_PAGE(n) ((n)<<8)
+#define SOLO_VD_IDX_DISP_RD_PAGE(n) ((n)<<0)
+
+#define SOLO_VD_WR_PAGE(n) (0x03F0 + ((n) * 4))
+
+
+#define SOLO_GPIO_CONFIG_0 0x0B00
+#define SOLO_GPIO_CONFIG_1 0x0B04
+#define SOLO_GPIO_DATA_OUT 0x0B08
+#define SOLO_GPIO_DATA_IN 0x0B0C
+#define SOLO_GPIO_INT_ACK_STA 0x0B10
+#define SOLO_GPIO_INT_ENA 0x0B14
+#define SOLO_GPIO_INT_CFG_0 0x0B18
+#define SOLO_GPIO_INT_CFG_1 0x0B1C
+
+
+#define SOLO_IIC_CFG 0x0B20
+#define SOLO_IIC_ENABLE (1<<8)
+#define SOLO_IIC_PRESCALE(n) ((n)<<0)
+
+#define SOLO_IIC_CTRL 0x0B24
+#define SOLO_IIC_AUTO_CLEAR (1<<20)
+#define SOLO_IIC_STATE_RX_ACK (1<<19)
+#define SOLO_IIC_STATE_BUSY (1<<18)
+#define SOLO_IIC_STATE_SIG_ERR (1<<17)
+#define SOLO_IIC_STATE_TRNS (1<<16)
+#define SOLO_IIC_CH_SET(n) ((n)<<5)
+#define SOLO_IIC_ACK_EN (1<<4)
+#define SOLO_IIC_START (1<<3)
+#define SOLO_IIC_STOP (1<<2)
+#define SOLO_IIC_READ (1<<1)
+#define SOLO_IIC_WRITE (1<<0)
+
+#define SOLO_IIC_TXD 0x0B28
+#define SOLO_IIC_RXD 0x0B2C
+
+/*
+ * UART REGISTER
+ */
+#define SOLO_UART_CONTROL(n) (0x0BA0 + ((n)*0x20))
+#define SOLO_UART_CLK_DIV(n) ((n)<<24)
+#define SOLO_MODEM_CTRL_EN (1<<20)
+#define SOLO_PARITY_ERROR_DROP (1<<18)
+#define SOLO_IRQ_ERR_EN (1<<17)
+#define SOLO_IRQ_RX_EN (1<<16)
+#define SOLO_IRQ_TX_EN (1<<15)
+#define SOLO_RX_EN (1<<14)
+#define SOLO_TX_EN (1<<13)
+#define SOLO_UART_HALF_DUPLEX (1<<12)
+#define SOLO_UART_LOOPBACK (1<<11)
+
+#define SOLO_BAUDRATE_230400 ((0<<9)|(0<<6))
+#define SOLO_BAUDRATE_115200 ((0<<9)|(1<<6))
+#define SOLO_BAUDRATE_57600 ((0<<9)|(2<<6))
+#define SOLO_BAUDRATE_38400 ((0<<9)|(3<<6))
+#define SOLO_BAUDRATE_19200 ((0<<9)|(4<<6))
+#define SOLO_BAUDRATE_9600 ((0<<9)|(5<<6))
+#define SOLO_BAUDRATE_4800 ((0<<9)|(6<<6))
+#define SOLO_BAUDRATE_2400 ((1<<9)|(6<<6))
+#define SOLO_BAUDRATE_1200 ((2<<9)|(6<<6))
+#define SOLO_BAUDRATE_300 ((3<<9)|(6<<6))
+
+#define SOLO_UART_DATA_BIT_8 (3<<4)
+#define SOLO_UART_DATA_BIT_7 (2<<4)
+#define SOLO_UART_DATA_BIT_6 (1<<4)
+#define SOLO_UART_DATA_BIT_5 (0<<4)
+
+#define SOLO_UART_STOP_BIT_1 (0<<2)
+#define SOLO_UART_STOP_BIT_2 (1<<2)
+
+#define SOLO_UART_PARITY_NONE (0<<0)
+#define SOLO_UART_PARITY_EVEN (2<<0)
+#define SOLO_UART_PARITY_ODD (3<<0)
+
+#define SOLO_UART_STATUS(n) (0x0BA4 + ((n)*0x20))
+#define SOLO_UART_CTS (1<<15)
+#define SOLO_UART_RX_BUSY (1<<14)
+#define SOLO_UART_OVERRUN (1<<13)
+#define SOLO_UART_FRAME_ERR (1<<12)
+#define SOLO_UART_PARITY_ERR (1<<11)
+#define SOLO_UART_TX_BUSY (1<<5)
+
+#define SOLO_UART_RX_BUFF_CNT(stat) (((stat)>>6) & 0x1f)
+#define SOLO_UART_RX_BUFF_SIZE 8
+#define SOLO_UART_TX_BUFF_CNT(stat) (((stat)>>0) & 0x1f)
+#define SOLO_UART_TX_BUFF_SIZE 8
+
+#define SOLO_UART_TX_DATA(n) (0x0BA8 + ((n)*0x20))
+#define SOLO_UART_TX_DATA_PUSH (1<<8)
+#define SOLO_UART_RX_DATA(n) (0x0BAC + ((n)*0x20))
+#define SOLO_UART_RX_DATA_POP (1<<8)
+
+#define SOLO_TIMER_CLOCK_NUM 0x0be0
+#define SOLO_TIMER_WATCHDOG 0x0be4
+#define SOLO_TIMER_USEC 0x0be8
+#define SOLO_TIMER_SEC 0x0bec
+
+#define SOLO_AUDIO_CONTROL 0x0D00
+#define SOLO_AUDIO_ENABLE (1<<31)
+#define SOLO_AUDIO_MASTER_MODE (1<<30)
+#define SOLO_AUDIO_I2S_MODE (1<<29)
+#define SOLO_AUDIO_I2S_LR_SWAP (1<<27)
+#define SOLO_AUDIO_I2S_8BIT (1<<26)
+#define SOLO_AUDIO_I2S_MULTI(n) ((n)<<24)
+#define SOLO_AUDIO_MIX_9TO0 (1<<23)
+#define SOLO_AUDIO_DEC_9TO0_VOL(n) ((n)<<20)
+#define SOLO_AUDIO_MIX_19TO10 (1<<19)
+#define SOLO_AUDIO_DEC_19TO10_VOL(n) ((n)<<16)
+#define SOLO_AUDIO_MODE(n) ((n)<<0)
+#define SOLO_AUDIO_SAMPLE 0x0D04
+#define SOLO_AUDIO_EE_MODE_ON (1<<30)
+#define SOLO_AUDIO_EE_ENC_CH(ch) ((ch)<<25)
+#define SOLO_AUDIO_BITRATE(n) ((n)<<16)
+#define SOLO_AUDIO_CLK_DIV(n) ((n)<<0)
+#define SOLO_AUDIO_FDMA_INTR 0x0D08
+#define SOLO_AUDIO_FDMA_INTERVAL(n) ((n)<<19)
+#define SOLO_AUDIO_INTR_ORDER(n) ((n)<<16)
+#define SOLO_AUDIO_FDMA_BASE(n) ((n)<<0)
+#define SOLO_AUDIO_EVOL_0 0x0D0C
+#define SOLO_AUDIO_EVOL_1 0x0D10
+#define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10))
+#define SOLO_AUDIO_STA 0x0D14
+
+
+#define SOLO_WATCHDOG 0x0BE4
+#define WATCHDOG_STAT(status) (status<<8)
+#define WATCHDOG_TIME(sec) (sec&0xff)
+
+#endif /* __SOLO6010_REGISTERS_H */
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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 "solo6010.h"
-#include "solo6010-tw28.h"
-
-MODULE_DESCRIPTION("Softlogic 6010 MP4 Encoder/Decoder V4L2/ALSA Driver");
-MODULE_AUTHOR("Ben Collins <bcollins@bluecherry.net>");
-MODULE_VERSION(SOLO6010_VERSION);
-MODULE_LICENSE("GPL");
-
-void solo6010_irq_on(struct solo6010_dev *solo_dev, u32 mask)
-{
- solo_dev->irq_mask |= mask;
- solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask);
-}
-
-void solo6010_irq_off(struct solo6010_dev *solo_dev, u32 mask)
-{
- solo_dev->irq_mask &= ~mask;
- solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask);
-}
-
-/* XXX We should check the return value of the sub-device ISR's */
-static irqreturn_t solo6010_isr(int irq, void *data)
-{
- struct solo6010_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) {
- u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR);
- solo_p2m_error_isr(solo_dev, err);
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR);
- }
-
- 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);
-
- /* Call this first so enc gets detected flag set */
- if (status & SOLO_IRQ_MOTION)
- solo_motion_isr(solo_dev);
-
- if (status & SOLO_IRQ_ENCODER)
- solo_enc_v4l2_isr(solo_dev);
-
- if (status & SOLO_IRQ_G723)
- solo_g723_isr(solo_dev);
-
- return IRQ_HANDLED;
-}
-
-static void free_solo_dev(struct solo6010_dev *solo_dev)
-{
- struct pci_dev *pdev;
-
- if (!solo_dev)
- return;
-
- pdev = solo_dev->pdev;
-
- /* If we never initialized the PCI device, then nothing else
- * below here needs cleanup */
- if (!pdev) {
- kfree(solo_dev);
- return;
- }
-
- /* 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 */
- if (solo_dev->reg_base) {
- solo6010_irq_off(solo_dev, ~0);
- pci_iounmap(pdev, solo_dev->reg_base);
- free_irq(pdev->irq, solo_dev);
- }
-
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
-
- kfree(solo_dev);
-}
-
-static int __devinit solo6010_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- struct solo6010_dev *solo_dev;
- int ret;
- int sdram;
- u8 chip_id;
- u32 reg;
-
- solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL);
- if (solo_dev == NULL)
- return -ENOMEM;
-
- solo_dev->pdev = pdev;
- spin_lock_init(&solo_dev->reg_io_lock);
- pci_set_drvdata(pdev, solo_dev);
-
- ret = pci_enable_device(pdev);
- if (ret)
- goto fail_probe;
-
- pci_set_master(pdev);
-
- ret = pci_request_regions(pdev, SOLO6010_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, "
- "defaulting to 4 channels\n",
- chip_id);
- case 5:
- solo_dev->nr_chans = 4;
- solo_dev->nr_ext = 1;
- }
-
- solo_dev->flags = id->driver_data;
-
- /* Disable all interrupts to start */
- solo6010_irq_off(solo_dev, ~0);
-
- reg = SOLO_SYS_CFG_SDRAM64BIT;
- /* Initial global settings */
- if (!(solo_dev->flags & FLAGS_6110))
- reg |= SOLO6010_SYS_CFG_INPUTDIV(25) |
- SOLO6010_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) |
- SOLO6010_SYS_CFG_OUTDIV(3);
- solo_reg_write(solo_dev, SOLO_SYS_CFG, reg);
-
- if (solo_dev->flags & FLAGS_6110) {
- u32 sys_clock_MHz = SOLO_CLOCK_MHZ;
- u32 pll_DIVQ;
- u32 pll_DIVF;
-
- if (sys_clock_MHz < 125) {
- pll_DIVQ = 3;
- pll_DIVF = (sys_clock_MHz * 4) / 3;
- } else {
- pll_DIVQ = 2;
- pll_DIVF = (sys_clock_MHz * 2) / 3;
- }
-
- solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG,
- SOLO6110_PLL_RANGE_5_10MHZ |
- SOLO6110_PLL_DIVR(9) |
- SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) |
- SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN);
- mdelay(1); // PLL Locking time (1ms)
-
- solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */
- } else
- solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */
-
- solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1);
-
- /* PLL locking time of 1ms */
- mdelay(1);
-
- ret = request_irq(pdev->irq, solo6010_isr, IRQF_SHARED, SOLO6010_NAME,
- solo_dev);
- if (ret)
- goto fail_probe;
-
- /* Handle this from the start */
- solo6010_irq_on(solo_dev, SOLO_IRQ_PCI_ERR);
-
- ret = solo_i2c_init(solo_dev);
- if (ret)
- goto fail_probe;
-
- /* Setup the DMA engine */
- sdram = (solo_dev->nr_chans >= 8) ? 2 : 1;
- solo_reg_write(solo_dev, SOLO_DMA_CTRL,
- SOLO_DMA_CTRL_REFRESH_CYCLE(1) |
- SOLO_DMA_CTRL_SDRAM_SIZE(sdram) |
- SOLO_DMA_CTRL_SDRAM_CLK_INVERT |
- SOLO_DMA_CTRL_READ_CLK_SELECT |
- SOLO_DMA_CTRL_LATENCY(1));
-
- 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);
- if (ret)
- goto fail_probe;
-
- ret = solo_enc_init(solo_dev);
- if (ret)
- goto fail_probe;
-
- ret = solo_enc_v4l2_init(solo_dev);
- if (ret)
- goto fail_probe;
-
- ret = solo_g723_init(solo_dev);
- if (ret)
- goto fail_probe;
-
- return 0;
-
-fail_probe:
- free_solo_dev(solo_dev);
- return ret;
-}
-
-static void __devexit solo6010_pci_remove(struct pci_dev *pdev)
-{
- struct solo6010_dev *solo_dev = pci_get_drvdata(pdev);
-
- free_solo_dev(solo_dev);
-}
-
-static struct pci_device_id solo6010_id_table[] = {
- /* 6010 based cards */
- {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)},
- {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110),
- .driver_data = FLAGS_6110},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16)},
- /* 6110 based cards */
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8)},
- {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16)},
- {0,}
-};
-
-MODULE_DEVICE_TABLE(pci, solo6010_id_table);
-
-static struct pci_driver solo6010_pci_driver = {
- .name = SOLO6010_NAME,
- .id_table = solo6010_id_table,
- .probe = solo6010_pci_probe,
- .remove = solo6010_pci_remove,
-};
-
-static int __init solo6010_module_init(void)
-{
- return pci_register_driver(&solo6010_pci_driver);
-}
-
-static void __exit solo6010_module_exit(void)
-{
- pci_unregister_driver(&solo6010_pci_driver);
-}
-
-module_init(solo6010_module_init);
-module_exit(solo6010_module_exit);
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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 "solo6010.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 512
-#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 32)
-
-static unsigned video_type;
-module_param(video_type, uint, 0644);
-MODULE_PARM_DESC(video_type, "video_type (0 = NTSC/Default, 1 = PAL)");
-
-static void solo_vin_config(struct solo6010_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));
-
- solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0);
- 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_disp_config(struct solo6010_dev *solo_dev)
-{
- solo_dev->vout_hstart = 6;
- solo_dev->vout_vstart = 8;
-
- 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_FMT_ENC,
- solo_dev->video_type |
- SOLO_VO_USER_COLOR_SET_NAV |
- 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));
-
- solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 5);
-
- 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_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
-
- /* Enable channels we support */
- solo_reg_write(solo_dev, SOLO_VI_CH_ENA, (1 << solo_dev->nr_chans) - 1);
-
- /* Disable the watchdog */
- solo_reg_write(solo_dev, SOLO_WATCHDOG, 0);
-}
-
-static int solo_dma_vin_region(struct solo6010_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] = val;
-
- for (i = 0; i < reg_size; i += sizeof(buf))
- ret |= solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_VIN, 1, buf,
- SOLO_MOTION_EXT_ADDR(solo_dev) + off + i,
- sizeof(buf));
-
- return ret;
-}
-
-void solo_set_motion_threshold(struct solo6010_dev *solo_dev, u8 ch, u16 val)
-{
- if (ch > solo_dev->nr_chans)
- return;
-
- solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +
- (ch * SOLO_MOT_THRESH_SIZE * 2),
- val, SOLO_MOT_THRESH_REAL);
-}
-
-/* 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 (take from rdk) has some mystery 8k
- * block right after the flag area, before the first thresh table. */
-static void solo_motion_config(struct solo6010_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 +
- SOLO_MOT_THRESH_SIZE +
- (i * SOLO_MOT_THRESH_SIZE * 2),
- 0x0000, SOLO_MOT_THRESH_REAL);
-
- /* 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 solo6010_dev *solo_dev)
-{
- int i;
-
- solo_dev->video_hsize = 704;
- if (video_type == 0) {
- solo_dev->video_type = SOLO_VO_FMT_TYPE_NTSC;
- solo_dev->video_vsize = 240;
- solo_dev->fps = 30;
- } else {
- solo_dev->video_type = SOLO_VO_FMT_TYPE_PAL;
- solo_dev->video_vsize = 288;
- solo_dev->fps = 25;
- }
-
- solo_vin_config(solo_dev);
- solo_motion_config(solo_dev);
- solo_disp_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 solo6010_dev *solo_dev)
-{
- int i;
-
- solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION);
-
- 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);
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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 "solo6010.h"
-#include "solo6010-osd-font.h"
-
-#define CAPTURE_MAX_BANDWIDTH 32 /* D1 4channel (D1 == 4) */
-#define OSG_BUFFER_SIZE 1024
-
-#define VI_PROG_HSIZE (1280 - 16)
-#define VI_PROG_VSIZE (1024 - 16)
-
-static void solo_capture_config(struct solo6010_dev *solo_dev)
-{
- int i, j;
- unsigned long height;
- unsigned long width;
- unsigned char *buf;
-
- solo_reg_write(solo_dev, SOLO_CAP_BASE,
- SOLO_CAP_MAX_PAGE(SOLO_CAP_EXT_MAX_PAGE *
- solo_dev->nr_chans) |
- SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16));
- solo_reg_write(solo_dev, SOLO_CAP_BTW,
- (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) |
- SOLO_CAP_MAX_BANDWIDTH(CAPTURE_MAX_BANDWIDTH));
-
- /* 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);
- solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, 0);
-
- /* Clear OSG buffer */
- buf = kzalloc(OSG_BUFFER_SIZE, GFP_KERNEL);
- if (!buf)
- return;
-
- for (i = 0; i < solo_dev->nr_chans; i++) {
- for (j = 0; j < SOLO_EOSD_EXT_SIZE; j += OSG_BUFFER_SIZE) {
- solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_MP4E, 1, buf,
- SOLO_EOSD_EXT_ADDR +
- (i * SOLO_EOSD_EXT_SIZE) + j,
- OSG_BUFFER_SIZE);
- }
- }
- kfree(buf);
-}
-
-int solo_osd_print(struct solo_enc_dev *solo_enc)
-{
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
- char *str = solo_enc->osd_text;
- u8 *buf;
- u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH);
- int len = strlen(str);
- int i, j;
- int x = 1, y = 1;
-
- if (len == 0) {
- reg &= ~(1 << solo_enc->ch);
- solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
- return 0;
- }
-
- buf = kzalloc(SOLO_EOSD_EXT_SIZE, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- for (i = 0; i < len; i++) {
- for (j = 0; j < 16; j++) {
- buf[(j*2) + (i%2) + ((x + (i/2)) * 32) + (y * 2048)] =
- (solo_osd_font[(str[i] * 4) + (j / 4)]
- >> ((3 - (j % 4)) * 8)) & 0xff;
- }
- }
-
- solo_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR +
- (solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE);
- reg |= (1 << solo_enc->ch);
- solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
-
- kfree(buf);
-
- return 0;
-}
-
-static void solo_jpeg_config(struct solo6010_dev *solo_dev)
-{
- u32 reg;
- if (solo_dev->flags & FLAGS_6110)
- reg = (4 << 24) | (3 << 16) | (2 << 8) | (1 << 0);
- else
- reg = (2 << 24) | (2 << 16) | (2 << 8) | (2 << 0);
- solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, reg);
- solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, 0);
- solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, 0);
- 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);
- /* que limit, samp limit, pos limit */
- solo_reg_write(solo_dev, 0x0688, (0 << 16) | (30 << 8) | 60);
-}
-
-static void solo_mp4e_config(struct solo6010_dev *solo_dev)
-{
- int i;
- u32 reg;
-
- /* We can only use VE_INTR_CTRL(0) if we want to support mjpeg */
- solo_reg_write(solo_dev, SOLO_VE_CFG0,
- SOLO_VE_INTR_CTRL(0) |
- SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) |
- SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16));
-
- solo_reg_write(solo_dev, SOLO_VE_CFG1,
- SOLO_VE_INSERT_INDEX | SOLO_VE_MOTION_MODE(0));
-
- 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);
- solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0);
- solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0);
-
- reg = SOLO_VE_LITTLE_ENDIAN | SOLO_COMP_ATTR_FCODE(1) |
- SOLO_COMP_TIME_INC(0) | SOLO_COMP_TIME_WIDTH(15);
- if (solo_dev->flags & FLAGS_6110)
- reg |= SOLO_DCT_INTERVAL(10);
- else
- reg |= SOLO_DCT_INTERVAL(36 / 4);
- solo_reg_write(solo_dev, SOLO_VE_ATTR, reg);
-
- 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);
-
- if (solo_dev->flags & FLAGS_6110)
- solo_reg_write(solo_dev, 0x0634, 0x00040008); /* ? */
-}
-
-int solo_enc_init(struct solo6010_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);
- }
-
- solo6010_irq_on(solo_dev, SOLO_IRQ_ENCODER);
-
- return 0;
-}
-
-void solo_enc_exit(struct solo6010_dev *solo_dev)
-{
- int i;
-
- solo6010_irq_off(solo_dev, SOLO_IRQ_ENCODER);
-
- 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);
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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 <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/control.h>
-
-#include "solo6010.h"
-#include "solo6010-tw28.h"
-
-#define G723_INTR_ORDER 0
-#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 MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX)
-#define IRQ_PAGES 4 /* 0 - 4 */
-#define PERIODS_MIN (1 << IRQ_PAGES)
-#define PERIODS_MAX G723_FDMA_PAGES
-
-struct solo_snd_pcm {
- int on;
- spinlock_t lock;
- struct solo6010_dev *solo_dev;
- unsigned char g723_buf[G723_PERIOD_BYTES];
-};
-
-static void solo_g723_config(struct solo6010_dev *solo_dev)
-{
- int clk_div;
-
- clk_div = SOLO_CLOCK_MHZ / (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(IRQ_PAGES) |
- 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 solo6010_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;
-
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_G723);
-
- 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 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 = 8000,
- .rate_max = 8000,
- .channels_min = 1,
- .channels_max = 1,
- .buffer_bytes_max = 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 solo6010_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)
- return -ENOMEM;
-
- 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;
-}
-
-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;
- 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 solo6010_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)
- solo6010_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)
- solo6010_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 solo6010_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 solo6010_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(solo_dev, SOLO_P2M_DMA_ID_G723E, 0,
- solo_pcm->g723_buf,
- SOLO_G723_EXT_ADDR(solo_dev) +
- (page * G723_PERIOD_BLOCK) +
- (ss->number * G723_PERIOD_BYTES),
- G723_PERIOD_BYTES);
- 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 solo6010_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 solo6010_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 solo6010_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),
- MAX_BUFFER, MAX_BUFFER);
- if (ret < 0)
- return ret;
-
- solo_dev->snd_pcm = pcm;
-
- return 0;
-}
-
-int solo_g723_init(struct solo6010_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, SOLO6010_NAME);
- strcpy(card->shortname, "SOLO-6010 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-6010");
- 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 solo6010_dev *solo_dev)
-{
- solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0);
- solo6010_irq_off(solo_dev, SOLO_IRQ_G723);
-
- snd_card_free(solo_dev->snd_card);
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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 <asm/uaccess.h>
-
-#include "solo6010.h"
-
-static void solo_gpio_mode(struct solo6010_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 solo6010_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 solo6010_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 solo6010_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 solo6010_dev *solo_dev)
-{
- solo_gpio_config(solo_dev);
- return 0;
-}
-
-void solo_gpio_exit(struct solo6010_dev *solo_dev)
-{
- solo_gpio_clear(solo_dev, 0x30);
- solo_gpio_config(solo_dev);
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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 SOLO6010 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 "solo6010.h"
-
-u8 solo_i2c_readbyte(struct solo6010_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 solo6010_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 solo6010_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 solo6010_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 solo6010_dev *solo_dev)
-{
- solo6010_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 solo6010_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 solo6010_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 solo6010_dev *solo_dev)
-{
- u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL);
- int ret = -EINVAL;
-
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC);
-
- if (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 solo6010_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);
- solo6010_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 struct i2c_algorithm solo_i2c_algo = {
- .master_xfer = solo_i2c_master_xfer,
- .functionality = solo_i2c_functionality,
-};
-
-int solo_i2c_init(struct solo6010_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",
- SOLO6010_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;
- }
-
- dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n",
- SOLO_I2C_ADAPTERS);
-
- return 0;
-}
-
-void solo_i2c_exit(struct solo6010_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;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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.
- */
-
-#ifndef __SOLO6010_JPEG_H
-#define __SOLO6010_JPEG_H
-
-static unsigned char jpeg_header[] = {
- 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c,
- 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79,
- 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16,
- 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c,
- 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30,
- 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, 0x3a, 0x50,
- 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, 0x70, 0x6e,
- 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a,
- 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4,
- 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, 0xf2, 0xe0,
- 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0xff, 0xdb,
- 0x00, 0x43, 0x01, 0x22, 0x24, 0x24, 0x30, 0x2a,
- 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6, 0x84, 0x70,
- 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xff, 0xc4, 0x01, 0xa2, 0x00,
- 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01,
- 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04,
- 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03,
- 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41,
- 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14,
- 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1,
- 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62,
- 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19,
- 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34,
- 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
- 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
- 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
- 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74,
- 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84,
- 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
- 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
- 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
- 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
- 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
- 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
- 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5,
- 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
- 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01,
- 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01,
- 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04,
- 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02,
- 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12,
- 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32,
- 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1,
- 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72,
- 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1,
- 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29,
- 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43,
- 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53,
- 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63,
- 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73,
- 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82,
- 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
- 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
- 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,
- 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
- 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,
- 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5,
- 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4,
- 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3,
- 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff,
- 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x02, 0xc0,
- 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03,
- 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01,
- 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
-};
-
-/* This is the byte marker for the start of SOF0: 0xffc0 marker */
-#define SOF0_START 575
-
-#endif /* __SOLO6010_JPEG_H */
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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.
- */
-
-#ifndef __SOLO6010_OFFSETS_H
-#define __SOLO6010_OFFSETS_H
-
-/* Offsets and sizes of the external address */
-#define SOLO_DISP_EXT_ADDR 0x00000000
-#define SOLO_DISP_EXT_SIZE 0x00480000
-
-#define SOLO_DEC2LIVE_EXT_ADDR (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE)
-#define SOLO_DEC2LIVE_EXT_SIZE 0x00240000
-
-#define SOLO_OSG_EXT_ADDR (SOLO_DEC2LIVE_EXT_ADDR + SOLO_DEC2LIVE_EXT_SIZE)
-#define SOLO_OSG_EXT_SIZE 0x00120000
-
-#define SOLO_EOSD_EXT_ADDR (SOLO_OSG_EXT_ADDR + SOLO_OSG_EXT_SIZE)
-#define SOLO_EOSD_EXT_SIZE 0x00010000
-
-#define SOLO_MOTION_EXT_ADDR(__solo) (SOLO_EOSD_EXT_ADDR + \
- (SOLO_EOSD_EXT_SIZE * __solo->nr_chans))
-#define SOLO_MOTION_EXT_SIZE 0x00080000
-
-#define SOLO_G723_EXT_ADDR(__solo) \
- (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE)
-#define SOLO_G723_EXT_SIZE 0x00010000
-
-#define SOLO_CAP_EXT_ADDR(__solo) \
- (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE)
-#define SOLO_CAP_EXT_MAX_PAGE (18 + 15)
-#define SOLO_CAP_EXT_SIZE (SOLO_CAP_EXT_MAX_PAGE * 65536)
-
-/* This +1 is very important -- Why?! -- BenC */
-#define SOLO_EREF_EXT_ADDR(__solo) \
- (SOLO_CAP_EXT_ADDR(__solo) + \
- (SOLO_CAP_EXT_SIZE * (__solo->nr_chans + 1)))
-#define SOLO_EREF_EXT_SIZE 0x00140000
-
-#define SOLO_MP4E_EXT_ADDR(__solo) \
- (SOLO_EREF_EXT_ADDR(__solo) + \
- (SOLO_EREF_EXT_SIZE * __solo->nr_chans))
-#define SOLO_MP4E_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans)
-
-#define SOLO_DREF_EXT_ADDR(__solo) \
- (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo))
-#define SOLO_DREF_EXT_SIZE 0x00140000
-
-#define SOLO_MP4D_EXT_ADDR(__solo) \
- (SOLO_DREF_EXT_ADDR(__solo) + \
- (SOLO_DREF_EXT_SIZE * __solo->nr_chans))
-#define SOLO_MP4D_EXT_SIZE 0x00080000
-
-#define SOLO_JPEG_EXT_ADDR(__solo) \
- (SOLO_MP4D_EXT_ADDR(__solo) + \
- (SOLO_MP4D_EXT_SIZE * __solo->nr_chans))
-#define SOLO_JPEG_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans)
-
-#endif /* __SOLO6010_OFFSETS_H */
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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.
- */
-
-#ifndef __SOLO6010_OSD_FONT_H
-#define __SOLO6010_OSD_FONT_H
-
-static const unsigned int solo_osd_font[] = {
- 0x00000000, 0x0000c0c8, 0xccfefe0c, 0x08000000,
- 0x00000000, 0x10103838, 0x7c7cfefe, 0x00000000, /* 0 */
- 0x00000000, 0xfefe7c7c, 0x38381010, 0x10000000,
- 0x00000000, 0x7c82fefe, 0xfefefe7c, 0x00000000,
- 0x00000000, 0x00001038, 0x10000000, 0x00000000,
- 0x00000000, 0x0010387c, 0xfe7c3810, 0x00000000,
- 0x00000000, 0x00384444, 0x44380000, 0x00000000,
- 0x00000000, 0x38448282, 0x82443800, 0x00000000,
- 0x00000000, 0x007c7c7c, 0x7c7c0000, 0x00000000,
- 0x00000000, 0x6c6c6c6c, 0x6c6c6c6c, 0x00000000,
- 0x00000000, 0x061e7efe, 0xfe7e1e06, 0x00000000,
- 0x00000000, 0xc0f0fcfe, 0xfefcf0c0, 0x00000000,
- 0x00000000, 0xc6cedefe, 0xfedecec6, 0x00000000,
- 0x00000000, 0xc6e6f6fe, 0xfef6e6c6, 0x00000000,
- 0x00000000, 0x12367efe, 0xfe7e3612, 0x00000000,
- 0x00000000, 0x90d8fcfe, 0xfefcd890, 0x00000000,
- 0x00000038, 0x7cc692ba, 0x92c67c38, 0x00000000,
- 0x00000038, 0x7cc6aa92, 0xaac67c38, 0x00000000,
- 0x00000038, 0x7830107c, 0xbaa8680c, 0x00000000,
- 0x00000038, 0x3c18127c, 0xb8382c60, 0x00000000,
- 0x00000044, 0xaa6c8254, 0x38eec67c, 0x00000000,
- 0x00000082, 0x44288244, 0x38c6827c, 0x00000000,
- 0x00000038, 0x444444fe, 0xfeeec6fe, 0x00000000,
- 0x00000018, 0x78187818, 0x3c7e7e3c, 0x00000000,
- 0x00000000, 0x3854929a, 0x82443800, 0x00000000,
- 0x00000000, 0x00c0c8cc, 0xfefe0c08, 0x00000000,
- 0x0000e0a0, 0xe040e00e, 0x8a0ea40e, 0x00000000,
- 0x0000e0a0, 0xe040e00e, 0x0a8e440e, 0x00000000,
- 0x0000007c, 0x82829292, 0x929282fe, 0x00000000,
- 0x000000f8, 0xfc046494, 0x946404fc, 0x00000000,
- 0x0000003f, 0x7f404c52, 0x524c407f, 0x00000000,
- 0x0000007c, 0x82ba82ba, 0x82ba82fe, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x183c3c3c, 0x18180018, 0x18000000, /* 32 ! */
- 0x00000066, 0x66240000, 0x00000000, 0x00000000,
- 0x00000000, 0x6c6cfe6c, 0x6c6cfe6c, 0x6c000000, /* 34 " # */
- 0x00001010, 0x7cd6d616, 0x7cd0d6d6, 0x7c101000,
- 0x00000000, 0x0086c660, 0x30180cc6, 0xc2000000, /* 36 $ % */
- 0x00000000, 0x386c6c38, 0xdc766666, 0xdc000000,
- 0x0000000c, 0x0c0c0600, 0x00000000, 0x00000000, /* 38 & ' */
- 0x00000000, 0x30180c0c, 0x0c0c0c18, 0x30000000,
- 0x00000000, 0x0c183030, 0x30303018, 0x0c000000, /* 40 ( ) */
- 0x00000000, 0x0000663c, 0xff3c6600, 0x00000000,
- 0x00000000, 0x00001818, 0x7e181800, 0x00000000, /* 42 * + */
- 0x00000000, 0x00000000, 0x00000e0e, 0x0c060000,
- 0x00000000, 0x00000000, 0x7e000000, 0x00000000, /* 44 , - */
- 0x00000000, 0x00000000, 0x00000006, 0x06000000,
- 0x00000000, 0x80c06030, 0x180c0602, 0x00000000, /* 46 . / */
- 0x0000007c, 0xc6e6f6de, 0xcec6c67c, 0x00000000,
- 0x00000030, 0x383c3030, 0x303030fc, 0x00000000, /* 48 0 1 */
- 0x0000007c, 0xc6c06030, 0x180cc6fe, 0x00000000,
- 0x0000007c, 0xc6c0c07c, 0xc0c0c67c, 0x00000000, /* 50 2 3 */
- 0x00000060, 0x70786c66, 0xfe6060f0, 0x00000000,
- 0x000000fe, 0x0606067e, 0xc0c0c67c, 0x00000000, /* 52 4 5 */
- 0x00000038, 0x0c06067e, 0xc6c6c67c, 0x00000000,
- 0x000000fe, 0xc6c06030, 0x18181818, 0x00000000, /* 54 6 7 */
- 0x0000007c, 0xc6c6c67c, 0xc6c6c67c, 0x00000000,
- 0x0000007c, 0xc6c6c6fc, 0xc0c06038, 0x00000000, /* 56 8 9 */
- 0x00000000, 0x18180000, 0x00181800, 0x00000000,
- 0x00000000, 0x18180000, 0x0018180c, 0x00000000, /* 58 : ; */
- 0x00000060, 0x30180c06, 0x0c183060, 0x00000000,
- 0x00000000, 0x007e0000, 0x007e0000, 0x00000000,
- 0x00000006, 0x0c183060, 0x30180c06, 0x00000000,
- 0x0000007c, 0xc6c66030, 0x30003030, 0x00000000,
- 0x0000007c, 0xc6f6d6d6, 0x7606067c, 0x00000000,
- 0x00000010, 0x386cc6c6, 0xfec6c6c6, 0x00000000, /* 64 @ A */
- 0x0000007e, 0xc6c6c67e, 0xc6c6c67e, 0x00000000,
- 0x00000078, 0xcc060606, 0x0606cc78, 0x00000000, /* 66 */
- 0x0000003e, 0x66c6c6c6, 0xc6c6663e, 0x00000000,
- 0x000000fe, 0x0606063e, 0x060606fe, 0x00000000, /* 68 */
- 0x000000fe, 0x0606063e, 0x06060606, 0x00000000,
- 0x00000078, 0xcc060606, 0xf6c6ccb8, 0x00000000, /* 70 */
- 0x000000c6, 0xc6c6c6fe, 0xc6c6c6c6, 0x00000000,
- 0x0000003c, 0x18181818, 0x1818183c, 0x00000000, /* 72 */
- 0x00000060, 0x60606060, 0x6066663c, 0x00000000,
- 0x000000c6, 0xc666361e, 0x3666c6c6, 0x00000000, /* 74 */
- 0x00000006, 0x06060606, 0x060606fe, 0x00000000,
- 0x000000c6, 0xeefed6c6, 0xc6c6c6c6, 0x00000000, /* 76 */
- 0x000000c6, 0xcedefef6, 0xe6c6c6c6, 0x00000000,
- 0x00000038, 0x6cc6c6c6, 0xc6c66c38, 0x00000000, /* 78 */
- 0x0000007e, 0xc6c6c67e, 0x06060606, 0x00000000,
- 0x00000038, 0x6cc6c6c6, 0xc6d67c38, 0x60000000, /* 80 */
- 0x0000007e, 0xc6c6c67e, 0x66c6c6c6, 0x00000000,
- 0x0000007c, 0xc6c60c38, 0x60c6c67c, 0x00000000, /* 82 */
- 0x0000007e, 0x18181818, 0x18181818, 0x00000000,
- 0x000000c6, 0xc6c6c6c6, 0xc6c6c67c, 0x00000000, /* 84 */
- 0x000000c6, 0xc6c6c6c6, 0xc66c3810, 0x00000000,
- 0x000000c6, 0xc6c6c6c6, 0xd6d6fe6c, 0x00000000, /* 86 */
- 0x000000c6, 0xc6c66c38, 0x6cc6c6c6, 0x00000000,
- 0x00000066, 0x66666666, 0x3c181818, 0x00000000, /* 88 */
- 0x000000fe, 0xc0603018, 0x0c0606fe, 0x00000000,
- 0x0000003c, 0x0c0c0c0c, 0x0c0c0c3c, 0x00000000, /* 90 */
- 0x00000002, 0x060c1830, 0x60c08000, 0x00000000,
- 0x0000003c, 0x30303030, 0x3030303c, 0x00000000, /* 92 */
- 0x00001038, 0x6cc60000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00fe0000,
- 0x00001818, 0x30000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00003c60, 0x7c66667c, 0x00000000,
- 0x0000000c, 0x0c0c7ccc, 0xcccccc7c, 0x00000000,
- 0x00000000, 0x00007cc6, 0x0606c67c, 0x00000000,
- 0x00000060, 0x60607c66, 0x6666667c, 0x00000000,
- 0x00000000, 0x00007cc6, 0xfe06c67c, 0x00000000,
- 0x00000078, 0x0c0c0c3e, 0x0c0c0c0c, 0x00000000,
- 0x00000000, 0x00007c66, 0x6666667c, 0x60603e00,
- 0x0000000c, 0x0c0c7ccc, 0xcccccccc, 0x00000000,
- 0x00000030, 0x30003830, 0x30303078, 0x00000000,
- 0x00000030, 0x30003c30, 0x30303030, 0x30301f00,
- 0x0000000c, 0x0c0ccc6c, 0x3c6ccccc, 0x00000000,
- 0x00000030, 0x30303030, 0x30303030, 0x00000000,
- 0x00000000, 0x000066fe, 0xd6d6d6d6, 0x00000000,
- 0x00000000, 0x000078cc, 0xcccccccc, 0x00000000,
- 0x00000000, 0x00007cc6, 0xc6c6c67c, 0x00000000,
- 0x00000000, 0x00007ccc, 0xcccccc7c, 0x0c0c0c00,
- 0x00000000, 0x00007c66, 0x6666667c, 0x60606000,
- 0x00000000, 0x000076dc, 0x0c0c0c0c, 0x00000000,
- 0x00000000, 0x00007cc6, 0x1c70c67c, 0x00000000,
- 0x00000000, 0x1818fe18, 0x18181870, 0x00000000,
- 0x00000000, 0x00006666, 0x6666663c, 0x00000000,
- 0x00000000, 0x0000c6c6, 0xc66c3810, 0x00000000,
- 0x00000000, 0x0000c6d6, 0xd6d6fe6c, 0x00000000,
- 0x00000000, 0x0000c66c, 0x38386cc6, 0x00000000,
- 0x00000000, 0x00006666, 0x6666667c, 0x60603e00,
- 0x00000000, 0x0000fe60, 0x30180cfe, 0x00000000,
- 0x00000070, 0x1818180e, 0x18181870, 0x00000000,
- 0x00000018, 0x18181800, 0x18181818, 0x00000000,
- 0x0000000e, 0x18181870, 0x1818180e, 0x00000000,
- 0x000000dc, 0x76000000, 0x00000000, 0x00000000,
- 0x00000000, 0x0010386c, 0xc6c6fe00, 0x00000000
-};
-
-#endif /* __SOLO6010_OSD_FONT_H */
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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/scatterlist.h>
-
-#include "solo6010.h"
-
-/* #define SOLO_TEST_P2M */
-
-int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr,
- void *sys_addr, u32 ext_addr, u32 size)
-{
- dma_addr_t dma_addr;
- int ret;
-
- WARN_ON(!size);
- BUG_ON(id >= SOLO_NR_P2M);
-
- if (!size)
- return -EINVAL;
-
- dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size,
- wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
-
- ret = solo_p2m_dma_t(solo_dev, id, wr, dma_addr, ext_addr, size);
-
- pci_unmap_single(solo_dev->pdev, dma_addr, size,
- wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
-
- return ret;
-}
-
-int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr,
- dma_addr_t dma_addr, u32 ext_addr, u32 size)
-{
- struct p2m_desc *desc = kzalloc(sizeof(*desc) * 2, GFP_DMA);
- int ret;
-
- if (desc == NULL)
- return -ENOMEM;
-
- solo_p2m_push_desc(&desc[1], wr, dma_addr, ext_addr, size, 0, 0);
- ret = solo_p2m_dma_desc(solo_dev, id, desc, 2);
- kfree(desc);
-
- return ret;
-}
-
-void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr,
- u32 ext_addr, u32 size, int repeat, u32 ext_size)
-{
- desc->ta = cpu_to_le32(dma_addr);
- desc->fa = cpu_to_le32(ext_addr);
-
- desc->ext = cpu_to_le32(SOLO_P2M_COPY_SIZE(size >> 2));
- desc->ctrl = cpu_to_le32(SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) |
- (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON);
-
- /* Ext size only matters when we're repeating */
- if (repeat) {
- desc->ext |= cpu_to_le32(SOLO_P2M_EXT_INC(ext_size >> 2));
- desc->ctrl |= cpu_to_le32(SOLO_P2M_PCI_INC(size >> 2) |
- SOLO_P2M_REPEAT(repeat));
- }
-}
-
-int solo_p2m_dma_desc(struct solo6010_dev *solo_dev, u8 id,
- struct p2m_desc *desc, int desc_count)
-{
- struct solo_p2m_dev *p2m_dev;
- unsigned int timeout;
- int ret = 0;
- u32 config = 0;
- dma_addr_t desc_dma = 0;
-
- BUG_ON(id >= SOLO_NR_P2M);
- BUG_ON(!desc_count || desc_count > SOLO_NR_P2M_DESC);
-
- p2m_dev = &solo_dev->p2m_dev[id];
-
- mutex_lock(&p2m_dev->mutex);
-
- solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
-
- INIT_COMPLETION(p2m_dev->completion);
- p2m_dev->error = 0;
-
- /* Enable the descriptors */
- config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(id));
- desc_dma = pci_map_single(solo_dev->pdev, desc,
- desc_count * sizeof(*desc),
- PCI_DMA_TODEVICE);
- solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), desc_dma);
- solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), desc_count - 1);
- solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config |
- SOLO_P2M_DESC_MODE);
-
- /* Should have all descriptors completed from one interrupt */
- timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ);
-
- solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
-
- /* Reset back to non-descriptor mode */
- solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config);
- solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), 0);
- solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), 0);
- pci_unmap_single(solo_dev->pdev, desc_dma,
- desc_count * sizeof(*desc),
- PCI_DMA_TODEVICE);
-
- if (p2m_dev->error)
- ret = -EIO;
- else if (timeout == 0)
- ret = -EAGAIN;
-
- mutex_unlock(&p2m_dev->mutex);
-
- WARN_ON_ONCE(ret);
-
- return ret;
-}
-
-int solo_p2m_dma_sg(struct solo6010_dev *solo_dev, u8 id,
- struct p2m_desc *pdesc, int wr,
- struct scatterlist *sg, u32 sg_off,
- u32 ext_addr, u32 size)
-{
- int i;
- int idx;
-
- BUG_ON(id >= SOLO_NR_P2M);
-
- if (WARN_ON_ONCE(!size))
- return -EINVAL;
-
- memset(pdesc, 0, sizeof(*pdesc));
-
- /* Should rewrite this to handle > SOLO_NR_P2M_DESC transactions */
- for (i = 0, idx = 1; idx < SOLO_NR_P2M_DESC && sg && size > 0;
- i++, sg = sg_next(sg)) {
- struct p2m_desc *desc = &pdesc[idx];
- u32 sg_len = sg_dma_len(sg);
- u32 len;
-
- if (sg_off >= sg_len) {
- sg_off -= sg_len;
- continue;
- }
-
- sg_len -= sg_off;
- len = min(sg_len, size);
-
- solo_p2m_push_desc(desc, wr, sg_dma_address(sg) + sg_off,
- ext_addr, len, 0, 0);
-
- size -= len;
- ext_addr += len;
- idx++;
-
- sg_off = 0;
- }
-
- WARN_ON_ONCE(size || i >= SOLO_NR_P2M_DESC);
-
- return solo_p2m_dma_desc(solo_dev, id, pdesc, idx);
-}
-
-#ifdef SOLO_TEST_P2M
-
-#define P2M_TEST_CHAR 0xbe
-
-static unsigned long long p2m_test(struct solo6010_dev *solo_dev, u8 id,
- u32 base, int size)
-{
- u8 *wr_buf;
- u8 *rd_buf;
- int i;
- unsigned long long err_cnt = 0;
-
- wr_buf = kmalloc(size, GFP_KERNEL);
- if (!wr_buf) {
- printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n");
- return size;
- }
-
- rd_buf = kmalloc(size, GFP_KERNEL);
- if (!rd_buf) {
- printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n");
- kfree(wr_buf);
- return size;
- }
-
- memset(wr_buf, P2M_TEST_CHAR, size);
- memset(rd_buf, P2M_TEST_CHAR + 1, size);
-
- solo_p2m_dma(solo_dev, id, 1, wr_buf, base, size);
- solo_p2m_dma(solo_dev, id, 0, rd_buf, base, size);
-
- for (i = 0; i < size; i++)
- if (wr_buf[i] != rd_buf[i])
- err_cnt++;
-
- kfree(wr_buf);
- kfree(rd_buf);
-
- return err_cnt;
-}
-
-#define TEST_CHUNK_SIZE (8 * 1024)
-
-static void run_p2m_test(struct solo6010_dev *solo_dev)
-{
- unsigned long long errs = 0;
- u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev);
- int i, d;
-
- printk(KERN_WARNING "%s: Testing %u bytes of external ram\n",
- SOLO6010_NAME, size);
-
- for (i = 0; i < size; i += TEST_CHUNK_SIZE)
- for (d = 0; d < 4; d++)
- errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE);
-
- printk(KERN_WARNING "%s: Found %llu errors during p2m test\n",
- SOLO6010_NAME, errs);
-
- return;
-}
-#else
-#define run_p2m_test(__solo) do {} while (0)
-#endif
-
-void solo_p2m_isr(struct solo6010_dev *solo_dev, int id)
-{
- struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id];
-
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id));
-
- complete(&p2m_dev->completion);
-}
-
-void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status)
-{
- struct solo_p2m_dev *p2m_dev;
- int i;
-
- if (!(status & SOLO_PCI_ERR_P2M))
- 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 solo6010_dev *solo_dev)
-{
- int i;
-
- for (i = 0; i < SOLO_NR_P2M; i++)
- solo6010_irq_off(solo_dev, SOLO_IRQ_P2M(i));
-}
-
-int solo_p2m_init(struct solo6010_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_DMA_INTERVAL(3) |
- SOLO_P2M_DESC_INTR_OPT |
- SOLO_P2M_PCI_MASTER_MODE);
- solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i));
- }
-
- run_p2m_test(solo_dev);
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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.
- */
-
-#ifndef __SOLO6010_REGISTERS_H
-#define __SOLO6010_REGISTERS_H
-
-#include "solo6010-offsets.h"
-
-/* Global 6010 system configuration */
-#define SOLO_SYS_CFG 0x0000
-#define SOLO6010_SYS_CFG_FOUT_EN 0x00000001 /* 6010 only */
-#define SOLO6010_SYS_CFG_PLL_BYPASS 0x00000002 /* 6010 only */
-#define SOLO6010_SYS_CFG_PLL_PWDN 0x00000004 /* 6010 only */
-#define SOLO6010_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) /* 6010 only */
-#define SOLO6010_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) /* 6010 only */
-#define SOLO6010_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) /* 6010 only */
-#define SOLO_SYS_CFG_CLOCK_DIV 0x00080000
-#define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24)
-#define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26)
-#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 /* 6110: must be set */
-#define SOLO_SYS_CFG_RESET 0x80000000
-
-#define SOLO_DMA_CTRL 0x0004
-#define SOLO_DMA_CTRL_REFRESH_CYCLE(n) ((n)<<8)
-/* 0=16/32MB, 1=32/64MB, 2=64/128MB, 3=128/256MB */
-#define SOLO_DMA_CTRL_SDRAM_SIZE(n) ((n)<<6)
-#define SOLO_DMA_CTRL_SDRAM_CLK_INVERT (1<<5)
-#define SOLO_DMA_CTRL_STROBE_SELECT (1<<4)
-#define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3)
-#define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2)
-#define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0)
-#define SOLO_DMA_CTRL1 0x0008
-
-#define SOLO_SYS_VCLK 0x000C
-#define SOLO_VCLK_INVERT (1<<22)
-/* 0=sys_clk/4, 1=sys_clk/2, 2=clk_in/2 of system input */
-#define SOLO_VCLK_SELECT(n) ((n)<<20)
-#define SOLO_VCLK_VIN1415_DELAY(n) ((n)<<14)
-#define SOLO_VCLK_VIN1213_DELAY(n) ((n)<<12)
-#define SOLO_VCLK_VIN1011_DELAY(n) ((n)<<10)
-#define SOLO_VCLK_VIN0809_DELAY(n) ((n)<<8)
-#define SOLO_VCLK_VIN0607_DELAY(n) ((n)<<6)
-#define SOLO_VCLK_VIN0405_DELAY(n) ((n)<<4)
-#define SOLO_VCLK_VIN0203_DELAY(n) ((n)<<2)
-#define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0)
-
-#define SOLO_IRQ_STAT 0x0010
-#define SOLO_IRQ_ENABLE 0x0014
-#define SOLO_IRQ_P2M(n) (1<<((n)+17))
-#define SOLO_IRQ_GPIO (1<<16)
-#define SOLO_IRQ_VIDEO_LOSS (1<<15)
-#define SOLO_IRQ_VIDEO_IN (1<<14)
-#define SOLO_IRQ_MOTION (1<<13)
-#define SOLO_IRQ_ATA_CMD (1<<12)
-#define SOLO_IRQ_ATA_DIR (1<<11)
-#define SOLO_IRQ_PCI_ERR (1<<10)
-#define SOLO_IRQ_PS2_1 (1<<9)
-#define SOLO_IRQ_PS2_0 (1<<8)
-#define SOLO_IRQ_SPI (1<<7)
-#define SOLO_IRQ_IIC (1<<6)
-#define SOLO_IRQ_UART(n) (1<<((n) + 4))
-#define SOLO_IRQ_G723 (1<<3)
-#define SOLO_IRQ_DECODER (1<<1)
-#define SOLO_IRQ_ENCODER (1<<0)
-
-#define SOLO_CHIP_OPTION 0x001C
-#define SOLO_CHIP_ID_MASK 0x00000007
-
-#define SOLO6110_PLL_CONFIG 0x0020
-#define SOLO6110_PLL_RANGE_BYPASS (0 << 20)
-#define SOLO6110_PLL_RANGE_5_10MHZ (1 << 20)
-#define SOLO6110_PLL_RANGE_8_16MHZ (2 << 20)
-#define SOLO6110_PLL_RANGE_13_26MHZ (3 << 20)
-#define SOLO6110_PLL_RANGE_21_42MHZ (4 << 20)
-#define SOLO6110_PLL_RANGE_34_68MHZ (5 << 20)
-#define SOLO6110_PLL_RANGE_54_108MHZ (6 << 20)
-#define SOLO6110_PLL_RANGE_88_200MHZ (7 << 20)
-#define SOLO6110_PLL_DIVR(x) (((x) - 1) << 15)
-#define SOLO6110_PLL_DIVQ_EXP(x) ((x) << 12)
-#define SOLO6110_PLL_DIVF(x) (((x) - 1) << 4)
-#define SOLO6110_PLL_RESET (1 << 3)
-#define SOLO6110_PLL_BYPASS (1 << 2)
-#define SOLO6110_PLL_FSEN (1 << 1)
-#define SOLO6110_PLL_FB (1 << 0)
-
-#define SOLO_EEPROM_CTRL 0x0060
-#define SOLO_EEPROM_ACCESS_EN (1<<7)
-#define SOLO_EEPROM_CS (1<<3)
-#define SOLO_EEPROM_CLK (1<<2)
-#define SOLO_EEPROM_DO (1<<1)
-#define SOLO_EEPROM_DI (1<<0)
-#define SOLO_EEPROM_ENABLE (EEPROM_ACCESS_EN | EEPROM_CS)
-
-#define SOLO_PCI_ERR 0x0070
-#define SOLO_PCI_ERR_FATAL 0x00000001
-#define SOLO_PCI_ERR_PARITY 0x00000002
-#define SOLO_PCI_ERR_TARGET 0x00000004
-#define SOLO_PCI_ERR_TIMEOUT 0x00000008
-#define SOLO_PCI_ERR_P2M 0x00000010
-#define SOLO_PCI_ERR_ATA 0x00000020
-#define SOLO_PCI_ERR_P2M_DESC 0x00000040
-#define SOLO_PCI_ERR_FSM0(__s) (((__s) >> 16) & 0x0f)
-#define SOLO_PCI_ERR_FSM1(__s) (((__s) >> 20) & 0x0f)
-#define SOLO_PCI_ERR_FSM2(__s) (((__s) >> 24) & 0x1f)
-
-#define SOLO_P2M_BASE 0x0080
-
-#define SOLO_P2M_CONFIG(n) (0x0080 + ((n)*0x20))
-#define SOLO_P2M_DMA_INTERVAL(n) ((n)<<6)/* N*32 clocks */
-#define SOLO_P2M_CSC_BYTE_REORDER (1<<5) /* BGR -> RGB */
-/* 0:r=[14:10] g=[9:5] b=[4:0], 1:r=[15:11] g=[10:5] b=[4:0] */
-#define SOLO_P2M_CSC_16BIT_565 (1<<4)
-#define SOLO_P2M_UV_SWAP (1<<3)
-#define SOLO_P2M_PCI_MASTER_MODE (1<<2)
-#define SOLO_P2M_DESC_INTR_OPT (1<<1) /* 1:Empty, 0:Each */
-#define SOLO_P2M_DESC_MODE (1<<0)
-
-#define SOLO_P2M_DES_ADR(n) (0x0084 + ((n)*0x20))
-
-#define SOLO_P2M_DESC_ID(n) (0x0088 + ((n)*0x20))
-#define SOLO_P2M_UPDATE_ID(n) ((n)<<0)
-
-#define SOLO_P2M_STATUS(n) (0x008C + ((n)*0x20))
-#define SOLO_P2M_COMMAND_DONE (1<<8)
-#define SOLO_P2M_CURRENT_ID(stat) (0xff & (stat))
-
-#define SOLO_P2M_CONTROL(n) (0x0090 + ((n)*0x20))
-#define SOLO_P2M_PCI_INC(n) ((n)<<20)
-#define SOLO_P2M_REPEAT(n) ((n)<<10)
-/* 0:512, 1:256, 2:128, 3:64, 4:32, 5:128(2page) */
-#define SOLO_P2M_BURST_SIZE(n) ((n)<<7)
-#define SOLO_P2M_BURST_512 0
-#define SOLO_P2M_BURST_256 1
-#define SOLO_P2M_BURST_128 2
-#define SOLO_P2M_BURST_64 3
-#define SOLO_P2M_BURST_32 4
-#define SOLO_P2M_CSC_16BIT (1<<6) /* 0:24bit, 1:16bit */
-/* 0:Y[0]<-0(OFF), 1:Y[0]<-1(ON), 2:Y[0]<-G[0], 3:Y[0]<-Bit[15] */
-#define SOLO_P2M_ALPHA_MODE(n) ((n)<<4)
-#define SOLO_P2M_CSC_ON (1<<3)
-#define SOLO_P2M_INTERRUPT_REQ (1<<2)
-#define SOLO_P2M_WRITE (1<<1)
-#define SOLO_P2M_TRANS_ON (1<<0)
-
-#define SOLO_P2M_EXT_CFG(n) (0x0094 + ((n)*0x20))
-#define SOLO_P2M_EXT_INC(n) ((n)<<20)
-#define SOLO_P2M_COPY_SIZE(n) ((n)<<0)
-
-#define SOLO_P2M_TAR_ADR(n) (0x0098 + ((n)*0x20))
-
-#define SOLO_P2M_EXT_ADR(n) (0x009C + ((n)*0x20))
-
-#define SOLO_P2M_BUFFER(i) (0x2000 + ((i)*4))
-
-#define SOLO_VI_CH_SWITCH_0 0x0100
-#define SOLO_VI_CH_SWITCH_1 0x0104
-#define SOLO_VI_CH_SWITCH_2 0x0108
-
-#define SOLO_VI_CH_ENA 0x010C
-#define SOLO_VI_CH_FORMAT 0x0110
-#define SOLO_VI_FD_SEL_MASK(n) ((n)<<16)
-#define SOLO_VI_PROG_MASK(n) ((n)<<0)
-
-#define SOLO_VI_FMT_CFG 0x0114
-#define SOLO_VI_FMT_CHECK_VCOUNT (1<<31)
-#define SOLO_VI_FMT_CHECK_HCOUNT (1<<30)
-#define SOLO_VI_FMT_TEST_SIGNAL (1<<28)
-
-#define SOLO_VI_PAGE_SW 0x0118
-#define SOLO_FI_INV_DISP_LIVE(n) ((n)<<8)
-#define SOLO_FI_INV_DISP_OUT(n) ((n)<<7)
-#define SOLO_DISP_SYNC_FI(n) ((n)<<6)
-#define SOLO_PIP_PAGE_ADD(n) ((n)<<3)
-#define SOLO_NORMAL_PAGE_ADD(n) ((n)<<0)
-
-#define SOLO_VI_ACT_I_P 0x011C
-#define SOLO_VI_ACT_I_S 0x0120
-#define SOLO_VI_ACT_P 0x0124
-#define SOLO_VI_FI_INVERT (1<<31)
-#define SOLO_VI_H_START(n) ((n)<<21)
-#define SOLO_VI_V_START(n) ((n)<<11)
-#define SOLO_VI_V_STOP(n) ((n)<<0)
-
-#define SOLO_VI_STATUS0 0x0128
-#define SOLO_VI_STATUS0_PAGE(__n) ((__n) & 0x07)
-#define SOLO_VI_STATUS1 0x012C
-
-/* XXX: Might be better off in kernel level disp.h */
-#define DISP_PAGE(stat) ((stat) & 0x07)
-
-#define SOLO_VI_PB_CONFIG 0x0130
-#define SOLO_VI_PB_USER_MODE (1<<1)
-#define SOLO_VI_PB_PAL (1<<0)
-#define SOLO_VI_PB_RANGE_HV 0x0134
-#define SOLO_VI_PB_HSIZE(h) ((h)<<12)
-#define SOLO_VI_PB_VSIZE(v) ((v)<<0)
-#define SOLO_VI_PB_ACT_H 0x0138
-#define SOLO_VI_PB_HSTART(n) ((n)<<12)
-#define SOLO_VI_PB_HSTOP(n) ((n)<<0)
-#define SOLO_VI_PB_ACT_V 0x013C
-#define SOLO_VI_PB_VSTART(n) ((n)<<12)
-#define SOLO_VI_PB_VSTOP(n) ((n)<<0)
-
-#define SOLO_VI_MOSAIC(ch) (0x0140 + ((ch)*4))
-#define SOLO_VI_MOSAIC_SX(x) ((x)<<24)
-#define SOLO_VI_MOSAIC_EX(x) ((x)<<16)
-#define SOLO_VI_MOSAIC_SY(x) ((x)<<8)
-#define SOLO_VI_MOSAIC_EY(x) ((x)<<0)
-
-#define SOLO_VI_WIN_CTRL0(ch) (0x0180 + ((ch)*4))
-#define SOLO_VI_WIN_CTRL1(ch) (0x01C0 + ((ch)*4))
-
-#define SOLO_VI_WIN_CHANNEL(n) ((n)<<28)
-
-#define SOLO_VI_WIN_PIP(n) ((n)<<27)
-#define SOLO_VI_WIN_SCALE(n) ((n)<<24)
-
-#define SOLO_VI_WIN_SX(x) ((x)<<12)
-#define SOLO_VI_WIN_EX(x) ((x)<<0)
-
-#define SOLO_VI_WIN_SY(x) ((x)<<12)
-#define SOLO_VI_WIN_EY(x) ((x)<<0)
-
-#define SOLO_VI_WIN_ON(ch) (0x0200 + ((ch)*4))
-
-#define SOLO_VI_WIN_SW 0x0240
-#define SOLO_VI_WIN_LIVE_AUTO_MUTE 0x0244
-
-#define SOLO_VI_MOT_ADR 0x0260
-#define SOLO_VI_MOTION_EN(mask) ((mask)<<16)
-#define SOLO_VI_MOT_CTRL 0x0264
-#define SOLO_VI_MOTION_FRAME_COUNT(n) ((n)<<24)
-#define SOLO_VI_MOTION_SAMPLE_LENGTH(n) ((n)<<16)
-#define SOLO_VI_MOTION_INTR_START_STOP (1<<15)
-#define SOLO_VI_MOTION_FREEZE_DATA (1<<14)
-#define SOLO_VI_MOTION_SAMPLE_COUNT(n) ((n)<<0)
-#define SOLO_VI_MOT_CLEAR 0x0268
-#define SOLO_VI_MOT_STATUS 0x026C
-#define SOLO_VI_MOTION_CNT(n) ((n)<<0)
-#define SOLO_VI_MOTION_BORDER 0x0270
-#define SOLO_VI_MOTION_BAR 0x0274
-#define SOLO_VI_MOTION_Y_SET (1<<29)
-#define SOLO_VI_MOTION_Y_ADD (1<<28)
-#define SOLO_VI_MOTION_CB_SET (1<<27)
-#define SOLO_VI_MOTION_CB_ADD (1<<26)
-#define SOLO_VI_MOTION_CR_SET (1<<25)
-#define SOLO_VI_MOTION_CR_ADD (1<<24)
-#define SOLO_VI_MOTION_Y_VALUE(v) ((v)<<16)
-#define SOLO_VI_MOTION_CB_VALUE(v) ((v)<<8)
-#define SOLO_VI_MOTION_CR_VALUE(v) ((v)<<0)
-
-#define SOLO_VO_FMT_ENC 0x0300
-#define SOLO_VO_SCAN_MODE_PROGRESSIVE (1<<31)
-#define SOLO_VO_FMT_TYPE_PAL (1<<30)
-#define SOLO_VO_FMT_TYPE_NTSC 0
-#define SOLO_VO_USER_SET (1<<29)
-
-#define SOLO_VO_FI_CHANGE (1<<20)
-#define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19)
-#define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18)
-#define SOLO_VO_USER_COLOR_SET_NAV (1<<17)
-#define SOLO_VO_USER_COLOR_SET_NAH (1<<16)
-#define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8)
-#define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4)
-#define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0)
-
-#define SOLO_VO_ACT_H 0x0304
-#define SOLO_VO_H_BLANK(n) ((n)<<22)
-#define SOLO_VO_H_START(n) ((n)<<11)
-#define SOLO_VO_H_STOP(n) ((n)<<0)
-
-#define SOLO_VO_ACT_V 0x0308
-#define SOLO_VO_V_BLANK(n) ((n)<<22)
-#define SOLO_VO_V_START(n) ((n)<<11)
-#define SOLO_VO_V_STOP(n) ((n)<<0)
-
-#define SOLO_VO_RANGE_HV 0x030C
-#define SOLO_VO_SYNC_INVERT (1<<24)
-#define SOLO_VO_HSYNC_INVERT (1<<23)
-#define SOLO_VO_VSYNC_INVERT (1<<22)
-#define SOLO_VO_H_LEN(n) ((n)<<11)
-#define SOLO_VO_V_LEN(n) ((n)<<0)
-
-#define SOLO_VO_DISP_CTRL 0x0310
-#define SOLO_VO_DISP_ON (1<<31)
-#define SOLO_VO_DISP_ERASE_COUNT(n) ((n&0xf)<<24)
-#define SOLO_VO_DISP_DOUBLE_SCAN (1<<22)
-#define SOLO_VO_DISP_SINGLE_PAGE (1<<21)
-#define SOLO_VO_DISP_BASE(n) (((n)>>16) & 0xffff)
-
-#define SOLO_VO_DISP_ERASE 0x0314
-#define SOLO_VO_DISP_ERASE_ON (1<<0)
-
-#define SOLO_VO_ZOOM_CTRL 0x0318
-#define SOLO_VO_ZOOM_VER_ON (1<<24)
-#define SOLO_VO_ZOOM_HOR_ON (1<<23)
-#define SOLO_VO_ZOOM_V_COMP (1<<22)
-#define SOLO_VO_ZOOM_SX(h) (((h)/2)<<11)
-#define SOLO_VO_ZOOM_SY(v) (((v)/2)<<0)
-
-#define SOLO_VO_FREEZE_CTRL 0x031C
-#define SOLO_VO_FREEZE_ON (1<<1)
-#define SOLO_VO_FREEZE_INTERPOLATION (1<<0)
-
-#define SOLO_VO_BKG_COLOR 0x0320
-#define SOLO_BG_Y(y) ((y)<<16)
-#define SOLO_BG_U(u) ((u)<<8)
-#define SOLO_BG_V(v) ((v)<<0)
-
-#define SOLO_VO_DEINTERLACE 0x0324
-#define SOLO_VO_DEINTERLACE_THRESHOLD(n) ((n)<<8)
-#define SOLO_VO_DEINTERLACE_EDGE_VALUE(n) ((n)<<0)
-
-#define SOLO_VO_BORDER_LINE_COLOR 0x0330
-#define SOLO_VO_BORDER_FILL_COLOR 0x0334
-#define SOLO_VO_BORDER_LINE_MASK 0x0338
-#define SOLO_VO_BORDER_FILL_MASK 0x033c
-
-#define SOLO_VO_BORDER_X(n) (0x0340+((n)*4))
-#define SOLO_VO_BORDER_Y(n) (0x0354+((n)*4))
-
-#define SOLO_VO_CELL_EXT_SET 0x0368
-#define SOLO_VO_CELL_EXT_START 0x036c
-#define SOLO_VO_CELL_EXT_STOP 0x0370
-
-#define SOLO_VO_CELL_EXT_SET2 0x0374
-#define SOLO_VO_CELL_EXT_START2 0x0378
-#define SOLO_VO_CELL_EXT_STOP2 0x037c
-
-#define SOLO_VO_RECTANGLE_CTRL(n) (0x0368+((n)*12))
-#define SOLO_VO_RECTANGLE_START(n) (0x036c+((n)*12))
-#define SOLO_VO_RECTANGLE_STOP(n) (0x0370+((n)*12))
-
-#define SOLO_VO_CURSOR_POS (0x0380)
-#define SOLO_VO_CURSOR_CLR (0x0384)
-#define SOLO_VO_CURSOR_CLR2 (0x0388)
-#define SOLO_VO_CURSOR_MASK(id) (0x0390+((id)*4))
-
-#define SOLO_VO_EXPANSION(id) (0x0250+((id)*4))
-
-#define SOLO_OSG_CONFIG 0x03E0
-#define SOLO_VO_OSG_ON (1<<31)
-#define SOLO_VO_OSG_COLOR_MUTE (1<<28)
-#define SOLO_VO_OSG_ALPHA_RATE(n) ((n)<<22)
-#define SOLO_VO_OSG_ALPHA_BG_RATE(n) ((n)<<16)
-#define SOLO_VO_OSG_BASE(offset) (((offset)>>16)&0xffff)
-
-#define SOLO_OSG_ERASE 0x03E4
-#define SOLO_OSG_ERASE_ON (0x80)
-#define SOLO_OSG_ERASE_OFF (0x00)
-
-#define SOLO_VO_OSG_BLINK 0x03E8
-#define SOLO_VO_OSG_BLINK_ON (1<<1)
-#define SOLO_VO_OSG_BLINK_INTREVAL18 (1<<0)
-
-#define SOLO_CAP_BASE 0x0400
-#define SOLO_CAP_MAX_PAGE(n) ((n)<<16)
-#define SOLO_CAP_BASE_ADDR(n) ((n)<<0)
-#define SOLO_CAP_BTW 0x0404
-#define SOLO_CAP_PROG_BANDWIDTH(n) ((n)<<8)
-#define SOLO_CAP_MAX_BANDWIDTH(n) ((n)<<0)
-
-#define SOLO_DIM_SCALE1 0x0408
-#define SOLO_DIM_SCALE2 0x040C
-#define SOLO_DIM_SCALE3 0x0410
-#define SOLO_DIM_SCALE4 0x0414
-#define SOLO_DIM_SCALE5 0x0418
-#define SOLO_DIM_V_MB_NUM_FRAME(n) ((n)<<16)
-#define SOLO_DIM_V_MB_NUM_FIELD(n) ((n)<<8)
-#define SOLO_DIM_H_MB_NUM(n) ((n)<<0)
-
-#define SOLO_DIM_PROG 0x041C
-#define SOLO_CAP_STATUS 0x0420
-
-#define SOLO_CAP_CH_SCALE(ch) (0x0440+((ch)*4))
-#define SOLO_CAP_CH_COMP_ENA_E(ch) (0x0480+((ch)*4))
-#define SOLO_CAP_CH_INTV(ch) (0x04C0+((ch)*4))
-#define SOLO_CAP_CH_INTV_E(ch) (0x0500+((ch)*4))
-
-
-#define SOLO_VE_CFG0 0x0610
-#define SOLO_VE_TWO_PAGE_MODE (1<<31)
-#define SOLO_VE_INTR_CTRL(n) ((n)<<24)
-#define SOLO_VE_BLOCK_SIZE(n) ((n)<<16)
-#define SOLO_VE_BLOCK_BASE(n) ((n)<<0)
-
-#define SOLO_VE_CFG1 0x0614
-#define SOLO6110_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 only */
-#define SOLO6010_VE_BYTE_ALIGN(n) ((n)<<24) /* 6010 only */
-#define SOLO6110_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 only */
-#define SOLO_VE_INSERT_INDEX (1<<18)
-#define SOLO_VE_MOTION_MODE(n) ((n)<<16)
-#define SOLO_VE_MOTION_BASE(n) ((n)<<0)
-
-#define SOLO_VE_WMRK_POLY 0x061C
-#define SOLO_VE_VMRK_INIT_KEY 0x0620
-#define SOLO_VE_WMRK_STRL 0x0624
-#define SOLO_VE_ENCRYP_POLY 0x0628
-#define SOLO_VE_ENCRYP_INIT 0x062C
-#define SOLO_VE_ATTR 0x0630
-#define SOLO_VE_LITTLE_ENDIAN (1<<31)
-#define SOLO_COMP_ATTR_RN (1<<30)
-#define SOLO_COMP_ATTR_FCODE(n) ((n)<<27)
-#define SOLO_COMP_TIME_INC(n) ((n)<<25)
-#define SOLO_COMP_TIME_WIDTH(n) ((n)<<21)
-#define SOLO_DCT_INTERVAL(n) ((n)<<16)
-
-#define SOLO_VE_STATE(n) (0x0640+((n)*4))
-
-#define SOLO_VE_JPEG_QP_TBL 0x0670
-#define SOLO_VE_JPEG_QP_CH_L 0x0674
-#define SOLO_VE_JPEG_QP_CH_H 0x0678
-#define SOLO_VE_JPEG_CFG 0x067C
-#define SOLO_VE_JPEG_CTRL 0x0680
-
-#define SOLO_VE_OSD_CH 0x0690
-#define SOLO_VE_OSD_BASE 0x0694
-#define SOLO_VE_OSD_CLR 0x0698
-#define SOLO_VE_OSD_OPT 0x069C
-
-#define SOLO_VE_CH_INTL(ch) (0x0700+((ch)*4))
-#define SOLO6010_VE_CH_MOT(ch) (0x0740+((ch)*4)) /* 6010 only */
-#define SOLO_VE_CH_QP(ch) (0x0780+((ch)*4))
-#define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4))
-#define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4))
-#define SOLO_VE_CH_GOP_E(ch) (0x0840+((ch)*4))
-#define SOLO_VE_CH_REF_BASE(ch) (0x0880+((ch)*4))
-#define SOLO_VE_CH_REF_BASE_E(ch) (0x08C0+((ch)*4))
-
-#define SOLO_VE_MPEG4_QUE(n) (0x0A00+((n)*8))
-#define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8))
-
-#define SOLO_VD_CFG0 0x0900
-#define SOLO6010_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) /* 6010 only */
-#define SOLO_VD_CFG_BUSY_WIAT_CODE (1<<23)
-#define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22)
-#define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21)
-#define SOLO_VD_CFG_BUSY_WIAT_MS (1<<20)
-#define SOLO_VD_CFG_SINGLE_MODE (1<<18)
-#define SOLO_VD_CFG_SCAL_MANUAL (1<<17)
-#define SOLO_VD_CFG_USER_PAGE_CTRL (1<<16)
-#define SOLO_VD_CFG_LITTLE_ENDIAN (1<<15)
-#define SOLO_VD_CFG_START_FI (1<<14)
-#define SOLO_VD_CFG_ERR_LOCK (1<<13)
-#define SOLO_VD_CFG_ERR_INT_ENA (1<<12)
-#define SOLO_VD_CFG_TIME_WIDTH(n) ((n)<<8)
-#define SOLO_VD_CFG_DCT_INTERVAL(n) ((n)<<0)
-
-#define SOLO_VD_CFG1 0x0904
-
-#define SOLO_VD_DEINTERLACE 0x0908
-#define SOLO_VD_DEINTERLACE_THRESHOLD(n) ((n)<<8)
-#define SOLO_VD_DEINTERLACE_EDGE_VALUE(n) ((n)<<0)
-
-#define SOLO_VD_CODE_ADR 0x090C
-
-#define SOLO_VD_CTRL 0x0910
-#define SOLO_VD_OPER_ON (1<<31)
-#define SOLO_VD_MAX_ITEM(n) ((n)<<0)
-
-#define SOLO_VD_STATUS0 0x0920
-#define SOLO_VD_STATUS0_INTR_ACK (1<<22)
-#define SOLO_VD_STATUS0_INTR_EMPTY (1<<21)
-#define SOLO_VD_STATUS0_INTR_ERR (1<<20)
-
-#define SOLO_VD_STATUS1 0x0924
-
-#define SOLO_VD_IDX0 0x0930
-#define SOLO_VD_IDX_INTERLACE (1<<30)
-#define SOLO_VD_IDX_CHANNEL(n) ((n)<<24)
-#define SOLO_VD_IDX_SIZE(n) ((n)<<0)
-
-#define SOLO_VD_IDX1 0x0934
-#define SOLO_VD_IDX_SRC_SCALE(n) ((n)<<28)
-#define SOLO_VD_IDX_WINDOW(n) ((n)<<24)
-#define SOLO_VD_IDX_DEINTERLACE (1<<16)
-#define SOLO_VD_IDX_H_BLOCK(n) ((n)<<8)
-#define SOLO_VD_IDX_V_BLOCK(n) ((n)<<0)
-
-#define SOLO_VD_IDX2 0x0938
-#define SOLO_VD_IDX_REF_BASE_SIDE (1<<31)
-#define SOLO_VD_IDX_REF_BASE(n) (((n)>>16)&0xffff)
-
-#define SOLO_VD_IDX3 0x093C
-#define SOLO_VD_IDX_DISP_SCALE(n) ((n)<<28)
-#define SOLO_VD_IDX_INTERLACE_WR (1<<27)
-#define SOLO_VD_IDX_INTERPOL (1<<26)
-#define SOLO_VD_IDX_HOR2X (1<<25)
-#define SOLO_VD_IDX_OFFSET_X(n) ((n)<<12)
-#define SOLO_VD_IDX_OFFSET_Y(n) ((n)<<0)
-
-#define SOLO_VD_IDX4 0x0940
-#define SOLO_VD_IDX_DEC_WR_PAGE(n) ((n)<<8)
-#define SOLO_VD_IDX_DISP_RD_PAGE(n) ((n)<<0)
-
-#define SOLO_VD_WR_PAGE(n) (0x03F0 + ((n) * 4))
-
-
-#define SOLO_GPIO_CONFIG_0 0x0B00
-#define SOLO_GPIO_CONFIG_1 0x0B04
-#define SOLO_GPIO_DATA_OUT 0x0B08
-#define SOLO_GPIO_DATA_IN 0x0B0C
-#define SOLO_GPIO_INT_ACK_STA 0x0B10
-#define SOLO_GPIO_INT_ENA 0x0B14
-#define SOLO_GPIO_INT_CFG_0 0x0B18
-#define SOLO_GPIO_INT_CFG_1 0x0B1C
-
-
-#define SOLO_IIC_CFG 0x0B20
-#define SOLO_IIC_ENABLE (1<<8)
-#define SOLO_IIC_PRESCALE(n) ((n)<<0)
-
-#define SOLO_IIC_CTRL 0x0B24
-#define SOLO_IIC_AUTO_CLEAR (1<<20)
-#define SOLO_IIC_STATE_RX_ACK (1<<19)
-#define SOLO_IIC_STATE_BUSY (1<<18)
-#define SOLO_IIC_STATE_SIG_ERR (1<<17)
-#define SOLO_IIC_STATE_TRNS (1<<16)
-#define SOLO_IIC_CH_SET(n) ((n)<<5)
-#define SOLO_IIC_ACK_EN (1<<4)
-#define SOLO_IIC_START (1<<3)
-#define SOLO_IIC_STOP (1<<2)
-#define SOLO_IIC_READ (1<<1)
-#define SOLO_IIC_WRITE (1<<0)
-
-#define SOLO_IIC_TXD 0x0B28
-#define SOLO_IIC_RXD 0x0B2C
-
-/*
- * UART REGISTER
- */
-#define SOLO_UART_CONTROL(n) (0x0BA0 + ((n)*0x20))
-#define SOLO_UART_CLK_DIV(n) ((n)<<24)
-#define SOLO_MODEM_CTRL_EN (1<<20)
-#define SOLO_PARITY_ERROR_DROP (1<<18)
-#define SOLO_IRQ_ERR_EN (1<<17)
-#define SOLO_IRQ_RX_EN (1<<16)
-#define SOLO_IRQ_TX_EN (1<<15)
-#define SOLO_RX_EN (1<<14)
-#define SOLO_TX_EN (1<<13)
-#define SOLO_UART_HALF_DUPLEX (1<<12)
-#define SOLO_UART_LOOPBACK (1<<11)
-
-#define SOLO_BAUDRATE_230400 ((0<<9)|(0<<6))
-#define SOLO_BAUDRATE_115200 ((0<<9)|(1<<6))
-#define SOLO_BAUDRATE_57600 ((0<<9)|(2<<6))
-#define SOLO_BAUDRATE_38400 ((0<<9)|(3<<6))
-#define SOLO_BAUDRATE_19200 ((0<<9)|(4<<6))
-#define SOLO_BAUDRATE_9600 ((0<<9)|(5<<6))
-#define SOLO_BAUDRATE_4800 ((0<<9)|(6<<6))
-#define SOLO_BAUDRATE_2400 ((1<<9)|(6<<6))
-#define SOLO_BAUDRATE_1200 ((2<<9)|(6<<6))
-#define SOLO_BAUDRATE_300 ((3<<9)|(6<<6))
-
-#define SOLO_UART_DATA_BIT_8 (3<<4)
-#define SOLO_UART_DATA_BIT_7 (2<<4)
-#define SOLO_UART_DATA_BIT_6 (1<<4)
-#define SOLO_UART_DATA_BIT_5 (0<<4)
-
-#define SOLO_UART_STOP_BIT_1 (0<<2)
-#define SOLO_UART_STOP_BIT_2 (1<<2)
-
-#define SOLO_UART_PARITY_NONE (0<<0)
-#define SOLO_UART_PARITY_EVEN (2<<0)
-#define SOLO_UART_PARITY_ODD (3<<0)
-
-#define SOLO_UART_STATUS(n) (0x0BA4 + ((n)*0x20))
-#define SOLO_UART_CTS (1<<15)
-#define SOLO_UART_RX_BUSY (1<<14)
-#define SOLO_UART_OVERRUN (1<<13)
-#define SOLO_UART_FRAME_ERR (1<<12)
-#define SOLO_UART_PARITY_ERR (1<<11)
-#define SOLO_UART_TX_BUSY (1<<5)
-
-#define SOLO_UART_RX_BUFF_CNT(stat) (((stat)>>6) & 0x1f)
-#define SOLO_UART_RX_BUFF_SIZE 8
-#define SOLO_UART_TX_BUFF_CNT(stat) (((stat)>>0) & 0x1f)
-#define SOLO_UART_TX_BUFF_SIZE 8
-
-#define SOLO_UART_TX_DATA(n) (0x0BA8 + ((n)*0x20))
-#define SOLO_UART_TX_DATA_PUSH (1<<8)
-#define SOLO_UART_RX_DATA(n) (0x0BAC + ((n)*0x20))
-#define SOLO_UART_RX_DATA_POP (1<<8)
-
-#define SOLO_TIMER_CLOCK_NUM 0x0be0
-#define SOLO_TIMER_WATCHDOG 0x0be4
-#define SOLO_TIMER_USEC 0x0be8
-#define SOLO_TIMER_SEC 0x0bec
-
-#define SOLO_AUDIO_CONTROL 0x0D00
-#define SOLO_AUDIO_ENABLE (1<<31)
-#define SOLO_AUDIO_MASTER_MODE (1<<30)
-#define SOLO_AUDIO_I2S_MODE (1<<29)
-#define SOLO_AUDIO_I2S_LR_SWAP (1<<27)
-#define SOLO_AUDIO_I2S_8BIT (1<<26)
-#define SOLO_AUDIO_I2S_MULTI(n) ((n)<<24)
-#define SOLO_AUDIO_MIX_9TO0 (1<<23)
-#define SOLO_AUDIO_DEC_9TO0_VOL(n) ((n)<<20)
-#define SOLO_AUDIO_MIX_19TO10 (1<<19)
-#define SOLO_AUDIO_DEC_19TO10_VOL(n) ((n)<<16)
-#define SOLO_AUDIO_MODE(n) ((n)<<0)
-#define SOLO_AUDIO_SAMPLE 0x0D04
-#define SOLO_AUDIO_EE_MODE_ON (1<<30)
-#define SOLO_AUDIO_EE_ENC_CH(ch) ((ch)<<25)
-#define SOLO_AUDIO_BITRATE(n) ((n)<<16)
-#define SOLO_AUDIO_CLK_DIV(n) ((n)<<0)
-#define SOLO_AUDIO_FDMA_INTR 0x0D08
-#define SOLO_AUDIO_FDMA_INTERVAL(n) ((n)<<19)
-#define SOLO_AUDIO_INTR_ORDER(n) ((n)<<16)
-#define SOLO_AUDIO_FDMA_BASE(n) ((n)<<0)
-#define SOLO_AUDIO_EVOL_0 0x0D0C
-#define SOLO_AUDIO_EVOL_1 0x0D10
-#define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10))
-#define SOLO_AUDIO_STA 0x0D14
-
-
-#define SOLO_WATCHDOG 0x0BE4
-#define WATCHDOG_STAT(status) (status<<8)
-#define WATCHDOG_TIME(sec) (sec&0xff)
-
-#endif /* __SOLO6010_REGISTERS_H */
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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 "solo6010.h"
-#include "solo6010-tw28.h"
-
-/* XXX: Some of these values are masked into an 8-bit regs, and shifted
- * around for other 8-bit regs. What are the magic bits in these values? */
-#define DEFAULT_HDELAY_NTSC (32 - 4)
-#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 u8 tbl_tw2864_template[] = {
- 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
- 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
- 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
- 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
- 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
- 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
- 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
- 0x12, 0xf5, 0x09, 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, 0xd1, 0x0f, 0x11, 0x11, 0x81,
- 0x10, 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 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 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 solo6010_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 solo6010_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 solo6010_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("solo6010/tw28: Error writing register: %02x->%02x [%02x]\n",
- addr, off, val); */
-}
-
-static int tw2865_setup(struct solo6010_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 */
- if (i >= 0xb8 && i <= 0xc1)
- continue;
- if ((i & ~0x30) == 0x00 ||
- (i & ~0x30) == 0x0c ||
- (i & ~0x30) == 0x0d)
- continue;
- if (i >= 0xc4 && i <= 0xc7)
- continue;
- if (i == 0xfd)
- continue;
-
- tw_write_and_verify(solo_dev, dev_addr, i,
- tbl_tw2865_common[i]);
- }
-
- return 0;
-}
-
-static int tw2864_setup(struct solo6010_dev *solo_dev, u8 dev_addr)
-{
- u8 tbl_tw2864_common[sizeof(tbl_tw2864_template)];
- int i;
-
- memcpy(tbl_tw2864_common, tbl_tw2864_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;
- }
- }
-
- /* NTSC or PAL */
- if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) {
- for (i = 0; i < 4; i++) {
- tbl_tw2864_common[0x07 | (i << 4)] |= 0x10;
- tbl_tw2864_common[0x08 | (i << 4)] |= 0x06;
- tbl_tw2864_common[0x0a | (i << 4)] |= 0x08;
- tbl_tw2864_common[0x0b | (i << 4)] |= 0x13;
- tbl_tw2864_common[0x0e | (i << 4)] |= 0x01;
- }
- tbl_tw2864_common[0x9d] = 0x90;
- tbl_tw2864_common[0xf3] = 0x00;
- tbl_tw2864_common[0xf4] = 0xa0;
- }
-
- for (i = 0; i < 0xff; i++) {
- /* Skip read only registers */
- if (i >= 0xb8 && i <= 0xc1)
- continue;
- if ((i & ~0x30) == 0x00 ||
- (i & ~0x30) == 0x0c ||
- (i & ~0x30) == 0x0d)
- continue;
- if (i == 0x74 || i == 0x77 || i == 0x78 ||
- i == 0x79 || i == 0x7a)
- continue;
- if (i == 0xfd)
- continue;
-
- tw_write_and_verify(solo_dev, dev_addr, i,
- tbl_tw2864_common[i]);
- }
-
- return 0;
-}
-
-static int tw2815_setup(struct solo6010_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 solo6010_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 solo6010_dev *solo_dev)
-{
- int i;
- u8 value;
-
- /* Detect techwell chip type */
- for (i = 0; i < TW_NUM_CHIP; 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)
- 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));
- }
-
- dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:",
- solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s");
-
- if (solo_dev->tw2865)
- printk(" tw2865[%d]", hweight32(solo_dev->tw2865));
- if (solo_dev->tw2864)
- printk(" tw2864[%d]", hweight32(solo_dev->tw2864));
- if (solo_dev->tw2815)
- printk(" tw2815[%d]", hweight32(solo_dev->tw2815));
- printk("\n");
-
- 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 solo6010_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 solo6010_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
-
-int tw28_set_ctrl_val(struct solo6010_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 (val > 0x0f || val < 0)
- return -ERANGE;
- 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 if (val != 0)
- return -ERANGE;
- 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:
- 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 solo6010_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 solo6010_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 solo6010_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 solo6010_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);
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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.
- */
-
-#ifndef __SOLO6010_TW28_H
-#define __SOLO6010_TW28_H
-
-#include "solo6010.h"
-
-#define TW_NUM_CHIP 4
-#define TW_BASE_ADDR 0x28
-#define TW_CHIP_OFFSET_ADDR(n) (TW_BASE_ADDR + (n))
-
-/* tw2815 */
-#define TW_AV_STAT_ADDR 0x5a
-#define TW_HUE_ADDR(n) (0x07 | ((n) << 4))
-#define TW_SATURATION_ADDR(n) (0x08 | ((n) << 4))
-#define TW_CONTRAST_ADDR(n) (0x09 | ((n) << 4))
-#define TW_BRIGHTNESS_ADDR(n) (0x0a | ((n) << 4))
-#define TW_AUDIO_OUTPUT_VOL_ADDR 0x70
-#define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0))
-
-/* tw286x */
-#define TW286X_AV_STAT_ADDR 0xfd
-#define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4))
-#define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4))
-#define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4))
-#define TW286x_CONTRAST_ADDR(n) (0x02 | ((n) << 4))
-#define TW286x_BRIGHTNESS_ADDR(n) (0x01 | ((n) << 4))
-#define TW286x_SHARPNESS(n) (0x03 | ((n) << 4))
-#define TW286x_AUDIO_OUTPUT_VOL_ADDR 0xdf
-#define TW286x_AUDIO_INPUT_GAIN_ADDR(n) (0xD0 + ((n > 1) ? 1 : 0))
-
-int solo_tw28_init(struct solo6010_dev *solo_dev);
-
-int tw28_set_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch,
- s32 val);
-int tw28_get_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch,
- s32 *val);
-
-u8 tw28_get_audio_gain(struct solo6010_dev *solo_dev, u8 ch);
-void tw28_set_audio_gain(struct solo6010_dev *solo_dev, u8 ch, u8 val);
-int tw28_get_video_status(struct solo6010_dev *solo_dev, u8 ch);
-
-#if 0
-unsigned int tw2815_get_audio_status(struct SOLO6010 *solo6010);
-void tw2815_Set_AudioOutVol(struct SOLO6010 *solo6010, unsigned int u_val);
-#endif
-
-#endif /* __SOLO6010_TW28_H */
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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/videobuf-dma-sg.h>
-
-#include "solo6010.h"
-#include "solo6010-tw28.h"
-#include "solo6010-jpeg.h"
-
-#define MIN_VID_BUFFERS 4
-#define FRAME_BUF_SIZE (128 * 1024)
-#define MP4_QS 16
-
-static int solo_enc_thread(void *data);
-
-extern unsigned video_nr;
-
-struct solo_enc_fh {
- struct solo_enc_dev *enc;
- u32 fmt;
- u16 rd_idx;
- u8 enc_on;
- enum solo_enc_types type;
- struct videobuf_queue vidq;
- struct list_head vidq_active;
- struct task_struct *kthread;
- struct p2m_desc desc[SOLO_NR_P2M_DESC];
-};
-
-static const u32 solo_user_ctrls[] = {
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- V4L2_CID_SHARPNESS,
- 0
-};
-
-static const u32 solo_mpeg_ctrls[] = {
- V4L2_CID_MPEG_VIDEO_ENCODING,
- V4L2_CID_MPEG_VIDEO_GOP_SIZE,
- 0
-};
-
-static const u32 solo_private_ctrls[] = {
- V4L2_CID_MOTION_ENABLE,
- V4L2_CID_MOTION_THRESHOLD,
- 0
-};
-
-static const u32 solo_fmtx_ctrls[] = {
- V4L2_CID_RDS_TX_RADIO_TEXT,
- 0
-};
-
-static const u32 *solo_ctrl_classes[] = {
- solo_user_ctrls,
- solo_mpeg_ctrls,
- solo_fmtx_ctrls,
- solo_private_ctrls,
- NULL
-};
-
-static int solo_is_motion_on(struct solo_enc_dev *solo_enc)
-{
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
- u8 ch = solo_enc->ch;
-
- if (solo_dev->motion_mask & (1 << ch))
- return 1;
- return 0;
-}
-
-static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on)
-{
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
- u8 ch = solo_enc->ch;
-
- spin_lock(&solo_enc->lock);
-
- if (on)
- solo_dev->motion_mask |= (1 << ch);
- else
- solo_dev->motion_mask &= ~(1 << ch);
-
- /* Do this regardless of if we are turning on or off */
- solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR,
- 1 << solo_enc->ch);
- solo_enc->motion_detected = 0;
-
- solo_reg_write(solo_dev, SOLO_VI_MOT_ADR,
- SOLO_VI_MOTION_EN(solo_dev->motion_mask) |
- (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16));
-
- if (solo_dev->motion_mask)
- solo6010_irq_on(solo_dev, SOLO_IRQ_MOTION);
- else
- solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION);
-
- spin_unlock(&solo_enc->lock);
-}
-
-/* Should be called with solo_enc->lock held */
-static void solo_update_mode(struct solo_enc_dev *solo_enc)
-{
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
-
- assert_spin_locked(&solo_enc->lock);
-
- solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0;
- solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1);
-
- switch (solo_enc->mode) {
- case SOLO_ENC_MODE_CIF:
- solo_enc->width = solo_dev->video_hsize >> 1;
- solo_enc->height = solo_dev->video_vsize;
- break;
- case SOLO_ENC_MODE_D1:
- solo_enc->width = solo_dev->video_hsize;
- solo_enc->height = solo_dev->video_vsize << 1;
- solo_enc->bw_weight <<= 2;
- break;
- default:
- WARN(1, "mode is unknown\n");
- }
-}
-
-/* Should be called with solo_enc->lock held */
-static int solo_enc_on(struct solo_enc_fh *fh)
-{
- struct solo_enc_dev *solo_enc = fh->enc;
- u8 ch = solo_enc->ch;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
- u8 interval;
-
- assert_spin_locked(&solo_enc->lock);
-
- if (fh->enc_on)
- return 0;
-
- solo_update_mode(solo_enc);
-
- /* Make sure to bw check on first reader */
- if (!atomic_read(&solo_enc->readers)) {
- if (solo_enc->bw_weight > solo_dev->enc_bw_remain)
- return -EBUSY;
- else
- solo_dev->enc_bw_remain -= solo_enc->bw_weight;
- }
-
- fh->enc_on = 1;
- fh->rd_idx = solo_enc->solo_dev->enc_wr_idx;
-
- if (fh->type == SOLO_ENC_TYPE_EXT)
- solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1);
-
- if (atomic_inc_return(&solo_enc->readers) > 1)
- return 0;
-
- /* 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);
-
- /* Settle down Beavis... */
- mdelay(10);
-
- return 0;
-}
-
-static void solo_enc_off(struct solo_enc_fh *fh)
-{
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
-
- if (!fh->enc_on)
- return;
-
- if (fh->kthread) {
- kthread_stop(fh->kthread);
- fh->kthread = NULL;
- }
-
- solo_dev->enc_bw_remain += solo_enc->bw_weight;
- fh->enc_on = 0;
-
- if (atomic_dec_return(&solo_enc->readers) > 0)
- return;
-
- 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 solo_start_fh_thread(struct solo_enc_fh *fh)
-{
- struct solo_enc_dev *solo_enc = fh->enc;
-
- fh->kthread = kthread_run(solo_enc_thread, fh, SOLO6010_NAME "_enc");
-
- /* Oops, we had a problem */
- if (IS_ERR(fh->kthread)) {
- spin_lock(&solo_enc->lock);
- solo_enc_off(fh);
- spin_unlock(&solo_enc->lock);
-
- return PTR_ERR(fh->kthread);
- }
-
- return 0;
-}
-
-static void enc_reset_gop(struct solo6010_dev *solo_dev, u8 ch)
-{
- BUG_ON(ch >= solo_dev->nr_chans);
- solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), 1);
- solo_dev->v4l2_enc[ch]->reset_gop = 1;
-}
-
-static int enc_gop_reset(struct solo6010_dev *solo_dev, u8 ch, u8 vop)
-{
- BUG_ON(ch >= solo_dev->nr_chans);
- if (!solo_dev->v4l2_enc[ch]->reset_gop)
- return 0;
- if (vop)
- return 1;
- solo_dev->v4l2_enc[ch]->reset_gop = 0;
- solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch),
- solo_dev->v4l2_enc[ch]->gop);
- return 0;
-}
-
-static void enc_write_sg(struct scatterlist *sglist, void *buf, int size)
-{
- struct scatterlist *sg;
- u8 *src = buf;
-
- for (sg = sglist; sg && size > 0; sg = sg_next(sg)) {
- u8 *p = sg_virt(sg);
- size_t len = sg_dma_len(sg);
- int i;
-
- for (i = 0; i < len && size; i++)
- p[i] = *(src++);
- }
-}
-
-static int enc_get_mpeg_dma_sg(struct solo6010_dev *solo_dev,
- struct p2m_desc *desc,
- struct scatterlist *sglist, int skip,
- unsigned int off, unsigned int size)
-{
- int ret;
-
- if (off > SOLO_MP4E_EXT_SIZE(solo_dev))
- return -EINVAL;
-
- if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) {
- return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E,
- desc, 0, sglist, skip,
- SOLO_MP4E_EXT_ADDR(solo_dev) + off, size);
- }
-
- /* Buffer wrap */
- ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0,
- sglist, skip, SOLO_MP4E_EXT_ADDR(solo_dev) + off,
- SOLO_MP4E_EXT_SIZE(solo_dev) - off);
-
- ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0,
- sglist, skip + SOLO_MP4E_EXT_SIZE(solo_dev) - off,
- SOLO_MP4E_EXT_ADDR(solo_dev),
- size + off - SOLO_MP4E_EXT_SIZE(solo_dev));
-
- return ret;
-}
-
-static int enc_get_mpeg_dma_t(struct solo6010_dev *solo_dev,
- dma_addr_t buf, unsigned int off,
- unsigned int size)
-{
- int ret;
-
- if (off > SOLO_MP4E_EXT_SIZE(solo_dev))
- return -EINVAL;
-
- if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) {
- return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf,
- SOLO_MP4E_EXT_ADDR(solo_dev) + off, size);
- }
-
- /* Buffer wrap */
- ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf,
- SOLO_MP4E_EXT_ADDR(solo_dev) + off,
- SOLO_MP4E_EXT_SIZE(solo_dev) - off);
-
- ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0,
- buf + SOLO_MP4E_EXT_SIZE(solo_dev) - off,
- SOLO_MP4E_EXT_ADDR(solo_dev),
- size + off - SOLO_MP4E_EXT_SIZE(solo_dev));
-
- return ret;
-}
-
-static int enc_get_mpeg_dma(struct solo6010_dev *solo_dev, void *buf,
- unsigned int off, unsigned int size)
-{
- int ret;
-
- dma_addr_t dma_addr = pci_map_single(solo_dev->pdev, buf, size,
- PCI_DMA_FROMDEVICE);
- ret = enc_get_mpeg_dma_t(solo_dev, dma_addr, off, size);
- pci_unmap_single(solo_dev->pdev, dma_addr, size, PCI_DMA_FROMDEVICE);
-
- return ret;
-}
-
-static int enc_get_jpeg_dma_sg(struct solo6010_dev *solo_dev,
- struct p2m_desc *desc,
- struct scatterlist *sglist, int skip,
- unsigned int off, unsigned int size)
-{
- int ret;
-
- if (off > SOLO_JPEG_EXT_SIZE(solo_dev))
- return -EINVAL;
-
- if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) {
- return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG,
- desc, 0, sglist, skip,
- SOLO_JPEG_EXT_ADDR(solo_dev) + off, size);
- }
-
- /* Buffer wrap */
- ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
- sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off,
- SOLO_JPEG_EXT_SIZE(solo_dev) - off);
-
- ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
- sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off,
- SOLO_JPEG_EXT_ADDR(solo_dev),
- size + off - SOLO_JPEG_EXT_SIZE(solo_dev));
-
- return ret;
-}
-
-/* Returns true of __chk is within the first __range bytes of __off */
-#define OFF_IN_RANGE(__off, __range, __chk) \
- ((__off <= __chk) && ((__off + __range) >= __chk))
-
-static void solo_jpeg_header(struct solo_enc_dev *solo_enc,
- struct videobuf_dmabuf *vbuf)
-{
- struct scatterlist *sg;
- void *src = jpeg_header;
- size_t copied = 0;
- size_t to_copy = sizeof(jpeg_header);
-
- for (sg = vbuf->sglist; sg && copied < to_copy; sg = sg_next(sg)) {
- size_t this_copy = min(sg_dma_len(sg),
- (unsigned int)(to_copy - copied));
- u8 *p = sg_virt(sg);
-
- memcpy(p, src + copied, this_copy);
-
- if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 5))
- p[(SOF0_START + 5) - copied] =
- 0xff & (solo_enc->height >> 8);
- if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 6))
- p[(SOF0_START + 6) - copied] = 0xff & solo_enc->height;
- if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 7))
- p[(SOF0_START + 7) - copied] =
- 0xff & (solo_enc->width >> 8);
- if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 8))
- p[(SOF0_START + 8) - copied] = 0xff & solo_enc->width;
-
- copied += this_copy;
- }
-}
-
-static int solo_fill_jpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
- struct videobuf_buffer *vb,
- struct videobuf_dmabuf *vbuf)
-{
- struct solo6010_dev *solo_dev = fh->enc->solo_dev;
- int size = enc_buf->jpeg_size;
-
- /* Copy the header first (direct write) */
- solo_jpeg_header(fh->enc, vbuf);
-
- vb->size = size + sizeof(jpeg_header);
-
- /* Grab the jpeg frame */
- return enc_get_jpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist,
- sizeof(jpeg_header),
- enc_buf->jpeg_off, size);
-}
-
-static inline int vop_interlaced(__le32 *vh)
-{
- return (__le32_to_cpu(vh[0]) >> 30) & 1;
-}
-
-static inline u32 vop_size(__le32 *vh)
-{
- return __le32_to_cpu(vh[0]) & 0xFFFFF;
-}
-
-static inline u8 vop_hsize(__le32 *vh)
-{
- return (__le32_to_cpu(vh[1]) >> 8) & 0xFF;
-}
-
-static inline u8 vop_vsize(__le32 *vh)
-{
- return __le32_to_cpu(vh[1]) & 0xFF;
-}
-
-/* must be called with *bits % 8 = 0 */
-static void write_bytes(u8 **out, unsigned *bits, const u8 *src, unsigned count)
-{
- memcpy(*out, src, count);
- *out += count;
- *bits += count * 8;
-}
-
-static void write_bits(u8 **out, unsigned *bits, u32 value, unsigned count)
-{
-
- value <<= 32 - count; // shift to the right
-
- while (count--) {
- **out <<= 1;
- **out |= !!(value & (1 << 31)); /* MSB */
- value <<= 1;
- if (++(*bits) % 8 == 0)
- (*out)++;
- }
-}
-
-static void write_ue(u8 **out, unsigned *bits, unsigned value) /* H.264 only */
-{
- uint32_t max = 0, cnt = 0;
-
- while (value > max) {
- max = (max + 2) * 2 - 2;
- cnt++;
- }
- write_bits(out, bits, 1, cnt + 1);
- write_bits(out, bits, ~(max - value), cnt);
-}
-
-static void write_se(u8 **out, unsigned *bits, int value) /* H.264 only */
-{
- if (value <= 0)
- write_ue(out, bits, -value * 2);
- else
- write_ue(out, bits, value * 2 - 1);
-}
-
-static void write_mpeg4_end(u8 **out, unsigned *bits)
-{
- write_bits(out, bits, 0, 1);
- /* align on 32-bit boundary */
- if (*bits % 32)
- write_bits(out, bits, 0xFFFFFFFF, 32 - *bits % 32);
-}
-
-static void write_h264_end(u8 **out, unsigned *bits, int align)
-{
- write_bits(out, bits, 1, 1);
- while ((*bits) % 8)
- write_bits(out, bits, 0, 1);
- if (align)
- while ((*bits) % 32)
- write_bits(out, bits, 0, 1);
-}
-
-static void mpeg4_write_vol(u8 **out, struct solo6010_dev *solo_dev,
- __le32 *vh, unsigned fps, unsigned interval)
-{
- static const u8 hdr[] = {
- 0, 0, 1, 0x00 /* video_object_start_code */,
- 0, 0, 1, 0x20 /* video_object_layer_start_code */
- };
- unsigned bits = 0;
- unsigned width = vop_hsize(vh) << 4;
- unsigned height = vop_vsize(vh) << 4;
- unsigned interlaced = vop_interlaced(vh);
-
- write_bytes(out, &bits, hdr, sizeof(hdr));
- write_bits(out, &bits, 0, 1); /* random_accessible_vol */
- write_bits(out, &bits, 0x04, 8); /* video_object_type_indication: main */
- write_bits(out, &bits, 1, 1); /* is_object_layer_identifier */
- write_bits(out, &bits, 2, 4); /* video_object_layer_verid: table V2-39 */
- write_bits(out, &bits, 0, 3); /* video_object_layer_priority */
- if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
- write_bits(out, &bits, 3, 4); /* aspect_ratio_info, assuming 4:3 */
- else
- write_bits(out, &bits, 2, 4);
- write_bits(out, &bits, 1, 1); /* vol_control_parameters */
- write_bits(out, &bits, 1, 2); /* chroma_format: 4:2:0 */
- write_bits(out, &bits, 1, 1); /* low_delay */
- write_bits(out, &bits, 0, 1); /* vbv_parameters */
- write_bits(out, &bits, 0, 2); /* video_object_layer_shape: rectangular */
- write_bits(out, &bits, 1, 1); /* marker_bit */
- write_bits(out, &bits, fps, 16); /* vop_time_increment_resolution */
- write_bits(out, &bits, 1, 1); /* marker_bit */
- write_bits(out, &bits, 1, 1); /* fixed_vop_rate */
- write_bits(out, &bits, interval, 15); /* fixed_vop_time_increment */
- write_bits(out, &bits, 1, 1); /* marker_bit */
- write_bits(out, &bits, width, 13); /* video_object_layer_width */
- write_bits(out, &bits, 1, 1); /* marker_bit */
- write_bits(out, &bits, height, 13); /* video_object_layer_height */
- write_bits(out, &bits, 1, 1); /* marker_bit */
- write_bits(out, &bits, interlaced, 1); /* interlaced */
- write_bits(out, &bits, 1, 1); /* obmc_disable */
- write_bits(out, &bits, 0, 2); /* sprite_enable */
- write_bits(out, &bits, 0, 1); /* not_8_bit */
- write_bits(out, &bits, 1, 0); /* quant_type */
- write_bits(out, &bits, 0, 1); /* load_intra_quant_mat */
- write_bits(out, &bits, 0, 1); /* load_nonintra_quant_mat */
- write_bits(out, &bits, 0, 1); /* quarter_sample */
- write_bits(out, &bits, 1, 1); /* complexity_estimation_disable */
- write_bits(out, &bits, 1, 1); /* resync_marker_disable */
- write_bits(out, &bits, 0, 1); /* data_partitioned */
- write_bits(out, &bits, 0, 1); /* newpred_enable */
- write_bits(out, &bits, 0, 1); /* reduced_resolution_vop_enable */
- write_bits(out, &bits, 0, 1); /* scalability */
- write_mpeg4_end(out, &bits);
-}
-
-static void h264_write_vol(u8 **out, struct solo6010_dev *solo_dev, __le32 *vh)
-{
- static const u8 sps[] = {
- 0, 0, 0, 1 /* start code */, 0x67, 66 /* profile_idc */,
- 0 /* constraints */, 30 /* level_idc */
- };
- static const u8 pps[] = {
- 0, 0, 0, 1 /* start code */, 0x68
- };
-
- unsigned bits = 0;
- unsigned mbs_w = vop_hsize(vh);
- unsigned mbs_h = vop_vsize(vh);
-
- write_bytes(out, &bits, sps, sizeof(sps));
- write_ue(out, &bits, 0); /* seq_parameter_set_id */
- write_ue(out, &bits, 5); /* log2_max_frame_num_minus4 */
- write_ue(out, &bits, 0); /* pic_order_cnt_type */
- write_ue(out, &bits, 6); /* log2_max_pic_order_cnt_lsb_minus4 */
- write_ue(out, &bits, 1); /* max_num_ref_frames */
- write_bits(out, &bits, 0, 1); /* gaps_in_frame_num_value_allowed_flag */
- write_ue(out, &bits, mbs_w - 1); /* pic_width_in_mbs_minus1 */
- write_ue(out, &bits, mbs_h - 1); /* pic_height_in_map_units_minus1 */
- write_bits(out, &bits, 1, 1); /* frame_mbs_only_flag */
- write_bits(out, &bits, 1, 1); /* direct_8x8_frame_field_flag */
- write_bits(out, &bits, 0, 1); /* frame_cropping_flag */
- write_bits(out, &bits, 0, 1); /* vui_parameters_present_flag */
- write_h264_end(out, &bits, 0);
-
- write_bytes(out, &bits, pps, sizeof(pps));
- write_ue(out, &bits, 0); /* pic_parameter_set_id */
- write_ue(out, &bits, 0); /* seq_parameter_set_id */
- write_bits(out, &bits, 0, 1); /* entropy_coding_mode_flag */
- write_bits(out, &bits, 0, 1); /* bottom_field_pic_order_in_frame_present_flag */
- write_ue(out, &bits, 0); /* num_slice_groups_minus1 */
- write_ue(out, &bits, 0); /* num_ref_idx_l0_default_active_minus1 */
- write_ue(out, &bits, 0); /* num_ref_idx_l1_default_active_minus1 */
- write_bits(out, &bits, 0, 1); /* weighted_pred_flag */
- write_bits(out, &bits, 0, 2); /* weighted_bipred_idc */
- write_se(out, &bits, 0); /* pic_init_qp_minus26 */
- write_se(out, &bits, 0); /* pic_init_qs_minus26 */
- write_se(out, &bits, 2); /* chroma_qp_index_offset */
- write_bits(out, &bits, 0, 1); /* deblocking_filter_control_present_flag */
- write_bits(out, &bits, 1, 1); /* constrained_intra_pred_flag */
- write_bits(out, &bits, 0, 1); /* redundant_pic_cnt_present_flag */
- write_h264_end(out, &bits, 1);
-}
-
-static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
- struct videobuf_buffer *vb,
- struct videobuf_dmabuf *vbuf)
-{
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
-
-#define VH_WORDS 16
-#define MAX_VOL_HEADER_LENGTH 64
-
- __le32 vh[VH_WORDS];
- int ret;
- int frame_size, frame_off;
- int skip = 0;
-
- if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh)))
- return -EINVAL;
-
- /* First get the hardware vop header (not real mpeg) */
- ret = enc_get_mpeg_dma(solo_dev, vh, enc_buf->off, sizeof(vh));
- if (WARN_ON_ONCE(ret))
- return ret;
-
- if (WARN_ON_ONCE(vop_size(vh) > enc_buf->size))
- return -EINVAL;
-
- vb->width = vop_hsize(vh) << 4;
- vb->height = vop_vsize(vh) << 4;
- vb->size = vop_size(vh);
-
- /* If this is a key frame, add extra m4v header */
- if (!enc_buf->vop) {
- u8 header[MAX_VOL_HEADER_LENGTH], *out = header;
-
- if (solo_dev->flags & FLAGS_6110)
- h264_write_vol(&out, solo_dev, vh);
- else
- mpeg4_write_vol(&out, solo_dev, vh,
- solo_dev->fps * 1000,
- solo_enc->interval * 1000);
- skip = out - header;
- enc_write_sg(vbuf->sglist, header, skip);
- /* Adjust the dma buffer past this header */
- vb->size += skip;
- }
-
- /* Now get the actual mpeg payload */
- frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev);
- frame_size = enc_buf->size - sizeof(vh);
-
- ret = enc_get_mpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist,
- skip, frame_off, frame_size);
- WARN_ON_ONCE(ret);
-
- return ret;
-}
-
-static void solo_enc_fillbuf(struct solo_enc_fh *fh,
- struct videobuf_buffer *vb)
-{
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
- struct solo_enc_buf *enc_buf = NULL;
- struct videobuf_dmabuf *vbuf;
- int ret;
- int error = 1;
- u16 idx = fh->rd_idx;
-
- while (idx != solo_dev->enc_wr_idx) {
- struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx];
-
- idx = (idx + 1) % SOLO_NR_RING_BUFS;
-
- if (ebuf->ch != solo_enc->ch)
- continue;
-
- if (fh->fmt == V4L2_PIX_FMT_MPEG) {
- if (fh->type == ebuf->type) {
- enc_buf = ebuf;
- break;
- }
- } else {
- /* For mjpeg, keep reading to the newest frame */
- enc_buf = ebuf;
- }
- }
-
- fh->rd_idx = idx;
-
- if (WARN_ON_ONCE(!enc_buf))
- goto buf_err;
-
- if ((fh->fmt == V4L2_PIX_FMT_MPEG &&
- vb->bsize < enc_buf->size) ||
- (fh->fmt == V4L2_PIX_FMT_MJPEG &&
- vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) {
- WARN_ON_ONCE(1);
- goto buf_err;
- }
-
- vbuf = videobuf_to_dma(vb);
- if (WARN_ON_ONCE(!vbuf))
- goto buf_err;
-
- if (fh->fmt == V4L2_PIX_FMT_MPEG)
- ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf);
- else
- ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf);
-
- if (!ret)
- error = 0;
-
-buf_err:
- if (error) {
- vb->state = VIDEOBUF_ERROR;
- } else {
- vb->field_count++;
- vb->ts = enc_buf->ts;
- vb->state = VIDEOBUF_DONE;
- }
-
- wake_up(&vb->done);
-
- return;
-}
-
-static void solo_enc_thread_try(struct solo_enc_fh *fh)
-{
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
- struct videobuf_buffer *vb;
-
- for (;;) {
- spin_lock(&solo_enc->lock);
-
- if (fh->rd_idx == solo_dev->enc_wr_idx)
- break;
-
- if (list_empty(&fh->vidq_active))
- break;
-
- vb = list_first_entry(&fh->vidq_active,
- struct videobuf_buffer, queue);
-
- if (!waitqueue_active(&vb->done))
- break;
-
- list_del(&vb->queue);
-
- spin_unlock(&solo_enc->lock);
-
- solo_enc_fillbuf(fh, vb);
- }
-
- assert_spin_locked(&solo_enc->lock);
- spin_unlock(&solo_enc->lock);
-}
-
-static int solo_enc_thread(void *data)
-{
- struct solo_enc_fh *fh = data;
- struct solo_enc_dev *solo_enc = fh->enc;
- DECLARE_WAITQUEUE(wait, current);
-
- set_freezable();
- add_wait_queue(&solo_enc->thread_wait, &wait);
-
- for (;;) {
- long timeout = schedule_timeout_interruptible(HZ);
- if (timeout == -ERESTARTSYS || kthread_should_stop())
- break;
- solo_enc_thread_try(fh);
- try_to_freeze();
- }
-
- remove_wait_queue(&solo_enc->thread_wait, &wait);
-
- return 0;
-}
-
-void solo_motion_isr(struct solo6010_dev *solo_dev)
-{
- u32 status;
- int i;
-
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_MOTION);
-
- status = solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS);
-
- for (i = 0; i < solo_dev->nr_chans; i++) {
- struct solo_enc_dev *solo_enc = solo_dev->v4l2_enc[i];
-
- BUG_ON(solo_enc == NULL);
-
- if (solo_enc->motion_detected)
- continue;
- if (!(status & (1 << i)))
- continue;
-
- solo_enc->motion_detected = 1;
- }
-}
-
-void solo_enc_v4l2_isr(struct solo6010_dev *solo_dev)
-{
- struct solo_enc_buf *enc_buf;
- u32 mpeg_current, mpeg_next, mpeg_size;
- u32 jpeg_current, jpeg_next, jpeg_size;
- u32 reg_mpeg_size;
- u8 cur_q, vop_type;
- u8 ch;
- enum solo_enc_types enc_type;
-
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_ENCODER);
-
- cur_q = ((solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xF) + 1) % MP4_QS;
-
- reg_mpeg_size = ((solo_reg_read(solo_dev, SOLO_VE_STATE(0)) & 0xFFFFF) + 64 + 8) & ~7;
-
- while (solo_dev->enc_idx != cur_q) {
- mpeg_current = solo_reg_read(solo_dev,
- SOLO_VE_MPEG4_QUE(solo_dev->enc_idx));
- jpeg_current = solo_reg_read(solo_dev,
- SOLO_VE_JPEG_QUE(solo_dev->enc_idx));
- solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS;
- mpeg_next = solo_reg_read(solo_dev,
- SOLO_VE_MPEG4_QUE(solo_dev->enc_idx));
- jpeg_next = solo_reg_read(solo_dev,
- SOLO_VE_JPEG_QUE(solo_dev->enc_idx));
-
- ch = (mpeg_current >> 24) & 0x1f;
- if (ch >= SOLO_MAX_CHANNELS) {
- ch -= SOLO_MAX_CHANNELS;
- enc_type = SOLO_ENC_TYPE_EXT;
- } else
- enc_type = SOLO_ENC_TYPE_STD;
-
- vop_type = (mpeg_current >> 29) & 3;
-
- mpeg_current &= 0x00ffffff;
- mpeg_next &= 0x00ffffff;
- jpeg_current &= 0x00ffffff;
- jpeg_next &= 0x00ffffff;
-
- mpeg_size = (SOLO_MP4E_EXT_SIZE(solo_dev) +
- mpeg_next - mpeg_current) %
- SOLO_MP4E_EXT_SIZE(solo_dev);
-
- jpeg_size = (SOLO_JPEG_EXT_SIZE(solo_dev) +
- jpeg_next - jpeg_current) %
- SOLO_JPEG_EXT_SIZE(solo_dev);
-
- /* XXX I think this means we had a ring overflow? */
- if (mpeg_current > mpeg_next && mpeg_size != reg_mpeg_size) {
- enc_reset_gop(solo_dev, ch);
- continue;
- }
-
- /* When resetting the GOP, skip frames until I-frame */
- if (enc_gop_reset(solo_dev, ch, vop_type))
- continue;
-
- enc_buf = &solo_dev->enc_buf[solo_dev->enc_wr_idx];
-
- enc_buf->vop = vop_type;
- enc_buf->ch = ch;
- enc_buf->off = mpeg_current;
- enc_buf->size = mpeg_size;
- enc_buf->jpeg_off = jpeg_current;
- enc_buf->jpeg_size = jpeg_size;
- enc_buf->type = enc_type;
-
- do_gettimeofday(&enc_buf->ts);
-
- solo_dev->enc_wr_idx = (solo_dev->enc_wr_idx + 1) %
- SOLO_NR_RING_BUFS;
-
- wake_up_interruptible(&solo_dev->v4l2_enc[ch]->thread_wait);
- }
-
- return;
-}
-
-static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- *size = FRAME_BUF_SIZE;
-
- if (*count < MIN_VID_BUFFERS)
- *count = MIN_VID_BUFFERS;
-
- return 0;
-}
-
-static int solo_enc_buf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct solo_enc_fh *fh = vq->priv_data;
- struct solo_enc_dev *solo_enc = fh->enc;
-
- vb->size = FRAME_BUF_SIZE;
- if (vb->baddr != 0 && vb->bsize < vb->size)
- return -EINVAL;
-
- /* These properties only change when queue is idle */
- vb->width = solo_enc->width;
- vb->height = solo_enc->height;
- vb->field = field;
-
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- int rc = videobuf_iolock(vq, vb, NULL);
- if (rc < 0) {
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
- vb->state = VIDEOBUF_NEEDS_INIT;
- return rc;
- }
- }
- vb->state = VIDEOBUF_PREPARED;
-
- return 0;
-}
-
-static void solo_enc_buf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct solo_enc_fh *fh = vq->priv_data;
-
- vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &fh->vidq_active);
- wake_up_interruptible(&fh->enc->thread_wait);
-}
-
-static void solo_enc_buf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static struct videobuf_queue_ops solo_enc_video_qops = {
- .buf_setup = solo_enc_buf_setup,
- .buf_prepare = solo_enc_buf_prepare,
- .buf_queue = solo_enc_buf_queue,
- .buf_release = solo_enc_buf_release,
-};
-
-static unsigned int solo_enc_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct solo_enc_fh *fh = file->private_data;
-
- return videobuf_poll_stream(file, &fh->vidq, wait);
-}
-
-static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct solo_enc_fh *fh = file->private_data;
-
- return videobuf_mmap_mapper(&fh->vidq, vma);
-}
-
-static int solo_enc_open(struct file *file)
-{
- struct solo_enc_dev *solo_enc = video_drvdata(file);
- struct solo_enc_fh *fh;
-
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (fh == NULL)
- return -ENOMEM;
-
- fh->enc = solo_enc;
- file->private_data = fh;
- INIT_LIST_HEAD(&fh->vidq_active);
- fh->fmt = V4L2_PIX_FMT_MPEG;
- fh->type = SOLO_ENC_TYPE_STD;
-
- videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops,
- &solo_enc->solo_dev->pdev->dev,
- &solo_enc->lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct videobuf_buffer), fh, NULL);
-
- return 0;
-}
-
-static ssize_t solo_enc_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct solo_enc_fh *fh = file->private_data;
- struct solo_enc_dev *solo_enc = fh->enc;
-
- /* Make sure the encoder is on */
- if (!fh->enc_on) {
- int ret;
-
- spin_lock(&solo_enc->lock);
- ret = solo_enc_on(fh);
- spin_unlock(&solo_enc->lock);
- if (ret)
- return ret;
-
- ret = solo_start_fh_thread(fh);
- if (ret)
- return ret;
- }
-
- return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
- file->f_flags & O_NONBLOCK);
-}
-
-static int solo_enc_release(struct file *file)
-{
- struct solo_enc_fh *fh = file->private_data;
- struct solo_enc_dev *solo_enc = fh->enc;
-
- videobuf_stop(&fh->vidq);
- videobuf_mmap_free(&fh->vidq);
-
- spin_lock(&solo_enc->lock);
- solo_enc_off(fh);
- spin_unlock(&solo_enc->lock);
-
- kfree(fh);
-
- return 0;
-}
-
-static int solo_enc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
-
- strcpy(cap->driver, SOLO6010_NAME);
- snprintf(cap->card, sizeof(cap->card), "Softlogic 6010 Enc %d",
- solo_enc->ch);
- snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
- pci_name(solo_dev->pdev));
- cap->version = SOLO6010_VER_NUM;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- return 0;
-}
-
-static int solo_enc_enum_input(struct file *file, void *priv,
- struct v4l2_input *input)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_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;
-
- if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
- input->std = V4L2_STD_NTSC_M;
- else
- input->std = V4L2_STD_PAL_B;
-
- 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_MPEG;
- 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_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
- struct v4l2_pix_format *pix = &f->fmt.pix;
-
- if (pix->pixelformat != V4L2_PIX_FMT_MPEG &&
- pix->pixelformat != V4L2_PIX_FMT_MJPEG)
- return -EINVAL;
-
- /* We cannot change width/height in mid read */
- if (atomic_read(&solo_enc->readers) > 0) {
- if (pix->width != solo_enc->width ||
- pix->height != solo_enc->height)
- return -EBUSY;
- }
-
- 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;
- }
-
- if (pix->field == V4L2_FIELD_ANY)
- pix->field = V4L2_FIELD_INTERLACED;
- else if (pix->field != V4L2_FIELD_INTERLACED)
- pix->field = V4L2_FIELD_INTERLACED;
-
- /* Just set these */
- pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
- pix->sizeimage = FRAME_BUF_SIZE;
-
- return 0;
-}
-
-static int solo_enc_set_fmt_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- int ret;
-
- spin_lock(&solo_enc->lock);
-
- ret = solo_enc_try_fmt_cap(file, priv, f);
- if (ret) {
- spin_unlock(&solo_enc->lock);
- 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 */
- fh->fmt = pix->pixelformat;
-
- if (pix->priv)
- fh->type = SOLO_ENC_TYPE_EXT;
- ret = solo_enc_on(fh);
-
- spin_unlock(&solo_enc->lock);
-
- if (ret)
- return ret;
-
- return solo_start_fh_thread(fh);
-}
-
-static int solo_enc_get_fmt_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct v4l2_pix_format *pix = &f->fmt.pix;
-
- pix->width = solo_enc->width;
- pix->height = solo_enc->height;
- pix->pixelformat = fh->fmt;
- pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED :
- V4L2_FIELD_NONE;
- pix->sizeimage = FRAME_BUF_SIZE;
- pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- return 0;
-}
-
-static int solo_enc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req)
-{
- struct solo_enc_fh *fh = priv;
-
- return videobuf_reqbufs(&fh->vidq, req);
-}
-
-static int solo_enc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct solo_enc_fh *fh = priv;
-
- return videobuf_querybuf(&fh->vidq, buf);
-}
-
-static int solo_enc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct solo_enc_fh *fh = priv;
-
- return videobuf_qbuf(&fh->vidq, buf);
-}
-
-static int solo_enc_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- int ret;
-
- /* Make sure the encoder is on */
- if (!fh->enc_on) {
- spin_lock(&solo_enc->lock);
- ret = solo_enc_on(fh);
- spin_unlock(&solo_enc->lock);
- if (ret)
- return ret;
-
- ret = solo_start_fh_thread(fh);
- if (ret)
- return ret;
- }
-
- ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
- if (ret)
- return ret;
-
- /* Signal motion detection */
- if (solo_is_motion_on(solo_enc)) {
- buf->flags |= V4L2_BUF_FLAG_MOTION_ON;
- if (solo_enc->motion_detected) {
- buf->flags |= V4L2_BUF_FLAG_MOTION_DETECTED;
- solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR,
- 1 << solo_enc->ch);
- solo_enc->motion_detected = 0;
- }
- }
-
- /* Check for key frame on mpeg data */
- if (fh->fmt == V4L2_PIX_FMT_MPEG) {
- struct videobuf_dmabuf *vbuf =
- videobuf_to_dma(fh->vidq.bufs[buf->index]);
-
- if (vbuf) {
- u8 *p = sg_virt(vbuf->sglist);
- if (p[3] == 0x00)
- buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
- else
- buf->flags |= V4L2_BUF_FLAG_PFRAME;
- }
- }
-
- return 0;
-}
-
-static int solo_enc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type i)
-{
- struct solo_enc_fh *fh = priv;
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return videobuf_streamon(&fh->vidq);
-}
-
-static int solo_enc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type i)
-{
- struct solo_enc_fh *fh = priv;
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return videobuf_streamoff(&fh->vidq);
-}
-
-static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i)
-{
- return 0;
-}
-
-static int solo_enum_framesizes(struct file *file, void *priv,
- struct v4l2_frmsizeenum *fsize)
-{
- struct solo_enc_fh *fh = priv;
- struct solo6010_dev *solo_dev = fh->enc->solo_dev;
-
- if (fsize->pixel_format != V4L2_PIX_FMT_MPEG)
- 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_fh *fh = priv;
- struct solo6010_dev *solo_dev = fh->enc->solo_dev;
-
- if (fintv->pixel_format != V4L2_PIX_FMT_MPEG || fintv->index)
- return -EINVAL;
-
- fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE;
-
- fintv->stepwise.min.numerator = solo_dev->fps;
- fintv->stepwise.min.denominator = 1;
-
- fintv->stepwise.max.numerator = solo_dev->fps;
- fintv->stepwise.max.denominator = 15;
-
- fintv->stepwise.step.numerator = 1;
- fintv->stepwise.step.denominator = 1;
-
- return 0;
-}
-
-static int solo_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *sp)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_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_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
- struct v4l2_captureparm *cp = &sp->parm.capture;
-
- spin_lock(&solo_enc->lock);
-
- if (atomic_read(&solo_enc->readers) > 0) {
- spin_unlock(&solo_enc->lock);
- 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;
-
- solo_enc->gop = max(solo_dev->fps / solo_enc->interval, 1);
- solo_update_mode(solo_enc);
-
- spin_unlock(&solo_enc->lock);
-
- return 0;
-}
-
-static int solo_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
-
- qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id);
- if (!qc->id)
- return -EINVAL;
-
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, 0x00, 0xff, 1, 0x80);
- case V4L2_CID_SHARPNESS:
- return v4l2_ctrl_query_fill(qc, 0x00, 0x0f, 1, 0x00);
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- return v4l2_ctrl_query_fill(
- qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- return v4l2_ctrl_query_fill(qc, 1, 255, 1, solo_dev->fps);
-#ifdef PRIVATE_CIDS
- case V4L2_CID_MOTION_THRESHOLD:
- qc->flags |= V4L2_CTRL_FLAG_SLIDER;
- qc->type = V4L2_CTRL_TYPE_INTEGER;
- qc->minimum = 0;
- qc->maximum = 0xffff;
- qc->step = 1;
- qc->default_value = SOLO_DEF_MOT_THRESH;
- strlcpy(qc->name, "Motion Detection Threshold",
- sizeof(qc->name));
- return 0;
- case V4L2_CID_MOTION_ENABLE:
- qc->type = V4L2_CTRL_TYPE_BOOLEAN;
- qc->minimum = 0;
- qc->maximum = qc->step = 1;
- qc->default_value = 0;
- strlcpy(qc->name, "Motion Detection Enable", sizeof(qc->name));
- return 0;
-#else
- case V4L2_CID_MOTION_THRESHOLD:
- return v4l2_ctrl_query_fill(qc, 0, 0xffff, 1,
- SOLO_DEF_MOT_THRESH);
- case V4L2_CID_MOTION_ENABLE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-#endif
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- qc->type = V4L2_CTRL_TYPE_STRING;
- qc->minimum = 0;
- qc->maximum = OSD_TEXT_MAX;
- qc->step = 1;
- qc->default_value = 0;
- strlcpy(qc->name, "OSD Text", sizeof(qc->name));
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int solo_querymenu(struct file *file, void *priv,
- struct v4l2_querymenu *qmenu)
-{
- struct v4l2_queryctrl qctrl;
- int err;
-
- qctrl.id = qmenu->id;
- err = solo_queryctrl(file, priv, &qctrl);
- if (err)
- return err;
-
- return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL);
-}
-
-static int solo_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
-
- 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_get_ctrl_val(solo_dev, ctrl->id, solo_enc->ch,
- &ctrl->value);
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
- break;
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- ctrl->value = solo_enc->gop;
- break;
- case V4L2_CID_MOTION_THRESHOLD:
- ctrl->value = solo_enc->motion_thresh;
- break;
- case V4L2_CID_MOTION_ENABLE:
- ctrl->value = solo_is_motion_on(solo_enc);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int solo_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo6010_dev *solo_dev = solo_enc->solo_dev;
-
- 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->value);
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- if (ctrl->value != V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC)
- return -ERANGE;
- break;
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- if (ctrl->value < 1 || ctrl->value > 255)
- return -ERANGE;
- solo_enc->gop = ctrl->value;
- solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch),
- solo_enc->gop);
- solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch),
- solo_enc->gop);
- break;
- case V4L2_CID_MOTION_THRESHOLD:
- /* TODO accept value on lower 16-bits and use high
- * 16-bits to assign the value to a specific block */
- if (ctrl->value < 0 || ctrl->value > 0xffff)
- return -ERANGE;
- solo_enc->motion_thresh = ctrl->value;
- solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->value);
- break;
- case V4L2_CID_MOTION_ENABLE:
- solo_motion_toggle(solo_enc, ctrl->value);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int solo_s_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctrls)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- int i;
-
- for (i = 0; i < ctrls->count; i++) {
- struct v4l2_ext_control *ctrl = (ctrls->controls + i);
- int err;
-
- switch (ctrl->id) {
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- if (ctrl->size - 1 > OSD_TEXT_MAX)
- err = -ERANGE;
- else {
- err = copy_from_user(solo_enc->osd_text,
- ctrl->string,
- OSD_TEXT_MAX);
- solo_enc->osd_text[OSD_TEXT_MAX] = '\0';
- if (!err)
- err = solo_osd_print(solo_enc);
- }
- break;
- default:
- err = -EINVAL;
- }
-
- if (err < 0) {
- ctrls->error_idx = i;
- return err;
- }
- }
-
- return 0;
-}
-
-static int solo_g_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctrls)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- int i;
-
- for (i = 0; i < ctrls->count; i++) {
- struct v4l2_ext_control *ctrl = (ctrls->controls + i);
- int err;
-
- switch (ctrl->id) {
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- if (ctrl->size < OSD_TEXT_MAX) {
- ctrl->size = OSD_TEXT_MAX;
- err = -ENOSPC;
- } else {
- err = copy_to_user(ctrl->string,
- solo_enc->osd_text,
- OSD_TEXT_MAX);
- }
- break;
- default:
- err = -EINVAL;
- }
-
- if (err < 0) {
- ctrls->error_idx = i;
- return err;
- }
- }
-
- return 0;
-}
-
-static const struct v4l2_file_operations solo_enc_fops = {
- .owner = THIS_MODULE,
- .open = solo_enc_open,
- .release = solo_enc_release,
- .read = solo_enc_read,
- .poll = solo_enc_poll,
- .mmap = solo_enc_mmap,
- .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,
- /* 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 = solo_enc_reqbufs,
- .vidioc_querybuf = solo_enc_querybuf,
- .vidioc_qbuf = solo_enc_qbuf,
- .vidioc_dqbuf = solo_enc_dqbuf,
- .vidioc_streamon = solo_enc_streamon,
- .vidioc_streamoff = solo_enc_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,
- /* Controls */
- .vidioc_queryctrl = solo_queryctrl,
- .vidioc_querymenu = solo_querymenu,
- .vidioc_g_ctrl = solo_g_ctrl,
- .vidioc_s_ctrl = solo_s_ctrl,
- .vidioc_g_ext_ctrls = solo_g_ext_ctrls,
- .vidioc_s_ext_ctrls = solo_s_ext_ctrls,
-};
-
-static struct video_device solo_enc_template = {
- .name = SOLO6010_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_B,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-static struct solo_enc_dev *solo_enc_alloc(struct solo6010_dev *solo_dev, u8 ch)
-{
- struct solo_enc_dev *solo_enc;
- int ret;
-
- solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL);
- if (!solo_enc)
- return ERR_PTR(-ENOMEM);
-
- solo_enc->vfd = video_device_alloc();
- if (!solo_enc->vfd) {
- kfree(solo_enc);
- return ERR_PTR(-ENOMEM);
- }
-
- solo_enc->solo_dev = solo_dev;
- solo_enc->ch = ch;
-
- *solo_enc->vfd = solo_enc_template;
- solo_enc->vfd->parent = &solo_dev->pdev->dev;
- ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER,
- video_nr);
- if (ret < 0) {
- video_device_release(solo_enc->vfd);
- kfree(solo_enc);
- return ERR_PTR(ret);
- }
-
- video_set_drvdata(solo_enc->vfd, solo_enc);
-
- snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name),
- "%s-enc (%i/%i)", SOLO6010_NAME, solo_dev->vfd->num,
- solo_enc->vfd->num);
-
- if (video_nr != -1)
- video_nr++;
-
- spin_lock_init(&solo_enc->lock);
- init_waitqueue_head(&solo_enc->thread_wait);
- atomic_set(&solo_enc->readers, 0);
-
- 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_thresh = SOLO_DEF_MOT_THRESH;
-
- spin_lock(&solo_enc->lock);
- solo_update_mode(solo_enc);
- spin_unlock(&solo_enc->lock);
-
- return solo_enc;
-}
-
-static void solo_enc_free(struct solo_enc_dev *solo_enc)
-{
- if (solo_enc == NULL)
- return;
-
- video_unregister_device(solo_enc->vfd);
- kfree(solo_enc);
-}
-
-int solo_enc_v4l2_init(struct solo6010_dev *solo_dev)
-{
- int i;
-
- for (i = 0; i < solo_dev->nr_chans; i++) {
- solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i);
- 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]);
- return ret;
- }
-
- /* D1@MAX-FPS * 4 */
- solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4;
-
- 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 solo6010_dev *solo_dev)
-{
- int i;
-
- solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION);
-
- for (i = 0; i < solo_dev->nr_chans; i++)
- solo_enc_free(solo_dev->v4l2_enc[i]);
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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/videobuf-dma-sg.h>
-
-#include "solo6010.h"
-#include "solo6010-tw28.h"
-
-#define SOLO_HW_BPL 2048
-#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED
-
-/* Image size is two fields, SOLO_HW_BPL is one horizontal line */
-#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 4
-
-/* Simple file handle */
-struct solo_filehandle {
- struct solo6010_dev *solo_dev;
- struct videobuf_queue vidq;
- struct task_struct *kthread;
- spinlock_t slock;
- int old_write;
- struct list_head vidq_active;
- struct p2m_desc desc[SOLO_NR_P2M_DESC];
- int desc_idx;
-};
-
-unsigned video_nr = -1;
-module_param(video_nr, uint, 0644);
-MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)");
-
-static void erase_on(struct solo6010_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 int erase_off(struct solo6010_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 solo6010_dev *solo_dev)
-{
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_VIDEO_IN);
- wake_up_interruptible(&solo_dev->disp_thread_wait);
-}
-
-static void solo_win_setup(struct solo6010_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 solo6010_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 solo6010_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 solo6010_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 solo6010_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 disp_reset_desc(struct solo_filehandle *fh)
-{
- /* We use desc mode, which ignores desc 0 */
- memset(fh->desc, 0, sizeof(*fh->desc));
- fh->desc_idx = 1;
-}
-
-static int disp_flush_descs(struct solo_filehandle *fh)
-{
- int ret;
-
- if (!fh->desc_idx)
- return 0;
-
- ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP,
- fh->desc, fh->desc_idx);
- disp_reset_desc(fh);
-
- return ret;
-}
-
-static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr,
- u32 ext_addr, int size, int repeat, int ext_size)
-{
- if (fh->desc_idx >= SOLO_NR_P2M_DESC) {
- int ret = disp_flush_descs(fh);
- if (ret)
- return ret;
- }
-
- solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr,
- size, repeat, ext_size);
- fh->desc_idx++;
-
- return 0;
-}
-
-static void solo_fillbuf(struct solo_filehandle *fh,
- struct videobuf_buffer *vb)
-{
- struct solo6010_dev *solo_dev = fh->solo_dev;
- struct videobuf_dmabuf *vbuf;
- unsigned int fdma_addr;
- int error = 1;
- int i;
- struct scatterlist *sg;
- dma_addr_t sg_dma;
- int sg_size_left;
-
- vbuf = videobuf_to_dma(vb);
- if (!vbuf)
- goto finish_buf;
-
- if (erase_off(solo_dev)) {
- int i;
-
- /* Just blit to the entire sg list, ignoring size */
- for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) {
- void *p = sg_virt(sg);
- size_t len = sg_dma_len(sg);
-
- for (i = 0; i < len; i += 2) {
- ((u8 *)p)[i] = 0x80;
- ((u8 *)p)[i + 1] = 0x00;
- }
- }
-
- error = 0;
- goto finish_buf;
- }
-
- disp_reset_desc(fh);
- sg = vbuf->sglist;
- sg_dma = sg_dma_address(sg);
- sg_size_left = sg_dma_len(sg);
-
- fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write *
- (SOLO_HW_BPL * solo_vlines(solo_dev)));
-
- for (i = 0; i < solo_vlines(solo_dev); i++) {
- int line_len = solo_bytesperline(solo_dev);
- int lines;
-
- if (!sg_size_left) {
- sg = sg_next(sg);
- if (sg == NULL)
- goto finish_buf;
- sg_dma = sg_dma_address(sg);
- sg_size_left = sg_dma_len(sg);
- }
-
- /* No room for an entire line, so chunk it up */
- if (sg_size_left < line_len) {
- int this_addr = fdma_addr;
-
- while (line_len > 0) {
- int this_write;
-
- if (!sg_size_left) {
- sg = sg_next(sg);
- if (sg == NULL)
- goto finish_buf;
- sg_dma = sg_dma_address(sg);
- sg_size_left = sg_dma_len(sg);
- }
-
- this_write = min(sg_size_left, line_len);
-
- if (disp_push_desc(fh, sg_dma, this_addr,
- this_write, 0, 0))
- goto finish_buf;
-
- line_len -= this_write;
- sg_size_left -= this_write;
- sg_dma += this_write;
- this_addr += this_write;
- }
-
- fdma_addr += SOLO_HW_BPL;
- continue;
- }
-
- /* Shove as many lines into a repeating descriptor as possible */
- lines = min(sg_size_left / line_len,
- solo_vlines(solo_dev) - i);
-
- if (disp_push_desc(fh, sg_dma, fdma_addr, line_len,
- lines - 1, SOLO_HW_BPL))
- goto finish_buf;
-
- i += lines - 1;
- fdma_addr += SOLO_HW_BPL * lines;
- sg_dma += lines * line_len;
- sg_size_left -= lines * line_len;
- }
-
- error = disp_flush_descs(fh);
-
-finish_buf:
- if (error) {
- vb->state = VIDEOBUF_ERROR;
- } else {
- vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev);
- vb->state = VIDEOBUF_DONE;
- vb->field_count++;
- do_gettimeofday(&vb->ts);
- }
-
- wake_up(&vb->done);
-
- return;
-}
-
-static void solo_thread_try(struct solo_filehandle *fh)
-{
- struct videobuf_buffer *vb;
- unsigned int cur_write;
-
- for (;;) {
- spin_lock(&fh->slock);
-
- if (list_empty(&fh->vidq_active))
- break;
-
- vb = list_first_entry(&fh->vidq_active, struct videobuf_buffer,
- queue);
-
- if (!waitqueue_active(&vb->done))
- break;
-
- cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev,
- SOLO_VI_STATUS0));
- if (cur_write == fh->old_write)
- break;
-
- fh->old_write = cur_write;
- list_del(&vb->queue);
-
- spin_unlock(&fh->slock);
-
- solo_fillbuf(fh, vb);
- }
-
- assert_spin_locked(&fh->slock);
- spin_unlock(&fh->slock);
-}
-
-static int solo_thread(void *data)
-{
- struct solo_filehandle *fh = data;
- struct solo6010_dev *solo_dev = fh->solo_dev;
- 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(fh);
- try_to_freeze();
- }
-
- remove_wait_queue(&solo_dev->disp_thread_wait, &wait);
-
- return 0;
-}
-
-static int solo_start_thread(struct solo_filehandle *fh)
-{
- fh->kthread = kthread_run(solo_thread, fh, SOLO6010_NAME "_disp");
-
- if (IS_ERR(fh->kthread))
- return PTR_ERR(fh->kthread);
-
- return 0;
-}
-
-static void solo_stop_thread(struct solo_filehandle *fh)
-{
- if (fh->kthread) {
- kthread_stop(fh->kthread);
- fh->kthread = NULL;
- }
-}
-
-static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- struct solo_filehandle *fh = vq->priv_data;
- struct solo6010_dev *solo_dev = fh->solo_dev;
-
- *size = solo_image_size(solo_dev);
-
- if (*count < MIN_VID_BUFFERS)
- *count = MIN_VID_BUFFERS;
-
- return 0;
-}
-
-static int solo_buf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb, enum v4l2_field field)
-{
- struct solo_filehandle *fh = vq->priv_data;
- struct solo6010_dev *solo_dev = fh->solo_dev;
-
- vb->size = solo_image_size(solo_dev);
- if (vb->baddr != 0 && vb->bsize < vb->size)
- return -EINVAL;
-
- /* XXX: These properties only change when queue is idle */
- vb->width = solo_dev->video_hsize;
- vb->height = solo_vlines(solo_dev);
- vb->bytesperline = solo_bytesperline(solo_dev);
- vb->field = field;
-
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- int rc = videobuf_iolock(vq, vb, NULL);
- if (rc < 0) {
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
- vb->state = VIDEOBUF_NEEDS_INIT;
- return rc;
- }
- }
- vb->state = VIDEOBUF_PREPARED;
-
- return 0;
-}
-
-static void solo_buf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct solo_filehandle *fh = vq->priv_data;
- struct solo6010_dev *solo_dev = fh->solo_dev;
-
- vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &fh->vidq_active);
- wake_up_interruptible(&solo_dev->disp_thread_wait);
-}
-
-static void solo_buf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static struct videobuf_queue_ops solo_video_qops = {
- .buf_setup = solo_buf_setup,
- .buf_prepare = solo_buf_prepare,
- .buf_queue = solo_buf_queue,
- .buf_release = solo_buf_release,
-};
-
-static unsigned int solo_v4l2_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct solo_filehandle *fh = file->private_data;
-
- return videobuf_poll_stream(file, &fh->vidq, wait);
-}
-
-static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct solo_filehandle *fh = file->private_data;
-
- return videobuf_mmap_mapper(&fh->vidq, vma);
-}
-
-static int solo_v4l2_open(struct file *file)
-{
- struct solo6010_dev *solo_dev = video_drvdata(file);
- struct solo_filehandle *fh;
- int ret;
-
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (fh == NULL)
- return -ENOMEM;
-
- spin_lock_init(&fh->slock);
- INIT_LIST_HEAD(&fh->vidq_active);
- fh->solo_dev = solo_dev;
- file->private_data = fh;
-
- ret = solo_start_thread(fh);
- if (ret) {
- kfree(fh);
- return ret;
- }
-
- videobuf_queue_sg_init(&fh->vidq, &solo_video_qops,
- &solo_dev->pdev->dev, &fh->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- SOLO_DISP_PIX_FIELD,
- sizeof(struct videobuf_buffer), fh, NULL);
-
- return 0;
-}
-
-static ssize_t solo_v4l2_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct solo_filehandle *fh = file->private_data;
-
- return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
- file->f_flags & O_NONBLOCK);
-}
-
-static int solo_v4l2_release(struct file *file)
-{
- struct solo_filehandle *fh = file->private_data;
-
- videobuf_stop(&fh->vidq);
- videobuf_mmap_free(&fh->vidq);
- solo_stop_thread(fh);
- kfree(fh);
-
- return 0;
-}
-
-static int solo_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct solo_filehandle *fh = priv;
- struct solo6010_dev *solo_dev = fh->solo_dev;
-
- strcpy(cap->driver, SOLO6010_NAME);
- strcpy(cap->card, "Softlogic 6010");
- snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
- pci_name(solo_dev->pdev));
- cap->version = SOLO6010_VER_NUM;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- return 0;
-}
-
-static int solo_enum_ext_input(struct solo6010_dev *solo_dev,
- struct v4l2_input *input)
-{
- static const char *dispnames_1[] = { "4UP" };
- static const char *dispnames_2[] = { "4UP-1", "4UP-2" };
- static const char *dispnames_5[] = {
- "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP"
- };
- const char **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_filehandle *fh = priv;
- struct solo6010_dev *solo_dev = fh->solo_dev;
-
- 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;
-
- if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
- input->std = V4L2_STD_NTSC_M;
- else
- input->std = V4L2_STD_PAL_B;
-
- return 0;
-}
-
-static int solo_set_input(struct file *file, void *priv, unsigned int index)
-{
- struct solo_filehandle *fh = priv;
-
- return solo_v4l2_set_ch(fh->solo_dev, index);
-}
-
-static int solo_get_input(struct file *file, void *priv, unsigned int *index)
-{
- struct solo_filehandle *fh = priv;
-
- *index = fh->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_filehandle *fh = priv;
- struct solo6010_dev *solo_dev = fh->solo_dev;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- int image_size = solo_image_size(solo_dev);
-
- /* Check supported sizes */
- if (pix->width != solo_dev->video_hsize)
- pix->width = solo_dev->video_hsize;
- if (pix->height != solo_vlines(solo_dev))
- pix->height = solo_vlines(solo_dev);
- if (pix->sizeimage != image_size)
- pix->sizeimage = image_size;
-
- /* Check formats */
- if (pix->field == V4L2_FIELD_ANY)
- pix->field = SOLO_DISP_PIX_FIELD;
-
- if (pix->pixelformat != V4L2_PIX_FMT_UYVY ||
- pix->field != SOLO_DISP_PIX_FIELD ||
- pix->colorspace != V4L2_COLORSPACE_SMPTE170M)
- return -EINVAL;
-
- return 0;
-}
-
-static int solo_set_fmt_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct solo_filehandle *fh = priv;
-
- if (videobuf_queue_is_busy(&fh->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_filehandle *fh = priv;
- struct solo6010_dev *solo_dev = fh->solo_dev;
- 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 = SOLO_DISP_PIX_FIELD;
- pix->sizeimage = solo_image_size(solo_dev);
- pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
- pix->bytesperline = solo_bytesperline(solo_dev);
-
- return 0;
-}
-
-static int solo_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req)
-{
- struct solo_filehandle *fh = priv;
-
- return videobuf_reqbufs(&fh->vidq, req);
-}
-
-static int solo_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct solo_filehandle *fh = priv;
-
- return videobuf_querybuf(&fh->vidq, buf);
-}
-
-static int solo_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct solo_filehandle *fh = priv;
-
- return videobuf_qbuf(&fh->vidq, buf);
-}
-
-static int solo_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct solo_filehandle *fh = priv;
-
- return videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
-}
-
-static int solo_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct solo_filehandle *fh = priv;
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return videobuf_streamon(&fh->vidq);
-}
-
-static int solo_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct solo_filehandle *fh = priv;
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return videobuf_streamoff(&fh->vidq);
-}
-
-static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i)
-{
- return 0;
-}
-
-static const u32 solo_motion_ctrls[] = {
- V4L2_CID_MOTION_TRACE,
- 0
-};
-
-static const u32 *solo_ctrl_classes[] = {
- solo_motion_ctrls,
- NULL
-};
-
-static int solo_disp_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id);
- if (!qc->id)
- return -EINVAL;
-
- switch (qc->id) {
-#ifdef PRIVATE_CIDS
- case V4L2_CID_MOTION_TRACE:
- qc->type = V4L2_CTRL_TYPE_BOOLEAN;
- qc->minimum = 0;
- qc->maximum = qc->step = 1;
- qc->default_value = 0;
- strlcpy(qc->name, "Motion Detection Trace", sizeof(qc->name));
- return 0;
-#else
- case V4L2_CID_MOTION_TRACE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-#endif
- }
- return -EINVAL;
-}
-
-static int solo_disp_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct solo_filehandle *fh = priv;
- struct solo6010_dev *solo_dev = fh->solo_dev;
-
- switch (ctrl->id) {
- case V4L2_CID_MOTION_TRACE:
- ctrl->value = solo_reg_read(solo_dev, SOLO_VI_MOTION_BAR)
- ? 1 : 0;
- return 0;
- }
- return -EINVAL;
-}
-
-static int solo_disp_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct solo_filehandle *fh = priv;
- struct solo6010_dev *solo_dev = fh->solo_dev;
-
- switch (ctrl->id) {
- case V4L2_CID_MOTION_TRACE:
- if (ctrl->value) {
- 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;
- }
- return -EINVAL;
-}
-
-static const struct v4l2_file_operations solo_v4l2_fops = {
- .owner = THIS_MODULE,
- .open = solo_v4l2_open,
- .release = solo_v4l2_release,
- .read = solo_v4l2_read,
- .poll = solo_v4l2_poll,
- .mmap = solo_v4l2_mmap,
- .ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
- .vidioc_querycap = solo_querycap,
- .vidioc_s_std = solo_s_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 = solo_reqbufs,
- .vidioc_querybuf = solo_querybuf,
- .vidioc_qbuf = solo_qbuf,
- .vidioc_dqbuf = solo_dqbuf,
- .vidioc_streamon = solo_streamon,
- .vidioc_streamoff = solo_streamoff,
- /* Controls */
- .vidioc_queryctrl = solo_disp_queryctrl,
- .vidioc_g_ctrl = solo_disp_g_ctrl,
- .vidioc_s_ctrl = solo_disp_s_ctrl,
-};
-
-static struct video_device solo_v4l2_template = {
- .name = SOLO6010_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_B,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-int solo_v4l2_init(struct solo6010_dev *solo_dev)
-{
- int ret;
- int i;
-
- init_waitqueue_head(&solo_dev->disp_thread_wait);
-
- solo_dev->vfd = video_device_alloc();
- if (!solo_dev->vfd)
- return -ENOMEM;
-
- *solo_dev->vfd = solo_v4l2_template;
- solo_dev->vfd->parent = &solo_dev->pdev->dev;
-
- ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr);
- if (ret < 0) {
- video_device_release(solo_dev->vfd);
- solo_dev->vfd = NULL;
- return ret;
- }
-
- video_set_drvdata(solo_dev->vfd, solo_dev);
-
- snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)",
- SOLO6010_NAME, solo_dev->vfd->num);
-
- if (video_nr != -1)
- video_nr++;
-
- 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);
-
- /* 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 */
-
- solo6010_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
-
- return 0;
-}
-
-void solo_v4l2_exit(struct solo6010_dev *solo_dev)
-{
- solo6010_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN);
- if (solo_dev->vfd) {
- video_unregister_device(solo_dev->vfd);
- solo_dev->vfd = NULL;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@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.
- */
-
-#ifndef __SOLO6010_H
-#define __SOLO6010_H
-
-#include <linux/version.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/semaphore.h>
-#include <linux/mutex.h>
-#include <linux/list.h>
-#include <linux/wait.h>
-#include <linux/delay.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
-
-#include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/videobuf-core.h>
-
-#include "solo6010-registers.h"
-
-#ifndef PCI_VENDOR_ID_SOFTLOGIC
-#define PCI_VENDOR_ID_SOFTLOGIC 0x9413
-#define PCI_DEVICE_ID_SOLO6010 0x6010
-#define PCI_DEVICE_ID_SOLO6110 0x6110
-#endif
-
-#ifndef PCI_VENDOR_ID_BLUECHERRY
-#define PCI_VENDOR_ID_BLUECHERRY 0x1BB3
-/* Neugent Softlogic 6010 based cards */
-#define PCI_DEVICE_ID_NEUSOLO_4 0x4304
-#define PCI_DEVICE_ID_NEUSOLO_9 0x4309
-#define PCI_DEVICE_ID_NEUSOLO_16 0x4310
-/* Bluecherry Softlogic 6010 based cards */
-#define PCI_DEVICE_ID_BC_SOLO_4 0x4E04
-#define PCI_DEVICE_ID_BC_SOLO_9 0x4E09
-#define PCI_DEVICE_ID_BC_SOLO_16 0x4E10
-/* Bluecherry Softlogic 6110 based cards */
-#define PCI_DEVICE_ID_BC_6110_4 0x5304
-#define PCI_DEVICE_ID_BC_6110_8 0x5308
-#define PCI_DEVICE_ID_BC_6110_16 0x5310
-#endif /* Bluecherry */
-
-#define SOLO6010_NAME "solo6010"
-
-#define SOLO_MAX_CHANNELS 16
-
-/* Make sure these two match */
-#define SOLO6010_VERSION "2.0.0"
-#define SOLO6010_VER_MAJOR 2
-#define SOLO6010_VER_MINOR 0
-#define SOLO6010_VER_SUB 0
-#define SOLO6010_VER_NUM \
- KERNEL_VERSION(SOLO6010_VER_MAJOR, SOLO6010_VER_MINOR, SOLO6010_VER_SUB)
-
-#define FLAGS_6110 1
-
-/*
- * The SOLO6010 actually has 8 i2c channels, but we only use 2.
- * 0 - Techwell chip(s)
- * 1 - SAA7128
- */
-#define SOLO_I2C_ADAPTERS 2
-#define SOLO_I2C_TW 0
-#define SOLO_I2C_SAA 1
-
-/* DMA Engine setup */
-#define SOLO_NR_P2M 4
-#define SOLO_NR_P2M_DESC 256
-/* MPEG and JPEG share the same interrupt and locks so they must be together
- * in the same dma channel. */
-#define SOLO_P2M_DMA_ID_MP4E 0
-#define SOLO_P2M_DMA_ID_JPEG 0
-#define SOLO_P2M_DMA_ID_MP4D 1
-#define SOLO_P2M_DMA_ID_G723D 1
-#define SOLO_P2M_DMA_ID_DISP 2
-#define SOLO_P2M_DMA_ID_OSG 2
-#define SOLO_P2M_DMA_ID_G723E 3
-#define SOLO_P2M_DMA_ID_VIN 3
-
-/* Encoder standard modes */
-#define SOLO_ENC_MODE_CIF 2
-#define SOLO_ENC_MODE_HD1 1
-#define SOLO_ENC_MODE_D1 9
-
-#define SOLO_DEFAULT_GOP 30
-#define SOLO_DEFAULT_QP 3
-
-/* There is 8MB memory available for solo to buffer MPEG4 frames.
- * This gives us 512 * 16kbyte queues. */
-#define SOLO_NR_RING_BUFS 512
-
-#define SOLO_CLOCK_MHZ 108
-
-#ifndef V4L2_BUF_FLAG_MOTION_ON
-#define V4L2_BUF_FLAG_MOTION_ON 0x0400
-#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800
-#endif
-#ifndef V4L2_CID_MOTION_ENABLE
-#define PRIVATE_CIDS
-#define V4L2_CID_MOTION_ENABLE (V4L2_CID_PRIVATE_BASE+0)
-#define V4L2_CID_MOTION_THRESHOLD (V4L2_CID_PRIVATE_BASE+1)
-#define V4L2_CID_MOTION_TRACE (V4L2_CID_PRIVATE_BASE+2)
-#endif
-
-enum SOLO_I2C_STATE {
- IIC_STATE_IDLE,
- IIC_STATE_START,
- IIC_STATE_READ,
- IIC_STATE_WRITE,
- IIC_STATE_STOP
-};
-
-struct p2m_desc {
- u32 ctrl;
- u32 ext;
- u32 ta;
- u32 fa;
-};
-
-struct solo_p2m_dev {
- struct mutex mutex;
- struct completion completion;
- int error;
-};
-
-#define OSD_TEXT_MAX 30
-
-enum solo_enc_types {
- SOLO_ENC_TYPE_STD,
- SOLO_ENC_TYPE_EXT,
-};
-
-struct solo_enc_dev {
- struct solo6010_dev *solo_dev;
- /* V4L2 Items */
- struct video_device *vfd;
- /* General accounting */
- wait_queue_head_t thread_wait;
- spinlock_t lock;
- atomic_t readers;
- u8 ch;
- u8 mode, gop, qp, interlaced, interval;
- u8 reset_gop;
- u8 bw_weight;
- u8 motion_detected;
- u16 motion_thresh;
- u16 width;
- u16 height;
- char osd_text[OSD_TEXT_MAX + 1];
-};
-
-struct solo_enc_buf {
- u8 vop;
- u8 ch;
- enum solo_enc_types type;
- u32 off;
- u32 size;
- u32 jpeg_off;
- u32 jpeg_size;
- struct timeval ts;
-};
-
-/* The SOLO6010 PCI Device */
-struct solo6010_dev {
- /* General stuff */
- struct pci_dev *pdev;
- u8 __iomem *reg_base;
- int nr_chans;
- int nr_ext;
- u32 flags;
- u32 irq_mask;
- u32 motion_mask;
- spinlock_t reg_io_lock;
-
- /* tw28xx accounting */
- u8 tw2865, tw2864, tw2815;
- u8 tw28_cnt;
-
- /* i2c related items */
- struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS];
- enum SOLO_I2C_STATE i2c_state;
- struct mutex i2c_mutex;
- int i2c_id;
- wait_queue_head_t i2c_wait;
- struct i2c_msg *i2c_msg;
- unsigned int i2c_msg_num;
- unsigned int i2c_msg_ptr;
-
- /* P2M DMA Engine */
- struct solo_p2m_dev p2m_dev[SOLO_NR_P2M];
-
- /* V4L2 Display items */
- struct video_device *vfd;
- unsigned int erasing;
- unsigned int frame_blank;
- u8 cur_disp_ch;
- wait_queue_head_t disp_thread_wait;
-
- /* V4L2 Encoder items */
- struct solo_enc_dev *v4l2_enc[SOLO_MAX_CHANNELS];
- u16 enc_bw_remain;
- /* IDX into hw mp4 encoder */
- u8 enc_idx;
- /* Our software ring of enc buf references */
- u16 enc_wr_idx;
- struct solo_enc_buf enc_buf[SOLO_NR_RING_BUFS];
-
- /* Current video settings */
- u32 video_type;
- u16 video_hsize, video_vsize;
- u16 vout_hstart, vout_vstart;
- u16 vin_hstart, vin_vstart;
- u8 fps;
-
- /* Audio components */
- struct snd_card *snd_card;
- struct snd_pcm *snd_pcm;
- atomic_t snd_users;
- int g723_hw_idx;
-};
-
-static inline u32 solo_reg_read(struct solo6010_dev *solo_dev, int reg)
-{
- unsigned long flags;
- u32 ret;
- u16 val;
-
- spin_lock_irqsave(&solo_dev->reg_io_lock, flags);
-
- ret = readl(solo_dev->reg_base + reg);
- rmb();
- pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val);
- rmb();
-
- spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags);
-
- return ret;
-}
-
-static inline void solo_reg_write(struct solo6010_dev *solo_dev, int reg,
- u32 data)
-{
- unsigned long flags;
- u16 val;
-
- spin_lock_irqsave(&solo_dev->reg_io_lock, flags);
-
- writel(data, solo_dev->reg_base + reg);
- wmb();
- pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val);
- rmb();
-
- spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags);
-}
-
-void solo6010_irq_on(struct solo6010_dev *solo_dev, u32 mask);
-void solo6010_irq_off(struct solo6010_dev *solo_dev, u32 mask);
-
-/* Init/exit routeines for subsystems */
-int solo_disp_init(struct solo6010_dev *solo_dev);
-void solo_disp_exit(struct solo6010_dev *solo_dev);
-
-int solo_gpio_init(struct solo6010_dev *solo_dev);
-void solo_gpio_exit(struct solo6010_dev *solo_dev);
-
-int solo_i2c_init(struct solo6010_dev *solo_dev);
-void solo_i2c_exit(struct solo6010_dev *solo_dev);
-
-int solo_p2m_init(struct solo6010_dev *solo_dev);
-void solo_p2m_exit(struct solo6010_dev *solo_dev);
-
-int solo_v4l2_init(struct solo6010_dev *solo_dev);
-void solo_v4l2_exit(struct solo6010_dev *solo_dev);
-
-int solo_enc_init(struct solo6010_dev *solo_dev);
-void solo_enc_exit(struct solo6010_dev *solo_dev);
-
-int solo_enc_v4l2_init(struct solo6010_dev *solo_dev);
-void solo_enc_v4l2_exit(struct solo6010_dev *solo_dev);
-
-int solo_g723_init(struct solo6010_dev *solo_dev);
-void solo_g723_exit(struct solo6010_dev *solo_dev);
-
-/* ISR's */
-int solo_i2c_isr(struct solo6010_dev *solo_dev);
-void solo_p2m_isr(struct solo6010_dev *solo_dev, int id);
-void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status);
-void solo_enc_v4l2_isr(struct solo6010_dev *solo_dev);
-void solo_g723_isr(struct solo6010_dev *solo_dev);
-void solo_motion_isr(struct solo6010_dev *solo_dev);
-void solo_video_in_isr(struct solo6010_dev *solo_dev);
-
-/* i2c read/write */
-u8 solo_i2c_readbyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off);
-void solo_i2c_writebyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off,
- u8 data);
-
-/* P2M DMA */
-int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr,
- dma_addr_t dma_addr, u32 ext_addr, u32 size);
-int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr,
- void *sys_addr, u32 ext_addr, u32 size);
-int solo_p2m_dma_sg(struct solo6010_dev *solo_dev, u8 id,
- struct p2m_desc *pdesc, int wr,
- struct scatterlist *sglist, u32 sg_off,
- u32 ext_addr, u32 size);
-void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr,
- u32 ext_addr, u32 size, int repeat, u32 ext_size);
-int solo_p2m_dma_desc(struct solo6010_dev *solo_dev, u8 id,
- struct p2m_desc *desc, int desc_count);
-
-/* Set the threshold for motion detection */
-void solo_set_motion_threshold(struct solo6010_dev *solo_dev, u8 ch, u16 val);
-#define SOLO_DEF_MOT_THRESH 0x0300
-
-/* Write text on OSD */
-int solo_osd_print(struct solo_enc_dev *solo_enc);
-
-#endif /* __SOLO6010_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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.
+ */
+
+#ifndef __SOLO6010_H
+#define __SOLO6010_H
+
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/semaphore.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf-core.h>
+#include "registers.h"
+
+#ifndef PCI_VENDOR_ID_SOFTLOGIC
+#define PCI_VENDOR_ID_SOFTLOGIC 0x9413
+#define PCI_DEVICE_ID_SOLO6010 0x6010
+#define PCI_DEVICE_ID_SOLO6110 0x6110
+#endif
+
+#ifndef PCI_VENDOR_ID_BLUECHERRY
+#define PCI_VENDOR_ID_BLUECHERRY 0x1BB3
+/* Neugent Softlogic 6010 based cards */
+#define PCI_DEVICE_ID_NEUSOLO_4 0x4304
+#define PCI_DEVICE_ID_NEUSOLO_9 0x4309
+#define PCI_DEVICE_ID_NEUSOLO_16 0x4310
+/* Bluecherry Softlogic 6010 based cards */
+#define PCI_DEVICE_ID_BC_SOLO_4 0x4E04
+#define PCI_DEVICE_ID_BC_SOLO_9 0x4E09
+#define PCI_DEVICE_ID_BC_SOLO_16 0x4E10
+/* Bluecherry Softlogic 6110 based cards */
+#define PCI_DEVICE_ID_BC_6110_4 0x5304
+#define PCI_DEVICE_ID_BC_6110_8 0x5308
+#define PCI_DEVICE_ID_BC_6110_16 0x5310
+#endif /* Bluecherry */
+
+#define SOLO6010_NAME "solo6010"
+
+#define SOLO_MAX_CHANNELS 16
+
+/* Make sure these two match */
+#define SOLO6010_VERSION "2.0.0"
+#define SOLO6010_VER_MAJOR 2
+#define SOLO6010_VER_MINOR 0
+#define SOLO6010_VER_SUB 0
+#define SOLO6010_VER_NUM \
+ KERNEL_VERSION(SOLO6010_VER_MAJOR, SOLO6010_VER_MINOR, SOLO6010_VER_SUB)
+
+#define FLAGS_6110 1
+
+/*
+ * The SOLO6010 actually has 8 i2c channels, but we only use 2.
+ * 0 - Techwell chip(s)
+ * 1 - SAA7128
+ */
+#define SOLO_I2C_ADAPTERS 2
+#define SOLO_I2C_TW 0
+#define SOLO_I2C_SAA 1
+
+/* DMA Engine setup */
+#define SOLO_NR_P2M 4
+#define SOLO_NR_P2M_DESC 256
+/* MPEG and JPEG share the same interrupt and locks so they must be together
+ * in the same dma channel. */
+#define SOLO_P2M_DMA_ID_MP4E 0
+#define SOLO_P2M_DMA_ID_JPEG 0
+#define SOLO_P2M_DMA_ID_MP4D 1
+#define SOLO_P2M_DMA_ID_G723D 1
+#define SOLO_P2M_DMA_ID_DISP 2
+#define SOLO_P2M_DMA_ID_OSG 2
+#define SOLO_P2M_DMA_ID_G723E 3
+#define SOLO_P2M_DMA_ID_VIN 3
+
+/* Encoder standard modes */
+#define SOLO_ENC_MODE_CIF 2
+#define SOLO_ENC_MODE_HD1 1
+#define SOLO_ENC_MODE_D1 9
+
+#define SOLO_DEFAULT_GOP 30
+#define SOLO_DEFAULT_QP 3
+
+/* There is 8MB memory available for solo to buffer MPEG4 frames.
+ * This gives us 512 * 16kbyte queues. */
+#define SOLO_NR_RING_BUFS 512
+
+#define SOLO_CLOCK_MHZ 108
+
+#ifndef V4L2_BUF_FLAG_MOTION_ON
+#define V4L2_BUF_FLAG_MOTION_ON 0x0400
+#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800
+#endif
+#ifndef V4L2_CID_MOTION_ENABLE
+#define PRIVATE_CIDS
+#define V4L2_CID_MOTION_ENABLE (V4L2_CID_PRIVATE_BASE+0)
+#define V4L2_CID_MOTION_THRESHOLD (V4L2_CID_PRIVATE_BASE+1)
+#define V4L2_CID_MOTION_TRACE (V4L2_CID_PRIVATE_BASE+2)
+#endif
+
+enum SOLO_I2C_STATE {
+ IIC_STATE_IDLE,
+ IIC_STATE_START,
+ IIC_STATE_READ,
+ IIC_STATE_WRITE,
+ IIC_STATE_STOP
+};
+
+struct p2m_desc {
+ u32 ctrl;
+ u32 ext;
+ u32 ta;
+ u32 fa;
+};
+
+struct solo_p2m_dev {
+ struct mutex mutex;
+ struct completion completion;
+ int error;
+};
+
+#define OSD_TEXT_MAX 30
+
+enum solo_enc_types {
+ SOLO_ENC_TYPE_STD,
+ SOLO_ENC_TYPE_EXT,
+};
+
+struct solo_enc_dev {
+ struct solo6010_dev *solo_dev;
+ /* V4L2 Items */
+ struct video_device *vfd;
+ /* General accounting */
+ wait_queue_head_t thread_wait;
+ spinlock_t lock;
+ atomic_t readers;
+ u8 ch;
+ u8 mode, gop, qp, interlaced, interval;
+ u8 reset_gop;
+ u8 bw_weight;
+ u8 motion_detected;
+ u16 motion_thresh;
+ u16 width;
+ u16 height;
+ char osd_text[OSD_TEXT_MAX + 1];
+};
+
+struct solo_enc_buf {
+ u8 vop;
+ u8 ch;
+ enum solo_enc_types type;
+ u32 off;
+ u32 size;
+ u32 jpeg_off;
+ u32 jpeg_size;
+ struct timeval ts;
+};
+
+/* The SOLO6010 PCI Device */
+struct solo6010_dev {
+ /* General stuff */
+ struct pci_dev *pdev;
+ u8 __iomem *reg_base;
+ int nr_chans;
+ int nr_ext;
+ u32 flags;
+ u32 irq_mask;
+ u32 motion_mask;
+ spinlock_t reg_io_lock;
+
+ /* tw28xx accounting */
+ u8 tw2865, tw2864, tw2815;
+ u8 tw28_cnt;
+
+ /* i2c related items */
+ struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS];
+ enum SOLO_I2C_STATE i2c_state;
+ struct mutex i2c_mutex;
+ int i2c_id;
+ wait_queue_head_t i2c_wait;
+ struct i2c_msg *i2c_msg;
+ unsigned int i2c_msg_num;
+ unsigned int i2c_msg_ptr;
+
+ /* P2M DMA Engine */
+ struct solo_p2m_dev p2m_dev[SOLO_NR_P2M];
+
+ /* V4L2 Display items */
+ struct video_device *vfd;
+ unsigned int erasing;
+ unsigned int frame_blank;
+ u8 cur_disp_ch;
+ wait_queue_head_t disp_thread_wait;
+
+ /* V4L2 Encoder items */
+ struct solo_enc_dev *v4l2_enc[SOLO_MAX_CHANNELS];
+ u16 enc_bw_remain;
+ /* IDX into hw mp4 encoder */
+ u8 enc_idx;
+ /* Our software ring of enc buf references */
+ u16 enc_wr_idx;
+ struct solo_enc_buf enc_buf[SOLO_NR_RING_BUFS];
+
+ /* Current video settings */
+ u32 video_type;
+ u16 video_hsize, video_vsize;
+ u16 vout_hstart, vout_vstart;
+ u16 vin_hstart, vin_vstart;
+ u8 fps;
+
+ /* Audio components */
+ struct snd_card *snd_card;
+ struct snd_pcm *snd_pcm;
+ atomic_t snd_users;
+ int g723_hw_idx;
+};
+
+static inline u32 solo_reg_read(struct solo6010_dev *solo_dev, int reg)
+{
+ unsigned long flags;
+ u32 ret;
+ u16 val;
+
+ spin_lock_irqsave(&solo_dev->reg_io_lock, flags);
+
+ ret = readl(solo_dev->reg_base + reg);
+ rmb();
+ pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val);
+ rmb();
+
+ spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags);
+
+ return ret;
+}
+
+static inline void solo_reg_write(struct solo6010_dev *solo_dev, int reg,
+ u32 data)
+{
+ unsigned long flags;
+ u16 val;
+
+ spin_lock_irqsave(&solo_dev->reg_io_lock, flags);
+
+ writel(data, solo_dev->reg_base + reg);
+ wmb();
+ pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val);
+ rmb();
+
+ spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags);
+}
+
+void solo6010_irq_on(struct solo6010_dev *solo_dev, u32 mask);
+void solo6010_irq_off(struct solo6010_dev *solo_dev, u32 mask);
+
+/* Init/exit routeines for subsystems */
+int solo_disp_init(struct solo6010_dev *solo_dev);
+void solo_disp_exit(struct solo6010_dev *solo_dev);
+
+int solo_gpio_init(struct solo6010_dev *solo_dev);
+void solo_gpio_exit(struct solo6010_dev *solo_dev);
+
+int solo_i2c_init(struct solo6010_dev *solo_dev);
+void solo_i2c_exit(struct solo6010_dev *solo_dev);
+
+int solo_p2m_init(struct solo6010_dev *solo_dev);
+void solo_p2m_exit(struct solo6010_dev *solo_dev);
+
+int solo_v4l2_init(struct solo6010_dev *solo_dev);
+void solo_v4l2_exit(struct solo6010_dev *solo_dev);
+
+int solo_enc_init(struct solo6010_dev *solo_dev);
+void solo_enc_exit(struct solo6010_dev *solo_dev);
+
+int solo_enc_v4l2_init(struct solo6010_dev *solo_dev);
+void solo_enc_v4l2_exit(struct solo6010_dev *solo_dev);
+
+int solo_g723_init(struct solo6010_dev *solo_dev);
+void solo_g723_exit(struct solo6010_dev *solo_dev);
+
+/* ISR's */
+int solo_i2c_isr(struct solo6010_dev *solo_dev);
+void solo_p2m_isr(struct solo6010_dev *solo_dev, int id);
+void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status);
+void solo_enc_v4l2_isr(struct solo6010_dev *solo_dev);
+void solo_g723_isr(struct solo6010_dev *solo_dev);
+void solo_motion_isr(struct solo6010_dev *solo_dev);
+void solo_video_in_isr(struct solo6010_dev *solo_dev);
+
+/* i2c read/write */
+u8 solo_i2c_readbyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off);
+void solo_i2c_writebyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off,
+ u8 data);
+
+/* P2M DMA */
+int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr,
+ dma_addr_t dma_addr, u32 ext_addr, u32 size);
+int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr,
+ void *sys_addr, u32 ext_addr, u32 size);
+int solo_p2m_dma_sg(struct solo6010_dev *solo_dev, u8 id,
+ struct p2m_desc *pdesc, int wr,
+ struct scatterlist *sglist, u32 sg_off,
+ u32 ext_addr, u32 size);
+void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr,
+ u32 ext_addr, u32 size, int repeat, u32 ext_size);
+int solo_p2m_dma_desc(struct solo6010_dev *solo_dev, u8 id,
+ struct p2m_desc *desc, int desc_count);
+
+/* Set the threshold for motion detection */
+void solo_set_motion_threshold(struct solo6010_dev *solo_dev, u8 ch, u16 val);
+#define SOLO_DEF_MOT_THRESH 0x0300
+
+/* Write text on OSD */
+int solo_osd_print(struct solo_enc_dev *solo_enc);
+
+#endif /* __SOLO6010_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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 "solo6x10.h"
+#include "tw28.h"
+
+/* XXX: Some of these values are masked into an 8-bit regs, and shifted
+ * around for other 8-bit regs. What are the magic bits in these values? */
+#define DEFAULT_HDELAY_NTSC (32 - 4)
+#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 u8 tbl_tw2864_template[] = {
+ 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
+ 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
+ 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
+ 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
+ 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
+ 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
+ 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
+ 0x12, 0xf5, 0x09, 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, 0xd1, 0x0f, 0x11, 0x11, 0x81,
+ 0x10, 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 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 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 solo6010_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 solo6010_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 solo6010_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("solo6010/tw28: Error writing register: %02x->%02x [%02x]\n",
+ addr, off, val); */
+}
+
+static int tw2865_setup(struct solo6010_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 */
+ if (i >= 0xb8 && i <= 0xc1)
+ continue;
+ if ((i & ~0x30) == 0x00 ||
+ (i & ~0x30) == 0x0c ||
+ (i & ~0x30) == 0x0d)
+ continue;
+ if (i >= 0xc4 && i <= 0xc7)
+ continue;
+ if (i == 0xfd)
+ continue;
+
+ tw_write_and_verify(solo_dev, dev_addr, i,
+ tbl_tw2865_common[i]);
+ }
+
+ return 0;
+}
+
+static int tw2864_setup(struct solo6010_dev *solo_dev, u8 dev_addr)
+{
+ u8 tbl_tw2864_common[sizeof(tbl_tw2864_template)];
+ int i;
+
+ memcpy(tbl_tw2864_common, tbl_tw2864_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;
+ }
+ }
+
+ /* NTSC or PAL */
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) {
+ for (i = 0; i < 4; i++) {
+ tbl_tw2864_common[0x07 | (i << 4)] |= 0x10;
+ tbl_tw2864_common[0x08 | (i << 4)] |= 0x06;
+ tbl_tw2864_common[0x0a | (i << 4)] |= 0x08;
+ tbl_tw2864_common[0x0b | (i << 4)] |= 0x13;
+ tbl_tw2864_common[0x0e | (i << 4)] |= 0x01;
+ }
+ tbl_tw2864_common[0x9d] = 0x90;
+ tbl_tw2864_common[0xf3] = 0x00;
+ tbl_tw2864_common[0xf4] = 0xa0;
+ }
+
+ for (i = 0; i < 0xff; i++) {
+ /* Skip read only registers */
+ if (i >= 0xb8 && i <= 0xc1)
+ continue;
+ if ((i & ~0x30) == 0x00 ||
+ (i & ~0x30) == 0x0c ||
+ (i & ~0x30) == 0x0d)
+ continue;
+ if (i == 0x74 || i == 0x77 || i == 0x78 ||
+ i == 0x79 || i == 0x7a)
+ continue;
+ if (i == 0xfd)
+ continue;
+
+ tw_write_and_verify(solo_dev, dev_addr, i,
+ tbl_tw2864_common[i]);
+ }
+
+ return 0;
+}
+
+static int tw2815_setup(struct solo6010_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 solo6010_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 solo6010_dev *solo_dev)
+{
+ int i;
+ u8 value;
+
+ /* Detect techwell chip type */
+ for (i = 0; i < TW_NUM_CHIP; 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)
+ 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));
+ }
+
+ dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:",
+ solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s");
+
+ if (solo_dev->tw2865)
+ printk(" tw2865[%d]", hweight32(solo_dev->tw2865));
+ if (solo_dev->tw2864)
+ printk(" tw2864[%d]", hweight32(solo_dev->tw2864));
+ if (solo_dev->tw2815)
+ printk(" tw2815[%d]", hweight32(solo_dev->tw2815));
+ printk("\n");
+
+ 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 solo6010_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 solo6010_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
+
+int tw28_set_ctrl_val(struct solo6010_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 (val > 0x0f || val < 0)
+ return -ERANGE;
+ 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 if (val != 0)
+ return -ERANGE;
+ 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:
+ 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 solo6010_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 solo6010_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 solo6010_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 solo6010_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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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.
+ */
+
+#ifndef __SOLO6010_TW28_H
+#define __SOLO6010_TW28_H
+
+#include "solo6x10.h"
+
+#define TW_NUM_CHIP 4
+#define TW_BASE_ADDR 0x28
+#define TW_CHIP_OFFSET_ADDR(n) (TW_BASE_ADDR + (n))
+
+/* tw2815 */
+#define TW_AV_STAT_ADDR 0x5a
+#define TW_HUE_ADDR(n) (0x07 | ((n) << 4))
+#define TW_SATURATION_ADDR(n) (0x08 | ((n) << 4))
+#define TW_CONTRAST_ADDR(n) (0x09 | ((n) << 4))
+#define TW_BRIGHTNESS_ADDR(n) (0x0a | ((n) << 4))
+#define TW_AUDIO_OUTPUT_VOL_ADDR 0x70
+#define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0))
+
+/* tw286x */
+#define TW286X_AV_STAT_ADDR 0xfd
+#define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4))
+#define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4))
+#define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4))
+#define TW286x_CONTRAST_ADDR(n) (0x02 | ((n) << 4))
+#define TW286x_BRIGHTNESS_ADDR(n) (0x01 | ((n) << 4))
+#define TW286x_SHARPNESS(n) (0x03 | ((n) << 4))
+#define TW286x_AUDIO_OUTPUT_VOL_ADDR 0xdf
+#define TW286x_AUDIO_INPUT_GAIN_ADDR(n) (0xD0 + ((n > 1) ? 1 : 0))
+
+int solo_tw28_init(struct solo6010_dev *solo_dev);
+
+int tw28_set_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch,
+ s32 val);
+int tw28_get_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch,
+ s32 *val);
+
+u8 tw28_get_audio_gain(struct solo6010_dev *solo_dev, u8 ch);
+void tw28_set_audio_gain(struct solo6010_dev *solo_dev, u8 ch, u8 val);
+int tw28_get_video_status(struct solo6010_dev *solo_dev, u8 ch);
+
+#if 0
+unsigned int tw2815_get_audio_status(struct SOLO6010 *solo6010);
+void tw2815_Set_AudioOutVol(struct SOLO6010 *solo6010, unsigned int u_val);
+#endif
+
+#endif /* __SOLO6010_TW28_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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/videobuf-dma-sg.h>
+#include "solo6x10.h"
+#include "tw28.h"
+#include "jpeg.h"
+
+#define MIN_VID_BUFFERS 4
+#define FRAME_BUF_SIZE (128 * 1024)
+#define MP4_QS 16
+
+static int solo_enc_thread(void *data);
+
+extern unsigned video_nr;
+
+struct solo_enc_fh {
+ struct solo_enc_dev *enc;
+ u32 fmt;
+ u16 rd_idx;
+ u8 enc_on;
+ enum solo_enc_types type;
+ struct videobuf_queue vidq;
+ struct list_head vidq_active;
+ struct task_struct *kthread;
+ struct p2m_desc desc[SOLO_NR_P2M_DESC];
+};
+
+static const u32 solo_user_ctrls[] = {
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_SHARPNESS,
+ 0
+};
+
+static const u32 solo_mpeg_ctrls[] = {
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ 0
+};
+
+static const u32 solo_private_ctrls[] = {
+ V4L2_CID_MOTION_ENABLE,
+ V4L2_CID_MOTION_THRESHOLD,
+ 0
+};
+
+static const u32 solo_fmtx_ctrls[] = {
+ V4L2_CID_RDS_TX_RADIO_TEXT,
+ 0
+};
+
+static const u32 *solo_ctrl_classes[] = {
+ solo_user_ctrls,
+ solo_mpeg_ctrls,
+ solo_fmtx_ctrls,
+ solo_private_ctrls,
+ NULL
+};
+
+static int solo_is_motion_on(struct solo_enc_dev *solo_enc)
+{
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+ u8 ch = solo_enc->ch;
+
+ if (solo_dev->motion_mask & (1 << ch))
+ return 1;
+ return 0;
+}
+
+static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on)
+{
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+ u8 ch = solo_enc->ch;
+
+ spin_lock(&solo_enc->lock);
+
+ if (on)
+ solo_dev->motion_mask |= (1 << ch);
+ else
+ solo_dev->motion_mask &= ~(1 << ch);
+
+ /* Do this regardless of if we are turning on or off */
+ solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR,
+ 1 << solo_enc->ch);
+ solo_enc->motion_detected = 0;
+
+ solo_reg_write(solo_dev, SOLO_VI_MOT_ADR,
+ SOLO_VI_MOTION_EN(solo_dev->motion_mask) |
+ (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16));
+
+ if (solo_dev->motion_mask)
+ solo6010_irq_on(solo_dev, SOLO_IRQ_MOTION);
+ else
+ solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION);
+
+ spin_unlock(&solo_enc->lock);
+}
+
+/* Should be called with solo_enc->lock held */
+static void solo_update_mode(struct solo_enc_dev *solo_enc)
+{
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+
+ assert_spin_locked(&solo_enc->lock);
+
+ solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0;
+ solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1);
+
+ switch (solo_enc->mode) {
+ case SOLO_ENC_MODE_CIF:
+ solo_enc->width = solo_dev->video_hsize >> 1;
+ solo_enc->height = solo_dev->video_vsize;
+ break;
+ case SOLO_ENC_MODE_D1:
+ solo_enc->width = solo_dev->video_hsize;
+ solo_enc->height = solo_dev->video_vsize << 1;
+ solo_enc->bw_weight <<= 2;
+ break;
+ default:
+ WARN(1, "mode is unknown\n");
+ }
+}
+
+/* Should be called with solo_enc->lock held */
+static int solo_enc_on(struct solo_enc_fh *fh)
+{
+ struct solo_enc_dev *solo_enc = fh->enc;
+ u8 ch = solo_enc->ch;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+ u8 interval;
+
+ assert_spin_locked(&solo_enc->lock);
+
+ if (fh->enc_on)
+ return 0;
+
+ solo_update_mode(solo_enc);
+
+ /* Make sure to bw check on first reader */
+ if (!atomic_read(&solo_enc->readers)) {
+ if (solo_enc->bw_weight > solo_dev->enc_bw_remain)
+ return -EBUSY;
+ else
+ solo_dev->enc_bw_remain -= solo_enc->bw_weight;
+ }
+
+ fh->enc_on = 1;
+ fh->rd_idx = solo_enc->solo_dev->enc_wr_idx;
+
+ if (fh->type == SOLO_ENC_TYPE_EXT)
+ solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1);
+
+ if (atomic_inc_return(&solo_enc->readers) > 1)
+ return 0;
+
+ /* 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);
+
+ /* Settle down Beavis... */
+ mdelay(10);
+
+ return 0;
+}
+
+static void solo_enc_off(struct solo_enc_fh *fh)
+{
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+
+ if (!fh->enc_on)
+ return;
+
+ if (fh->kthread) {
+ kthread_stop(fh->kthread);
+ fh->kthread = NULL;
+ }
+
+ solo_dev->enc_bw_remain += solo_enc->bw_weight;
+ fh->enc_on = 0;
+
+ if (atomic_dec_return(&solo_enc->readers) > 0)
+ return;
+
+ 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 solo_start_fh_thread(struct solo_enc_fh *fh)
+{
+ struct solo_enc_dev *solo_enc = fh->enc;
+
+ fh->kthread = kthread_run(solo_enc_thread, fh, SOLO6010_NAME "_enc");
+
+ /* Oops, we had a problem */
+ if (IS_ERR(fh->kthread)) {
+ spin_lock(&solo_enc->lock);
+ solo_enc_off(fh);
+ spin_unlock(&solo_enc->lock);
+
+ return PTR_ERR(fh->kthread);
+ }
+
+ return 0;
+}
+
+static void enc_reset_gop(struct solo6010_dev *solo_dev, u8 ch)
+{
+ BUG_ON(ch >= solo_dev->nr_chans);
+ solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), 1);
+ solo_dev->v4l2_enc[ch]->reset_gop = 1;
+}
+
+static int enc_gop_reset(struct solo6010_dev *solo_dev, u8 ch, u8 vop)
+{
+ BUG_ON(ch >= solo_dev->nr_chans);
+ if (!solo_dev->v4l2_enc[ch]->reset_gop)
+ return 0;
+ if (vop)
+ return 1;
+ solo_dev->v4l2_enc[ch]->reset_gop = 0;
+ solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch),
+ solo_dev->v4l2_enc[ch]->gop);
+ return 0;
+}
+
+static void enc_write_sg(struct scatterlist *sglist, void *buf, int size)
+{
+ struct scatterlist *sg;
+ u8 *src = buf;
+
+ for (sg = sglist; sg && size > 0; sg = sg_next(sg)) {
+ u8 *p = sg_virt(sg);
+ size_t len = sg_dma_len(sg);
+ int i;
+
+ for (i = 0; i < len && size; i++)
+ p[i] = *(src++);
+ }
+}
+
+static int enc_get_mpeg_dma_sg(struct solo6010_dev *solo_dev,
+ struct p2m_desc *desc,
+ struct scatterlist *sglist, int skip,
+ unsigned int off, unsigned int size)
+{
+ int ret;
+
+ if (off > SOLO_MP4E_EXT_SIZE(solo_dev))
+ return -EINVAL;
+
+ if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) {
+ return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E,
+ desc, 0, sglist, skip,
+ SOLO_MP4E_EXT_ADDR(solo_dev) + off, size);
+ }
+
+ /* Buffer wrap */
+ ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0,
+ sglist, skip, SOLO_MP4E_EXT_ADDR(solo_dev) + off,
+ SOLO_MP4E_EXT_SIZE(solo_dev) - off);
+
+ ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0,
+ sglist, skip + SOLO_MP4E_EXT_SIZE(solo_dev) - off,
+ SOLO_MP4E_EXT_ADDR(solo_dev),
+ size + off - SOLO_MP4E_EXT_SIZE(solo_dev));
+
+ return ret;
+}
+
+static int enc_get_mpeg_dma_t(struct solo6010_dev *solo_dev,
+ dma_addr_t buf, unsigned int off,
+ unsigned int size)
+{
+ int ret;
+
+ if (off > SOLO_MP4E_EXT_SIZE(solo_dev))
+ return -EINVAL;
+
+ if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) {
+ return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf,
+ SOLO_MP4E_EXT_ADDR(solo_dev) + off, size);
+ }
+
+ /* Buffer wrap */
+ ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf,
+ SOLO_MP4E_EXT_ADDR(solo_dev) + off,
+ SOLO_MP4E_EXT_SIZE(solo_dev) - off);
+
+ ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0,
+ buf + SOLO_MP4E_EXT_SIZE(solo_dev) - off,
+ SOLO_MP4E_EXT_ADDR(solo_dev),
+ size + off - SOLO_MP4E_EXT_SIZE(solo_dev));
+
+ return ret;
+}
+
+static int enc_get_mpeg_dma(struct solo6010_dev *solo_dev, void *buf,
+ unsigned int off, unsigned int size)
+{
+ int ret;
+
+ dma_addr_t dma_addr = pci_map_single(solo_dev->pdev, buf, size,
+ PCI_DMA_FROMDEVICE);
+ ret = enc_get_mpeg_dma_t(solo_dev, dma_addr, off, size);
+ pci_unmap_single(solo_dev->pdev, dma_addr, size, PCI_DMA_FROMDEVICE);
+
+ return ret;
+}
+
+static int enc_get_jpeg_dma_sg(struct solo6010_dev *solo_dev,
+ struct p2m_desc *desc,
+ struct scatterlist *sglist, int skip,
+ unsigned int off, unsigned int size)
+{
+ int ret;
+
+ if (off > SOLO_JPEG_EXT_SIZE(solo_dev))
+ return -EINVAL;
+
+ if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) {
+ return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG,
+ desc, 0, sglist, skip,
+ SOLO_JPEG_EXT_ADDR(solo_dev) + off, size);
+ }
+
+ /* Buffer wrap */
+ ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
+ sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off,
+ SOLO_JPEG_EXT_SIZE(solo_dev) - off);
+
+ ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0,
+ sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off,
+ SOLO_JPEG_EXT_ADDR(solo_dev),
+ size + off - SOLO_JPEG_EXT_SIZE(solo_dev));
+
+ return ret;
+}
+
+/* Returns true of __chk is within the first __range bytes of __off */
+#define OFF_IN_RANGE(__off, __range, __chk) \
+ ((__off <= __chk) && ((__off + __range) >= __chk))
+
+static void solo_jpeg_header(struct solo_enc_dev *solo_enc,
+ struct videobuf_dmabuf *vbuf)
+{
+ struct scatterlist *sg;
+ void *src = jpeg_header;
+ size_t copied = 0;
+ size_t to_copy = sizeof(jpeg_header);
+
+ for (sg = vbuf->sglist; sg && copied < to_copy; sg = sg_next(sg)) {
+ size_t this_copy = min(sg_dma_len(sg),
+ (unsigned int)(to_copy - copied));
+ u8 *p = sg_virt(sg);
+
+ memcpy(p, src + copied, this_copy);
+
+ if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 5))
+ p[(SOF0_START + 5) - copied] =
+ 0xff & (solo_enc->height >> 8);
+ if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 6))
+ p[(SOF0_START + 6) - copied] = 0xff & solo_enc->height;
+ if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 7))
+ p[(SOF0_START + 7) - copied] =
+ 0xff & (solo_enc->width >> 8);
+ if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 8))
+ p[(SOF0_START + 8) - copied] = 0xff & solo_enc->width;
+
+ copied += this_copy;
+ }
+}
+
+static int solo_fill_jpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
+ struct videobuf_buffer *vb,
+ struct videobuf_dmabuf *vbuf)
+{
+ struct solo6010_dev *solo_dev = fh->enc->solo_dev;
+ int size = enc_buf->jpeg_size;
+
+ /* Copy the header first (direct write) */
+ solo_jpeg_header(fh->enc, vbuf);
+
+ vb->size = size + sizeof(jpeg_header);
+
+ /* Grab the jpeg frame */
+ return enc_get_jpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist,
+ sizeof(jpeg_header),
+ enc_buf->jpeg_off, size);
+}
+
+static inline int vop_interlaced(__le32 *vh)
+{
+ return (__le32_to_cpu(vh[0]) >> 30) & 1;
+}
+
+static inline u32 vop_size(__le32 *vh)
+{
+ return __le32_to_cpu(vh[0]) & 0xFFFFF;
+}
+
+static inline u8 vop_hsize(__le32 *vh)
+{
+ return (__le32_to_cpu(vh[1]) >> 8) & 0xFF;
+}
+
+static inline u8 vop_vsize(__le32 *vh)
+{
+ return __le32_to_cpu(vh[1]) & 0xFF;
+}
+
+/* must be called with *bits % 8 = 0 */
+static void write_bytes(u8 **out, unsigned *bits, const u8 *src, unsigned count)
+{
+ memcpy(*out, src, count);
+ *out += count;
+ *bits += count * 8;
+}
+
+static void write_bits(u8 **out, unsigned *bits, u32 value, unsigned count)
+{
+
+ value <<= 32 - count; // shift to the right
+
+ while (count--) {
+ **out <<= 1;
+ **out |= !!(value & (1 << 31)); /* MSB */
+ value <<= 1;
+ if (++(*bits) % 8 == 0)
+ (*out)++;
+ }
+}
+
+static void write_ue(u8 **out, unsigned *bits, unsigned value) /* H.264 only */
+{
+ uint32_t max = 0, cnt = 0;
+
+ while (value > max) {
+ max = (max + 2) * 2 - 2;
+ cnt++;
+ }
+ write_bits(out, bits, 1, cnt + 1);
+ write_bits(out, bits, ~(max - value), cnt);
+}
+
+static void write_se(u8 **out, unsigned *bits, int value) /* H.264 only */
+{
+ if (value <= 0)
+ write_ue(out, bits, -value * 2);
+ else
+ write_ue(out, bits, value * 2 - 1);
+}
+
+static void write_mpeg4_end(u8 **out, unsigned *bits)
+{
+ write_bits(out, bits, 0, 1);
+ /* align on 32-bit boundary */
+ if (*bits % 32)
+ write_bits(out, bits, 0xFFFFFFFF, 32 - *bits % 32);
+}
+
+static void write_h264_end(u8 **out, unsigned *bits, int align)
+{
+ write_bits(out, bits, 1, 1);
+ while ((*bits) % 8)
+ write_bits(out, bits, 0, 1);
+ if (align)
+ while ((*bits) % 32)
+ write_bits(out, bits, 0, 1);
+}
+
+static void mpeg4_write_vol(u8 **out, struct solo6010_dev *solo_dev,
+ __le32 *vh, unsigned fps, unsigned interval)
+{
+ static const u8 hdr[] = {
+ 0, 0, 1, 0x00 /* video_object_start_code */,
+ 0, 0, 1, 0x20 /* video_object_layer_start_code */
+ };
+ unsigned bits = 0;
+ unsigned width = vop_hsize(vh) << 4;
+ unsigned height = vop_vsize(vh) << 4;
+ unsigned interlaced = vop_interlaced(vh);
+
+ write_bytes(out, &bits, hdr, sizeof(hdr));
+ write_bits(out, &bits, 0, 1); /* random_accessible_vol */
+ write_bits(out, &bits, 0x04, 8); /* video_object_type_indication: main */
+ write_bits(out, &bits, 1, 1); /* is_object_layer_identifier */
+ write_bits(out, &bits, 2, 4); /* video_object_layer_verid: table V2-39 */
+ write_bits(out, &bits, 0, 3); /* video_object_layer_priority */
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
+ write_bits(out, &bits, 3, 4); /* aspect_ratio_info, assuming 4:3 */
+ else
+ write_bits(out, &bits, 2, 4);
+ write_bits(out, &bits, 1, 1); /* vol_control_parameters */
+ write_bits(out, &bits, 1, 2); /* chroma_format: 4:2:0 */
+ write_bits(out, &bits, 1, 1); /* low_delay */
+ write_bits(out, &bits, 0, 1); /* vbv_parameters */
+ write_bits(out, &bits, 0, 2); /* video_object_layer_shape: rectangular */
+ write_bits(out, &bits, 1, 1); /* marker_bit */
+ write_bits(out, &bits, fps, 16); /* vop_time_increment_resolution */
+ write_bits(out, &bits, 1, 1); /* marker_bit */
+ write_bits(out, &bits, 1, 1); /* fixed_vop_rate */
+ write_bits(out, &bits, interval, 15); /* fixed_vop_time_increment */
+ write_bits(out, &bits, 1, 1); /* marker_bit */
+ write_bits(out, &bits, width, 13); /* video_object_layer_width */
+ write_bits(out, &bits, 1, 1); /* marker_bit */
+ write_bits(out, &bits, height, 13); /* video_object_layer_height */
+ write_bits(out, &bits, 1, 1); /* marker_bit */
+ write_bits(out, &bits, interlaced, 1); /* interlaced */
+ write_bits(out, &bits, 1, 1); /* obmc_disable */
+ write_bits(out, &bits, 0, 2); /* sprite_enable */
+ write_bits(out, &bits, 0, 1); /* not_8_bit */
+ write_bits(out, &bits, 1, 0); /* quant_type */
+ write_bits(out, &bits, 0, 1); /* load_intra_quant_mat */
+ write_bits(out, &bits, 0, 1); /* load_nonintra_quant_mat */
+ write_bits(out, &bits, 0, 1); /* quarter_sample */
+ write_bits(out, &bits, 1, 1); /* complexity_estimation_disable */
+ write_bits(out, &bits, 1, 1); /* resync_marker_disable */
+ write_bits(out, &bits, 0, 1); /* data_partitioned */
+ write_bits(out, &bits, 0, 1); /* newpred_enable */
+ write_bits(out, &bits, 0, 1); /* reduced_resolution_vop_enable */
+ write_bits(out, &bits, 0, 1); /* scalability */
+ write_mpeg4_end(out, &bits);
+}
+
+static void h264_write_vol(u8 **out, struct solo6010_dev *solo_dev, __le32 *vh)
+{
+ static const u8 sps[] = {
+ 0, 0, 0, 1 /* start code */, 0x67, 66 /* profile_idc */,
+ 0 /* constraints */, 30 /* level_idc */
+ };
+ static const u8 pps[] = {
+ 0, 0, 0, 1 /* start code */, 0x68
+ };
+
+ unsigned bits = 0;
+ unsigned mbs_w = vop_hsize(vh);
+ unsigned mbs_h = vop_vsize(vh);
+
+ write_bytes(out, &bits, sps, sizeof(sps));
+ write_ue(out, &bits, 0); /* seq_parameter_set_id */
+ write_ue(out, &bits, 5); /* log2_max_frame_num_minus4 */
+ write_ue(out, &bits, 0); /* pic_order_cnt_type */
+ write_ue(out, &bits, 6); /* log2_max_pic_order_cnt_lsb_minus4 */
+ write_ue(out, &bits, 1); /* max_num_ref_frames */
+ write_bits(out, &bits, 0, 1); /* gaps_in_frame_num_value_allowed_flag */
+ write_ue(out, &bits, mbs_w - 1); /* pic_width_in_mbs_minus1 */
+ write_ue(out, &bits, mbs_h - 1); /* pic_height_in_map_units_minus1 */
+ write_bits(out, &bits, 1, 1); /* frame_mbs_only_flag */
+ write_bits(out, &bits, 1, 1); /* direct_8x8_frame_field_flag */
+ write_bits(out, &bits, 0, 1); /* frame_cropping_flag */
+ write_bits(out, &bits, 0, 1); /* vui_parameters_present_flag */
+ write_h264_end(out, &bits, 0);
+
+ write_bytes(out, &bits, pps, sizeof(pps));
+ write_ue(out, &bits, 0); /* pic_parameter_set_id */
+ write_ue(out, &bits, 0); /* seq_parameter_set_id */
+ write_bits(out, &bits, 0, 1); /* entropy_coding_mode_flag */
+ write_bits(out, &bits, 0, 1); /* bottom_field_pic_order_in_frame_present_flag */
+ write_ue(out, &bits, 0); /* num_slice_groups_minus1 */
+ write_ue(out, &bits, 0); /* num_ref_idx_l0_default_active_minus1 */
+ write_ue(out, &bits, 0); /* num_ref_idx_l1_default_active_minus1 */
+ write_bits(out, &bits, 0, 1); /* weighted_pred_flag */
+ write_bits(out, &bits, 0, 2); /* weighted_bipred_idc */
+ write_se(out, &bits, 0); /* pic_init_qp_minus26 */
+ write_se(out, &bits, 0); /* pic_init_qs_minus26 */
+ write_se(out, &bits, 2); /* chroma_qp_index_offset */
+ write_bits(out, &bits, 0, 1); /* deblocking_filter_control_present_flag */
+ write_bits(out, &bits, 1, 1); /* constrained_intra_pred_flag */
+ write_bits(out, &bits, 0, 1); /* redundant_pic_cnt_present_flag */
+ write_h264_end(out, &bits, 1);
+}
+
+static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf,
+ struct videobuf_buffer *vb,
+ struct videobuf_dmabuf *vbuf)
+{
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+
+#define VH_WORDS 16
+#define MAX_VOL_HEADER_LENGTH 64
+
+ __le32 vh[VH_WORDS];
+ int ret;
+ int frame_size, frame_off;
+ int skip = 0;
+
+ if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh)))
+ return -EINVAL;
+
+ /* First get the hardware vop header (not real mpeg) */
+ ret = enc_get_mpeg_dma(solo_dev, vh, enc_buf->off, sizeof(vh));
+ if (WARN_ON_ONCE(ret))
+ return ret;
+
+ if (WARN_ON_ONCE(vop_size(vh) > enc_buf->size))
+ return -EINVAL;
+
+ vb->width = vop_hsize(vh) << 4;
+ vb->height = vop_vsize(vh) << 4;
+ vb->size = vop_size(vh);
+
+ /* If this is a key frame, add extra m4v header */
+ if (!enc_buf->vop) {
+ u8 header[MAX_VOL_HEADER_LENGTH], *out = header;
+
+ if (solo_dev->flags & FLAGS_6110)
+ h264_write_vol(&out, solo_dev, vh);
+ else
+ mpeg4_write_vol(&out, solo_dev, vh,
+ solo_dev->fps * 1000,
+ solo_enc->interval * 1000);
+ skip = out - header;
+ enc_write_sg(vbuf->sglist, header, skip);
+ /* Adjust the dma buffer past this header */
+ vb->size += skip;
+ }
+
+ /* Now get the actual mpeg payload */
+ frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev);
+ frame_size = enc_buf->size - sizeof(vh);
+
+ ret = enc_get_mpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist,
+ skip, frame_off, frame_size);
+ WARN_ON_ONCE(ret);
+
+ return ret;
+}
+
+static void solo_enc_fillbuf(struct solo_enc_fh *fh,
+ struct videobuf_buffer *vb)
+{
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+ struct solo_enc_buf *enc_buf = NULL;
+ struct videobuf_dmabuf *vbuf;
+ int ret;
+ int error = 1;
+ u16 idx = fh->rd_idx;
+
+ while (idx != solo_dev->enc_wr_idx) {
+ struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx];
+
+ idx = (idx + 1) % SOLO_NR_RING_BUFS;
+
+ if (ebuf->ch != solo_enc->ch)
+ continue;
+
+ if (fh->fmt == V4L2_PIX_FMT_MPEG) {
+ if (fh->type == ebuf->type) {
+ enc_buf = ebuf;
+ break;
+ }
+ } else {
+ /* For mjpeg, keep reading to the newest frame */
+ enc_buf = ebuf;
+ }
+ }
+
+ fh->rd_idx = idx;
+
+ if (WARN_ON_ONCE(!enc_buf))
+ goto buf_err;
+
+ if ((fh->fmt == V4L2_PIX_FMT_MPEG &&
+ vb->bsize < enc_buf->size) ||
+ (fh->fmt == V4L2_PIX_FMT_MJPEG &&
+ vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) {
+ WARN_ON_ONCE(1);
+ goto buf_err;
+ }
+
+ vbuf = videobuf_to_dma(vb);
+ if (WARN_ON_ONCE(!vbuf))
+ goto buf_err;
+
+ if (fh->fmt == V4L2_PIX_FMT_MPEG)
+ ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf);
+ else
+ ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf);
+
+ if (!ret)
+ error = 0;
+
+buf_err:
+ if (error) {
+ vb->state = VIDEOBUF_ERROR;
+ } else {
+ vb->field_count++;
+ vb->ts = enc_buf->ts;
+ vb->state = VIDEOBUF_DONE;
+ }
+
+ wake_up(&vb->done);
+
+ return;
+}
+
+static void solo_enc_thread_try(struct solo_enc_fh *fh)
+{
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+ struct videobuf_buffer *vb;
+
+ for (;;) {
+ spin_lock(&solo_enc->lock);
+
+ if (fh->rd_idx == solo_dev->enc_wr_idx)
+ break;
+
+ if (list_empty(&fh->vidq_active))
+ break;
+
+ vb = list_first_entry(&fh->vidq_active,
+ struct videobuf_buffer, queue);
+
+ if (!waitqueue_active(&vb->done))
+ break;
+
+ list_del(&vb->queue);
+
+ spin_unlock(&solo_enc->lock);
+
+ solo_enc_fillbuf(fh, vb);
+ }
+
+ assert_spin_locked(&solo_enc->lock);
+ spin_unlock(&solo_enc->lock);
+}
+
+static int solo_enc_thread(void *data)
+{
+ struct solo_enc_fh *fh = data;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ DECLARE_WAITQUEUE(wait, current);
+
+ set_freezable();
+ add_wait_queue(&solo_enc->thread_wait, &wait);
+
+ for (;;) {
+ long timeout = schedule_timeout_interruptible(HZ);
+ if (timeout == -ERESTARTSYS || kthread_should_stop())
+ break;
+ solo_enc_thread_try(fh);
+ try_to_freeze();
+ }
+
+ remove_wait_queue(&solo_enc->thread_wait, &wait);
+
+ return 0;
+}
+
+void solo_motion_isr(struct solo6010_dev *solo_dev)
+{
+ u32 status;
+ int i;
+
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_MOTION);
+
+ status = solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS);
+
+ for (i = 0; i < solo_dev->nr_chans; i++) {
+ struct solo_enc_dev *solo_enc = solo_dev->v4l2_enc[i];
+
+ BUG_ON(solo_enc == NULL);
+
+ if (solo_enc->motion_detected)
+ continue;
+ if (!(status & (1 << i)))
+ continue;
+
+ solo_enc->motion_detected = 1;
+ }
+}
+
+void solo_enc_v4l2_isr(struct solo6010_dev *solo_dev)
+{
+ struct solo_enc_buf *enc_buf;
+ u32 mpeg_current, mpeg_next, mpeg_size;
+ u32 jpeg_current, jpeg_next, jpeg_size;
+ u32 reg_mpeg_size;
+ u8 cur_q, vop_type;
+ u8 ch;
+ enum solo_enc_types enc_type;
+
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_ENCODER);
+
+ cur_q = ((solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xF) + 1) % MP4_QS;
+
+ reg_mpeg_size = ((solo_reg_read(solo_dev, SOLO_VE_STATE(0)) & 0xFFFFF) + 64 + 8) & ~7;
+
+ while (solo_dev->enc_idx != cur_q) {
+ mpeg_current = solo_reg_read(solo_dev,
+ SOLO_VE_MPEG4_QUE(solo_dev->enc_idx));
+ jpeg_current = solo_reg_read(solo_dev,
+ SOLO_VE_JPEG_QUE(solo_dev->enc_idx));
+ solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS;
+ mpeg_next = solo_reg_read(solo_dev,
+ SOLO_VE_MPEG4_QUE(solo_dev->enc_idx));
+ jpeg_next = solo_reg_read(solo_dev,
+ SOLO_VE_JPEG_QUE(solo_dev->enc_idx));
+
+ ch = (mpeg_current >> 24) & 0x1f;
+ if (ch >= SOLO_MAX_CHANNELS) {
+ ch -= SOLO_MAX_CHANNELS;
+ enc_type = SOLO_ENC_TYPE_EXT;
+ } else
+ enc_type = SOLO_ENC_TYPE_STD;
+
+ vop_type = (mpeg_current >> 29) & 3;
+
+ mpeg_current &= 0x00ffffff;
+ mpeg_next &= 0x00ffffff;
+ jpeg_current &= 0x00ffffff;
+ jpeg_next &= 0x00ffffff;
+
+ mpeg_size = (SOLO_MP4E_EXT_SIZE(solo_dev) +
+ mpeg_next - mpeg_current) %
+ SOLO_MP4E_EXT_SIZE(solo_dev);
+
+ jpeg_size = (SOLO_JPEG_EXT_SIZE(solo_dev) +
+ jpeg_next - jpeg_current) %
+ SOLO_JPEG_EXT_SIZE(solo_dev);
+
+ /* XXX I think this means we had a ring overflow? */
+ if (mpeg_current > mpeg_next && mpeg_size != reg_mpeg_size) {
+ enc_reset_gop(solo_dev, ch);
+ continue;
+ }
+
+ /* When resetting the GOP, skip frames until I-frame */
+ if (enc_gop_reset(solo_dev, ch, vop_type))
+ continue;
+
+ enc_buf = &solo_dev->enc_buf[solo_dev->enc_wr_idx];
+
+ enc_buf->vop = vop_type;
+ enc_buf->ch = ch;
+ enc_buf->off = mpeg_current;
+ enc_buf->size = mpeg_size;
+ enc_buf->jpeg_off = jpeg_current;
+ enc_buf->jpeg_size = jpeg_size;
+ enc_buf->type = enc_type;
+
+ do_gettimeofday(&enc_buf->ts);
+
+ solo_dev->enc_wr_idx = (solo_dev->enc_wr_idx + 1) %
+ SOLO_NR_RING_BUFS;
+
+ wake_up_interruptible(&solo_dev->v4l2_enc[ch]->thread_wait);
+ }
+
+ return;
+}
+
+static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ *size = FRAME_BUF_SIZE;
+
+ if (*count < MIN_VID_BUFFERS)
+ *count = MIN_VID_BUFFERS;
+
+ return 0;
+}
+
+static int solo_enc_buf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct solo_enc_fh *fh = vq->priv_data;
+ struct solo_enc_dev *solo_enc = fh->enc;
+
+ vb->size = FRAME_BUF_SIZE;
+ if (vb->baddr != 0 && vb->bsize < vb->size)
+ return -EINVAL;
+
+ /* These properties only change when queue is idle */
+ vb->width = solo_enc->width;
+ vb->height = solo_enc->height;
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ int rc = videobuf_iolock(vq, vb, NULL);
+ if (rc < 0) {
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ videobuf_dma_unmap(vq->dev, dma);
+ videobuf_dma_free(dma);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ return rc;
+ }
+ }
+ vb->state = VIDEOBUF_PREPARED;
+
+ return 0;
+}
+
+static void solo_enc_buf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct solo_enc_fh *fh = vq->priv_data;
+
+ vb->state = VIDEOBUF_QUEUED;
+ list_add_tail(&vb->queue, &fh->vidq_active);
+ wake_up_interruptible(&fh->enc->thread_wait);
+}
+
+static void solo_enc_buf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ videobuf_dma_unmap(vq->dev, dma);
+ videobuf_dma_free(dma);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops solo_enc_video_qops = {
+ .buf_setup = solo_enc_buf_setup,
+ .buf_prepare = solo_enc_buf_prepare,
+ .buf_queue = solo_enc_buf_queue,
+ .buf_release = solo_enc_buf_release,
+};
+
+static unsigned int solo_enc_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct solo_enc_fh *fh = file->private_data;
+
+ return videobuf_poll_stream(file, &fh->vidq, wait);
+}
+
+static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct solo_enc_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(&fh->vidq, vma);
+}
+
+static int solo_enc_open(struct file *file)
+{
+ struct solo_enc_dev *solo_enc = video_drvdata(file);
+ struct solo_enc_fh *fh;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (fh == NULL)
+ return -ENOMEM;
+
+ fh->enc = solo_enc;
+ file->private_data = fh;
+ INIT_LIST_HEAD(&fh->vidq_active);
+ fh->fmt = V4L2_PIX_FMT_MPEG;
+ fh->type = SOLO_ENC_TYPE_STD;
+
+ videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops,
+ &solo_enc->solo_dev->pdev->dev,
+ &solo_enc->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct videobuf_buffer), fh, NULL);
+
+ return 0;
+}
+
+static ssize_t solo_enc_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct solo_enc_fh *fh = file->private_data;
+ struct solo_enc_dev *solo_enc = fh->enc;
+
+ /* Make sure the encoder is on */
+ if (!fh->enc_on) {
+ int ret;
+
+ spin_lock(&solo_enc->lock);
+ ret = solo_enc_on(fh);
+ spin_unlock(&solo_enc->lock);
+ if (ret)
+ return ret;
+
+ ret = solo_start_fh_thread(fh);
+ if (ret)
+ return ret;
+ }
+
+ return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int solo_enc_release(struct file *file)
+{
+ struct solo_enc_fh *fh = file->private_data;
+ struct solo_enc_dev *solo_enc = fh->enc;
+
+ videobuf_stop(&fh->vidq);
+ videobuf_mmap_free(&fh->vidq);
+
+ spin_lock(&solo_enc->lock);
+ solo_enc_off(fh);
+ spin_unlock(&solo_enc->lock);
+
+ kfree(fh);
+
+ return 0;
+}
+
+static int solo_enc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+
+ strcpy(cap->driver, SOLO6010_NAME);
+ snprintf(cap->card, sizeof(cap->card), "Softlogic 6010 Enc %d",
+ solo_enc->ch);
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
+ pci_name(solo_dev->pdev));
+ cap->version = SOLO6010_VER_NUM;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int solo_enc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_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;
+
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
+ input->std = V4L2_STD_NTSC_M;
+ else
+ input->std = V4L2_STD_PAL_B;
+
+ 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_MPEG;
+ 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_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->pixelformat != V4L2_PIX_FMT_MPEG &&
+ pix->pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+
+ /* We cannot change width/height in mid read */
+ if (atomic_read(&solo_enc->readers) > 0) {
+ if (pix->width != solo_enc->width ||
+ pix->height != solo_enc->height)
+ return -EBUSY;
+ }
+
+ 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;
+ }
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_INTERLACED;
+ else if (pix->field != V4L2_FIELD_INTERLACED)
+ pix->field = V4L2_FIELD_INTERLACED;
+
+ /* Just set these */
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pix->sizeimage = FRAME_BUF_SIZE;
+
+ return 0;
+}
+
+static int solo_enc_set_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ int ret;
+
+ spin_lock(&solo_enc->lock);
+
+ ret = solo_enc_try_fmt_cap(file, priv, f);
+ if (ret) {
+ spin_unlock(&solo_enc->lock);
+ 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 */
+ fh->fmt = pix->pixelformat;
+
+ if (pix->priv)
+ fh->type = SOLO_ENC_TYPE_EXT;
+ ret = solo_enc_on(fh);
+
+ spin_unlock(&solo_enc->lock);
+
+ if (ret)
+ return ret;
+
+ return solo_start_fh_thread(fh);
+}
+
+static int solo_enc_get_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ pix->width = solo_enc->width;
+ pix->height = solo_enc->height;
+ pix->pixelformat = fh->fmt;
+ pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED :
+ V4L2_FIELD_NONE;
+ pix->sizeimage = FRAME_BUF_SIZE;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ return 0;
+}
+
+static int solo_enc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req)
+{
+ struct solo_enc_fh *fh = priv;
+
+ return videobuf_reqbufs(&fh->vidq, req);
+}
+
+static int solo_enc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct solo_enc_fh *fh = priv;
+
+ return videobuf_querybuf(&fh->vidq, buf);
+}
+
+static int solo_enc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct solo_enc_fh *fh = priv;
+
+ return videobuf_qbuf(&fh->vidq, buf);
+}
+
+static int solo_enc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ int ret;
+
+ /* Make sure the encoder is on */
+ if (!fh->enc_on) {
+ spin_lock(&solo_enc->lock);
+ ret = solo_enc_on(fh);
+ spin_unlock(&solo_enc->lock);
+ if (ret)
+ return ret;
+
+ ret = solo_start_fh_thread(fh);
+ if (ret)
+ return ret;
+ }
+
+ ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
+ if (ret)
+ return ret;
+
+ /* Signal motion detection */
+ if (solo_is_motion_on(solo_enc)) {
+ buf->flags |= V4L2_BUF_FLAG_MOTION_ON;
+ if (solo_enc->motion_detected) {
+ buf->flags |= V4L2_BUF_FLAG_MOTION_DETECTED;
+ solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR,
+ 1 << solo_enc->ch);
+ solo_enc->motion_detected = 0;
+ }
+ }
+
+ /* Check for key frame on mpeg data */
+ if (fh->fmt == V4L2_PIX_FMT_MPEG) {
+ struct videobuf_dmabuf *vbuf =
+ videobuf_to_dma(fh->vidq.bufs[buf->index]);
+
+ if (vbuf) {
+ u8 *p = sg_virt(vbuf->sglist);
+ if (p[3] == 0x00)
+ buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ else
+ buf->flags |= V4L2_BUF_FLAG_PFRAME;
+ }
+ }
+
+ return 0;
+}
+
+static int solo_enc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct solo_enc_fh *fh = priv;
+
+ if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return videobuf_streamon(&fh->vidq);
+}
+
+static int solo_enc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct solo_enc_fh *fh = priv;
+
+ if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return videobuf_streamoff(&fh->vidq);
+}
+
+static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i)
+{
+ return 0;
+}
+
+static int solo_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo6010_dev *solo_dev = fh->enc->solo_dev;
+
+ if (fsize->pixel_format != V4L2_PIX_FMT_MPEG)
+ 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_fh *fh = priv;
+ struct solo6010_dev *solo_dev = fh->enc->solo_dev;
+
+ if (fintv->pixel_format != V4L2_PIX_FMT_MPEG || fintv->index)
+ return -EINVAL;
+
+ fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+
+ fintv->stepwise.min.numerator = solo_dev->fps;
+ fintv->stepwise.min.denominator = 1;
+
+ fintv->stepwise.max.numerator = solo_dev->fps;
+ fintv->stepwise.max.denominator = 15;
+
+ fintv->stepwise.step.numerator = 1;
+ fintv->stepwise.step.denominator = 1;
+
+ return 0;
+}
+
+static int solo_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *sp)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_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_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+ spin_lock(&solo_enc->lock);
+
+ if (atomic_read(&solo_enc->readers) > 0) {
+ spin_unlock(&solo_enc->lock);
+ 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;
+
+ solo_enc->gop = max(solo_dev->fps / solo_enc->interval, 1);
+ solo_update_mode(solo_enc);
+
+ spin_unlock(&solo_enc->lock);
+
+ return 0;
+}
+
+static int solo_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+
+ qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id);
+ if (!qc->id)
+ return -EINVAL;
+
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, 0x00, 0xff, 1, 0x80);
+ case V4L2_CID_SHARPNESS:
+ return v4l2_ctrl_query_fill(qc, 0x00, 0x0f, 1, 0x00);
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return v4l2_ctrl_query_fill(
+ qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ return v4l2_ctrl_query_fill(qc, 1, 255, 1, solo_dev->fps);
+#ifdef PRIVATE_CIDS
+ case V4L2_CID_MOTION_THRESHOLD:
+ qc->flags |= V4L2_CTRL_FLAG_SLIDER;
+ qc->type = V4L2_CTRL_TYPE_INTEGER;
+ qc->minimum = 0;
+ qc->maximum = 0xffff;
+ qc->step = 1;
+ qc->default_value = SOLO_DEF_MOT_THRESH;
+ strlcpy(qc->name, "Motion Detection Threshold",
+ sizeof(qc->name));
+ return 0;
+ case V4L2_CID_MOTION_ENABLE:
+ qc->type = V4L2_CTRL_TYPE_BOOLEAN;
+ qc->minimum = 0;
+ qc->maximum = qc->step = 1;
+ qc->default_value = 0;
+ strlcpy(qc->name, "Motion Detection Enable", sizeof(qc->name));
+ return 0;
+#else
+ case V4L2_CID_MOTION_THRESHOLD:
+ return v4l2_ctrl_query_fill(qc, 0, 0xffff, 1,
+ SOLO_DEF_MOT_THRESH);
+ case V4L2_CID_MOTION_ENABLE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+#endif
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ qc->type = V4L2_CTRL_TYPE_STRING;
+ qc->minimum = 0;
+ qc->maximum = OSD_TEXT_MAX;
+ qc->step = 1;
+ qc->default_value = 0;
+ strlcpy(qc->name, "OSD Text", sizeof(qc->name));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int solo_querymenu(struct file *file, void *priv,
+ struct v4l2_querymenu *qmenu)
+{
+ struct v4l2_queryctrl qctrl;
+ int err;
+
+ qctrl.id = qmenu->id;
+ err = solo_queryctrl(file, priv, &qctrl);
+ if (err)
+ return err;
+
+ return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL);
+}
+
+static int solo_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+
+ 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_get_ctrl_val(solo_dev, ctrl->id, solo_enc->ch,
+ &ctrl->value);
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctrl->value = solo_enc->gop;
+ break;
+ case V4L2_CID_MOTION_THRESHOLD:
+ ctrl->value = solo_enc->motion_thresh;
+ break;
+ case V4L2_CID_MOTION_ENABLE:
+ ctrl->value = solo_is_motion_on(solo_enc);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int solo_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo6010_dev *solo_dev = solo_enc->solo_dev;
+
+ 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->value);
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ if (ctrl->value != V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC)
+ return -ERANGE;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ if (ctrl->value < 1 || ctrl->value > 255)
+ return -ERANGE;
+ solo_enc->gop = ctrl->value;
+ solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch),
+ solo_enc->gop);
+ solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch),
+ solo_enc->gop);
+ break;
+ case V4L2_CID_MOTION_THRESHOLD:
+ /* TODO accept value on lower 16-bits and use high
+ * 16-bits to assign the value to a specific block */
+ if (ctrl->value < 0 || ctrl->value > 0xffff)
+ return -ERANGE;
+ solo_enc->motion_thresh = ctrl->value;
+ solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->value);
+ break;
+ case V4L2_CID_MOTION_ENABLE:
+ solo_motion_toggle(solo_enc, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int solo_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ int i;
+
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = (ctrls->controls + i);
+ int err;
+
+ switch (ctrl->id) {
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ if (ctrl->size - 1 > OSD_TEXT_MAX)
+ err = -ERANGE;
+ else {
+ err = copy_from_user(solo_enc->osd_text,
+ ctrl->string,
+ OSD_TEXT_MAX);
+ solo_enc->osd_text[OSD_TEXT_MAX] = '\0';
+ if (!err)
+ err = solo_osd_print(solo_enc);
+ }
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if (err < 0) {
+ ctrls->error_idx = i;
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int solo_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ int i;
+
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = (ctrls->controls + i);
+ int err;
+
+ switch (ctrl->id) {
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ if (ctrl->size < OSD_TEXT_MAX) {
+ ctrl->size = OSD_TEXT_MAX;
+ err = -ENOSPC;
+ } else {
+ err = copy_to_user(ctrl->string,
+ solo_enc->osd_text,
+ OSD_TEXT_MAX);
+ }
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if (err < 0) {
+ ctrls->error_idx = i;
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static const struct v4l2_file_operations solo_enc_fops = {
+ .owner = THIS_MODULE,
+ .open = solo_enc_open,
+ .release = solo_enc_release,
+ .read = solo_enc_read,
+ .poll = solo_enc_poll,
+ .mmap = solo_enc_mmap,
+ .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,
+ /* 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 = solo_enc_reqbufs,
+ .vidioc_querybuf = solo_enc_querybuf,
+ .vidioc_qbuf = solo_enc_qbuf,
+ .vidioc_dqbuf = solo_enc_dqbuf,
+ .vidioc_streamon = solo_enc_streamon,
+ .vidioc_streamoff = solo_enc_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,
+ /* Controls */
+ .vidioc_queryctrl = solo_queryctrl,
+ .vidioc_querymenu = solo_querymenu,
+ .vidioc_g_ctrl = solo_g_ctrl,
+ .vidioc_s_ctrl = solo_s_ctrl,
+ .vidioc_g_ext_ctrls = solo_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = solo_s_ext_ctrls,
+};
+
+static struct video_device solo_enc_template = {
+ .name = SOLO6010_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_B,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static struct solo_enc_dev *solo_enc_alloc(struct solo6010_dev *solo_dev, u8 ch)
+{
+ struct solo_enc_dev *solo_enc;
+ int ret;
+
+ solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL);
+ if (!solo_enc)
+ return ERR_PTR(-ENOMEM);
+
+ solo_enc->vfd = video_device_alloc();
+ if (!solo_enc->vfd) {
+ kfree(solo_enc);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ solo_enc->solo_dev = solo_dev;
+ solo_enc->ch = ch;
+
+ *solo_enc->vfd = solo_enc_template;
+ solo_enc->vfd->parent = &solo_dev->pdev->dev;
+ ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER,
+ video_nr);
+ if (ret < 0) {
+ video_device_release(solo_enc->vfd);
+ kfree(solo_enc);
+ return ERR_PTR(ret);
+ }
+
+ video_set_drvdata(solo_enc->vfd, solo_enc);
+
+ snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name),
+ "%s-enc (%i/%i)", SOLO6010_NAME, solo_dev->vfd->num,
+ solo_enc->vfd->num);
+
+ if (video_nr != -1)
+ video_nr++;
+
+ spin_lock_init(&solo_enc->lock);
+ init_waitqueue_head(&solo_enc->thread_wait);
+ atomic_set(&solo_enc->readers, 0);
+
+ 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_thresh = SOLO_DEF_MOT_THRESH;
+
+ spin_lock(&solo_enc->lock);
+ solo_update_mode(solo_enc);
+ spin_unlock(&solo_enc->lock);
+
+ return solo_enc;
+}
+
+static void solo_enc_free(struct solo_enc_dev *solo_enc)
+{
+ if (solo_enc == NULL)
+ return;
+
+ video_unregister_device(solo_enc->vfd);
+ kfree(solo_enc);
+}
+
+int solo_enc_v4l2_init(struct solo6010_dev *solo_dev)
+{
+ int i;
+
+ for (i = 0; i < solo_dev->nr_chans; i++) {
+ solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i);
+ 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]);
+ return ret;
+ }
+
+ /* D1@MAX-FPS * 4 */
+ solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4;
+
+ 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 solo6010_dev *solo_dev)
+{
+ int i;
+
+ solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION);
+
+ for (i = 0; i < solo_dev->nr_chans; i++)
+ solo_enc_free(solo_dev->v4l2_enc[i]);
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
+ * Copyright (C) 2010 Ben Collins <bcollins@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/videobuf-dma-sg.h>
+#include "solo6x10.h"
+#include "tw28.h"
+
+#define SOLO_HW_BPL 2048
+#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED
+
+/* Image size is two fields, SOLO_HW_BPL is one horizontal line */
+#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 4
+
+/* Simple file handle */
+struct solo_filehandle {
+ struct solo6010_dev *solo_dev;
+ struct videobuf_queue vidq;
+ struct task_struct *kthread;
+ spinlock_t slock;
+ int old_write;
+ struct list_head vidq_active;
+ struct p2m_desc desc[SOLO_NR_P2M_DESC];
+ int desc_idx;
+};
+
+unsigned video_nr = -1;
+module_param(video_nr, uint, 0644);
+MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)");
+
+static void erase_on(struct solo6010_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 int erase_off(struct solo6010_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 solo6010_dev *solo_dev)
+{
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_VIDEO_IN);
+ wake_up_interruptible(&solo_dev->disp_thread_wait);
+}
+
+static void solo_win_setup(struct solo6010_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 solo6010_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 solo6010_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 solo6010_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 solo6010_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 disp_reset_desc(struct solo_filehandle *fh)
+{
+ /* We use desc mode, which ignores desc 0 */
+ memset(fh->desc, 0, sizeof(*fh->desc));
+ fh->desc_idx = 1;
+}
+
+static int disp_flush_descs(struct solo_filehandle *fh)
+{
+ int ret;
+
+ if (!fh->desc_idx)
+ return 0;
+
+ ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP,
+ fh->desc, fh->desc_idx);
+ disp_reset_desc(fh);
+
+ return ret;
+}
+
+static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr,
+ u32 ext_addr, int size, int repeat, int ext_size)
+{
+ if (fh->desc_idx >= SOLO_NR_P2M_DESC) {
+ int ret = disp_flush_descs(fh);
+ if (ret)
+ return ret;
+ }
+
+ solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr,
+ size, repeat, ext_size);
+ fh->desc_idx++;
+
+ return 0;
+}
+
+static void solo_fillbuf(struct solo_filehandle *fh,
+ struct videobuf_buffer *vb)
+{
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+ struct videobuf_dmabuf *vbuf;
+ unsigned int fdma_addr;
+ int error = 1;
+ int i;
+ struct scatterlist *sg;
+ dma_addr_t sg_dma;
+ int sg_size_left;
+
+ vbuf = videobuf_to_dma(vb);
+ if (!vbuf)
+ goto finish_buf;
+
+ if (erase_off(solo_dev)) {
+ int i;
+
+ /* Just blit to the entire sg list, ignoring size */
+ for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) {
+ void *p = sg_virt(sg);
+ size_t len = sg_dma_len(sg);
+
+ for (i = 0; i < len; i += 2) {
+ ((u8 *)p)[i] = 0x80;
+ ((u8 *)p)[i + 1] = 0x00;
+ }
+ }
+
+ error = 0;
+ goto finish_buf;
+ }
+
+ disp_reset_desc(fh);
+ sg = vbuf->sglist;
+ sg_dma = sg_dma_address(sg);
+ sg_size_left = sg_dma_len(sg);
+
+ fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write *
+ (SOLO_HW_BPL * solo_vlines(solo_dev)));
+
+ for (i = 0; i < solo_vlines(solo_dev); i++) {
+ int line_len = solo_bytesperline(solo_dev);
+ int lines;
+
+ if (!sg_size_left) {
+ sg = sg_next(sg);
+ if (sg == NULL)
+ goto finish_buf;
+ sg_dma = sg_dma_address(sg);
+ sg_size_left = sg_dma_len(sg);
+ }
+
+ /* No room for an entire line, so chunk it up */
+ if (sg_size_left < line_len) {
+ int this_addr = fdma_addr;
+
+ while (line_len > 0) {
+ int this_write;
+
+ if (!sg_size_left) {
+ sg = sg_next(sg);
+ if (sg == NULL)
+ goto finish_buf;
+ sg_dma = sg_dma_address(sg);
+ sg_size_left = sg_dma_len(sg);
+ }
+
+ this_write = min(sg_size_left, line_len);
+
+ if (disp_push_desc(fh, sg_dma, this_addr,
+ this_write, 0, 0))
+ goto finish_buf;
+
+ line_len -= this_write;
+ sg_size_left -= this_write;
+ sg_dma += this_write;
+ this_addr += this_write;
+ }
+
+ fdma_addr += SOLO_HW_BPL;
+ continue;
+ }
+
+ /* Shove as many lines into a repeating descriptor as possible */
+ lines = min(sg_size_left / line_len,
+ solo_vlines(solo_dev) - i);
+
+ if (disp_push_desc(fh, sg_dma, fdma_addr, line_len,
+ lines - 1, SOLO_HW_BPL))
+ goto finish_buf;
+
+ i += lines - 1;
+ fdma_addr += SOLO_HW_BPL * lines;
+ sg_dma += lines * line_len;
+ sg_size_left -= lines * line_len;
+ }
+
+ error = disp_flush_descs(fh);
+
+finish_buf:
+ if (error) {
+ vb->state = VIDEOBUF_ERROR;
+ } else {
+ vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev);
+ vb->state = VIDEOBUF_DONE;
+ vb->field_count++;
+ do_gettimeofday(&vb->ts);
+ }
+
+ wake_up(&vb->done);
+
+ return;
+}
+
+static void solo_thread_try(struct solo_filehandle *fh)
+{
+ struct videobuf_buffer *vb;
+ unsigned int cur_write;
+
+ for (;;) {
+ spin_lock(&fh->slock);
+
+ if (list_empty(&fh->vidq_active))
+ break;
+
+ vb = list_first_entry(&fh->vidq_active, struct videobuf_buffer,
+ queue);
+
+ if (!waitqueue_active(&vb->done))
+ break;
+
+ cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev,
+ SOLO_VI_STATUS0));
+ if (cur_write == fh->old_write)
+ break;
+
+ fh->old_write = cur_write;
+ list_del(&vb->queue);
+
+ spin_unlock(&fh->slock);
+
+ solo_fillbuf(fh, vb);
+ }
+
+ assert_spin_locked(&fh->slock);
+ spin_unlock(&fh->slock);
+}
+
+static int solo_thread(void *data)
+{
+ struct solo_filehandle *fh = data;
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+ 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(fh);
+ try_to_freeze();
+ }
+
+ remove_wait_queue(&solo_dev->disp_thread_wait, &wait);
+
+ return 0;
+}
+
+static int solo_start_thread(struct solo_filehandle *fh)
+{
+ fh->kthread = kthread_run(solo_thread, fh, SOLO6010_NAME "_disp");
+
+ if (IS_ERR(fh->kthread))
+ return PTR_ERR(fh->kthread);
+
+ return 0;
+}
+
+static void solo_stop_thread(struct solo_filehandle *fh)
+{
+ if (fh->kthread) {
+ kthread_stop(fh->kthread);
+ fh->kthread = NULL;
+ }
+}
+
+static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct solo_filehandle *fh = vq->priv_data;
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+
+ *size = solo_image_size(solo_dev);
+
+ if (*count < MIN_VID_BUFFERS)
+ *count = MIN_VID_BUFFERS;
+
+ return 0;
+}
+
+static int solo_buf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct solo_filehandle *fh = vq->priv_data;
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+
+ vb->size = solo_image_size(solo_dev);
+ if (vb->baddr != 0 && vb->bsize < vb->size)
+ return -EINVAL;
+
+ /* XXX: These properties only change when queue is idle */
+ vb->width = solo_dev->video_hsize;
+ vb->height = solo_vlines(solo_dev);
+ vb->bytesperline = solo_bytesperline(solo_dev);
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ int rc = videobuf_iolock(vq, vb, NULL);
+ if (rc < 0) {
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ videobuf_dma_unmap(vq->dev, dma);
+ videobuf_dma_free(dma);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ return rc;
+ }
+ }
+ vb->state = VIDEOBUF_PREPARED;
+
+ return 0;
+}
+
+static void solo_buf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct solo_filehandle *fh = vq->priv_data;
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+
+ vb->state = VIDEOBUF_QUEUED;
+ list_add_tail(&vb->queue, &fh->vidq_active);
+ wake_up_interruptible(&solo_dev->disp_thread_wait);
+}
+
+static void solo_buf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ videobuf_dma_unmap(vq->dev, dma);
+ videobuf_dma_free(dma);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops solo_video_qops = {
+ .buf_setup = solo_buf_setup,
+ .buf_prepare = solo_buf_prepare,
+ .buf_queue = solo_buf_queue,
+ .buf_release = solo_buf_release,
+};
+
+static unsigned int solo_v4l2_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct solo_filehandle *fh = file->private_data;
+
+ return videobuf_poll_stream(file, &fh->vidq, wait);
+}
+
+static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct solo_filehandle *fh = file->private_data;
+
+ return videobuf_mmap_mapper(&fh->vidq, vma);
+}
+
+static int solo_v4l2_open(struct file *file)
+{
+ struct solo6010_dev *solo_dev = video_drvdata(file);
+ struct solo_filehandle *fh;
+ int ret;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (fh == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&fh->slock);
+ INIT_LIST_HEAD(&fh->vidq_active);
+ fh->solo_dev = solo_dev;
+ file->private_data = fh;
+
+ ret = solo_start_thread(fh);
+ if (ret) {
+ kfree(fh);
+ return ret;
+ }
+
+ videobuf_queue_sg_init(&fh->vidq, &solo_video_qops,
+ &solo_dev->pdev->dev, &fh->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ SOLO_DISP_PIX_FIELD,
+ sizeof(struct videobuf_buffer), fh, NULL);
+
+ return 0;
+}
+
+static ssize_t solo_v4l2_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct solo_filehandle *fh = file->private_data;
+
+ return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int solo_v4l2_release(struct file *file)
+{
+ struct solo_filehandle *fh = file->private_data;
+
+ videobuf_stop(&fh->vidq);
+ videobuf_mmap_free(&fh->vidq);
+ solo_stop_thread(fh);
+ kfree(fh);
+
+ return 0;
+}
+
+static int solo_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct solo_filehandle *fh = priv;
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+
+ strcpy(cap->driver, SOLO6010_NAME);
+ strcpy(cap->card, "Softlogic 6010");
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
+ pci_name(solo_dev->pdev));
+ cap->version = SOLO6010_VER_NUM;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int solo_enum_ext_input(struct solo6010_dev *solo_dev,
+ struct v4l2_input *input)
+{
+ static const char *dispnames_1[] = { "4UP" };
+ static const char *dispnames_2[] = { "4UP-1", "4UP-2" };
+ static const char *dispnames_5[] = {
+ "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP"
+ };
+ const char **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_filehandle *fh = priv;
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+
+ 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;
+
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
+ input->std = V4L2_STD_NTSC_M;
+ else
+ input->std = V4L2_STD_PAL_B;
+
+ return 0;
+}
+
+static int solo_set_input(struct file *file, void *priv, unsigned int index)
+{
+ struct solo_filehandle *fh = priv;
+
+ return solo_v4l2_set_ch(fh->solo_dev, index);
+}
+
+static int solo_get_input(struct file *file, void *priv, unsigned int *index)
+{
+ struct solo_filehandle *fh = priv;
+
+ *index = fh->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_filehandle *fh = priv;
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ int image_size = solo_image_size(solo_dev);
+
+ /* Check supported sizes */
+ if (pix->width != solo_dev->video_hsize)
+ pix->width = solo_dev->video_hsize;
+ if (pix->height != solo_vlines(solo_dev))
+ pix->height = solo_vlines(solo_dev);
+ if (pix->sizeimage != image_size)
+ pix->sizeimage = image_size;
+
+ /* Check formats */
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = SOLO_DISP_PIX_FIELD;
+
+ if (pix->pixelformat != V4L2_PIX_FMT_UYVY ||
+ pix->field != SOLO_DISP_PIX_FIELD ||
+ pix->colorspace != V4L2_COLORSPACE_SMPTE170M)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int solo_set_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct solo_filehandle *fh = priv;
+
+ if (videobuf_queue_is_busy(&fh->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_filehandle *fh = priv;
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+ 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 = SOLO_DISP_PIX_FIELD;
+ pix->sizeimage = solo_image_size(solo_dev);
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pix->bytesperline = solo_bytesperline(solo_dev);
+
+ return 0;
+}
+
+static int solo_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req)
+{
+ struct solo_filehandle *fh = priv;
+
+ return videobuf_reqbufs(&fh->vidq, req);
+}
+
+static int solo_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct solo_filehandle *fh = priv;
+
+ return videobuf_querybuf(&fh->vidq, buf);
+}
+
+static int solo_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct solo_filehandle *fh = priv;
+
+ return videobuf_qbuf(&fh->vidq, buf);
+}
+
+static int solo_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct solo_filehandle *fh = priv;
+
+ return videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
+}
+
+static int solo_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct solo_filehandle *fh = priv;
+
+ if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return videobuf_streamon(&fh->vidq);
+}
+
+static int solo_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct solo_filehandle *fh = priv;
+
+ if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return videobuf_streamoff(&fh->vidq);
+}
+
+static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i)
+{
+ return 0;
+}
+
+static const u32 solo_motion_ctrls[] = {
+ V4L2_CID_MOTION_TRACE,
+ 0
+};
+
+static const u32 *solo_ctrl_classes[] = {
+ solo_motion_ctrls,
+ NULL
+};
+
+static int solo_disp_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id);
+ if (!qc->id)
+ return -EINVAL;
+
+ switch (qc->id) {
+#ifdef PRIVATE_CIDS
+ case V4L2_CID_MOTION_TRACE:
+ qc->type = V4L2_CTRL_TYPE_BOOLEAN;
+ qc->minimum = 0;
+ qc->maximum = qc->step = 1;
+ qc->default_value = 0;
+ strlcpy(qc->name, "Motion Detection Trace", sizeof(qc->name));
+ return 0;
+#else
+ case V4L2_CID_MOTION_TRACE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+#endif
+ }
+ return -EINVAL;
+}
+
+static int solo_disp_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct solo_filehandle *fh = priv;
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MOTION_TRACE:
+ ctrl->value = solo_reg_read(solo_dev, SOLO_VI_MOTION_BAR)
+ ? 1 : 0;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int solo_disp_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct solo_filehandle *fh = priv;
+ struct solo6010_dev *solo_dev = fh->solo_dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MOTION_TRACE:
+ if (ctrl->value) {
+ 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;
+ }
+ return -EINVAL;
+}
+
+static const struct v4l2_file_operations solo_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .open = solo_v4l2_open,
+ .release = solo_v4l2_release,
+ .read = solo_v4l2_read,
+ .poll = solo_v4l2_poll,
+ .mmap = solo_v4l2_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
+ .vidioc_querycap = solo_querycap,
+ .vidioc_s_std = solo_s_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 = solo_reqbufs,
+ .vidioc_querybuf = solo_querybuf,
+ .vidioc_qbuf = solo_qbuf,
+ .vidioc_dqbuf = solo_dqbuf,
+ .vidioc_streamon = solo_streamon,
+ .vidioc_streamoff = solo_streamoff,
+ /* Controls */
+ .vidioc_queryctrl = solo_disp_queryctrl,
+ .vidioc_g_ctrl = solo_disp_g_ctrl,
+ .vidioc_s_ctrl = solo_disp_s_ctrl,
+};
+
+static struct video_device solo_v4l2_template = {
+ .name = SOLO6010_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_B,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+int solo_v4l2_init(struct solo6010_dev *solo_dev)
+{
+ int ret;
+ int i;
+
+ init_waitqueue_head(&solo_dev->disp_thread_wait);
+
+ solo_dev->vfd = video_device_alloc();
+ if (!solo_dev->vfd)
+ return -ENOMEM;
+
+ *solo_dev->vfd = solo_v4l2_template;
+ solo_dev->vfd->parent = &solo_dev->pdev->dev;
+
+ ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr);
+ if (ret < 0) {
+ video_device_release(solo_dev->vfd);
+ solo_dev->vfd = NULL;
+ return ret;
+ }
+
+ video_set_drvdata(solo_dev->vfd, solo_dev);
+
+ snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)",
+ SOLO6010_NAME, solo_dev->vfd->num);
+
+ if (video_nr != -1)
+ video_nr++;
+
+ 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);
+
+ /* 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 */
+
+ solo6010_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
+
+ return 0;
+}
+
+void solo_v4l2_exit(struct solo6010_dev *solo_dev)
+{
+ solo6010_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN);
+ if (solo_dev->vfd) {
+ video_unregister_device(solo_dev->vfd);
+ solo_dev->vfd = NULL;
+ }
+}