ccflags-y += -Idrivers/media/common/tuners/
# For the staging CI driver cxd2099
-ccflags-y += -Idrivers/staging/cxd2099/
+ccflags-y += -Idrivers/staging/media/cxd2099/
ccflags-y += -Idrivers/media/common/tuners/
# For the staging CI driver cxd2099
-ccflags-y += -Idrivers/staging/cxd2099/
+ccflags-y += -Idrivers/staging/media/cxd2099/
source "drivers/staging/slicoss/Kconfig"
-source "drivers/staging/go7007/Kconfig"
-
-source "drivers/staging/cxd2099/Kconfig"
-
source "drivers/staging/usbip/Kconfig"
source "drivers/staging/winbond/Kconfig"
source "drivers/staging/sm7xx/Kconfig"
-source "drivers/staging/dt3155v4l/Kconfig"
-
source "drivers/staging/crystalhd/Kconfig"
source "drivers/staging/cxt1e1/Kconfig"
source "drivers/staging/xgifb/Kconfig"
-source "drivers/staging/lirc/Kconfig"
-
-source "drivers/staging/easycap/Kconfig"
-
-source "drivers/staging/solo6x10/Kconfig"
-
source "drivers/staging/tidspbridge/Kconfig"
source "drivers/staging/quickstart/Kconfig"
source "drivers/staging/nvec/Kconfig"
-source "drivers/staging/media/as102/Kconfig"
+source "drivers/staging/media/Kconfig"
endif # STAGING
obj-$(CONFIG_STAGING) += staging.o
obj-y += serial/
+obj-y += media/
obj-$(CONFIG_ET131X) += et131x/
obj-$(CONFIG_SLICOSS) += slicoss/
-obj-$(CONFIG_VIDEO_GO7007) += go7007/
-obj-$(CONFIG_DVB_CXD2099) += cxd2099/
-obj-$(CONFIG_LIRC_STAGING) += lirc/
obj-$(CONFIG_USBIP_CORE) += usbip/
obj-$(CONFIG_W35UND) += winbond/
obj-$(CONFIG_PRISM2_USB) += wlan-ng/
obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
obj-$(CONFIG_FB_SM7XX) += sm7xx/
-obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/
obj-$(CONFIG_CRYSTALHD) += crystalhd/
obj-$(CONFIG_CXT1E1) += cxt1e1/
obj-$(CONFIG_FB_XGI) += xgifb/
-obj-$(CONFIG_EASYCAP) += easycap/
-obj-$(CONFIG_SOLO6X10) += solo6x10/
obj-$(CONFIG_TIDSPBRIDGE) += tidspbridge/
obj-$(CONFIG_ACPI_QUICKSTART) += quickstart/
obj-$(CONFIG_SBE_2T3E3) += sbe-2t3e3/
obj-$(CONFIG_DRM_PSB) += gma500/
obj-$(CONFIG_INTEL_MEI) += mei/
obj-$(CONFIG_MFD_NVEC) += nvec/
-obj-$(CONFIG_DVB_AS102) += media/as102/
+++ /dev/null
-config DVB_CXD2099
- tristate "CXD2099AR Common Interface driver"
- depends on DVB_CORE && PCI && I2C
- ---help---
- Support for the CI module found on cards based on
- - Micronas ngene PCIe bridge: cineS2 etc.
- - Digital Devices PCIe bridge: Octopus series
-
- For now, data is passed through '/dev/dvb/adapterX/sec0':
- - Encrypted data must be written to 'sec0'.
- - Decrypted data can be read from 'sec0'.
- - Setup the CAM using device 'ca0'.
+++ /dev/null
-obj-$(CONFIG_DVB_CXD2099) += cxd2099.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core/
-ccflags-y += -Idrivers/media/dvb/frontends/
-ccflags-y += -Idrivers/media/common/tuners/
+++ /dev/null
-For now, data is passed through '/dev/dvb/adapterX/sec0':
- - Encrypted data must be written to 'sec0'.
- - Decrypted data can be read from 'sec0'.
- - Setup the CAM using device 'ca0'.
-
-But this is wrong. There are some discussions about the proper way for
-doing it, as seen at:
- http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html
-
-While there's no proper fix for it, the driver should be kept in staging.
-
-Patches should be submitted to: linux-media@vger.kernel.org.
+++ /dev/null
-/*
- * cxd2099.c: Driver for the CXD2099AR Common Interface Controller
- *
- * Copyright (C) 2010-2011 Digital Devices GmbH
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/wait.h>
-#include <linux/delay.h>
-#include <linux/mutex.h>
-#include <linux/io.h>
-
-#include "cxd2099.h"
-
-#define MAX_BUFFER_SIZE 248
-
-struct cxd {
- struct dvb_ca_en50221 en;
-
- struct i2c_adapter *i2c;
- struct cxd2099_cfg cfg;
-
- u8 regs[0x23];
- u8 lastaddress;
- u8 clk_reg_f;
- u8 clk_reg_b;
- int mode;
- int ready;
- int dr;
- int slot_stat;
-
- u8 amem[1024];
- int amem_read;
-
- int cammode;
- struct mutex lock;
-};
-
-static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr,
- u8 reg, u8 data)
-{
- u8 m[2] = {reg, data};
- struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2};
-
- if (i2c_transfer(adapter, &msg, 1) != 1) {
- printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n",
- reg, adr);
- return -1;
- }
- return 0;
-}
-
-static int i2c_write(struct i2c_adapter *adapter, u8 adr,
- u8 *data, u8 len)
-{
- struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len};
-
- if (i2c_transfer(adapter, &msg, 1) != 1) {
- printk(KERN_ERR "Failed to write to I2C!\n");
- return -1;
- }
- return 0;
-}
-
-static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr,
- u8 reg, u8 *val)
-{
- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
- .buf = ®, .len = 1},
- {.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1} };
-
- if (i2c_transfer(adapter, msgs, 2) != 2) {
- printk(KERN_ERR "error in i2c_read_reg\n");
- return -1;
- }
- return 0;
-}
-
-static int i2c_read(struct i2c_adapter *adapter, u8 adr,
- u8 reg, u8 *data, u8 n)
-{
- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
- .buf = ®, .len = 1},
- {.addr = adr, .flags = I2C_M_RD,
- .buf = data, .len = n} };
-
- if (i2c_transfer(adapter, msgs, 2) != 2) {
- printk(KERN_ERR "error in i2c_read\n");
- return -1;
- }
- return 0;
-}
-
-static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n)
-{
- int status;
-
- status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
- if (!status) {
- ci->lastaddress = adr;
- status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, n);
- }
- return status;
-}
-
-static int read_reg(struct cxd *ci, u8 reg, u8 *val)
-{
- return read_block(ci, reg, val, 1);
-}
-
-
-static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
-{
- int status;
- u8 addr[3] = {2, address & 0xff, address >> 8};
-
- status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
- if (!status)
- status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n);
- return status;
-}
-
-static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
-{
- int status;
- u8 addr[3] = {2, address & 0xff, address >> 8};
-
- status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
- if (!status) {
- u8 buf[256] = {3};
- memcpy(buf+1, data, n);
- status = i2c_write(ci->i2c, ci->cfg.adr, buf, n+1);
- }
- return status;
-}
-
-static int read_io(struct cxd *ci, u16 address, u8 *val)
-{
- int status;
- u8 addr[3] = {2, address & 0xff, address >> 8};
-
- status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
- if (!status)
- status = i2c_read(ci->i2c, ci->cfg.adr, 3, val, 1);
- return status;
-}
-
-static int write_io(struct cxd *ci, u16 address, u8 val)
-{
- int status;
- u8 addr[3] = {2, address & 0xff, address >> 8};
- u8 buf[2] = {3, val};
-
- status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
- if (!status)
- status = i2c_write(ci->i2c, ci->cfg.adr, buf, 2);
- return status;
-}
-
-#if 0
-static int read_io_data(struct cxd *ci, u8 *data, u8 n)
-{
- int status;
- u8 addr[3] = { 2, 0, 0 };
-
- status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
- if (!status)
- status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n);
- return 0;
-}
-
-static int write_io_data(struct cxd *ci, u8 *data, u8 n)
-{
- int status;
- u8 addr[3] = {2, 0, 0};
-
- status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
- if (!status) {
- u8 buf[256] = {3};
- memcpy(buf+1, data, n);
- status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
- }
- return 0;
-}
-#endif
-
-static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask)
-{
- int status;
-
- status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg);
- if (!status && reg >= 6 && reg <= 8 && mask != 0xff)
- status = i2c_read_reg(ci->i2c, ci->cfg.adr, 1, &ci->regs[reg]);
- ci->regs[reg] = (ci->regs[reg] & (~mask)) | val;
- if (!status) {
- ci->lastaddress = reg;
- status = i2c_write_reg(ci->i2c, ci->cfg.adr, 1, ci->regs[reg]);
- }
- if (reg == 0x20)
- ci->regs[reg] &= 0x7f;
- return status;
-}
-
-static int write_reg(struct cxd *ci, u8 reg, u8 val)
-{
- return write_regm(ci, reg, val, 0xff);
-}
-
-#ifdef BUFFER_MODE
-static int write_block(struct cxd *ci, u8 adr, u8 *data, int n)
-{
- int status;
- u8 buf[256] = {1};
-
- status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
- if (!status) {
- ci->lastaddress = adr;
- memcpy(buf + 1, data, n);
- status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
- }
- return status;
-}
-#endif
-
-static void set_mode(struct cxd *ci, int mode)
-{
- if (mode == ci->mode)
- return;
-
- switch (mode) {
- case 0x00: /* IO mem */
- write_regm(ci, 0x06, 0x00, 0x07);
- break;
- case 0x01: /* ATT mem */
- write_regm(ci, 0x06, 0x02, 0x07);
- break;
- default:
- break;
- }
- ci->mode = mode;
-}
-
-static void cam_mode(struct cxd *ci, int mode)
-{
- if (mode == ci->cammode)
- return;
-
- switch (mode) {
- case 0x00:
- write_regm(ci, 0x20, 0x80, 0x80);
- break;
- case 0x01:
-#ifdef BUFFER_MODE
- if (!ci->en.read_data)
- return;
- printk(KERN_INFO "enable cam buffer mode\n");
- /* write_reg(ci, 0x0d, 0x00); */
- /* write_reg(ci, 0x0e, 0x01); */
- write_regm(ci, 0x08, 0x40, 0x40);
- /* read_reg(ci, 0x12, &dummy); */
- write_regm(ci, 0x08, 0x80, 0x80);
-#endif
- break;
- default:
- break;
- }
- ci->cammode = mode;
-}
-
-
-
-static int init(struct cxd *ci)
-{
- int status;
-
- mutex_lock(&ci->lock);
- ci->mode = -1;
- do {
- status = write_reg(ci, 0x00, 0x00);
- if (status < 0)
- break;
- status = write_reg(ci, 0x01, 0x00);
- if (status < 0)
- break;
- status = write_reg(ci, 0x02, 0x10);
- if (status < 0)
- break;
- status = write_reg(ci, 0x03, 0x00);
- if (status < 0)
- break;
- status = write_reg(ci, 0x05, 0xFF);
- if (status < 0)
- break;
- status = write_reg(ci, 0x06, 0x1F);
- if (status < 0)
- break;
- status = write_reg(ci, 0x07, 0x1F);
- if (status < 0)
- break;
- status = write_reg(ci, 0x08, 0x28);
- if (status < 0)
- break;
- status = write_reg(ci, 0x14, 0x20);
- if (status < 0)
- break;
-
-#if 0
- status = write_reg(ci, 0x09, 0x4D); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */
- if (status < 0)
- break;
-#endif
- status = write_reg(ci, 0x0A, 0xA7); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */
- if (status < 0)
- break;
-
- status = write_reg(ci, 0x0B, 0x33);
- if (status < 0)
- break;
- status = write_reg(ci, 0x0C, 0x33);
- if (status < 0)
- break;
-
- status = write_regm(ci, 0x14, 0x00, 0x0F);
- if (status < 0)
- break;
- status = write_reg(ci, 0x15, ci->clk_reg_b);
- if (status < 0)
- break;
- status = write_regm(ci, 0x16, 0x00, 0x0F);
- if (status < 0)
- break;
- status = write_reg(ci, 0x17, ci->clk_reg_f);
- if (status < 0)
- break;
-
- if (ci->cfg.clock_mode) {
- if (ci->cfg.polarity) {
- status = write_reg(ci, 0x09, 0x6f);
- if (status < 0)
- break;
- } else {
- status = write_reg(ci, 0x09, 0x6d);
- if (status < 0)
- break;
- }
- status = write_reg(ci, 0x20, 0x68);
- if (status < 0)
- break;
- status = write_reg(ci, 0x21, 0x00);
- if (status < 0)
- break;
- status = write_reg(ci, 0x22, 0x02);
- if (status < 0)
- break;
- } else {
- if (ci->cfg.polarity) {
- status = write_reg(ci, 0x09, 0x4f);
- if (status < 0)
- break;
- } else {
- status = write_reg(ci, 0x09, 0x4d);
- if (status < 0)
- break;
- }
-
- status = write_reg(ci, 0x20, 0x28);
- if (status < 0)
- break;
- status = write_reg(ci, 0x21, 0x00);
- if (status < 0)
- break;
- status = write_reg(ci, 0x22, 0x07);
- if (status < 0)
- break;
- }
-
- status = write_regm(ci, 0x20, 0x80, 0x80);
- if (status < 0)
- break;
- status = write_regm(ci, 0x03, 0x02, 0x02);
- if (status < 0)
- break;
- status = write_reg(ci, 0x01, 0x04);
- if (status < 0)
- break;
- status = write_reg(ci, 0x00, 0x31);
- if (status < 0)
- break;
-
- /* Put TS in bypass */
- status = write_regm(ci, 0x09, 0x08, 0x08);
- if (status < 0)
- break;
- ci->cammode = -1;
- cam_mode(ci, 0);
- } while (0);
- mutex_unlock(&ci->lock);
-
- return 0;
-}
-
-static int read_attribute_mem(struct dvb_ca_en50221 *ca,
- int slot, int address)
-{
- struct cxd *ci = ca->data;
-#if 0
- if (ci->amem_read) {
- if (address <= 0 || address > 1024)
- return -EIO;
- return ci->amem[address];
- }
-
- mutex_lock(&ci->lock);
- write_regm(ci, 0x06, 0x00, 0x05);
- read_pccard(ci, 0, &ci->amem[0], 128);
- read_pccard(ci, 128, &ci->amem[0], 128);
- read_pccard(ci, 256, &ci->amem[0], 128);
- read_pccard(ci, 384, &ci->amem[0], 128);
- write_regm(ci, 0x06, 0x05, 0x05);
- mutex_unlock(&ci->lock);
- return ci->amem[address];
-#else
- u8 val;
- mutex_lock(&ci->lock);
- set_mode(ci, 1);
- read_pccard(ci, address, &val, 1);
- mutex_unlock(&ci->lock);
- /* printk(KERN_INFO "%02x:%02x\n", address,val); */
- return val;
-#endif
-}
-
-static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot,
- int address, u8 value)
-{
- struct cxd *ci = ca->data;
-
- mutex_lock(&ci->lock);
- set_mode(ci, 1);
- write_pccard(ci, address, &value, 1);
- mutex_unlock(&ci->lock);
- return 0;
-}
-
-static int read_cam_control(struct dvb_ca_en50221 *ca,
- int slot, u8 address)
-{
- struct cxd *ci = ca->data;
- u8 val;
-
- mutex_lock(&ci->lock);
- set_mode(ci, 0);
- read_io(ci, address, &val);
- mutex_unlock(&ci->lock);
- return val;
-}
-
-static int write_cam_control(struct dvb_ca_en50221 *ca, int slot,
- u8 address, u8 value)
-{
- struct cxd *ci = ca->data;
-
- mutex_lock(&ci->lock);
- set_mode(ci, 0);
- write_io(ci, address, value);
- mutex_unlock(&ci->lock);
- return 0;
-}
-
-static int slot_reset(struct dvb_ca_en50221 *ca, int slot)
-{
- struct cxd *ci = ca->data;
-
- mutex_lock(&ci->lock);
-#if 0
- write_reg(ci, 0x00, 0x21);
- write_reg(ci, 0x06, 0x1F);
- write_reg(ci, 0x00, 0x31);
-#else
-#if 0
- write_reg(ci, 0x06, 0x1F);
- write_reg(ci, 0x06, 0x2F);
-#else
- cam_mode(ci, 0);
- write_reg(ci, 0x00, 0x21);
- write_reg(ci, 0x06, 0x1F);
- write_reg(ci, 0x00, 0x31);
- write_regm(ci, 0x20, 0x80, 0x80);
- write_reg(ci, 0x03, 0x02);
- ci->ready = 0;
-#endif
-#endif
- ci->mode = -1;
- {
- int i;
-#if 0
- u8 val;
-#endif
- for (i = 0; i < 100; i++) {
- msleep(10);
-#if 0
- read_reg(ci, 0x06, &val);
- printk(KERN_INFO "%d:%02x\n", i, val);
- if (!(val&0x10))
- break;
-#else
- if (ci->ready)
- break;
-#endif
- }
- }
- mutex_unlock(&ci->lock);
- /* msleep(500); */
- return 0;
-}
-
-static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
-{
- struct cxd *ci = ca->data;
-
- printk(KERN_INFO "slot_shutdown\n");
- mutex_lock(&ci->lock);
- write_regm(ci, 0x09, 0x08, 0x08);
- write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */
- write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */
- ci->mode = -1;
- mutex_unlock(&ci->lock);
- return 0;
-}
-
-static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
-{
- struct cxd *ci = ca->data;
-
- mutex_lock(&ci->lock);
- write_regm(ci, 0x09, 0x00, 0x08);
- set_mode(ci, 0);
-#ifdef BUFFER_MODE
- cam_mode(ci, 1);
-#endif
- mutex_unlock(&ci->lock);
- return 0;
-}
-
-
-static int campoll(struct cxd *ci)
-{
- u8 istat;
-
- read_reg(ci, 0x04, &istat);
- if (!istat)
- return 0;
- write_reg(ci, 0x05, istat);
-
- if (istat&0x40) {
- ci->dr = 1;
- printk(KERN_INFO "DR\n");
- }
- if (istat&0x20)
- printk(KERN_INFO "WC\n");
-
- if (istat&2) {
- u8 slotstat;
-
- read_reg(ci, 0x01, &slotstat);
- if (!(2&slotstat)) {
- if (!ci->slot_stat) {
- ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_PRESENT;
- write_regm(ci, 0x03, 0x08, 0x08);
- }
-
- } else {
- if (ci->slot_stat) {
- ci->slot_stat = 0;
- write_regm(ci, 0x03, 0x00, 0x08);
- printk(KERN_INFO "NO CAM\n");
- ci->ready = 0;
- }
- }
- if (istat&8 && ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) {
- ci->ready = 1;
- ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY;
- }
- }
- return 0;
-}
-
-
-static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
-{
- struct cxd *ci = ca->data;
- u8 slotstat;
-
- mutex_lock(&ci->lock);
- campoll(ci);
- read_reg(ci, 0x01, &slotstat);
- mutex_unlock(&ci->lock);
-
- return ci->slot_stat;
-}
-
-#ifdef BUFFER_MODE
-static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
-{
- struct cxd *ci = ca->data;
- u8 msb, lsb;
- u16 len;
-
- mutex_lock(&ci->lock);
- campoll(ci);
- mutex_unlock(&ci->lock);
-
- printk(KERN_INFO "read_data\n");
- if (!ci->dr)
- return 0;
-
- mutex_lock(&ci->lock);
- read_reg(ci, 0x0f, &msb);
- read_reg(ci, 0x10, &lsb);
- len = (msb<<8)|lsb;
- read_block(ci, 0x12, ebuf, len);
- ci->dr = 0;
- mutex_unlock(&ci->lock);
-
- return len;
-}
-
-static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
-{
- struct cxd *ci = ca->data;
-
- mutex_lock(&ci->lock);
- printk(kern_INFO "write_data %d\n", ecount);
- write_reg(ci, 0x0d, ecount>>8);
- write_reg(ci, 0x0e, ecount&0xff);
- write_block(ci, 0x11, ebuf, ecount);
- mutex_unlock(&ci->lock);
- return ecount;
-}
-#endif
-
-static struct dvb_ca_en50221 en_templ = {
- .read_attribute_mem = read_attribute_mem,
- .write_attribute_mem = write_attribute_mem,
- .read_cam_control = read_cam_control,
- .write_cam_control = write_cam_control,
- .slot_reset = slot_reset,
- .slot_shutdown = slot_shutdown,
- .slot_ts_enable = slot_ts_enable,
- .poll_slot_status = poll_slot_status,
-#ifdef BUFFER_MODE
- .read_data = read_data,
- .write_data = write_data,
-#endif
-
-};
-
-struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
- void *priv,
- struct i2c_adapter *i2c)
-{
- struct cxd *ci = 0;
- u8 val;
-
- if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) {
- printk(KERN_INFO "No CXD2099 detected at %02x\n", cfg->adr);
- return 0;
- }
-
- ci = kmalloc(sizeof(struct cxd), GFP_KERNEL);
- if (!ci)
- return 0;
- memset(ci, 0, sizeof(*ci));
-
- mutex_init(&ci->lock);
- memcpy(&ci->cfg, cfg, sizeof(struct cxd2099_cfg));
- ci->i2c = i2c;
- ci->lastaddress = 0xff;
- ci->clk_reg_b = 0x4a;
- ci->clk_reg_f = 0x1b;
-
- memcpy(&ci->en, &en_templ, sizeof(en_templ));
- ci->en.data = ci;
- init(ci);
- printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->cfg.adr);
- return &ci->en;
-}
-EXPORT_SYMBOL(cxd2099_attach);
-
-MODULE_DESCRIPTION("cxd2099");
-MODULE_AUTHOR("Ralph Metzler");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * cxd2099.h: Driver for the CXD2099AR Common Interface Controller
- *
- * Copyright (C) 2010-2011 Digital Devices GmbH
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- */
-
-#ifndef _CXD2099_H_
-#define _CXD2099_H_
-
-#include <dvb_ca_en50221.h>
-
-struct cxd2099_cfg {
- u32 bitrate;
- u8 adr;
- u8 polarity:1;
- u8 clock_mode:1;
-};
-
-#if defined(CONFIG_DVB_CXD2099) || \
- (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE))
-struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
- void *priv, struct i2c_adapter *i2c);
-#else
-
-static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
- void *priv, struct i2c_adapter *i2c)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
+++ /dev/null
-config VIDEO_DT3155
- tristate "DT3155 frame grabber, Video4Linux interface"
- depends on PCI && VIDEO_DEV && VIDEO_V4L2
- select VIDEOBUF2_DMA_CONTIG
- default n
- ---help---
- Enables dt3155 device driver for the DataTranslation DT3155 frame grabber.
- Say Y here if you have this hardware.
- In doubt, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called dt3155v4l.
-
-config DT3155_CCIR
- bool "Selects CCIR/50Hz vertical refresh"
- depends on VIDEO_DT3155
- default y
- ---help---
- Select it for CCIR/50Hz (European region),
- or leave it unselected for RS-170/60Hz (North America).
-
-config DT3155_STREAMING
- bool "Selects streaming capture method"
- depends on VIDEO_DT3155
- default y
- ---help---
- Select it if you want to use streaming of memory mapped buffers
- or leave it unselected if you want to use read method (one copy more).
+++ /dev/null
-obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l.o
+++ /dev/null
-/***************************************************************************
- * Copyright (C) 2006-2010 by Marin Mitov *
- * mitov@issp.bas.bg *
- * *
- * 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/module.h>
-#include <linux/version.h>
-#include <linux/stringify.h>
-#include <linux/delay.h>
-#include <linux/kthread.h>
-#include <linux/slab.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "dt3155v4l.h"
-
-#define DT3155_VENDOR_ID 0x8086
-#define DT3155_DEVICE_ID 0x1223
-
-/* DT3155_CHUNK_SIZE is 4M (2^22) 8 full size buffers */
-#define DT3155_CHUNK_SIZE (1U << 22)
-
-#define DT3155_COH_FLAGS (GFP_KERNEL | GFP_DMA32 | __GFP_COLD | __GFP_NOWARN)
-
-#define DT3155_BUF_SIZE (768 * 576)
-
-#ifdef CONFIG_DT3155_STREAMING
-#define DT3155_CAPTURE_METHOD V4L2_CAP_STREAMING
-#else
-#define DT3155_CAPTURE_METHOD V4L2_CAP_READWRITE
-#endif
-
-/* global initializers (for all boards) */
-#ifdef CONFIG_DT3155_CCIR
-static const u8 csr2_init = VT_50HZ;
-#define DT3155_CURRENT_NORM V4L2_STD_625_50
-static const unsigned int img_width = 768;
-static const unsigned int img_height = 576;
-static const unsigned int frames_per_sec = 25;
-static const struct v4l2_fmtdesc frame_std[] = {
- {
- .index = 0,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .flags = 0,
- .description = "CCIR/50Hz 8 bits gray",
- .pixelformat = V4L2_PIX_FMT_GREY,
- },
-};
-#else
-static const u8 csr2_init = VT_60HZ;
-#define DT3155_CURRENT_NORM V4L2_STD_525_60
-static const unsigned int img_width = 640;
-static const unsigned int img_height = 480;
-static const unsigned int frames_per_sec = 30;
-static const struct v4l2_fmtdesc frame_std[] = {
- {
- .index = 0,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .flags = 0,
- .description = "RS-170/60Hz 8 bits gray",
- .pixelformat = V4L2_PIX_FMT_GREY,
- },
-};
-#endif
-
-#define NUM_OF_FORMATS ARRAY_SIZE(frame_std)
-
-static u8 config_init = ACQ_MODE_EVEN;
-
-/**
- * read_i2c_reg - reads an internal i2c register
- *
- * @addr: dt3155 mmio base address
- * @index: index (internal address) of register to read
- * @data: pointer to byte the read data will be placed in
- *
- * returns: zero on success or error code
- *
- * This function starts reading the specified (by index) register
- * and busy waits for the process to finish. The result is placed
- * in a byte pointed by data.
- */
-static int
-read_i2c_reg(void __iomem *addr, u8 index, u8 *data)
-{
- u32 tmp = index;
-
- iowrite32((tmp<<17) | IIC_READ, addr + IIC_CSR2);
- mmiowb();
- udelay(45); /* wait at least 43 usec for NEW_CYCLE to clear */
- if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
- return -EIO; /* error: NEW_CYCLE not cleared */
- tmp = ioread32(addr + IIC_CSR1);
- if (tmp & DIRECT_ABORT) {
- /* reset DIRECT_ABORT bit */
- iowrite32(DIRECT_ABORT, addr + IIC_CSR1);
- return -EIO; /* error: DIRECT_ABORT set */
- }
- *data = tmp>>24;
- return 0;
-}
-
-/**
- * write_i2c_reg - writes to an internal i2c register
- *
- * @addr: dt3155 mmio base address
- * @index: index (internal address) of register to read
- * @data: data to be written
- *
- * returns: zero on success or error code
- *
- * This function starts writting the specified (by index) register
- * and busy waits for the process to finish.
- */
-static int
-write_i2c_reg(void __iomem *addr, u8 index, u8 data)
-{
- u32 tmp = index;
-
- iowrite32((tmp<<17) | IIC_WRITE | data, addr + IIC_CSR2);
- mmiowb();
- udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */
- if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
- return -EIO; /* error: NEW_CYCLE not cleared */
- if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) {
- /* reset DIRECT_ABORT bit */
- iowrite32(DIRECT_ABORT, addr + IIC_CSR1);
- return -EIO; /* error: DIRECT_ABORT set */
- }
- return 0;
-}
-
-/**
- * write_i2c_reg_nowait - writes to an internal i2c register
- *
- * @addr: dt3155 mmio base address
- * @index: index (internal address) of register to read
- * @data: data to be written
- *
- * This function starts writting the specified (by index) register
- * and then returns.
- */
-static void write_i2c_reg_nowait(void __iomem *addr, u8 index, u8 data)
-{
- u32 tmp = index;
-
- iowrite32((tmp<<17) | IIC_WRITE | data, addr + IIC_CSR2);
- mmiowb();
-}
-
-/**
- * wait_i2c_reg - waits the read/write to finish
- *
- * @addr: dt3155 mmio base address
- *
- * returns: zero on success or error code
- *
- * This function waits reading/writting to finish.
- */
-static int wait_i2c_reg(void __iomem *addr)
-{
- if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
- udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */
- if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
- return -EIO; /* error: NEW_CYCLE not cleared */
- if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) {
- /* reset DIRECT_ABORT bit */
- iowrite32(DIRECT_ABORT, addr + IIC_CSR1);
- return -EIO; /* error: DIRECT_ABORT set */
- }
- return 0;
-}
-
-static int
-dt3155_start_acq(struct dt3155_priv *pd)
-{
- struct vb2_buffer *vb = pd->curr_buf;
- dma_addr_t dma_addr;
-
- dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
- iowrite32(dma_addr, pd->regs + EVEN_DMA_START);
- iowrite32(dma_addr + img_width, pd->regs + ODD_DMA_START);
- iowrite32(img_width, pd->regs + EVEN_DMA_STRIDE);
- iowrite32(img_width, pd->regs + ODD_DMA_STRIDE);
- /* enable interrupts, clear all irq flags */
- iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START |
- FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR);
- iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |
- FLD_DN_ODD | FLD_DN_EVEN | CAP_CONT_EVEN | CAP_CONT_ODD,
- pd->regs + CSR1);
- wait_i2c_reg(pd->regs);
- write_i2c_reg(pd->regs, CONFIG, pd->config);
- write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE);
- write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_DONE);
-
- /* start the board */
- write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | BUSY_ODD);
- return 0; /* success */
-}
-
-/*
- * driver-specific callbacks (vb2_ops)
- */
-static int
-dt3155_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
- unsigned int *num_planes, unsigned long sizes[],
- void *alloc_ctxs[])
-{
- struct dt3155_priv *pd = vb2_get_drv_priv(q);
- void *ret;
-
- if (*num_buffers == 0)
- *num_buffers = 1;
- *num_planes = 1;
- sizes[0] = img_width * img_height;
- if (pd->q->alloc_ctx[0])
- return 0;
- ret = vb2_dma_contig_init_ctx(&pd->pdev->dev);
- if (IS_ERR(ret))
- return PTR_ERR(ret);
- pd->q->alloc_ctx[0] = ret;
- return 0;
-}
-
-static void
-dt3155_wait_prepare(struct vb2_queue *q)
-{
- struct dt3155_priv *pd = vb2_get_drv_priv(q);
-
- mutex_unlock(pd->vdev->lock);
-}
-
-static void
-dt3155_wait_finish(struct vb2_queue *q)
-{
- struct dt3155_priv *pd = vb2_get_drv_priv(q);
-
- mutex_lock(pd->vdev->lock);
-}
-
-static int
-dt3155_buf_prepare(struct vb2_buffer *vb)
-{
- vb2_set_plane_payload(vb, 0, img_width * img_height);
- return 0;
-}
-
-static int
-dt3155_start_streaming(struct vb2_queue *q)
-{
- return 0;
-}
-
-static int
-dt3155_stop_streaming(struct vb2_queue *q)
-{
- struct dt3155_priv *pd = vb2_get_drv_priv(q);
- struct vb2_buffer *vb;
-
- spin_lock_irq(&pd->lock);
- while (!list_empty(&pd->dmaq)) {
- vb = list_first_entry(&pd->dmaq, typeof(*vb), done_entry);
- list_del(&vb->done_entry);
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
- }
- spin_unlock_irq(&pd->lock);
- msleep(45); /* irq hendler will stop the hardware */
- return 0;
-}
-
-static void
-dt3155_buf_queue(struct vb2_buffer *vb)
-{
- struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue);
-
- /* pd->q->streaming = 1 when dt3155_buf_queue() is invoked */
- spin_lock_irq(&pd->lock);
- if (pd->curr_buf)
- list_add_tail(&vb->done_entry, &pd->dmaq);
- else {
- pd->curr_buf = vb;
- dt3155_start_acq(pd);
- }
- spin_unlock_irq(&pd->lock);
-}
-/*
- * end driver-specific callbacks
- */
-
-const struct vb2_ops q_ops = {
- .queue_setup = dt3155_queue_setup,
- .wait_prepare = dt3155_wait_prepare,
- .wait_finish = dt3155_wait_finish,
- .buf_prepare = dt3155_buf_prepare,
- .start_streaming = dt3155_start_streaming,
- .stop_streaming = dt3155_stop_streaming,
- .buf_queue = dt3155_buf_queue,
-};
-
-static irqreturn_t
-dt3155_irq_handler_even(int irq, void *dev_id)
-{
- struct dt3155_priv *ipd = dev_id;
- struct vb2_buffer *ivb;
- dma_addr_t dma_addr;
- u32 tmp;
-
- tmp = ioread32(ipd->regs + INT_CSR) & (FLD_START | FLD_END_ODD);
- if (!tmp)
- return IRQ_NONE; /* not our irq */
- if ((tmp & FLD_START) && !(tmp & FLD_END_ODD)) {
- iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START,
- ipd->regs + INT_CSR);
- ipd->field_count++;
- return IRQ_HANDLED; /* start of field irq */
- }
- if ((tmp & FLD_START) && (tmp & FLD_END_ODD))
- ipd->stats.start_before_end++;
- /* check for corrupted fields */
-/* write_i2c_reg(ipd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE); */
-/* write_i2c_reg(ipd->regs, ODD_CSR, CSR_ERROR | CSR_DONE); */
- tmp = ioread32(ipd->regs + CSR1) & (FLD_CRPT_EVEN | FLD_CRPT_ODD);
- if (tmp) {
- ipd->stats.corrupted_fields++;
- iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |
- FLD_DN_ODD | FLD_DN_EVEN |
- CAP_CONT_EVEN | CAP_CONT_ODD,
- ipd->regs + CSR1);
- mmiowb();
- }
-
- spin_lock(&ipd->lock);
- if (ipd->curr_buf) {
- do_gettimeofday(&ipd->curr_buf->v4l2_buf.timestamp);
- ipd->curr_buf->v4l2_buf.sequence = (ipd->field_count) >> 1;
- vb2_buffer_done(ipd->curr_buf, VB2_BUF_STATE_DONE);
- }
-
- if (!ipd->q->streaming || list_empty(&ipd->dmaq))
- goto stop_dma;
- ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry);
- list_del(&ivb->done_entry);
- ipd->curr_buf = ivb;
- dma_addr = vb2_dma_contig_plane_dma_addr(ivb, 0);
- iowrite32(dma_addr, ipd->regs + EVEN_DMA_START);
- iowrite32(dma_addr + img_width, ipd->regs + ODD_DMA_START);
- iowrite32(img_width, ipd->regs + EVEN_DMA_STRIDE);
- iowrite32(img_width, ipd->regs + ODD_DMA_STRIDE);
- mmiowb();
- /* enable interrupts, clear all irq flags */
- iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START |
- FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR);
- spin_unlock(&ipd->lock);
- return IRQ_HANDLED;
-
-stop_dma:
- ipd->curr_buf = NULL;
- /* stop the board */
- write_i2c_reg_nowait(ipd->regs, CSR2, ipd->csr2);
- iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |
- FLD_DN_ODD | FLD_DN_EVEN, ipd->regs + CSR1);
- /* disable interrupts, clear all irq flags */
- iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR);
- spin_unlock(&ipd->lock);
- return IRQ_HANDLED;
-}
-
-static int
-dt3155_open(struct file *filp)
-{
- int ret = 0;
- struct dt3155_priv *pd = video_drvdata(filp);
-
- if (!pd->users) {
- pd->q = kzalloc(sizeof(*pd->q), GFP_KERNEL);
- if (!pd->q) {
- ret = -ENOMEM;
- goto err_alloc_queue;
- }
- pd->q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- pd->q->io_modes = VB2_READ | VB2_MMAP;
- pd->q->ops = &q_ops;
- pd->q->mem_ops = &vb2_dma_contig_memops;
- pd->q->drv_priv = pd;
- pd->curr_buf = NULL;
- pd->field_count = 0;
- vb2_queue_init(pd->q); /* cannot fail */
- INIT_LIST_HEAD(&pd->dmaq);
- spin_lock_init(&pd->lock);
- /* disable all irqs, clear all irq flags */
- iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD,
- pd->regs + INT_CSR);
- ret = request_irq(pd->pdev->irq, dt3155_irq_handler_even,
- IRQF_SHARED, DT3155_NAME, pd);
- if (ret)
- goto err_request_irq;
- }
- pd->users++;
- return 0; /* success */
-err_request_irq:
- kfree(pd->q);
- pd->q = NULL;
-err_alloc_queue:
- return ret;
-}
-
-static int
-dt3155_release(struct file *filp)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- pd->users--;
- BUG_ON(pd->users < 0);
- if (!pd->users) {
- vb2_queue_release(pd->q);
- free_irq(pd->pdev->irq, pd);
- if (pd->q->alloc_ctx[0])
- vb2_dma_contig_cleanup_ctx(pd->q->alloc_ctx[0]);
- kfree(pd->q);
- pd->q = NULL;
- }
- return 0;
-}
-
-static ssize_t
-dt3155_read(struct file *filp, char __user *user, size_t size, loff_t *loff)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_read(pd->q, user, size, loff, filp->f_flags & O_NONBLOCK);
-}
-
-static unsigned int
-dt3155_poll(struct file *filp, struct poll_table_struct *polltbl)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_poll(pd->q, filp, polltbl);
-}
-
-static int
-dt3155_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_mmap(pd->q, vma);
-}
-
-static const struct v4l2_file_operations dt3155_fops = {
- .owner = THIS_MODULE,
- .open = dt3155_open,
- .release = dt3155_release,
- .read = dt3155_read,
- .poll = dt3155_poll,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = dt3155_mmap,
-};
-
-static int
-dt3155_ioc_streamon(struct file *filp, void *p, enum v4l2_buf_type type)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_streamon(pd->q, type);
-}
-
-static int
-dt3155_ioc_streamoff(struct file *filp, void *p, enum v4l2_buf_type type)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_streamoff(pd->q, type);
-}
-
-static int
-dt3155_ioc_querycap(struct file *filp, void *p, struct v4l2_capability *cap)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- strcpy(cap->driver, DT3155_NAME);
- strcpy(cap->card, DT3155_NAME " frame grabber");
- sprintf(cap->bus_info, "PCI:%s", pci_name(pd->pdev));
- cap->version =
- KERNEL_VERSION(DT3155_VER_MAJ, DT3155_VER_MIN, DT3155_VER_EXT);
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- DT3155_CAPTURE_METHOD;
- return 0;
-}
-
-static int
-dt3155_ioc_enum_fmt_vid_cap(struct file *filp, void *p, struct v4l2_fmtdesc *f)
-{
- if (f->index >= NUM_OF_FORMATS)
- return -EINVAL;
- *f = frame_std[f->index];
- return 0;
-}
-
-static int
-dt3155_ioc_g_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
-{
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- f->fmt.pix.width = img_width;
- f->fmt.pix.height = img_height;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;
- f->fmt.pix.field = V4L2_FIELD_NONE;
- f->fmt.pix.bytesperline = f->fmt.pix.width;
- f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height;
- f->fmt.pix.colorspace = 0;
- f->fmt.pix.priv = 0;
- return 0;
-}
-
-static int
-dt3155_ioc_try_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
-{
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (f->fmt.pix.width == img_width &&
- f->fmt.pix.height == img_height &&
- f->fmt.pix.pixelformat == V4L2_PIX_FMT_GREY &&
- f->fmt.pix.field == V4L2_FIELD_NONE &&
- f->fmt.pix.bytesperline == f->fmt.pix.width &&
- f->fmt.pix.sizeimage == f->fmt.pix.width * f->fmt.pix.height)
- return 0;
- else
- return -EINVAL;
-}
-
-static int
-dt3155_ioc_s_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
-{
- return dt3155_ioc_g_fmt_vid_cap(filp, p, f);
-}
-
-static int
-dt3155_ioc_reqbufs(struct file *filp, void *p, struct v4l2_requestbuffers *b)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_reqbufs(pd->q, b);
-}
-
-static int
-dt3155_ioc_querybuf(struct file *filp, void *p, struct v4l2_buffer *b)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_querybuf(pd->q, b);
-}
-
-static int
-dt3155_ioc_qbuf(struct file *filp, void *p, struct v4l2_buffer *b)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_qbuf(pd->q, b);
-}
-
-static int
-dt3155_ioc_dqbuf(struct file *filp, void *p, struct v4l2_buffer *b)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_dqbuf(pd->q, b, filp->f_flags & O_NONBLOCK);
-}
-
-static int
-dt3155_ioc_querystd(struct file *filp, void *p, v4l2_std_id *norm)
-{
- *norm = DT3155_CURRENT_NORM;
- return 0;
-}
-
-static int
-dt3155_ioc_g_std(struct file *filp, void *p, v4l2_std_id *norm)
-{
- *norm = DT3155_CURRENT_NORM;
- return 0;
-}
-
-static int
-dt3155_ioc_s_std(struct file *filp, void *p, v4l2_std_id *norm)
-{
- if (*norm & DT3155_CURRENT_NORM)
- return 0;
- return -EINVAL;
-}
-
-static int
-dt3155_ioc_enum_input(struct file *filp, void *p, struct v4l2_input *input)
-{
- if (input->index)
- return -EINVAL;
- strcpy(input->name, "Coax in");
- input->type = V4L2_INPUT_TYPE_CAMERA;
- /*
- * FIXME: input->std = 0 according to v4l2 API
- * VIDIOC_G_STD, VIDIOC_S_STD, VIDIOC_QUERYSTD and VIDIOC_ENUMSTD
- * should return -EINVAL
- */
- input->std = DT3155_CURRENT_NORM;
- input->status = 0;/* FIXME: add sync detection & V4L2_IN_ST_NO_H_LOCK */
- return 0;
-}
-
-static int
-dt3155_ioc_g_input(struct file *filp, void *p, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int
-dt3155_ioc_s_input(struct file *filp, void *p, unsigned int i)
-{
- if (i)
- return -EINVAL;
- return 0;
-}
-
-static int
-dt3155_ioc_g_parm(struct file *filp, void *p, struct v4l2_streamparm *parms)
-{
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- parms->parm.capture.capturemode = 0;
- parms->parm.capture.timeperframe.numerator = 1001;
- parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000;
- parms->parm.capture.extendedmode = 0;
- parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */
- return 0;
-}
-
-static int
-dt3155_ioc_s_parm(struct file *filp, void *p, struct v4l2_streamparm *parms)
-{
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- parms->parm.capture.capturemode = 0;
- parms->parm.capture.timeperframe.numerator = 1001;
- parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000;
- parms->parm.capture.extendedmode = 0;
- parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */
- return 0;
-}
-
-static const struct v4l2_ioctl_ops dt3155_ioctl_ops = {
- .vidioc_streamon = dt3155_ioc_streamon,
- .vidioc_streamoff = dt3155_ioc_streamoff,
- .vidioc_querycap = dt3155_ioc_querycap,
-/*
- .vidioc_g_priority = dt3155_ioc_g_priority,
- .vidioc_s_priority = dt3155_ioc_s_priority,
-*/
- .vidioc_enum_fmt_vid_cap = dt3155_ioc_enum_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = dt3155_ioc_try_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = dt3155_ioc_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = dt3155_ioc_s_fmt_vid_cap,
- .vidioc_reqbufs = dt3155_ioc_reqbufs,
- .vidioc_querybuf = dt3155_ioc_querybuf,
- .vidioc_qbuf = dt3155_ioc_qbuf,
- .vidioc_dqbuf = dt3155_ioc_dqbuf,
- .vidioc_querystd = dt3155_ioc_querystd,
- .vidioc_g_std = dt3155_ioc_g_std,
- .vidioc_s_std = dt3155_ioc_s_std,
- .vidioc_enum_input = dt3155_ioc_enum_input,
- .vidioc_g_input = dt3155_ioc_g_input,
- .vidioc_s_input = dt3155_ioc_s_input,
-/*
- .vidioc_queryctrl = dt3155_ioc_queryctrl,
- .vidioc_g_ctrl = dt3155_ioc_g_ctrl,
- .vidioc_s_ctrl = dt3155_ioc_s_ctrl,
- .vidioc_querymenu = dt3155_ioc_querymenu,
- .vidioc_g_ext_ctrls = dt3155_ioc_g_ext_ctrls,
- .vidioc_s_ext_ctrls = dt3155_ioc_s_ext_ctrls,
-*/
- .vidioc_g_parm = dt3155_ioc_g_parm,
- .vidioc_s_parm = dt3155_ioc_s_parm,
-/*
- .vidioc_cropcap = dt3155_ioc_cropcap,
- .vidioc_g_crop = dt3155_ioc_g_crop,
- .vidioc_s_crop = dt3155_ioc_s_crop,
- .vidioc_enum_framesizes = dt3155_ioc_enum_framesizes,
- .vidioc_enum_frameintervals = dt3155_ioc_enum_frameintervals,
-*/
-};
-
-static int __devinit
-dt3155_init_board(struct pci_dev *pdev)
-{
- struct dt3155_priv *pd = pci_get_drvdata(pdev);
- void *buf_cpu;
- dma_addr_t buf_dma;
- int i;
- u8 tmp;
-
- pci_set_master(pdev); /* dt3155 needs it */
-
- /* resetting the adapter */
- iowrite32(FLD_CRPT_ODD | FLD_CRPT_EVEN | FLD_DN_ODD | FLD_DN_EVEN,
- pd->regs + CSR1);
- mmiowb();
- msleep(20);
-
- /* initializing adaper registers */
- iowrite32(FIFO_EN | SRST, pd->regs + CSR1);
- mmiowb();
- iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT);
- iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT);
- iowrite32(0x00000020, pd->regs + FIFO_TRIGER);
- iowrite32(0x00000103, pd->regs + XFER_MODE);
- iowrite32(0, pd->regs + RETRY_WAIT_CNT);
- iowrite32(0, pd->regs + INT_CSR);
- iowrite32(1, pd->regs + EVEN_FLD_MASK);
- iowrite32(1, pd->regs + ODD_FLD_MASK);
- iowrite32(0, pd->regs + MASK_LENGTH);
- iowrite32(0x0005007C, pd->regs + FIFO_FLAG_CNT);
- iowrite32(0x01010101, pd->regs + IIC_CLK_DUR);
- mmiowb();
-
- /* verifying that we have a DT3155 board (not just a SAA7116 chip) */
- read_i2c_reg(pd->regs, DT_ID, &tmp);
- if (tmp != DT3155_ID)
- return -ENODEV;
-
- /* initialize AD LUT */
- write_i2c_reg(pd->regs, AD_ADDR, 0);
- for (i = 0; i < 256; i++)
- write_i2c_reg(pd->regs, AD_LUT, i);
-
- /* initialize ADC references */
- /* FIXME: pos_ref & neg_ref depend on VT_50HZ */
- write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG);
- write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3);
- write_i2c_reg(pd->regs, AD_ADDR, AD_POS_REF);
- write_i2c_reg(pd->regs, AD_CMD, 34);
- write_i2c_reg(pd->regs, AD_ADDR, AD_NEG_REF);
- write_i2c_reg(pd->regs, AD_CMD, 0);
-
- /* initialize PM LUT */
- write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM);
- for (i = 0; i < 256; i++) {
- write_i2c_reg(pd->regs, PM_LUT_ADDR, i);
- write_i2c_reg(pd->regs, PM_LUT_DATA, i);
- }
- write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM | PM_LUT_SEL);
- for (i = 0; i < 256; i++) {
- write_i2c_reg(pd->regs, PM_LUT_ADDR, i);
- write_i2c_reg(pd->regs, PM_LUT_DATA, i);
- }
- write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */
-
- /* select chanel 1 for input and set sync level */
- write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG);
- write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3);
-
- /* allocate memory, and initialize the DMA machine */
- buf_cpu = dma_alloc_coherent(&pdev->dev, DT3155_BUF_SIZE, &buf_dma,
- GFP_KERNEL);
- if (!buf_cpu)
- return -ENOMEM;
- iowrite32(buf_dma, pd->regs + EVEN_DMA_START);
- iowrite32(buf_dma, pd->regs + ODD_DMA_START);
- iowrite32(0, pd->regs + EVEN_DMA_STRIDE);
- iowrite32(0, pd->regs + ODD_DMA_STRIDE);
-
- /* Perform a pseudo even field acquire */
- iowrite32(FIFO_EN | SRST | CAP_CONT_ODD, pd->regs + CSR1);
- write_i2c_reg(pd->regs, CSR2, pd->csr2 | SYNC_SNTL);
- write_i2c_reg(pd->regs, CONFIG, pd->config);
- write_i2c_reg(pd->regs, EVEN_CSR, CSR_SNGL);
- write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | SYNC_SNTL);
- msleep(100);
- read_i2c_reg(pd->regs, CSR2, &tmp);
- write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE);
- write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE);
- write_i2c_reg(pd->regs, CSR2, pd->csr2);
- iowrite32(FIFO_EN | SRST | FLD_DN_EVEN | FLD_DN_ODD, pd->regs + CSR1);
-
- /* deallocate memory */
- dma_free_coherent(&pdev->dev, DT3155_BUF_SIZE, buf_cpu, buf_dma);
- if (tmp & BUSY_EVEN)
- return -EIO;
- return 0;
-}
-
-static struct video_device dt3155_vdev = {
- .name = DT3155_NAME,
- .fops = &dt3155_fops,
- .ioctl_ops = &dt3155_ioctl_ops,
- .minor = -1,
- .release = video_device_release,
- .tvnorms = DT3155_CURRENT_NORM,
- .current_norm = DT3155_CURRENT_NORM,
-};
-
-/* same as in drivers/base/dma-coherent.c */
-struct dma_coherent_mem {
- void *virt_base;
- dma_addr_t device_base;
- int size;
- int flags;
- unsigned long *bitmap;
-};
-
-static int __devinit
-dt3155_alloc_coherent(struct device *dev, size_t size, int flags)
-{
- struct dma_coherent_mem *mem;
- dma_addr_t dev_base;
- int pages = size >> PAGE_SHIFT;
- int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
-
- if ((flags & DMA_MEMORY_MAP) == 0)
- goto out;
- if (!size)
- goto out;
- if (dev->dma_mem)
- goto out;
-
- mem = kzalloc(sizeof(*mem), GFP_KERNEL);
- if (!mem)
- goto out;
- mem->virt_base = dma_alloc_coherent(dev, size, &dev_base,
- DT3155_COH_FLAGS);
- if (!mem->virt_base)
- goto err_alloc_coherent;
- mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
- if (!mem->bitmap)
- goto err_bitmap;
-
- /* coherent_dma_mask is already set to 32 bits */
- mem->device_base = dev_base;
- mem->size = pages;
- mem->flags = flags;
- dev->dma_mem = mem;
- return DMA_MEMORY_MAP;
-
-err_bitmap:
- dma_free_coherent(dev, size, mem->virt_base, dev_base);
-err_alloc_coherent:
- kfree(mem);
-out:
- return 0;
-}
-
-static void __devexit
-dt3155_free_coherent(struct device *dev)
-{
- struct dma_coherent_mem *mem = dev->dma_mem;
-
- if (!mem)
- return;
- dev->dma_mem = NULL;
- dma_free_coherent(dev, mem->size << PAGE_SHIFT,
- mem->virt_base, mem->device_base);
- kfree(mem->bitmap);
- kfree(mem);
-}
-
-static int __devinit
-dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- int err;
- struct dt3155_priv *pd;
-
- err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
- if (err)
- return -ENODEV;
- err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
- if (err)
- return -ENODEV;
- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return -ENOMEM;
- pd->vdev = video_device_alloc();
- if (!pd->vdev)
- goto err_video_device_alloc;
- *pd->vdev = dt3155_vdev;
- pci_set_drvdata(pdev, pd); /* for use in dt3155_remove() */
- video_set_drvdata(pd->vdev, pd); /* for use in video_fops */
- pd->users = 0;
- pd->pdev = pdev;
- INIT_LIST_HEAD(&pd->dmaq);
- mutex_init(&pd->mux);
- pd->vdev->lock = &pd->mux; /* for locking v4l2_file_operations */
- spin_lock_init(&pd->lock);
- pd->csr2 = csr2_init;
- pd->config = config_init;
- err = pci_enable_device(pdev);
- if (err)
- goto err_enable_dev;
- err = pci_request_region(pdev, 0, pci_name(pdev));
- if (err)
- goto err_req_region;
- pd->regs = pci_iomap(pdev, 0, pci_resource_len(pd->pdev, 0));
- if (!pd->regs)
- err = -ENOMEM;
- goto err_pci_iomap;
- err = dt3155_init_board(pdev);
- if (err)
- goto err_init_board;
- err = video_register_device(pd->vdev, VFL_TYPE_GRABBER, -1);
- if (err)
- goto err_init_board;
- if (dt3155_alloc_coherent(&pdev->dev, DT3155_CHUNK_SIZE,
- DMA_MEMORY_MAP))
- dev_info(&pdev->dev, "preallocated 8 buffers\n");
- dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev->minor);
- return 0; /* success */
-
-err_init_board:
- pci_iounmap(pdev, pd->regs);
-err_pci_iomap:
- pci_release_region(pdev, 0);
-err_req_region:
- pci_disable_device(pdev);
-err_enable_dev:
- video_device_release(pd->vdev);
-err_video_device_alloc:
- kfree(pd);
- return err;
-}
-
-static void __devexit
-dt3155_remove(struct pci_dev *pdev)
-{
- struct dt3155_priv *pd = pci_get_drvdata(pdev);
-
- dt3155_free_coherent(&pdev->dev);
- video_unregister_device(pd->vdev);
- pci_iounmap(pdev, pd->regs);
- pci_release_region(pdev, 0);
- pci_disable_device(pdev);
- /*
- * video_device_release() is invoked automatically
- * see: struct video_device dt3155_vdev
- */
- kfree(pd);
-}
-
-static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
- { PCI_DEVICE(DT3155_VENDOR_ID, DT3155_DEVICE_ID) },
- { 0, /* zero marks the end */ },
-};
-MODULE_DEVICE_TABLE(pci, pci_ids);
-
-static struct pci_driver pci_driver = {
- .name = DT3155_NAME,
- .id_table = pci_ids,
- .probe = dt3155_probe,
- .remove = __devexit_p(dt3155_remove),
-};
-
-static int __init
-dt3155_init_module(void)
-{
- return pci_register_driver(&pci_driver);
-}
-
-static void __exit
-dt3155_exit_module(void)
-{
- pci_unregister_driver(&pci_driver);
-}
-
-module_init(dt3155_init_module);
-module_exit(dt3155_exit_module);
-
-MODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber");
-MODULE_AUTHOR("Marin Mitov <mitov@issp.bas.bg>");
-MODULE_VERSION(DT3155_VERSION);
-MODULE_LICENSE("GPL");
+++ /dev/null
-/***************************************************************************
- * Copyright (C) 2006-2010 by Marin Mitov *
- * mitov@issp.bas.bg *
- * *
- * 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. *
- ***************************************************************************/
-
-/* DT3155 header file */
-#ifndef _DT3155_H_
-#define _DT3155_H_
-
-#ifdef __KERNEL__
-
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-
-#define DT3155_NAME "dt3155"
-#define DT3155_VER_MAJ 1
-#define DT3155_VER_MIN 1
-#define DT3155_VER_EXT 0
-#define DT3155_VERSION __stringify(DT3155_VER_MAJ) "." \
- __stringify(DT3155_VER_MIN) "." \
- __stringify(DT3155_VER_EXT)
-
-/* DT3155 Base Register offsets (memory mapped) */
-#define EVEN_DMA_START 0x00
-#define ODD_DMA_START 0x0C
-#define EVEN_DMA_STRIDE 0x18
-#define ODD_DMA_STRIDE 0x24
-#define EVEN_PIXEL_FMT 0x30
-#define ODD_PIXEL_FMT 0x34
-#define FIFO_TRIGER 0x38
-#define XFER_MODE 0x3C
-#define CSR1 0x40
-#define RETRY_WAIT_CNT 0x44
-#define INT_CSR 0x48
-#define EVEN_FLD_MASK 0x4C
-#define ODD_FLD_MASK 0x50
-#define MASK_LENGTH 0x54
-#define FIFO_FLAG_CNT 0x58
-#define IIC_CLK_DUR 0x5C
-#define IIC_CSR1 0x60
-#define IIC_CSR2 0x64
-
-/* DT3155 Internal Registers indexes (i2c/IIC mapped) */
-#define CSR2 0x10
-#define EVEN_CSR 0x11
-#define ODD_CSR 0x12
-#define CONFIG 0x13
-#define DT_ID 0x1F
-#define X_CLIP_START 0x20
-#define Y_CLIP_START 0x22
-#define X_CLIP_END 0x24
-#define Y_CLIP_END 0x26
-#define AD_ADDR 0x30
-#define AD_LUT 0x31
-#define AD_CMD 0x32
-#define DIG_OUT 0x40
-#define PM_LUT_ADDR 0x50
-#define PM_LUT_DATA 0x51
-
-/* AD command register values */
-#define AD_CMD_REG 0x00
-#define AD_POS_REF 0x01
-#define AD_NEG_REF 0x02
-
-/* CSR1 bit masks */
-#define CRPT_DIS 0x00004000
-#define FLD_CRPT_ODD 0x00000200
-#define FLD_CRPT_EVEN 0x00000100
-#define FIFO_EN 0x00000080
-#define SRST 0x00000040
-#define FLD_DN_ODD 0x00000020
-#define FLD_DN_EVEN 0x00000010
-/* These should not be used.
- * Use CAP_CONT_ODD/EVEN instead
-#define CAP_SNGL_ODD 0x00000008
-#define CAP_SNGL_EVEN 0x00000004
-*/
-#define CAP_CONT_ODD 0x00000002
-#define CAP_CONT_EVEN 0x00000001
-
-/* INT_CSR bit masks */
-#define FLD_START_EN 0x00000400
-#define FLD_END_ODD_EN 0x00000200
-#define FLD_END_EVEN_EN 0x00000100
-#define FLD_START 0x00000004
-#define FLD_END_ODD 0x00000002
-#define FLD_END_EVEN 0x00000001
-
-/* IIC_CSR1 bit masks */
-#define DIRECT_ABORT 0x00000200
-
-/* IIC_CSR2 bit masks */
-#define NEW_CYCLE 0x01000000
-#define DIR_RD 0x00010000
-#define IIC_READ 0x01010000
-#define IIC_WRITE 0x01000000
-
-/* CSR2 bit masks */
-#define DISP_PASS 0x40
-#define BUSY_ODD 0x20
-#define BUSY_EVEN 0x10
-#define SYNC_PRESENT 0x08
-#define VT_50HZ 0x04
-#define SYNC_SNTL 0x02
-#define CHROM_FILT 0x01
-#define VT_60HZ 0x00
-
-/* CSR_EVEN/ODD bit masks */
-#define CSR_ERROR 0x04
-#define CSR_SNGL 0x02
-#define CSR_DONE 0x01
-
-/* CONFIG bit masks */
-#define PM_LUT_PGM 0x80
-#define PM_LUT_SEL 0x40
-#define CLIP_EN 0x20
-#define HSCALE_EN 0x10
-#define EXT_TRIG_UP 0x0C
-#define EXT_TRIG_DOWN 0x04
-#define ACQ_MODE_NEXT 0x02
-#define ACQ_MODE_ODD 0x01
-#define ACQ_MODE_EVEN 0x00
-
-/* AD_CMD bit masks */
-#define VIDEO_CNL_1 0x00
-#define VIDEO_CNL_2 0x40
-#define VIDEO_CNL_3 0x80
-#define VIDEO_CNL_4 0xC0
-#define SYNC_CNL_1 0x00
-#define SYNC_CNL_2 0x10
-#define SYNC_CNL_3 0x20
-#define SYNC_CNL_4 0x30
-#define SYNC_LVL_1 0x00
-#define SYNC_LVL_2 0x04
-#define SYNC_LVL_3 0x08
-#define SYNC_LVL_4 0x0C
-
-/* DT3155 identificator */
-#define DT3155_ID 0x20
-
-#ifdef CONFIG_DT3155_CCIR
-#define DMA_STRIDE 768
-#else
-#define DMA_STRIDE 640
-#endif
-
-/**
- * struct dt3155_stats - statistics structure
- *
- * @free_bufs_empty: no free image buffers
- * @corrupted_fields: corrupted fields
- * @dma_map_failed: dma mapping failed
- * @start_before_end: new started before old ended
- */
-struct dt3155_stats {
- int free_bufs_empty;
- int corrupted_fields;
- int dma_map_failed;
- int start_before_end;
-};
-
-/* per board private data structure */
-/**
- * struct dt3155_priv - private data structure
- *
- * @vdev: pointer to video_device structure
- * @pdev: pointer to pci_dev structure
- * @q pointer to vb2_queue structure
- * @curr_buf: pointer to curren buffer
- * @mux: mutex to protect the instance
- * @dmaq queue for dma buffers
- * @lock spinlock for dma queue
- * @field_count fields counter
- * @stats: statistics structure
- * @users open count
- * @regs: local copy of mmio base register
- * @csr2: local copy of csr2 register
- * @config: local copy of config register
- */
-struct dt3155_priv {
- struct video_device *vdev;
- struct pci_dev *pdev;
- struct vb2_queue *q;
- struct vb2_buffer *curr_buf;
- struct mutex mux;
- struct list_head dmaq;
- spinlock_t lock;
- unsigned int field_count;
- struct dt3155_stats stats;
- void __iomem *regs;
- int users;
- u8 csr2, config;
-};
-
-#endif /* __KERNEL__ */
-
-#endif /* _DT3155_H_ */
+++ /dev/null
-config EASYCAP
- tristate "EasyCAP USB ID 05e1:0408 support"
- depends on USB && VIDEO_DEV && SND
- select SND_PCM
-
- ---help---
- This is an integrated audio/video driver for EasyCAP cards with
- USB ID 05e1:0408. It supports two hardware variants:
-
- * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60,
- having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R)
-
- * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled
- 1, 2, 3, 4 and an unlabelled input cable for a microphone.
-
- To compile this driver as a module, choose M here: the
- module will be called easycap
-
-config EASYCAP_DEBUG
- bool "Enable EasyCAP driver debugging"
- depends on EASYCAP
-
- ---help---
- This option enables debug printouts
-
- To enable debug, pass the debug level to the debug module
- parameter:
-
- modprobe easycap debug=[0..9]
-
+++ /dev/null
-easycap-objs := easycap_main.o
-easycap-objs += easycap_low.o
-easycap-objs += easycap_ioctl.o
-easycap-objs += easycap_settings.o
-easycap-objs += easycap_testcard.o
-easycap-objs += easycap_sound.o
-obj-$(CONFIG_EASYCAP) += easycap.o
-
-ccflags-y := -Wall
-
+++ /dev/null
-
- ***********************************************************
- * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60 *
- * and *
- * EasyCAP002 4-Channel USB 2.0 DVR *
- ***********************************************************
- Mike Thomas <rmthomas@sciolus.org>
-
-
-
-SUPPORTED HARDWARE
-------------------
-
-This driver is intended for use with hardware having USB ID 05e1:0408.
-Two kinds of EasyCAP have this USB ID, namely:
-
- * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60,
- having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R)
-
- * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled
- 1, 2, 3, 4 and an unlabelled input cable for a microphone.
-
-
-BUILD OPTIONS AND DEPENDENCIES
-------------------------------
-
-Unless EASYCAP_DEBUG is defined during compilation it will not be possible
-to select a debug level at the time of module installation.
-
-
-KNOWN RUNTIME ISSUES
---------------------
-
-(1) Intentionally, this driver will not stream material which is unambiguously
-identified by the hardware as copy-protected. Normal video output will be
-present for about a minute but will then freeze when this situation arises.
-
-(2) The controls for luminance, contrast, saturation, hue and volume may not
-always work properly.
-
-(3) Reduced-resolution S-Video seems to suffer from moire artefacts.
-
-
-INPUT NUMBERING
----------------
-
-For the EasyCAP with S-VIDEO input cable the driver regards a request for
-inputs numbered 0 or 1 as referring to CVBS and a request for input
-numbered 5 as referring to S-VIDEO.
-
-For the EasyCAP with four CVBS inputs the driver expects to be asked for
-any one of inputs numbered 1,2,3,4. If input 0 is asked for, it is
-interpreted as input 1.
-
-
-MODULE PARAMETERS
------------------
-
-Three module parameters are defined:
-
-debug the easycap module is configured at diagnostic level n (0 to 9)
-gain audio gain level n (0 to 31, default is 16)
-bars whether to display testcard bars when incoming video signal is lost
- 0 => no, 1 => yes (default)
-
-
-SUPPORTED TV STANDARDS AND RESOLUTIONS
---------------------------------------
-
-The following TV standards are natively supported by the hardware and are
-usable as (for example) the "norm=" parameter in the mplayer command:
-
- PAL_BGHIN, NTSC_N_443,
- PAL_Nc, NTSC_N,
- SECAM, NTSC_M, NTSC_M_JP,
- PAL_60, NTSC_443,
- PAL_M.
-
-In addition, the driver offers "custom" pseudo-standards with a framerate
-which is 20% of the usual framerate. These pseudo-standards are named:
-
- PAL_BGHIN_SLOW, NTSC_N_443_SLOW,
- PAL_Nc_SLOW, NTSC_N_SLOW,
- SECAM_SLOW, NTSC_M_SLOW, NTSC_M_JP_SLOW,
- PAL_60_SLOW, NTSC_443_SLOW,
- PAL_M_SLOW.
-
-
-The available picture sizes are:
-
- at 25 frames per second: 720x576, 704x576, 640x480, 360x288, 320x240;
- at 30 frames per second: 720x480, 640x480, 360x240, 320x240.
-
-
-WHAT'S TESTED AND WHAT'S NOT
-----------------------------
-
-This driver is known to work with mplayer, mencoder, tvtime, zoneminder,
-xawtv, gstreamer and sufficiently recent versions of vlc. An interface
-to ffmpeg is implemented, but serious audio-video synchronization problems
-remain.
-
-The driver is designed to support all the TV standards accepted by the
-hardware, but as yet it has actually been tested on only a few of these.
-
-I have been unable to test and calibrate the S-video input myself because I
-do not possess any equipment with S-video output.
-
-
-UDEV RULES
-----------
-
-In order that the special files /dev/easycap0 and /dev/easysnd1 are created
-with conveniently relaxed permissions when the EasyCAP is plugged in, a file
-is preferably to be provided in directory /etc/udev/rules.d with content:
-
-ACTION!="add|change", GOTO="easycap_rules_end"
-ATTRS{idVendor}=="05e1", ATTRS{idProduct}=="0408", \
- MODE="0666", OWNER="root", GROUP="root"
-LABEL="easycap_rules_end"
-
-
-MODPROBE CONFIGURATION
-----------------------
-
-The easycap module is in competition with the module snd-usb-audio for the
-EasyCAP's audio channel, and its installation can be aided by providing a
-file in directory /etc/modprobe.d with content:
-
-options easycap gain=16 bars=1
-install easycap /sbin/rmmod snd-usb-audio; /sbin/modprobe --ignore-install easycap
-
-
-ACKNOWLEGEMENTS AND REFERENCES
-------------------------------
-This driver makes use of information contained in the Syntek Semicon DC-1125
-Driver, presently maintained at http://sourceforge.net/projects/syntekdriver/
-by Nicolas Vivien. Particularly useful has been a patch to the latter driver
-provided by Ivor Hewitt in January 2009. The NTSC implementation is taken
-from the work of Ben Trask.
-
+++ /dev/null
-/*****************************************************************************
-* *
-* easycap.h *
-* *
-*****************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This 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.
- *
- * The software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * THE FOLLOWING PARAMETERS ARE UNDEFINED:
- *
- * EASYCAP_DEBUG
- *
- * IF REQUIRED THEY MUST BE EXTERNALLY DEFINED, FOR EXAMPLE AS COMPILER
- * OPTIONS.
- */
-/*---------------------------------------------------------------------------*/
-
-#ifndef __EASYCAP_H__
-#define __EASYCAP_H__
-
-/*---------------------------------------------------------------------------*/
-/*
- * THESE ARE NORMALLY DEFINED
- */
-/*---------------------------------------------------------------------------*/
-#define PATIENCE 500
-#define PERSEVERE
-/*---------------------------------------------------------------------------*/
-/*
- * THESE ARE FOR MAINTENANCE ONLY - NORMALLY UNDEFINED:
- */
-/*---------------------------------------------------------------------------*/
-#undef EASYCAP_TESTCARD
-/*---------------------------------------------------------------------------*/
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/kref.h>
-#include <linux/usb.h>
-#include <linux/uaccess.h>
-
-#include <linux/i2c.h>
-#include <linux/workqueue.h>
-#include <linux/poll.h>
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/types.h>
-
-#include <linux/vmalloc.h>
-#include <linux/sound.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/info.h>
-#include <sound/initval.h>
-#include <sound/control.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-device.h>
-#include <linux/videodev2.h>
-#include <linux/soundcard.h>
-
-/*---------------------------------------------------------------------------*/
-/* VENDOR, PRODUCT: Syntek Semiconductor Co., Ltd
- *
- * EITHER EasyCAP USB 2.0 Video Adapter with Audio, Model No. DC60
- * with input cabling: AUDIO(L), AUDIO(R), CVBS, S-VIDEO.
- *
- * OR EasyCAP 4CHANNEL USB 2.0 DVR, Model No. EasyCAP002
- * with input cabling: MICROPHONE, CVBS1, CVBS2, CVBS3, CVBS4.
- */
-/*---------------------------------------------------------------------------*/
-#define USB_EASYCAP_VENDOR_ID 0x05e1
-#define USB_EASYCAP_PRODUCT_ID 0x0408
-
-#define EASYCAP_DRIVER_VERSION "0.9.01"
-#define EASYCAP_DRIVER_DESCRIPTION "easycapdc60"
-
-#define USB_SKEL_MINOR_BASE 192
-#define DONGLE_MANY 8
-#define INPUT_MANY 6
-/*---------------------------------------------------------------------------*/
-/*
- * DEFAULT LUMINANCE, CONTRAST, SATURATION AND HUE
- */
-/*---------------------------------------------------------------------------*/
-#define SAA_0A_DEFAULT 0x7F
-#define SAA_0B_DEFAULT 0x3F
-#define SAA_0C_DEFAULT 0x2F
-#define SAA_0D_DEFAULT 0x00
-/*---------------------------------------------------------------------------*/
-/*
- * VIDEO STREAMING PARAMETERS:
- * USB 2.0 PROVIDES FOR HIGH-BANDWIDTH ENDPOINTS WITH AN UPPER LIMIT
- * OF 3072 BYTES PER MICROFRAME for wMaxPacketSize.
- */
-/*---------------------------------------------------------------------------*/
-#define VIDEO_ISOC_BUFFER_MANY 16
-#define VIDEO_ISOC_ORDER 3
-#define VIDEO_ISOC_FRAMESPERDESC ((unsigned int) 1 << VIDEO_ISOC_ORDER)
-#define USB_2_0_MAXPACKETSIZE 3072
-#if (USB_2_0_MAXPACKETSIZE > PAGE_SIZE)
-#error video_isoc_buffer[.] will not be big enough
-#endif
-#define VIDEO_JUNK_TOLERATE VIDEO_ISOC_BUFFER_MANY
-#define VIDEO_LOST_TOLERATE 50
-/*---------------------------------------------------------------------------*/
-/*
- * VIDEO BUFFERS
- */
-/*---------------------------------------------------------------------------*/
-#define FIELD_BUFFER_SIZE (203 * PAGE_SIZE)
-#define FRAME_BUFFER_SIZE (405 * PAGE_SIZE)
-#define FIELD_BUFFER_MANY 4
-#define FRAME_BUFFER_MANY 6
-/*---------------------------------------------------------------------------*/
-/*
- * AUDIO STREAMING PARAMETERS
- */
-/*---------------------------------------------------------------------------*/
-#define AUDIO_ISOC_BUFFER_MANY 16
-#define AUDIO_ISOC_ORDER 1
-#define AUDIO_ISOC_FRAMESPERDESC 32
-#define AUDIO_ISOC_BUFFER_SIZE (PAGE_SIZE << AUDIO_ISOC_ORDER)
-/*---------------------------------------------------------------------------*/
-/*
- * AUDIO BUFFERS
- */
-/*---------------------------------------------------------------------------*/
-#define AUDIO_FRAGMENT_MANY 32
-#define PAGES_PER_AUDIO_FRAGMENT 4
-/*---------------------------------------------------------------------------*/
-/*
- * IT IS ESSENTIAL THAT EVEN-NUMBERED STANDARDS ARE 25 FRAMES PER SECOND,
- * ODD-NUMBERED STANDARDS ARE 30 FRAMES PER SECOND.
- * THE NUMBERING OF STANDARDS MUST NOT BE CHANGED WITHOUT DUE CARE. NOT
- * ONLY MUST THE PARAMETER
- * STANDARD_MANY
- * BE CHANGED TO CORRESPOND TO THE NEW NUMBER OF STANDARDS, BUT ALSO THE
- * NUMBERING MUST REMAIN AN UNBROKEN ASCENDING SEQUENCE: DUMMY STANDARDS
- * MAY NEED TO BE ADDED. APPROPRIATE CHANGES WILL ALWAYS BE REQUIRED IN
- * ROUTINE fillin_formats() AND POSSIBLY ELSEWHERE. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
-#define PAL_BGHIN 0
-#define PAL_Nc 2
-#define SECAM 4
-#define NTSC_N 6
-#define NTSC_N_443 8
-#define NTSC_M 1
-#define NTSC_443 3
-#define NTSC_M_JP 5
-#define PAL_60 7
-#define PAL_M 9
-#define PAL_BGHIN_SLOW 10
-#define PAL_Nc_SLOW 12
-#define SECAM_SLOW 14
-#define NTSC_N_SLOW 16
-#define NTSC_N_443_SLOW 18
-#define NTSC_M_SLOW 11
-#define NTSC_443_SLOW 13
-#define NTSC_M_JP_SLOW 15
-#define PAL_60_SLOW 17
-#define PAL_M_SLOW 19
-#define STANDARD_MANY 20
-/*---------------------------------------------------------------------------*/
-/*
- * ENUMS
- */
-/*---------------------------------------------------------------------------*/
-enum {
- AT_720x576,
- AT_704x576,
- AT_640x480,
- AT_720x480,
- AT_360x288,
- AT_320x240,
- AT_360x240,
- RESOLUTION_MANY
-};
-enum {
- FMT_UYVY,
- FMT_YUY2,
- FMT_RGB24,
- FMT_RGB32,
- FMT_BGR24,
- FMT_BGR32,
- PIXELFORMAT_MANY
-};
-enum {
- FIELD_NONE,
- FIELD_INTERLACED,
- INTERLACE_MANY
-};
-#define SETTINGS_MANY (STANDARD_MANY * \
- RESOLUTION_MANY * \
- 2 * \
- PIXELFORMAT_MANY * \
- INTERLACE_MANY)
-/*---------------------------------------------------------------------------*/
-/*
- * STRUCTURE DEFINITIONS
- */
-/*---------------------------------------------------------------------------*/
-struct easycap_dongle {
- struct easycap *peasycap;
- struct mutex mutex_video;
- struct mutex mutex_audio;
-};
-/*---------------------------------------------------------------------------*/
-struct data_buffer {
- struct list_head list_head;
- void *pgo;
- void *pto;
- u16 kount;
- u16 input;
-};
-/*---------------------------------------------------------------------------*/
-struct data_urb {
- struct list_head list_head;
- struct urb *purb;
- int isbuf;
- int length;
-};
-/*---------------------------------------------------------------------------*/
-struct easycap_standard {
- u16 mask;
-struct v4l2_standard v4l2_standard;
-};
-struct easycap_format {
- u16 mask;
- char name[128];
-struct v4l2_format v4l2_format;
-};
-struct inputset {
- int input;
- int input_ok;
- int standard_offset;
- int standard_offset_ok;
- int format_offset;
- int format_offset_ok;
- int brightness;
- int brightness_ok;
- int contrast;
- int contrast_ok;
- int saturation;
- int saturation_ok;
- int hue;
- int hue_ok;
-};
-/*---------------------------------------------------------------------------*/
-/*
- * easycap.ilk == 0 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=256
- * easycap.ilk == 2 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=9
- * easycap.ilk == 3 => FOUR-CVBS HARDWARE, AUDIO wMaxPacketSize=9
- */
-/*---------------------------------------------------------------------------*/
-struct easycap {
- int isdongle;
- int minor;
-
- struct video_device video_device;
- struct v4l2_device v4l2_device;
-
- int status;
- unsigned int audio_pages_per_fragment;
- unsigned int audio_bytes_per_fragment;
- unsigned int audio_buffer_page_many;
-
-#define UPSAMPLE
-#ifdef UPSAMPLE
- s16 oldaudio;
-#endif /*UPSAMPLE*/
-
- int ilk;
- bool microphone;
-
- struct usb_device *pusb_device;
- struct usb_interface *pusb_interface;
-
- struct kref kref;
-
- int queued[FRAME_BUFFER_MANY];
- int done[FRAME_BUFFER_MANY];
-
- wait_queue_head_t wq_video;
- wait_queue_head_t wq_audio;
- wait_queue_head_t wq_trigger;
-
- int input;
- int polled;
- int standard_offset;
- int format_offset;
- struct inputset inputset[INPUT_MANY];
-
- bool ntsc;
- int fps;
- int usec;
- int tolerate;
- int skip;
- int skipped;
- int lost[INPUT_MANY];
- int merit[180];
-
- long long int dnbydt;
-
- int video_interface;
- int video_altsetting_on;
- int video_altsetting_off;
- int video_endpointnumber;
- int video_isoc_maxframesize;
- int video_isoc_buffer_size;
- int video_isoc_framesperdesc;
-
- int video_isoc_streaming;
- int video_isoc_sequence;
- int video_idle;
- int video_eof;
- int video_junk;
-
- struct data_buffer video_isoc_buffer[VIDEO_ISOC_BUFFER_MANY];
- struct data_buffer field_buffer[FIELD_BUFFER_MANY]
- [(FIELD_BUFFER_SIZE/PAGE_SIZE)];
- struct data_buffer frame_buffer[FRAME_BUFFER_MANY]
- [(FRAME_BUFFER_SIZE/PAGE_SIZE)];
-
- struct list_head urb_video_head;
- struct list_head *purb_video_head;
-
- u8 cache[8];
- u8 *pcache;
- int video_mt;
- int audio_mt;
- long long audio_bytes;
- u32 isequence;
-
- int vma_many;
-/*---------------------------------------------------------------------------*/
-/*
- * BUFFER INDICATORS
- */
-/*---------------------------------------------------------------------------*/
- int field_fill; /* Field buffer being filled by easycap_complete(). */
- /* Bumped only by easycap_complete(). */
- int field_page; /* Page of field buffer page being filled by */
- /* easycap_complete(). */
- int field_read; /* Field buffer to be read by field2frame(). */
- /* Bumped only by easycap_complete(). */
- int frame_fill; /* Frame buffer being filled by field2frame(). */
- /* Bumped only by easycap_dqbuf() when */
- /* field2frame() has created a complete frame. */
- int frame_read; /* Frame buffer offered to user by DQBUF. */
- /* Set only by easycap_dqbuf() to trail frame_fill.*/
- int frame_lock; /* Flag set to 1 by DQBUF and cleared by QBUF */
-/*---------------------------------------------------------------------------*/
-/*
- * IMAGE PROPERTIES
- */
-/*---------------------------------------------------------------------------*/
- u32 pixelformat;
- int width;
- int height;
- int bytesperpixel;
- bool byteswaporder;
- bool decimatepixel;
- bool offerfields;
- int frame_buffer_used;
- int frame_buffer_many;
- int videofieldamount;
-
- int brightness;
- int contrast;
- int saturation;
- int hue;
-
- int allocation_video_urb;
- int allocation_video_page;
- int allocation_video_struct;
- int registered_video;
-/*---------------------------------------------------------------------------*/
-/*
- * ALSA
- */
-/*---------------------------------------------------------------------------*/
- struct snd_pcm_hardware alsa_hardware;
- struct snd_card *psnd_card;
- struct snd_pcm *psnd_pcm;
- struct snd_pcm_substream *psubstream;
- int dma_fill;
- int dma_next;
- int dma_read;
-/*---------------------------------------------------------------------------*/
-/*
- * SOUND PROPERTIES
- */
-/*---------------------------------------------------------------------------*/
- int audio_interface;
- int audio_altsetting_on;
- int audio_altsetting_off;
- int audio_endpointnumber;
- int audio_isoc_maxframesize;
- int audio_isoc_buffer_size;
- int audio_isoc_framesperdesc;
-
- int audio_isoc_streaming;
- int audio_idle;
- int audio_eof;
- int volume;
- int mute;
- s8 gain;
-
- struct data_buffer audio_isoc_buffer[AUDIO_ISOC_BUFFER_MANY];
-
- struct list_head urb_audio_head;
- struct list_head *purb_audio_head;
-/*---------------------------------------------------------------------------*/
-/*
- * BUFFER INDICATORS
- */
-/*---------------------------------------------------------------------------*/
- int audio_fill; /* Audio buffer being filled by easycap_complete(). */
- /* Bumped only by easycap_complete(). */
- int audio_read; /* Audio buffer page being read by easycap_read(). */
- /* Set by easycap_read() to trail audio_fill by */
- /* one fragment. */
-/*---------------------------------------------------------------------------*/
-/*
- * SOUND PROPERTIES
- */
-/*---------------------------------------------------------------------------*/
-
- int audio_buffer_many;
-
- int allocation_audio_urb;
- int allocation_audio_page;
- int allocation_audio_struct;
- int registered_audio;
-
- long long int audio_sample;
- long long int audio_niveau;
- long long int audio_square;
-
- struct data_buffer audio_buffer[];
-};
-/*---------------------------------------------------------------------------*/
-/*
- * VIDEO FUNCTION PROTOTYPES
- */
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-long easycap_unlocked_ioctl(struct file *, unsigned int, unsigned long);
-int easycap_dqbuf(struct easycap *, int);
-int submit_video_urbs(struct easycap *);
-int kill_video_urbs(struct easycap *);
-int field2frame(struct easycap *);
-int redaub(struct easycap *, void *, void *,
- int, int, u8, u8, bool);
-void easycap_testcard(struct easycap *, int);
-int fillin_formats(void);
-int newinput(struct easycap *, int);
-int adjust_standard(struct easycap *, v4l2_std_id);
-int adjust_format(struct easycap *, u32, u32, u32,
- int, bool);
-int adjust_brightness(struct easycap *, int);
-int adjust_contrast(struct easycap *, int);
-int adjust_saturation(struct easycap *, int);
-int adjust_hue(struct easycap *, int);
-int adjust_volume(struct easycap *, int);
-/*---------------------------------------------------------------------------*/
-/*
- * AUDIO FUNCTION PROTOTYPES
- */
-/*---------------------------------------------------------------------------*/
-int easycap_alsa_probe(struct easycap *);
-void easycap_alsa_complete(struct urb *);
-
-int easycap_sound_setup(struct easycap *);
-int submit_audio_urbs(struct easycap *);
-int kill_audio_urbs(struct easycap *);
-void easyoss_testtone(struct easycap *, int);
-int audio_setup(struct easycap *);
-/*---------------------------------------------------------------------------*/
-/*
- * LOW-LEVEL FUNCTION PROTOTYPES
- */
-/*---------------------------------------------------------------------------*/
-int audio_gainget(struct usb_device *);
-int audio_gainset(struct usb_device *, s8);
-
-int set_interface(struct usb_device *, u16);
-int wakeup_device(struct usb_device *);
-int confirm_resolution(struct usb_device *);
-int confirm_stream(struct usb_device *);
-
-int setup_stk(struct usb_device *, bool);
-int setup_saa(struct usb_device *, bool);
-int setup_vt(struct usb_device *);
-int check_stk(struct usb_device *, bool);
-int check_saa(struct usb_device *, bool);
-int ready_saa(struct usb_device *);
-int merit_saa(struct usb_device *);
-int check_vt(struct usb_device *);
-int select_input(struct usb_device *, int, int);
-int set_resolution(struct usb_device *,
- u16, u16, u16, u16);
-
-int read_saa(struct usb_device *, u16);
-int read_stk(struct usb_device *, u32);
-int write_saa(struct usb_device *, u16, u16);
-int write_000(struct usb_device *, u16, u16);
-int start_100(struct usb_device *);
-int stop_100(struct usb_device *);
-int write_300(struct usb_device *);
-int read_vt(struct usb_device *, u16);
-int write_vt(struct usb_device *, u16, u16);
-int isdongle(struct easycap *);
-/*---------------------------------------------------------------------------*/
-
-
-/*---------------------------------------------------------------------------*/
-/*
- * MACROS SAM(...) AND JOM(...) ALLOW DIAGNOSTIC OUTPUT TO BE TAGGED WITH
- * THE IDENTITY OF THE DONGLE TO WHICH IT APPLIES, BUT IF INVOKED WHEN THE
- * POINTER peasycap IS INVALID AN Oops IS LIKELY, AND ITS CAUSE MAY NOT BE
- * IMMEDIATELY OBVIOUS FROM A CASUAL READING OF THE SOURCE CODE. BEWARE.
-*/
-/*---------------------------------------------------------------------------*/
-const char *strerror(int err);
-
-#define SAY(format, args...) do { \
- printk(KERN_DEBUG "easycap:: %s: " \
- format, __func__, ##args); \
-} while (0)
-#define SAM(format, args...) do { \
- printk(KERN_DEBUG "easycap::%i%s: " \
- format, peasycap->isdongle, __func__, ##args);\
-} while (0)
-
-#ifdef CONFIG_EASYCAP_DEBUG
-extern int easycap_debug;
-#define JOT(n, format, args...) do { \
- if (n <= easycap_debug) { \
- printk(KERN_DEBUG "easycap:: %s: " \
- format, __func__, ##args);\
- } \
-} while (0)
-#define JOM(n, format, args...) do { \
- if (n <= easycap_debug) { \
- printk(KERN_DEBUG "easycap::%i%s: " \
- format, peasycap->isdongle, __func__, ##args);\
- } \
-} while (0)
-
-#else
-#define JOT(n, format, args...) do {} while (0)
-#define JOM(n, format, args...) do {} while (0)
-#endif /* CONFIG_EASYCAP_DEBUG */
-
-/*---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------*/
-/* globals
- */
-/*---------------------------------------------------------------------------*/
-
-extern bool easycap_readback;
-extern const struct easycap_standard easycap_standard[];
-extern struct easycap_format easycap_format[];
-extern struct v4l2_queryctrl easycap_control[];
-extern struct usb_driver easycap_usb_driver;
-extern struct easycap_dongle easycapdc60_dongle[];
-
-#endif /* !__EASYCAP_H__ */
+++ /dev/null
-/******************************************************************************
-* *
-* easycap_ioctl.c *
-* *
-******************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This 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.
- *
- * The software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-
-#include <linux/version.h>
-#include "easycap.h"
-
-/*--------------------------------------------------------------------------*/
-/*
- * UNLESS THERE IS A PREMATURE ERROR RETURN THIS ROUTINE UPDATES THE
- * FOLLOWING:
- * peasycap->standard_offset
- * peasycap->inputset[peasycap->input].standard_offset
- * peasycap->fps
- * peasycap->usec
- * peasycap->tolerate
- * peasycap->skip
- */
-/*---------------------------------------------------------------------------*/
-int adjust_standard(struct easycap *peasycap, v4l2_std_id std_id)
-{
- struct easycap_standard const *peasycap_standard;
- u16 reg, set;
- int ir, rc, need, k;
- unsigned int itwas, isnow;
- bool resubmit;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- peasycap_standard = &easycap_standard[0];
- while (0xFFFF != peasycap_standard->mask) {
- if (std_id == peasycap_standard->v4l2_standard.id)
- break;
- peasycap_standard++;
- }
- if (0xFFFF == peasycap_standard->mask) {
- peasycap_standard = &easycap_standard[0];
- while (0xFFFF != peasycap_standard->mask) {
- if (std_id & peasycap_standard->v4l2_standard.id)
- break;
- peasycap_standard++;
- }
- }
- if (0xFFFF == peasycap_standard->mask) {
- SAM("ERROR: 0x%08X=std_id: standard not found\n",
- (unsigned int)std_id);
- return -EINVAL;
- }
- SAM("selected standard: %s\n",
- &(peasycap_standard->v4l2_standard.name[0]));
- if (peasycap->standard_offset == peasycap_standard - easycap_standard) {
- SAM("requested standard already in effect\n");
- return 0;
- }
- peasycap->standard_offset = peasycap_standard - easycap_standard;
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].standard_offset_ok) {
- peasycap->inputset[k].standard_offset =
- peasycap->standard_offset;
- }
- }
- if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
- peasycap->inputset[peasycap->input].standard_offset =
- peasycap->standard_offset;
- peasycap->inputset[peasycap->input].standard_offset_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
-
- peasycap->fps = peasycap_standard->v4l2_standard.frameperiod.denominator /
- peasycap_standard->v4l2_standard.frameperiod.numerator;
- switch (peasycap->fps) {
- case 6:
- case 30: {
- peasycap->ntsc = true;
- break;
- }
- case 5:
- case 25: {
- peasycap->ntsc = false;
- break;
- }
- default: {
- SAM("MISTAKE: %i=frames-per-second\n", peasycap->fps);
- return -ENOENT;
- }
- }
- JOM(8, "%i frames-per-second\n", peasycap->fps);
- if (0x8000 & peasycap_standard->mask) {
- peasycap->skip = 5;
- peasycap->usec = 1000000 / (2 * (5 * peasycap->fps));
- peasycap->tolerate = 1000 * (25 / (5 * peasycap->fps));
- } else {
- peasycap->skip = 0;
- peasycap->usec = 1000000 / (2 * peasycap->fps);
- peasycap->tolerate = 1000 * (25 / peasycap->fps);
- }
- if (peasycap->video_isoc_streaming) {
- resubmit = true;
- kill_video_urbs(peasycap);
- } else
- resubmit = false;
-/*--------------------------------------------------------------------------*/
-/*
- * SAA7113H DATASHEET PAGE 44, TABLE 42
- */
-/*--------------------------------------------------------------------------*/
- need = 0;
- itwas = 0;
- reg = 0x00;
- set = 0x00;
- switch (peasycap_standard->mask & 0x000F) {
- case NTSC_M_JP: {
- reg = 0x0A;
- set = 0x95;
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: cannot read SAA register 0x%02X\n", reg);
- else
- itwas = (unsigned int)ir;
- rc = write_saa(peasycap->pusb_device, reg, set);
- if (rc)
- SAM("ERROR: failed to set SAA register "
- "0x%02X to 0x%02X for JP standard\n", reg, set);
- else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed "
- "to 0x%02X\n", reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
-
- reg = 0x0B;
- set = 0x48;
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: cannot read SAA register 0x%02X\n", reg);
- else
- itwas = (unsigned int)ir;
- rc = write_saa(peasycap->pusb_device, reg, set);
- if (rc)
- SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X "
- "for JP standard\n", reg, set);
- else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed "
- "to 0x%02X\n", reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
-/*--------------------------------------------------------------------------*/
-/*
- * NOTE: NO break HERE: RUN ON TO NEXT CASE
- */
-/*--------------------------------------------------------------------------*/
- }
- case NTSC_M:
- case PAL_BGHIN: {
- reg = 0x0E;
- set = 0x01;
- need = 1;
- break;
- }
- case NTSC_N_443:
- case PAL_60: {
- reg = 0x0E;
- set = 0x11;
- need = 1;
- break;
- }
- case NTSC_443:
- case PAL_Nc: {
- reg = 0x0E;
- set = 0x21;
- need = 1;
- break;
- }
- case NTSC_N:
- case PAL_M: {
- reg = 0x0E;
- set = 0x31;
- need = 1;
- break;
- }
- case SECAM: {
- reg = 0x0E;
- set = 0x51;
- need = 1;
- break;
- }
- default:
- break;
- }
-/*--------------------------------------------------------------------------*/
- if (need) {
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: failed to read SAA register 0x%02X\n", reg);
- else
- itwas = (unsigned int)ir;
- rc = write_saa(peasycap->pusb_device, reg, set);
- if (0 != write_saa(peasycap->pusb_device, reg, set)) {
- SAM("ERROR: failed to set SAA register "
- "0x%02X to 0x%02X for table 42\n", reg, set);
- } else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed "
- "to 0x%02X\n", reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
- }
-/*--------------------------------------------------------------------------*/
-/*
- * SAA7113H DATASHEET PAGE 41
- */
-/*--------------------------------------------------------------------------*/
- reg = 0x08;
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: failed to read SAA register 0x%02X "
- "so cannot reset\n", reg);
- else {
- itwas = (unsigned int)ir;
- if (peasycap_standard->mask & 0x0001)
- set = itwas | 0x40 ;
- else
- set = itwas & ~0x40 ;
- rc = write_saa(peasycap->pusb_device, reg, set);
- if (rc)
- SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
- reg, set);
- else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed to 0x%02X\n",
- reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
- }
-/*--------------------------------------------------------------------------*/
-/*
- * SAA7113H DATASHEET PAGE 51, TABLE 57
- */
-/*---------------------------------------------------------------------------*/
- reg = 0x40;
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: failed to read SAA register 0x%02X "
- "so cannot reset\n", reg);
- else {
- itwas = (unsigned int)ir;
- if (peasycap_standard->mask & 0x0001)
- set = itwas | 0x80 ;
- else
- set = itwas & ~0x80 ;
- rc = write_saa(peasycap->pusb_device, reg, set);
- if (rc)
- SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
- reg, set);
- else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed to 0x%02X\n",
- reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
- }
-/*--------------------------------------------------------------------------*/
-/*
- * SAA7113H DATASHEET PAGE 53, TABLE 66
- */
-/*--------------------------------------------------------------------------*/
- reg = 0x5A;
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: failed to read SAA register 0x%02X but continuing\n", reg);
- itwas = (unsigned int)ir;
- if (peasycap_standard->mask & 0x0001)
- set = 0x0A ;
- else
- set = 0x07 ;
- if (0 != write_saa(peasycap->pusb_device, reg, set))
- SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
- reg, set);
- else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed "
- "to 0x%02X\n", reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
- if (resubmit)
- submit_video_urbs(peasycap);
- return 0;
-}
-/*****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * THE ALGORITHM FOR RESPONDING TO THE VIDIO_S_FMT IOCTL REQUIRES
- * A VALID VALUE OF peasycap->standard_offset, OTHERWISE -EBUSY IS RETURNED.
- *
- * PROVIDED THE ARGUMENT try IS false AND THERE IS NO PREMATURE ERROR RETURN
- * THIS ROUTINE UPDATES THE FOLLOWING:
- * peasycap->format_offset
- * peasycap->inputset[peasycap->input].format_offset
- * peasycap->pixelformat
- * peasycap->height
- * peasycap->width
- * peasycap->bytesperpixel
- * peasycap->byteswaporder
- * peasycap->decimatepixel
- * peasycap->frame_buffer_used
- * peasycap->videofieldamount
- * peasycap->offerfields
- *
- * IF SUCCESSFUL THE FUNCTION RETURNS THE OFFSET IN easycap_format[]
- * IDENTIFYING THE FORMAT WHICH IS TO RETURNED TO THE USER.
- * ERRORS RETURN A NEGATIVE NUMBER.
- */
-/*--------------------------------------------------------------------------*/
-int adjust_format(struct easycap *peasycap,
- u32 width, u32 height, u32 pixelformat, int field, bool try)
-{
- struct easycap_format *peasycap_format, *peasycap_best_format;
- u16 mask;
- struct usb_device *p;
- int miss, multiplier, best, k;
- char bf[5], fo[32], *pc;
- u32 uc;
- bool resubmit;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (0 > peasycap->standard_offset) {
- JOM(8, "%i=peasycap->standard_offset\n", peasycap->standard_offset);
- return -EBUSY;
- }
- p = peasycap->pusb_device;
- if (!p) {
- SAM("ERROR: peaycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- pc = &bf[0];
- uc = pixelformat;
- memcpy((void *)pc, (void *)(&uc), 4);
- bf[4] = 0;
- mask = 0xFF & easycap_standard[peasycap->standard_offset].mask;
- SAM("sought: %ix%i,%s(0x%08X),%i=field,0x%02X=std mask\n",
- width, height, pc, pixelformat, field, mask);
- switch (field) {
- case V4L2_FIELD_ANY: {
- strcpy(&fo[0], "V4L2_FIELD_ANY ");
- break;
- }
- case V4L2_FIELD_NONE: {
- strcpy(&fo[0], "V4L2_FIELD_NONE");
- break;
- }
- case V4L2_FIELD_TOP: {
- strcpy(&fo[0], "V4L2_FIELD_TOP");
- break;
- }
- case V4L2_FIELD_BOTTOM: {
- strcpy(&fo[0], "V4L2_FIELD_BOTTOM");
- break;
- }
- case V4L2_FIELD_INTERLACED: {
- strcpy(&fo[0], "V4L2_FIELD_INTERLACED");
- break;
- }
- case V4L2_FIELD_SEQ_TB: {
- strcpy(&fo[0], "V4L2_FIELD_SEQ_TB");
- break;
- }
- case V4L2_FIELD_SEQ_BT: {
- strcpy(&fo[0], "V4L2_FIELD_SEQ_BT");
- break;
- }
- case V4L2_FIELD_ALTERNATE: {
- strcpy(&fo[0], "V4L2_FIELD_ALTERNATE");
- break;
- }
- case V4L2_FIELD_INTERLACED_TB: {
- strcpy(&fo[0], "V4L2_FIELD_INTERLACED_TB");
- break;
- }
- case V4L2_FIELD_INTERLACED_BT: {
- strcpy(&fo[0], "V4L2_FIELD_INTERLACED_BT");
- break;
- }
- default: {
- strcpy(&fo[0], "V4L2_FIELD_... UNKNOWN ");
- break;
- }
- }
- SAM("sought: %s\n", &fo[0]);
- if (V4L2_FIELD_ANY == field) {
- field = V4L2_FIELD_NONE;
- SAM("prefer: V4L2_FIELD_NONE=field, was V4L2_FIELD_ANY\n");
- }
- peasycap_best_format = NULL;
- peasycap_format = &easycap_format[0];
- while (0 != peasycap_format->v4l2_format.fmt.pix.width) {
- JOM(16, ".> %i %i 0x%08X %ix%i\n",
- peasycap_format->mask & 0x01,
- peasycap_format->v4l2_format.fmt.pix.field,
- peasycap_format->v4l2_format.fmt.pix.pixelformat,
- peasycap_format->v4l2_format.fmt.pix.width,
- peasycap_format->v4l2_format.fmt.pix.height);
-
- if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) &&
- (peasycap_format->v4l2_format.fmt.pix.field == field) &&
- (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat) &&
- (peasycap_format->v4l2_format.fmt.pix.width == width) &&
- (peasycap_format->v4l2_format.fmt.pix.height == height)) {
-
- peasycap_best_format = peasycap_format;
- break;
- }
- peasycap_format++;
- }
- if (0 == peasycap_format->v4l2_format.fmt.pix.width) {
- SAM("cannot do: %ix%i with standard mask 0x%02X\n",
- width, height, mask);
- peasycap_format = &easycap_format[0];
- best = -1;
- while (0 != peasycap_format->v4l2_format.fmt.pix.width) {
- if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) &&
- (peasycap_format->v4l2_format.fmt.pix.field == field) &&
- (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat)) {
-
- miss = abs(peasycap_format->v4l2_format.fmt.pix.width - width);
- if ((best > miss) || (best < 0)) {
- best = miss;
- peasycap_best_format = peasycap_format;
- if (!miss)
- break;
- }
- }
- peasycap_format++;
- }
- if (-1 == best) {
- SAM("cannot do %ix... with standard mask 0x%02X\n",
- width, mask);
- SAM("cannot do ...x%i with standard mask 0x%02X\n",
- height, mask);
- SAM(" %ix%i unmatched\n", width, height);
- return peasycap->format_offset;
- }
- }
- if (!peasycap_best_format) {
- SAM("MISTAKE: peasycap_best_format is NULL");
- return -EINVAL;
- }
- peasycap_format = peasycap_best_format;
-
-/*...........................................................................*/
- if (try)
- return peasycap_best_format - easycap_format;
-/*...........................................................................*/
-
- if (false != try) {
- SAM("MISTAKE: true==try where is should be false\n");
- return -EINVAL;
- }
- SAM("actioning: %ix%i %s\n",
- peasycap_format->v4l2_format.fmt.pix.width,
- peasycap_format->v4l2_format.fmt.pix.height,
- &peasycap_format->name[0]);
- peasycap->height = peasycap_format->v4l2_format.fmt.pix.height;
- peasycap->width = peasycap_format->v4l2_format.fmt.pix.width;
- peasycap->pixelformat = peasycap_format->v4l2_format.fmt.pix.pixelformat;
- peasycap->format_offset = peasycap_format - easycap_format;
-
-
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].format_offset_ok) {
- peasycap->inputset[k].format_offset =
- peasycap->format_offset;
- }
- }
- if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
- peasycap->inputset[peasycap->input].format_offset =
- peasycap->format_offset;
- peasycap->inputset[peasycap->input].format_offset_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
-
-
-
- peasycap->bytesperpixel = (0x00E0 & peasycap_format->mask) >> 5 ;
- if (0x0100 & peasycap_format->mask)
- peasycap->byteswaporder = true;
- else
- peasycap->byteswaporder = false;
- if (0x0200 & peasycap_format->mask)
- peasycap->skip = 5;
- else
- peasycap->skip = 0;
- if (0x0800 & peasycap_format->mask)
- peasycap->decimatepixel = true;
- else
- peasycap->decimatepixel = false;
- if (0x1000 & peasycap_format->mask)
- peasycap->offerfields = true;
- else
- peasycap->offerfields = false;
- if (peasycap->decimatepixel)
- multiplier = 2;
- else
- multiplier = 1;
- peasycap->videofieldamount =
- multiplier * peasycap->width * multiplier * peasycap->height;
- peasycap->frame_buffer_used =
- peasycap->bytesperpixel * peasycap->width * peasycap->height;
- if (peasycap->video_isoc_streaming) {
- resubmit = true;
- kill_video_urbs(peasycap);
- } else
- resubmit = false;
-/*---------------------------------------------------------------------------*/
-/*
- * PAL
- */
-/*---------------------------------------------------------------------------*/
- if (0 == (0x01 & peasycap_format->mask)) {
- if (((720 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (576 == peasycap_format->v4l2_format.fmt.pix.height)) ||
- ((360 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (288 == peasycap_format->v4l2_format.fmt.pix.height))) {
- if (set_resolution(p, 0x0000, 0x0001, 0x05A0, 0x0121)) {
- SAM("ERROR: set_resolution() failed\n");
- return -EINVAL;
- }
- } else if ((704 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (576 == peasycap_format->v4l2_format.fmt.pix.height)) {
- if (set_resolution(p, 0x0004, 0x0001, 0x0584, 0x0121)) {
- SAM("ERROR: set_resolution() failed\n");
- return -EINVAL;
- }
- } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
- ((320 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
- if (set_resolution(p, 0x0014, 0x0020, 0x0514, 0x0110)) {
- SAM("ERROR: set_resolution() failed\n");
- return -EINVAL;
- }
- } else {
- SAM("MISTAKE: bad format, cannot set resolution\n");
- return -EINVAL;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * NTSC
- */
-/*---------------------------------------------------------------------------*/
- } else {
- if (((720 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
- ((360 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
- if (set_resolution(p, 0x0000, 0x0003, 0x05A0, 0x00F3)) {
- SAM("ERROR: set_resolution() failed\n");
- return -EINVAL;
- }
- } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
- ((320 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
- if (set_resolution(p, 0x0014, 0x0003, 0x0514, 0x00F3)) {
- SAM("ERROR: set_resolution() failed\n");
- return -EINVAL;
- }
- } else {
- SAM("MISTAKE: bad format, cannot set resolution\n");
- return -EINVAL;
- }
- }
-/*---------------------------------------------------------------------------*/
- if (resubmit)
- submit_video_urbs(peasycap);
-
- return peasycap_best_format - easycap_format;
-}
-/*****************************************************************************/
-int adjust_brightness(struct easycap *peasycap, int value)
-{
- unsigned int mood;
- int i1, k;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_BRIGHTNESS == easycap_control[i1].id) {
- if ((easycap_control[i1].minimum > value) ||
- (easycap_control[i1].maximum < value))
- value = easycap_control[i1].default_value;
-
- if ((easycap_control[i1].minimum <= peasycap->brightness) &&
- (easycap_control[i1].maximum >= peasycap->brightness)) {
- if (peasycap->brightness == value) {
- SAM("unchanged brightness at 0x%02X\n",
- value);
- return 0;
- }
- }
- peasycap->brightness = value;
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].brightness_ok)
- peasycap->inputset[k].brightness =
- peasycap->brightness;
- }
- if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
- peasycap->inputset[peasycap->input].brightness =
- peasycap->brightness;
- peasycap->inputset[peasycap->input].brightness_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
- mood = 0x00FF & (unsigned int)peasycap->brightness;
- if (!write_saa(peasycap->pusb_device, 0x0A, mood)) {
- SAM("adjusting brightness to 0x%02X\n", mood);
- return 0;
- } else {
- SAM("WARNING: failed to adjust brightness "
- "to 0x%02X\n", mood);
- return -ENOENT;
- }
- break;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust brightness: control not found\n");
- return -ENOENT;
-}
-/*****************************************************************************/
-int adjust_contrast(struct easycap *peasycap, int value)
-{
- unsigned int mood;
- int i1, k;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_CONTRAST == easycap_control[i1].id) {
- if ((easycap_control[i1].minimum > value) ||
- (easycap_control[i1].maximum < value))
- value = easycap_control[i1].default_value;
-
-
- if ((easycap_control[i1].minimum <= peasycap->contrast) &&
- (easycap_control[i1].maximum >= peasycap->contrast)) {
- if (peasycap->contrast == value) {
- SAM("unchanged contrast at 0x%02X\n", value);
- return 0;
- }
- }
- peasycap->contrast = value;
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].contrast_ok)
- peasycap->inputset[k].contrast = peasycap->contrast;
- }
-
- if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
- peasycap->inputset[peasycap->input].contrast =
- peasycap->contrast;
- peasycap->inputset[peasycap->input].contrast_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
-
- mood = 0x00FF & (unsigned int) (peasycap->contrast - 128);
- if (!write_saa(peasycap->pusb_device, 0x0B, mood)) {
- SAM("adjusting contrast to 0x%02X\n", mood);
- return 0;
- } else {
- SAM("WARNING: failed to adjust contrast to "
- "0x%02X\n", mood);
- return -ENOENT;
- }
- break;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust contrast: control not found\n");
- return -ENOENT;
-}
-/*****************************************************************************/
-int adjust_saturation(struct easycap *peasycap, int value)
-{
- unsigned int mood;
- int i1, k;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_SATURATION == easycap_control[i1].id) {
- if ((easycap_control[i1].minimum > value) ||
- (easycap_control[i1].maximum < value))
- value = easycap_control[i1].default_value;
-
-
- if ((easycap_control[i1].minimum <= peasycap->saturation) &&
- (easycap_control[i1].maximum >= peasycap->saturation)) {
- if (peasycap->saturation == value) {
- SAM("unchanged saturation at 0x%02X\n",
- value);
- return 0;
- }
- }
- peasycap->saturation = value;
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].saturation_ok)
- peasycap->inputset[k].saturation =
- peasycap->saturation;
- }
- if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
- peasycap->inputset[peasycap->input].saturation =
- peasycap->saturation;
- peasycap->inputset[peasycap->input].saturation_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
- mood = 0x00FF & (unsigned int) (peasycap->saturation - 128);
- if (!write_saa(peasycap->pusb_device, 0x0C, mood)) {
- SAM("adjusting saturation to 0x%02X\n", mood);
- return 0;
- } else {
- SAM("WARNING: failed to adjust saturation to "
- "0x%02X\n", mood);
- return -ENOENT;
- }
- break;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust saturation: control not found\n");
- return -ENOENT;
-}
-/*****************************************************************************/
-int adjust_hue(struct easycap *peasycap, int value)
-{
- unsigned int mood;
- int i1, i2, k;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_HUE == easycap_control[i1].id) {
- if ((easycap_control[i1].minimum > value) ||
- (easycap_control[i1].maximum < value))
- value = easycap_control[i1].default_value;
-
- if ((easycap_control[i1].minimum <= peasycap->hue) &&
- (easycap_control[i1].maximum >= peasycap->hue)) {
- if (peasycap->hue == value) {
- SAM("unchanged hue at 0x%02X\n", value);
- return 0;
- }
- }
- peasycap->hue = value;
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].hue_ok)
- peasycap->inputset[k].hue = peasycap->hue;
- }
- if (0 <= peasycap->input && INPUT_MANY > peasycap->input) {
- peasycap->inputset[peasycap->input].hue = peasycap->hue;
- peasycap->inputset[peasycap->input].hue_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
- i2 = peasycap->hue - 128;
- mood = 0x00FF & ((int) i2);
- if (!write_saa(peasycap->pusb_device, 0x0D, mood)) {
- SAM("adjusting hue to 0x%02X\n", mood);
- return 0;
- } else {
- SAM("WARNING: failed to adjust hue to 0x%02X\n", mood);
- return -ENOENT;
- }
- break;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust hue: control not found\n");
- return -ENOENT;
-}
-/*****************************************************************************/
-int adjust_volume(struct easycap *peasycap, int value)
-{
- s8 mood;
- int i1;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_AUDIO_VOLUME == easycap_control[i1].id) {
- if ((easycap_control[i1].minimum > value) ||
- (easycap_control[i1].maximum < value))
- value = easycap_control[i1].default_value;
-
- if ((easycap_control[i1].minimum <= peasycap->volume) &&
- (easycap_control[i1].maximum >= peasycap->volume)) {
- if (peasycap->volume == value) {
- SAM("unchanged volume at 0x%02X\n", value);
- return 0;
- }
- }
- peasycap->volume = value;
- mood = (16 > peasycap->volume) ? 16 :
- ((31 < peasycap->volume) ? 31 :
- (s8) peasycap->volume);
- if (!audio_gainset(peasycap->pusb_device, mood)) {
- SAM("adjusting volume to 0x%02X\n", mood);
- return 0;
- } else {
- SAM("WARNING: failed to adjust volume to "
- "0x%2X\n", mood);
- return -ENOENT;
- }
- break;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust volume: control not found\n");
- return -ENOENT;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * AN ALTERNATIVE METHOD OF MUTING MIGHT SEEM TO BE:
- * usb_set_interface(peasycap->pusb_device,
- * peasycap->audio_interface,
- * peasycap->audio_altsetting_off);
- * HOWEVER, AFTER THIS COMMAND IS ISSUED ALL SUBSEQUENT URBS RECEIVE STATUS
- * -ESHUTDOWN. THE HANDLER ROUTINE easyxxx_complete() DECLINES TO RESUBMIT
- * THE URB AND THE PIPELINE COLLAPSES IRRETRIEVABLY. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
-static int adjust_mute(struct easycap *peasycap, int value)
-{
- int i1;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_AUDIO_MUTE == easycap_control[i1].id) {
- peasycap->mute = value;
- switch (peasycap->mute) {
- case 1: {
- peasycap->audio_idle = 1;
- SAM("adjusting mute: %i=peasycap->audio_idle\n",
- peasycap->audio_idle);
- return 0;
- }
- default: {
- peasycap->audio_idle = 0;
- SAM("adjusting mute: %i=peasycap->audio_idle\n",
- peasycap->audio_idle);
- return 0;
- }
- }
- break;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust mute: control not found\n");
- return -ENOENT;
-}
-/*---------------------------------------------------------------------------*/
-long easycap_unlocked_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct easycap *peasycap;
- struct usb_device *p;
- int kd;
-
- if (!file) {
- SAY("ERROR: file is NULL\n");
- return -ERESTARTSYS;
- }
- peasycap = file->private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -1;
- }
- p = peasycap->pusb_device;
- if (!p) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- kd = isdongle(peasycap);
- if (0 <= kd && DONGLE_MANY > kd) {
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
- SAY("ERROR: cannot lock "
- "easycapdc60_dongle[%i].mutex_video\n", kd);
- return -ERESTARTSYS;
- }
- JOM(4, "locked easycapdc60_dongle[%i].mutex_video\n", kd);
-/*---------------------------------------------------------------------------*/
-/*
- * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
- * IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
- * IF NECESSARY, BAIL OUT.
- */
-/*---------------------------------------------------------------------------*/
- if (kd != isdongle(peasycap))
- return -ERESTARTSYS;
- if (!file) {
- SAY("ERROR: file is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- peasycap = file->private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- } else {
-/*---------------------------------------------------------------------------*/
-/*
- * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
- * ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT.
- */
-/*---------------------------------------------------------------------------*/
- return -ERESTARTSYS;
- }
-/*---------------------------------------------------------------------------*/
- switch (cmd) {
- case VIDIOC_QUERYCAP: {
- struct v4l2_capability v4l2_capability;
- char version[16], *p1, *p2;
- int i, rc, k[3];
- long lng;
-
- JOM(8, "VIDIOC_QUERYCAP\n");
-
- if (16 <= strlen(EASYCAP_DRIVER_VERSION)) {
- SAM("ERROR: bad driver version string\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- strcpy(&version[0], EASYCAP_DRIVER_VERSION);
- for (i = 0; i < 3; i++)
- k[i] = 0;
- p2 = &version[0];
- i = 0;
- while (*p2) {
- p1 = p2;
- while (*p2 && ('.' != *p2))
- p2++;
- if (*p2)
- *p2++ = 0;
- if (3 > i) {
- rc = (int) strict_strtol(p1, 10, &lng);
- if (rc) {
- SAM("ERROR: %i=strict_strtol(%s,.,,)\n",
- rc, p1);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- k[i] = (int)lng;
- }
- i++;
- }
-
- memset(&v4l2_capability, 0, sizeof(struct v4l2_capability));
- strlcpy(&v4l2_capability.driver[0],
- "easycap", sizeof(v4l2_capability.driver));
-
- v4l2_capability.capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_AUDIO |
- V4L2_CAP_READWRITE;
-
- v4l2_capability.version = KERNEL_VERSION(k[0], k[1], k[2]);
- JOM(8, "v4l2_capability.version=(%i,%i,%i)\n", k[0], k[1], k[2]);
-
- strlcpy(&v4l2_capability.card[0],
- "EasyCAP DC60", sizeof(v4l2_capability.card));
-
- if (usb_make_path(peasycap->pusb_device,
- &v4l2_capability.bus_info[0],
- sizeof(v4l2_capability.bus_info)) < 0) {
-
- strlcpy(&v4l2_capability.bus_info[0], "EasyCAP bus_info",
- sizeof(v4l2_capability.bus_info));
- JOM(8, "%s=v4l2_capability.bus_info\n",
- &v4l2_capability.bus_info[0]);
- }
- if (copy_to_user((void __user *)arg, &v4l2_capability,
- sizeof(struct v4l2_capability))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUMINPUT: {
- struct v4l2_input v4l2_input;
- u32 index;
-
- JOM(8, "VIDIOC_ENUMINPUT\n");
-
- if (copy_from_user(&v4l2_input, (void __user *)arg,
- sizeof(struct v4l2_input))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- index = v4l2_input.index;
- memset(&v4l2_input, 0, sizeof(struct v4l2_input));
-
- switch (index) {
- case 0: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "CVBS0");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL |
- V4L2_STD_SECAM |
- V4L2_STD_NTSC ;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- case 1: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "CVBS1");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
- V4L2_STD_NTSC;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- case 2: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "CVBS2");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
- V4L2_STD_NTSC ;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- case 3: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "CVBS3");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
- V4L2_STD_NTSC ;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- case 4: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "CVBS4");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
- V4L2_STD_NTSC ;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- case 5: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "S-VIDEO");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
- V4L2_STD_NTSC ;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- default: {
- JOM(8, "%i=index: exhausts inputs\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
-
- if (copy_to_user((void __user *)arg, &v4l2_input,
- sizeof(struct v4l2_input))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_INPUT: {
- u32 index;
-
- JOM(8, "VIDIOC_G_INPUT\n");
- index = (u32)peasycap->input;
- JOM(8, "user is told: %i\n", index);
- if (copy_to_user((void __user *)arg, &index, sizeof(u32))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_INPUT:
- {
- u32 index;
- int rc;
-
- JOM(8, "VIDIOC_S_INPUT\n");
-
- if (0 != copy_from_user(&index, (void __user *)arg, sizeof(u32))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- JOM(8, "user requests input %i\n", index);
-
- if ((int)index == peasycap->input) {
- SAM("requested input already in effect\n");
- break;
- }
-
- if ((0 > index) || (INPUT_MANY <= index)) {
- JOM(8, "ERROR: bad requested input: %i\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-
- rc = newinput(peasycap, (int)index);
- if (0 == rc) {
- JOM(8, "newinput(.,%i) OK\n", (int)index);
- } else {
- SAM("ERROR: newinput(.,%i) returned %i\n", (int)index, rc);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUMAUDIO: {
- JOM(8, "VIDIOC_ENUMAUDIO\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUMAUDOUT: {
- struct v4l2_audioout v4l2_audioout;
-
- JOM(8, "VIDIOC_ENUMAUDOUT\n");
-
- if (copy_from_user(&v4l2_audioout, (void __user *)arg,
- sizeof(struct v4l2_audioout))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (0 != v4l2_audioout.index) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- memset(&v4l2_audioout, 0, sizeof(struct v4l2_audioout));
- v4l2_audioout.index = 0;
- strcpy(&v4l2_audioout.name[0], "Soundtrack");
-
- if (copy_to_user((void __user *)arg, &v4l2_audioout,
- sizeof(struct v4l2_audioout))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_QUERYCTRL: {
- int i1;
- struct v4l2_queryctrl v4l2_queryctrl;
-
- JOM(8, "VIDIOC_QUERYCTRL\n");
-
- if (0 != copy_from_user(&v4l2_queryctrl, (void __user *)arg,
- sizeof(struct v4l2_queryctrl))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (easycap_control[i1].id == v4l2_queryctrl.id) {
- JOM(8, "VIDIOC_QUERYCTRL %s=easycap_control[%i]"
- ".name\n", &easycap_control[i1].name[0], i1);
- memcpy(&v4l2_queryctrl, &easycap_control[i1],
- sizeof(struct v4l2_queryctrl));
- break;
- }
- i1++;
- }
- if (0xFFFFFFFF == easycap_control[i1].id) {
- JOM(8, "%i=index: exhausts controls\n", i1);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- if (copy_to_user((void __user *)arg, &v4l2_queryctrl,
- sizeof(struct v4l2_queryctrl))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_QUERYMENU: {
- JOM(8, "VIDIOC_QUERYMENU unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_CTRL: {
- struct v4l2_control *pv4l2_control;
-
- JOM(8, "VIDIOC_G_CTRL\n");
- pv4l2_control = memdup_user((void __user *)arg,
- sizeof(struct v4l2_control));
- if (IS_ERR(pv4l2_control)) {
- SAM("ERROR: copy from user failed\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return PTR_ERR(pv4l2_control);
- }
-
- switch (pv4l2_control->id) {
- case V4L2_CID_BRIGHTNESS: {
- pv4l2_control->value = peasycap->brightness;
- JOM(8, "user enquires brightness: %i\n", pv4l2_control->value);
- break;
- }
- case V4L2_CID_CONTRAST: {
- pv4l2_control->value = peasycap->contrast;
- JOM(8, "user enquires contrast: %i\n", pv4l2_control->value);
- break;
- }
- case V4L2_CID_SATURATION: {
- pv4l2_control->value = peasycap->saturation;
- JOM(8, "user enquires saturation: %i\n", pv4l2_control->value);
- break;
- }
- case V4L2_CID_HUE: {
- pv4l2_control->value = peasycap->hue;
- JOM(8, "user enquires hue: %i\n", pv4l2_control->value);
- break;
- }
- case V4L2_CID_AUDIO_VOLUME: {
- pv4l2_control->value = peasycap->volume;
- JOM(8, "user enquires volume: %i\n", pv4l2_control->value);
- break;
- }
- case V4L2_CID_AUDIO_MUTE: {
- if (1 == peasycap->mute)
- pv4l2_control->value = true;
- else
- pv4l2_control->value = false;
- JOM(8, "user enquires mute: %i\n", pv4l2_control->value);
- break;
- }
- default: {
- SAM("ERROR: unknown V4L2 control: 0x%08X=id\n",
- pv4l2_control->id);
- kfree(pv4l2_control);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- if (copy_to_user((void __user *)arg, pv4l2_control,
- sizeof(struct v4l2_control))) {
- kfree(pv4l2_control);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- kfree(pv4l2_control);
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_CTRL: {
- struct v4l2_control v4l2_control;
-
- JOM(8, "VIDIOC_S_CTRL\n");
-
- if (0 != copy_from_user(&v4l2_control, (void __user *)arg,
- sizeof(struct v4l2_control))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- switch (v4l2_control.id) {
- case V4L2_CID_BRIGHTNESS: {
- JOM(8, "user requests brightness %i\n", v4l2_control.value);
- if (0 != adjust_brightness(peasycap, v4l2_control.value))
- ;
- break;
- }
- case V4L2_CID_CONTRAST: {
- JOM(8, "user requests contrast %i\n", v4l2_control.value);
- if (0 != adjust_contrast(peasycap, v4l2_control.value))
- ;
- break;
- }
- case V4L2_CID_SATURATION: {
- JOM(8, "user requests saturation %i\n", v4l2_control.value);
- if (0 != adjust_saturation(peasycap, v4l2_control.value))
- ;
- break;
- }
- case V4L2_CID_HUE: {
- JOM(8, "user requests hue %i\n", v4l2_control.value);
- if (0 != adjust_hue(peasycap, v4l2_control.value))
- ;
- break;
- }
- case V4L2_CID_AUDIO_VOLUME: {
- JOM(8, "user requests volume %i\n", v4l2_control.value);
- if (0 != adjust_volume(peasycap, v4l2_control.value))
- ;
- break;
- }
- case V4L2_CID_AUDIO_MUTE: {
- int mute;
-
- JOM(8, "user requests mute %i\n", v4l2_control.value);
- if (v4l2_control.value)
- mute = 1;
- else
- mute = 0;
-
- if (0 != adjust_mute(peasycap, mute))
- SAM("WARNING: failed to adjust mute to %i\n", mute);
- break;
- }
- default: {
- SAM("ERROR: unknown V4L2 control: 0x%08X=id\n",
- v4l2_control.id);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_EXT_CTRLS: {
- JOM(8, "VIDIOC_S_EXT_CTRLS unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUM_FMT: {
- u32 index;
- struct v4l2_fmtdesc v4l2_fmtdesc;
-
- JOM(8, "VIDIOC_ENUM_FMT\n");
-
- if (0 != copy_from_user(&v4l2_fmtdesc, (void __user *)arg,
- sizeof(struct v4l2_fmtdesc))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- index = v4l2_fmtdesc.index;
- memset(&v4l2_fmtdesc, 0, sizeof(struct v4l2_fmtdesc));
-
- v4l2_fmtdesc.index = index;
- v4l2_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- switch (index) {
- case 0: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "uyvy");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_UYVY;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- case 1: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "yuy2");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- case 2: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "rgb24");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB24;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- case 3: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "rgb32");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB32;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- case 4: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "bgr24");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR24;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- case 5: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "bgr32");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR32;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- default: {
- JOM(8, "%i=index: exhausts formats\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- if (copy_to_user((void __user *)arg, &v4l2_fmtdesc,
- sizeof(struct v4l2_fmtdesc))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-/*
- * THE RESPONSE TO VIDIOC_ENUM_FRAMESIZES MUST BE CONDITIONED ON THE
- * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE.
- */
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUM_FRAMESIZES: {
- u32 index;
- struct v4l2_frmsizeenum v4l2_frmsizeenum;
-
- JOM(8, "VIDIOC_ENUM_FRAMESIZES\n");
-
- if (0 != copy_from_user(&v4l2_frmsizeenum, (void __user *)arg,
- sizeof(struct v4l2_frmsizeenum))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- index = v4l2_frmsizeenum.index;
-
- v4l2_frmsizeenum.type = (u32) V4L2_FRMSIZE_TYPE_DISCRETE;
-
- if (peasycap->ntsc) {
- switch (index) {
- case 0: {
- v4l2_frmsizeenum.discrete.width = 640;
- v4l2_frmsizeenum.discrete.height = 480;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 1: {
- v4l2_frmsizeenum.discrete.width = 320;
- v4l2_frmsizeenum.discrete.height = 240;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 2: {
- v4l2_frmsizeenum.discrete.width = 720;
- v4l2_frmsizeenum.discrete.height = 480;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 3: {
- v4l2_frmsizeenum.discrete.width = 360;
- v4l2_frmsizeenum.discrete.height = 240;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- default: {
- JOM(8, "%i=index: exhausts framesizes\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- } else {
- switch (index) {
- case 0: {
- v4l2_frmsizeenum.discrete.width = 640;
- v4l2_frmsizeenum.discrete.height = 480;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 1: {
- v4l2_frmsizeenum.discrete.width = 320;
- v4l2_frmsizeenum.discrete.height = 240;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 2: {
- v4l2_frmsizeenum.discrete.width = 704;
- v4l2_frmsizeenum.discrete.height = 576;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 3: {
- v4l2_frmsizeenum.discrete.width = 720;
- v4l2_frmsizeenum.discrete.height = 576;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 4: {
- v4l2_frmsizeenum.discrete.width = 360;
- v4l2_frmsizeenum.discrete.height = 288;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- default: {
- JOM(8, "%i=index: exhausts framesizes\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- }
- if (copy_to_user((void __user *)arg, &v4l2_frmsizeenum,
- sizeof(struct v4l2_frmsizeenum))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-/*
- * THE RESPONSE TO VIDIOC_ENUM_FRAMEINTERVALS MUST BE CONDITIONED ON THE
- * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE.
- */
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUM_FRAMEINTERVALS: {
- u32 index;
- int denominator;
- struct v4l2_frmivalenum v4l2_frmivalenum;
-
- JOM(8, "VIDIOC_ENUM_FRAMEINTERVALS\n");
-
- if (peasycap->fps)
- denominator = peasycap->fps;
- else {
- if (peasycap->ntsc)
- denominator = 30;
- else
- denominator = 25;
- }
-
- if (0 != copy_from_user(&v4l2_frmivalenum, (void __user *)arg,
- sizeof(struct v4l2_frmivalenum))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- index = v4l2_frmivalenum.index;
-
- v4l2_frmivalenum.type = (u32) V4L2_FRMIVAL_TYPE_DISCRETE;
-
- switch (index) {
- case 0: {
- v4l2_frmivalenum.discrete.numerator = 1;
- v4l2_frmivalenum.discrete.denominator = denominator;
- JOM(8, "%i=index: %i/%i\n", index,
- (int)(v4l2_frmivalenum.discrete.numerator),
- (int)(v4l2_frmivalenum.discrete.denominator));
- break;
- }
- case 1: {
- v4l2_frmivalenum.discrete.numerator = 1;
- v4l2_frmivalenum.discrete.denominator = denominator/5;
- JOM(8, "%i=index: %i/%i\n", index,
- (int)(v4l2_frmivalenum.discrete.numerator),
- (int)(v4l2_frmivalenum.discrete.denominator));
- break;
- }
- default: {
- JOM(8, "%i=index: exhausts frameintervals\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- if (copy_to_user((void __user *)arg, &v4l2_frmivalenum,
- sizeof(struct v4l2_frmivalenum))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_FMT: {
- struct v4l2_format *pv4l2_format;
- struct v4l2_pix_format *pv4l2_pix_format;
-
- JOM(8, "VIDIOC_G_FMT\n");
- pv4l2_format = kzalloc(sizeof(struct v4l2_format), GFP_KERNEL);
- if (!pv4l2_format) {
- SAM("ERROR: out of memory\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ENOMEM;
- }
- pv4l2_pix_format = kzalloc(sizeof(struct v4l2_pix_format), GFP_KERNEL);
- if (!pv4l2_pix_format) {
- SAM("ERROR: out of memory\n");
- kfree(pv4l2_format);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ENOMEM;
- }
- if (0 != copy_from_user(pv4l2_format, (void __user *)arg,
- sizeof(struct v4l2_format))) {
- kfree(pv4l2_format);
- kfree(pv4l2_pix_format);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (pv4l2_format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- kfree(pv4l2_format);
- kfree(pv4l2_pix_format);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-
- memset(pv4l2_pix_format, 0, sizeof(struct v4l2_pix_format));
- pv4l2_format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- memcpy(&pv4l2_format->fmt.pix,
- &easycap_format[peasycap->format_offset]
- .v4l2_format.fmt.pix, sizeof(struct v4l2_pix_format));
- JOM(8, "user is told: %s\n",
- &easycap_format[peasycap->format_offset].name[0]);
-
- if (copy_to_user((void __user *)arg, pv4l2_format,
- sizeof(struct v4l2_format))) {
- kfree(pv4l2_format);
- kfree(pv4l2_pix_format);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- kfree(pv4l2_format);
- kfree(pv4l2_pix_format);
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_TRY_FMT:
- case VIDIOC_S_FMT: {
- struct v4l2_format v4l2_format;
- struct v4l2_pix_format v4l2_pix_format;
- bool try;
- int best_format;
-
- if (VIDIOC_TRY_FMT == cmd) {
- JOM(8, "VIDIOC_TRY_FMT\n");
- try = true;
- } else {
- JOM(8, "VIDIOC_S_FMT\n");
- try = false;
- }
-
- if (0 != copy_from_user(&v4l2_format, (void __user *)arg,
- sizeof(struct v4l2_format))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- best_format = adjust_format(peasycap,
- v4l2_format.fmt.pix.width,
- v4l2_format.fmt.pix.height,
- v4l2_format.fmt.pix.pixelformat,
- v4l2_format.fmt.pix.field,
- try);
- if (0 > best_format) {
- if (-EBUSY == best_format) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EBUSY;
- }
- JOM(8, "WARNING: adjust_format() returned %i\n", best_format);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ENOENT;
- }
-/*...........................................................................*/
- memset(&v4l2_pix_format, 0, sizeof(struct v4l2_pix_format));
- v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- memcpy(&(v4l2_format.fmt.pix),
- &(easycap_format[best_format].v4l2_format.fmt.pix),
- sizeof(v4l2_pix_format));
- JOM(8, "user is told: %s\n", &easycap_format[best_format].name[0]);
-
- if (copy_to_user((void __user *)arg, &v4l2_format,
- sizeof(struct v4l2_format))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_CROPCAP: {
- struct v4l2_cropcap v4l2_cropcap;
-
- JOM(8, "VIDIOC_CROPCAP\n");
-
- if (0 != copy_from_user(&v4l2_cropcap, (void __user *)arg,
- sizeof(struct v4l2_cropcap))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- JOM(8, "v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
-
- memset(&v4l2_cropcap, 0, sizeof(struct v4l2_cropcap));
- v4l2_cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_cropcap.bounds.left = 0;
- v4l2_cropcap.bounds.top = 0;
- v4l2_cropcap.bounds.width = peasycap->width;
- v4l2_cropcap.bounds.height = peasycap->height;
- v4l2_cropcap.defrect.left = 0;
- v4l2_cropcap.defrect.top = 0;
- v4l2_cropcap.defrect.width = peasycap->width;
- v4l2_cropcap.defrect.height = peasycap->height;
- v4l2_cropcap.pixelaspect.numerator = 1;
- v4l2_cropcap.pixelaspect.denominator = 1;
-
- JOM(8, "user is told: %ix%i\n", peasycap->width, peasycap->height);
-
- if (copy_to_user((void __user *)arg, &v4l2_cropcap,
- sizeof(struct v4l2_cropcap))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_CROP:
- case VIDIOC_S_CROP: {
- JOM(8, "VIDIOC_G_CROP|VIDIOC_S_CROP unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_QUERYSTD: {
- JOM(8, "VIDIOC_QUERYSTD: "
- "EasyCAP is incapable of detecting standard\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- break;
- }
- /*-------------------------------------------------------------------*/
- /*
- * THE MANIPULATIONS INVOLVING last0,last1,last2,last3
- * CONSTITUTE A WORKAROUND * FOR WHAT APPEARS TO BE
- * A BUG IN 64-BIT mplayer.
- * NOT NEEDED, BUT HOPEFULLY HARMLESS, FOR 32-BIT mplayer.
- */
- /*------------------------------------------------------------------*/
- case VIDIOC_ENUMSTD: {
- int last0 = -1, last1 = -1, last2 = -1, last3 = -1;
- struct v4l2_standard v4l2_standard;
- u32 index;
- struct easycap_standard const *peasycap_standard;
-
- JOM(8, "VIDIOC_ENUMSTD\n");
-
- if (0 != copy_from_user(&v4l2_standard, (void __user *)arg,
- sizeof(struct v4l2_standard))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- index = v4l2_standard.index;
-
- last3 = last2;
- last2 = last1;
- last1 = last0;
- last0 = index;
- if ((index == last3) && (index == last2) &&
- (index == last1) && (index == last0)) {
- index++;
- last3 = last2;
- last2 = last1;
- last1 = last0;
- last0 = index;
- }
-
- memset(&v4l2_standard, 0, sizeof(struct v4l2_standard));
-
- peasycap_standard = &easycap_standard[0];
- while (0xFFFF != peasycap_standard->mask) {
- if ((int)(peasycap_standard - &easycap_standard[0]) == index)
- break;
- peasycap_standard++;
- }
- if (0xFFFF == peasycap_standard->mask) {
- JOM(8, "%i=index: exhausts standards\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- JOM(8, "%i=index: %s\n", index,
- &(peasycap_standard->v4l2_standard.name[0]));
- memcpy(&v4l2_standard, &(peasycap_standard->v4l2_standard),
- sizeof(struct v4l2_standard));
-
- v4l2_standard.index = index;
-
- if (copy_to_user((void __user *)arg, &v4l2_standard,
- sizeof(struct v4l2_standard))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_STD: {
- v4l2_std_id std_id;
- struct easycap_standard const *peasycap_standard;
-
- JOM(8, "VIDIOC_G_STD\n");
-
- if (0 > peasycap->standard_offset) {
- JOM(8, "%i=peasycap->standard_offset\n",
- peasycap->standard_offset);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EBUSY;
- }
-
- if (0 != copy_from_user(&std_id, (void __user *)arg,
- sizeof(v4l2_std_id))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- peasycap_standard = &easycap_standard[peasycap->standard_offset];
- std_id = peasycap_standard->v4l2_standard.id;
-
- JOM(8, "user is told: %s\n",
- &peasycap_standard->v4l2_standard.name[0]);
-
- if (copy_to_user((void __user *)arg, &std_id,
- sizeof(v4l2_std_id))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_STD: {
- v4l2_std_id std_id;
- int rc;
-
- JOM(8, "VIDIOC_S_STD\n");
-
- if (0 != copy_from_user(&std_id, (void __user *)arg,
- sizeof(v4l2_std_id))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- JOM(8, "User requests standard: 0x%08X%08X\n",
- (int)((std_id & (((v4l2_std_id)0xFFFFFFFF) << 32)) >> 32),
- (int)(std_id & ((v4l2_std_id)0xFFFFFFFF)));
-
- rc = adjust_standard(peasycap, std_id);
- if (0 > rc) {
- JOM(8, "WARNING: adjust_standard() returned %i\n", rc);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ENOENT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_REQBUFS: {
- int nbuffers;
- struct v4l2_requestbuffers v4l2_requestbuffers;
-
- JOM(8, "VIDIOC_REQBUFS\n");
-
- if (0 != copy_from_user(&v4l2_requestbuffers,
- (void __user *)arg,
- sizeof(struct v4l2_requestbuffers))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- nbuffers = v4l2_requestbuffers.count;
- JOM(8, " User requests %i buffers ...\n", nbuffers);
- if (nbuffers < 2)
- nbuffers = 2;
- if (nbuffers > FRAME_BUFFER_MANY)
- nbuffers = FRAME_BUFFER_MANY;
- if (v4l2_requestbuffers.count == nbuffers) {
- JOM(8, " ... agree to %i buffers\n",
- nbuffers);
- } else {
- JOM(8, " ... insist on %i buffers\n",
- nbuffers);
- v4l2_requestbuffers.count = nbuffers;
- }
- peasycap->frame_buffer_many = nbuffers;
-
- if (copy_to_user((void __user *)arg, &v4l2_requestbuffers,
- sizeof(struct v4l2_requestbuffers))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_QUERYBUF: {
- u32 index;
- struct v4l2_buffer v4l2_buffer;
-
- JOM(8, "VIDIOC_QUERYBUF\n");
-
- if (peasycap->video_eof) {
- JOM(8, "returning -EIO because %i=video_eof\n",
- peasycap->video_eof);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EIO;
- }
-
- if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- index = v4l2_buffer.index;
- if (index < 0 || index >= peasycap->frame_buffer_many)
- return -EINVAL;
- memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
- v4l2_buffer.index = index;
- v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buffer.bytesused = peasycap->frame_buffer_used;
- v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED |
- peasycap->done[index] |
- peasycap->queued[index];
- v4l2_buffer.field = V4L2_FIELD_NONE;
- v4l2_buffer.memory = V4L2_MEMORY_MMAP;
- v4l2_buffer.m.offset = index * FRAME_BUFFER_SIZE;
- v4l2_buffer.length = FRAME_BUFFER_SIZE;
-
- JOM(16, " %10i=index\n", v4l2_buffer.index);
- JOM(16, " 0x%08X=type\n", v4l2_buffer.type);
- JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused);
- JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags);
- JOM(16, " %10i=field\n", v4l2_buffer.field);
- JOM(16, " %10li=timestamp.tv_usec\n",
- (long)v4l2_buffer.timestamp.tv_usec);
- JOM(16, " %10i=sequence\n", v4l2_buffer.sequence);
- JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory);
- JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset);
- JOM(16, " %10i=length\n", v4l2_buffer.length);
-
- if (copy_to_user((void __user *)arg, &v4l2_buffer,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_QBUF: {
- struct v4l2_buffer v4l2_buffer;
-
- JOM(8, "VIDIOC_QBUF\n");
-
- if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- if (v4l2_buffer.memory != V4L2_MEMORY_MMAP) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- if (v4l2_buffer.index < 0 ||
- v4l2_buffer.index >= peasycap->frame_buffer_many) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED;
-
- peasycap->done[v4l2_buffer.index] = 0;
- peasycap->queued[v4l2_buffer.index] = V4L2_BUF_FLAG_QUEUED;
-
- if (copy_to_user((void __user *)arg, &v4l2_buffer,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- JOM(8, "..... user queueing frame buffer %i\n",
- (int)v4l2_buffer.index);
-
- peasycap->frame_lock = 0;
-
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_DQBUF:
- {
- struct timeval timeval, timeval2;
- int i, j;
- struct v4l2_buffer v4l2_buffer;
- int rcdq;
- u16 input;
-
- JOM(8, "VIDIOC_DQBUF\n");
-
- if ((peasycap->video_idle) || (peasycap->video_eof)) {
- JOM(8, "returning -EIO because "
- "%i=video_idle %i=video_eof\n",
- peasycap->video_idle, peasycap->video_eof);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EIO;
- }
-
- if (copy_from_user(&v4l2_buffer, (void __user *)arg,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-
- if (peasycap->offerfields) {
- /*---------------------------------------------------*/
- /*
- * IN ITS 50 "fps" MODE tvtime SEEMS ALWAYS TO REQUEST
- * V4L2_FIELD_BOTTOM
- */
- /*---------------------------------------------------*/
- if (V4L2_FIELD_TOP == v4l2_buffer.field)
- JOM(8, "user wants V4L2_FIELD_TOP\n");
- else if (V4L2_FIELD_BOTTOM == v4l2_buffer.field)
- JOM(8, "user wants V4L2_FIELD_BOTTOM\n");
- else if (V4L2_FIELD_ANY == v4l2_buffer.field)
- JOM(8, "user wants V4L2_FIELD_ANY\n");
- else
- JOM(8, "user wants V4L2_FIELD_...UNKNOWN: %i\n",
- v4l2_buffer.field);
- }
-
- if (!peasycap->video_isoc_streaming) {
- JOM(16, "returning -EIO because video urbs not streaming\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EIO;
- }
- /*-------------------------------------------------------------------*/
- /*
- * IF THE USER HAS PREVIOUSLY CALLED easycap_poll(),
- * AS DETERMINED BY FINDING
- * THE FLAG peasycap->polled SET, THERE MUST BE
- * NO FURTHER WAIT HERE. IN THIS
- * CASE, JUST CHOOSE THE FRAME INDICATED BY peasycap->frame_read
- */
- /*-------------------------------------------------------------------*/
-
- if (!peasycap->polled) {
- do {
- rcdq = easycap_dqbuf(peasycap, 0);
- if (-EIO == rcdq) {
- JOM(8, "returning -EIO because "
- "dqbuf() returned -EIO\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EIO;
- }
- } while (0 != rcdq);
- } else {
- if (peasycap->video_eof) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EIO;
- }
- }
- if (V4L2_BUF_FLAG_DONE != peasycap->done[peasycap->frame_read]) {
- JOM(8, "V4L2_BUF_FLAG_DONE != 0x%08X\n",
- peasycap->done[peasycap->frame_read]);
- }
- peasycap->polled = 0;
-
- if (!(peasycap->isequence % 10)) {
- for (i = 0; i < 179; i++)
- peasycap->merit[i] = peasycap->merit[i+1];
- peasycap->merit[179] = merit_saa(peasycap->pusb_device);
- j = 0;
- for (i = 0; i < 180; i++)
- j += peasycap->merit[i];
- if (90 < j) {
- SAM("easycap driver shutting down "
- "on condition blue\n");
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- }
- }
-
- v4l2_buffer.index = peasycap->frame_read;
- v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buffer.bytesused = peasycap->frame_buffer_used;
- v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE;
- if (peasycap->offerfields)
- v4l2_buffer.field = V4L2_FIELD_BOTTOM;
- else
- v4l2_buffer.field = V4L2_FIELD_NONE;
- do_gettimeofday(&timeval);
- timeval2 = timeval;
-
- v4l2_buffer.timestamp = timeval2;
- v4l2_buffer.sequence = peasycap->isequence++;
- v4l2_buffer.memory = V4L2_MEMORY_MMAP;
- v4l2_buffer.m.offset = v4l2_buffer.index * FRAME_BUFFER_SIZE;
- v4l2_buffer.length = FRAME_BUFFER_SIZE;
-
- JOM(16, " %10i=index\n", v4l2_buffer.index);
- JOM(16, " 0x%08X=type\n", v4l2_buffer.type);
- JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused);
- JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags);
- JOM(16, " %10i=field\n", v4l2_buffer.field);
- JOM(16, " %10li=timestamp.tv_sec\n",
- (long)v4l2_buffer.timestamp.tv_sec);
- JOM(16, " %10li=timestamp.tv_usec\n",
- (long)v4l2_buffer.timestamp.tv_usec);
- JOM(16, " %10i=sequence\n", v4l2_buffer.sequence);
- JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory);
- JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset);
- JOM(16, " %10i=length\n", v4l2_buffer.length);
-
- if (copy_to_user((void __user *)arg, &v4l2_buffer,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- input = peasycap->frame_buffer[peasycap->frame_read][0].input;
- if (0x08 & input) {
- JOM(8, "user is offered frame buffer %i, input %i\n",
- peasycap->frame_read, (0x07 & input));
- } else {
- JOM(8, "user is offered frame buffer %i\n",
- peasycap->frame_read);
- }
- peasycap->frame_lock = 1;
- JOM(8, "%i=peasycap->frame_fill\n", peasycap->frame_fill);
- if (peasycap->frame_read == peasycap->frame_fill) {
- if (peasycap->frame_lock) {
- JOM(8, "WORRY: filling frame buffer "
- "while offered to user\n");
- }
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_STREAMON: {
- int i;
-
- JOM(8, "VIDIOC_STREAMON\n");
-
- peasycap->isequence = 0;
- for (i = 0; i < 180; i++)
- peasycap->merit[i] = 0;
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- submit_video_urbs(peasycap);
- peasycap->video_idle = 0;
- peasycap->audio_idle = 0;
- peasycap->video_eof = 0;
- peasycap->audio_eof = 0;
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_STREAMOFF: {
- JOM(8, "VIDIOC_STREAMOFF\n");
-
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- peasycap->video_idle = 1;
- peasycap->audio_idle = 1;
-/*---------------------------------------------------------------------------*/
-/*
- * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO THE STREAMOFF COMMAND
- * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
- JOM(8, "calling wake_up on wq_video and wq_audio\n");
- wake_up_interruptible(&(peasycap->wq_video));
- if (peasycap->psubstream)
- snd_pcm_period_elapsed(peasycap->psubstream);
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_PARM: {
- struct v4l2_streamparm *pv4l2_streamparm;
-
- JOM(8, "VIDIOC_G_PARM\n");
- pv4l2_streamparm = memdup_user((void __user *)arg,
- sizeof(struct v4l2_streamparm));
- if (IS_ERR(pv4l2_streamparm)) {
- SAM("ERROR: copy from user failed\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return PTR_ERR(pv4l2_streamparm);
- }
-
- if (pv4l2_streamparm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- kfree(pv4l2_streamparm);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- pv4l2_streamparm->parm.capture.capability = 0;
- pv4l2_streamparm->parm.capture.capturemode = 0;
- pv4l2_streamparm->parm.capture.timeperframe.numerator = 1;
-
- if (peasycap->fps) {
- pv4l2_streamparm->parm.capture.timeperframe.
- denominator = peasycap->fps;
- } else {
- if (peasycap->ntsc) {
- pv4l2_streamparm->parm.capture.timeperframe.
- denominator = 30;
- } else {
- pv4l2_streamparm->parm.capture.timeperframe.
- denominator = 25;
- }
- }
-
- pv4l2_streamparm->parm.capture.readbuffers =
- peasycap->frame_buffer_many;
- pv4l2_streamparm->parm.capture.extendedmode = 0;
- if (copy_to_user((void __user *)arg,
- pv4l2_streamparm,
- sizeof(struct v4l2_streamparm))) {
- kfree(pv4l2_streamparm);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- kfree(pv4l2_streamparm);
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_PARM: {
- JOM(8, "VIDIOC_S_PARM unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_AUDIO: {
- JOM(8, "VIDIOC_G_AUDIO unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_AUDIO: {
- JOM(8, "VIDIOC_S_AUDIO unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_TUNER: {
- JOM(8, "VIDIOC_S_TUNER unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_FBUF:
- case VIDIOC_S_FBUF:
- case VIDIOC_OVERLAY: {
- JOM(8, "VIDIOC_G_FBUF|VIDIOC_S_FBUF|VIDIOC_OVERLAY unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_TUNER: {
- JOM(8, "VIDIOC_G_TUNER unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- case VIDIOC_G_FREQUENCY:
- case VIDIOC_S_FREQUENCY: {
- JOM(8, "VIDIOC_G_FREQUENCY|VIDIOC_S_FREQUENCY unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- default: {
- JOM(8, "ERROR: unrecognized V4L2 IOCTL command: 0x%08X\n", cmd);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ENOIOCTLCMD;
- }
- }
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- JOM(4, "unlocked easycapdc60_dongle[%i].mutex_video\n", kd);
- return 0;
-}
-/*****************************************************************************/
+++ /dev/null
-/*****************************************************************************
-* *
-* *
-* easycap_low.c *
-* *
-* *
-*****************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This 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.
- *
- * The software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-/*
- * ACKNOWLEGEMENTS AND REFERENCES
- * ------------------------------
- * This driver makes use of register information contained in the Syntek
- * Semicon DC-1125 driver hosted at
- * http://sourceforge.net/projects/syntekdriver/.
- * Particularly useful has been a patch to the latter driver provided by
- * Ivor Hewitt in January 2009. The NTSC implementation is taken from the
- * work of Ben Trask.
-*/
-/****************************************************************************/
-
-#include "easycap.h"
-
-#define GET(X, Y, Z) do { \
- int __rc; \
- *(Z) = (u16)0; \
- __rc = regget(X, Y, Z, sizeof(u8)); \
- if (0 > __rc) { \
- JOT(8, ":-(%i\n", __LINE__); return __rc; \
- } \
-} while (0)
-
-#define SET(X, Y, Z) do { \
- int __rc; \
- __rc = regset(X, Y, Z); \
- if (0 > __rc) { \
- JOT(8, ":-(%i\n", __LINE__); return __rc; \
- } \
-} while (0)
-
-/*--------------------------------------------------------------------------*/
-static const struct stk1160config {
- int reg;
- int set;
-} stk1160configPAL[256] = {
- {0x000, 0x0098},
- {0x002, 0x0093},
-
- {0x001, 0x0003},
- {0x003, 0x0080},
- {0x00D, 0x0000},
- {0x00F, 0x0002},
- {0x018, 0x0010},
- {0x019, 0x0000},
- {0x01A, 0x0014},
- {0x01B, 0x000E},
- {0x01C, 0x0046},
-
- {0x100, 0x0033},
- {0x103, 0x0000},
- {0x104, 0x0000},
- {0x105, 0x0000},
- {0x106, 0x0000},
-
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-/*
- * RESOLUTION 640x480
-*/
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {0x110, 0x0008},
- {0x111, 0x0000},
- {0x112, 0x0020},
- {0x113, 0x0000},
- {0x114, 0x0508},
- {0x115, 0x0005},
- {0x116, 0x0110},
- {0x117, 0x0001},
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- {0x202, 0x000F},
- {0x203, 0x004A},
- {0x2FF, 0x0000},
-
- {0xFFF, 0xFFFF}
-};
-/*--------------------------------------------------------------------------*/
-static const struct stk1160config stk1160configNTSC[256] = {
- {0x000, 0x0098},
- {0x002, 0x0093},
-
- {0x001, 0x0003},
- {0x003, 0x0080},
- {0x00D, 0x0000},
- {0x00F, 0x0002},
- {0x018, 0x0010},
- {0x019, 0x0000},
- {0x01A, 0x0014},
- {0x01B, 0x000E},
- {0x01C, 0x0046},
-
- {0x100, 0x0033},
- {0x103, 0x0000},
- {0x104, 0x0000},
- {0x105, 0x0000},
- {0x106, 0x0000},
-
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-/*
- * RESOLUTION 640x480
-*/
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {0x110, 0x0008},
- {0x111, 0x0000},
- {0x112, 0x0003},
- {0x113, 0x0000},
- {0x114, 0x0508},
- {0x115, 0x0005},
- {0x116, 0x00F3},
- {0x117, 0x0000},
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- {0x202, 0x000F},
- {0x203, 0x004A},
- {0x2FF, 0x0000},
-
- {0xFFF, 0xFFFF}
-};
-/*--------------------------------------------------------------------------*/
-static const struct saa7113config {
- int reg;
- int set;
-} saa7113configPAL[256] = {
- {0x01, 0x08},
- {0x02, 0x80},
- {0x03, 0x33},
- {0x04, 0x00},
- {0x05, 0x00},
- {0x06, 0xE9},
- {0x07, 0x0D},
- {0x08, 0x38},
- {0x09, 0x00},
- {0x0A, SAA_0A_DEFAULT},
- {0x0B, SAA_0B_DEFAULT},
- {0x0C, SAA_0C_DEFAULT},
- {0x0D, SAA_0D_DEFAULT},
- {0x0E, 0x01},
- {0x0F, 0x36},
- {0x10, 0x00},
- {0x11, 0x0C},
- {0x12, 0xE7},
- {0x13, 0x00},
- {0x15, 0x00},
- {0x16, 0x00},
- {0x40, 0x02},
- {0x41, 0xFF},
- {0x42, 0xFF},
- {0x43, 0xFF},
- {0x44, 0xFF},
- {0x45, 0xFF},
- {0x46, 0xFF},
- {0x47, 0xFF},
- {0x48, 0xFF},
- {0x49, 0xFF},
- {0x4A, 0xFF},
- {0x4B, 0xFF},
- {0x4C, 0xFF},
- {0x4D, 0xFF},
- {0x4E, 0xFF},
- {0x4F, 0xFF},
- {0x50, 0xFF},
- {0x51, 0xFF},
- {0x52, 0xFF},
- {0x53, 0xFF},
- {0x54, 0xFF},
- {0x55, 0xFF},
- {0x56, 0xFF},
- {0x57, 0xFF},
- {0x58, 0x40},
- {0x59, 0x54},
- {0x5A, 0x07},
- {0x5B, 0x83},
-
- {0xFF, 0xFF}
-};
-/*--------------------------------------------------------------------------*/
-static const struct saa7113config saa7113configNTSC[256] = {
- {0x01, 0x08},
- {0x02, 0x80},
- {0x03, 0x33},
- {0x04, 0x00},
- {0x05, 0x00},
- {0x06, 0xE9},
- {0x07, 0x0D},
- {0x08, 0x78},
- {0x09, 0x00},
- {0x0A, SAA_0A_DEFAULT},
- {0x0B, SAA_0B_DEFAULT},
- {0x0C, SAA_0C_DEFAULT},
- {0x0D, SAA_0D_DEFAULT},
- {0x0E, 0x01},
- {0x0F, 0x36},
- {0x10, 0x00},
- {0x11, 0x0C},
- {0x12, 0xE7},
- {0x13, 0x00},
- {0x15, 0x00},
- {0x16, 0x00},
- {0x40, 0x82},
- {0x41, 0xFF},
- {0x42, 0xFF},
- {0x43, 0xFF},
- {0x44, 0xFF},
- {0x45, 0xFF},
- {0x46, 0xFF},
- {0x47, 0xFF},
- {0x48, 0xFF},
- {0x49, 0xFF},
- {0x4A, 0xFF},
- {0x4B, 0xFF},
- {0x4C, 0xFF},
- {0x4D, 0xFF},
- {0x4E, 0xFF},
- {0x4F, 0xFF},
- {0x50, 0xFF},
- {0x51, 0xFF},
- {0x52, 0xFF},
- {0x53, 0xFF},
- {0x54, 0xFF},
- {0x55, 0xFF},
- {0x56, 0xFF},
- {0x57, 0xFF},
- {0x58, 0x40},
- {0x59, 0x54},
- {0x5A, 0x0A},
- {0x5B, 0x83},
-
- {0xFF, 0xFF}
-};
-
-static int regget(struct usb_device *pusb_device,
- u16 index, void *reg, int reg_size)
-{
- int rc;
-
- if (!pusb_device)
- return -ENODEV;
-
- rc = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0),
- 0x00,
- (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
- 0x00,
- index, reg, reg_size, 50000);
-
- return rc;
-}
-
-static int regset(struct usb_device *pusb_device, u16 index, u16 value)
-{
- int rc;
-
- if (!pusb_device)
- return -ENODEV;
-
- rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
- 0x01,
- (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
- value, index, NULL, 0, 500);
-
- if (rc < 0)
- return rc;
-
- if (easycap_readback) {
- u16 igot = 0;
- rc = regget(pusb_device, index, &igot, sizeof(igot));
- igot = 0xFF & igot;
- switch (index) {
- case 0x000:
- case 0x500:
- case 0x502:
- case 0x503:
- case 0x504:
- case 0x506:
- case 0x507:
- break;
-
- case 0x204:
- case 0x205:
- case 0x350:
- case 0x351:
- if (igot)
- JOT(8, "unexpected 0x%02X "
- "for STK register 0x%03X\n",
- igot, index);
- break;
-
- default:
- if ((0xFF & value) != igot)
- JOT(8, "unexpected 0x%02X != 0x%02X "
- "for STK register 0x%03X\n",
- igot, value, index);
- break;
- }
- }
-
- return rc;
-}
-/*--------------------------------------------------------------------------*/
-/*
- * FUNCTION wait_i2c() RETURNS 0 ON SUCCESS
-*/
-/*--------------------------------------------------------------------------*/
-static int wait_i2c(struct usb_device *p)
-{
- u16 get0;
- u8 igot;
- const int max = 2;
- int k;
-
- if (!p)
- return -ENODEV;
-
- for (k = 0; k < max; k++) {
- GET(p, 0x0201, &igot); get0 = igot;
- switch (get0) {
- case 0x04:
- case 0x01:
- return 0;
- case 0x00:
- msleep(20);
- continue;
- default:
- return get0 - 1;
- }
- }
- return -1;
-}
-
-/****************************************************************************/
-int confirm_resolution(struct usb_device *p)
-{
- u8 get0, get1, get2, get3, get4, get5, get6, get7;
-
- if (!p)
- return -ENODEV;
- GET(p, 0x0110, &get0);
- GET(p, 0x0111, &get1);
- GET(p, 0x0112, &get2);
- GET(p, 0x0113, &get3);
- GET(p, 0x0114, &get4);
- GET(p, 0x0115, &get5);
- GET(p, 0x0116, &get6);
- GET(p, 0x0117, &get7);
- JOT(8, "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X\n",
- get0, get1, get2, get3, get4, get5, get6, get7);
- JOT(8, "....cf PAL_720x526: "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X\n",
- 0x000, 0x000, 0x001, 0x000, 0x5A0, 0x005, 0x121, 0x001);
- JOT(8, "....cf PAL_704x526: "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X\n",
- 0x004, 0x000, 0x001, 0x000, 0x584, 0x005, 0x121, 0x001);
- JOT(8, "....cf VGA_640x480: "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X, "
- "0x%03X, 0x%03X\n",
- 0x008, 0x000, 0x020, 0x000, 0x508, 0x005, 0x110, 0x001);
- return 0;
-}
-/****************************************************************************/
-int confirm_stream(struct usb_device *p)
-{
- u16 get2;
- u8 igot;
-
- if (!p)
- return -ENODEV;
- GET(p, 0x0100, &igot); get2 = 0x80 & igot;
- if (0x80 == get2)
- JOT(8, "confirm_stream: OK\n");
- else
- JOT(8, "confirm_stream: STUCK\n");
- return 0;
-}
-/****************************************************************************/
-int setup_stk(struct usb_device *p, bool ntsc)
-{
- int i;
- const struct stk1160config *cfg;
- if (!p)
- return -ENODEV;
- cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL;
- for (i = 0; cfg[i].reg != 0xFFF; i++)
- SET(p, cfg[i].reg, cfg[i].set);
-
- write_300(p);
-
- return 0;
-}
-/****************************************************************************/
-int setup_saa(struct usb_device *p, bool ntsc)
-{
- int i, ir;
- const struct saa7113config *cfg;
- if (!p)
- return -ENODEV;
- cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL;
- for (i = 0; cfg[i].reg != 0xFF; i++)
- ir = write_saa(p, cfg[i].reg, cfg[i].set);
- return 0;
-}
-/****************************************************************************/
-int write_000(struct usb_device *p, u16 set2, u16 set0)
-{
- u8 igot0, igot2;
-
- if (!p)
- return -ENODEV;
- GET(p, 0x0002, &igot2);
- GET(p, 0x0000, &igot0);
- SET(p, 0x0002, set2);
- SET(p, 0x0000, set0);
- return 0;
-}
-/****************************************************************************/
-int write_saa(struct usb_device *p, u16 reg0, u16 set0)
-{
- if (!p)
- return -ENODEV;
- SET(p, 0x200, 0x00);
- SET(p, 0x204, reg0);
- SET(p, 0x205, set0);
- SET(p, 0x200, 0x01);
- return wait_i2c(p);
-}
-/****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?)
- * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A
- * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO SET
- * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO SET
- * REGISTER 504: TARGET ADDRESS ON VT1612A
- */
-/*--------------------------------------------------------------------------*/
-int
-write_vt(struct usb_device *p, u16 reg0, u16 set0)
-{
- u8 igot;
- u16 got502, got503;
- u16 set502, set503;
-
- if (!p)
- return -ENODEV;
- SET(p, 0x0504, reg0);
- SET(p, 0x0500, 0x008B);
-
- GET(p, 0x0502, &igot); got502 = (0xFF & igot);
- GET(p, 0x0503, &igot); got503 = (0xFF & igot);
-
- JOT(16, "write_vt(., 0x%04X, 0x%04X): was 0x%04X\n",
- reg0, set0, ((got503 << 8) | got502));
-
- set502 = (0x00FF & set0);
- set503 = ((0xFF00 & set0) >> 8);
-
- SET(p, 0x0504, reg0);
- SET(p, 0x0502, set502);
- SET(p, 0x0503, set503);
- SET(p, 0x0500, 0x008C);
-
- return 0;
-}
-/****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?)
- * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A
- * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO GET
- * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO GET
- * REGISTER 504: TARGET ADDRESS ON VT1612A
- */
-/*--------------------------------------------------------------------------*/
-int read_vt(struct usb_device *p, u16 reg0)
-{
- u8 igot;
- u16 got502, got503;
-
- if (!p)
- return -ENODEV;
- SET(p, 0x0504, reg0);
- SET(p, 0x0500, 0x008B);
-
- GET(p, 0x0502, &igot); got502 = (0xFF & igot);
- GET(p, 0x0503, &igot); got503 = (0xFF & igot);
-
- JOT(16, "read_vt(., 0x%04X): has 0x%04X\n",
- reg0, ((got503 << 8) | got502));
-
- return (got503 << 8) | got502;
-}
-/****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * THESE APPEAR TO HAVE NO EFFECT ON EITHER VIDEO OR AUDIO.
- */
-/*--------------------------------------------------------------------------*/
-int write_300(struct usb_device *p)
-{
- if (!p)
- return -ENODEV;
- SET(p, 0x300, 0x0012);
- SET(p, 0x350, 0x002D);
- SET(p, 0x351, 0x0001);
- SET(p, 0x352, 0x0000);
- SET(p, 0x353, 0x0000);
- SET(p, 0x300, 0x0080);
- return 0;
-}
-/****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * NOTE: THE FOLLOWING IS NOT CHECKED:
- * REGISTER 0x0F, WHICH IS INVOLVED IN CHROMINANCE AUTOMATIC GAIN CONTROL.
- */
-/*--------------------------------------------------------------------------*/
-int check_saa(struct usb_device *p, bool ntsc)
-{
- int i, ir, rc = 0;
- struct saa7113config const *cfg;
- if (!p)
- return -ENODEV;
-
- cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL;
- for (i = 0; cfg[i].reg != 0xFF; i++) {
- if (0x0F == cfg[i].reg)
- continue;
- ir = read_saa(p, cfg[i].reg);
- if (ir != cfg[i].set) {
- SAY("SAA register 0x%02X has 0x%02X, expected 0x%02X\n",
- cfg[i].reg, ir, cfg[i].set);
- rc--;
- }
- }
-
- return (rc < -8) ? rc : 0;
-}
-/****************************************************************************/
-int merit_saa(struct usb_device *p)
-{
- int rc;
-
- if (!p)
- return -ENODEV;
- rc = read_saa(p, 0x1F);
- return ((0 > rc) || (0x02 & rc)) ? 1 : 0;
-}
-/****************************************************************************/
-int ready_saa(struct usb_device *p)
-{
- int j, rc, rate;
- const int max = 5, marktime = PATIENCE/5;
-/*--------------------------------------------------------------------------*/
-/*
- * RETURNS 0 FOR INTERLACED 50 Hz
- * 1 FOR NON-INTERLACED 50 Hz
- * 2 FOR INTERLACED 60 Hz
- * 3 FOR NON-INTERLACED 60 Hz
-*/
-/*--------------------------------------------------------------------------*/
- if (!p)
- return -ENODEV;
- j = 0;
- while (max > j) {
- rc = read_saa(p, 0x1F);
- if (0 <= rc) {
- if (0 == (0x40 & rc))
- break;
- if (1 == (0x01 & rc))
- break;
- }
- msleep(marktime);
- j++;
- }
- if (max == j)
- return -1;
- else {
- if (0x20 & rc) {
- rate = 2;
- JOT(8, "hardware detects 60 Hz\n");
- } else {
- rate = 0;
- JOT(8, "hardware detects 50 Hz\n");
- }
- if (0x80 & rc)
- JOT(8, "hardware detects interlacing\n");
- else {
- rate++;
- JOT(8, "hardware detects no interlacing\n");
- }
- }
- return 0;
-}
-/****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * NOTE: THE FOLLOWING ARE NOT CHECKED:
- * REGISTERS 0x000, 0x002: FUNCTIONALITY IS NOT KNOWN
- * REGISTER 0x100: ACCEPT ALSO (0x80 | stk1160config....[.].set)
- */
-/*--------------------------------------------------------------------------*/
-int check_stk(struct usb_device *p, bool ntsc)
-{
- int i, ir;
- const struct stk1160config *cfg;
-
- if (!p)
- return -ENODEV;
- cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL;
-
- for (i = 0; 0xFFF != cfg[i].reg; i++) {
- if (0x000 == cfg[i].reg || 0x002 == cfg[i].reg)
- continue;
-
-
- ir = read_stk(p, cfg[i].reg);
- if (0x100 == cfg[i].reg) {
- if ((ir != (0xFF & cfg[i].set)) &&
- (ir != (0x80 | (0xFF & cfg[i].set))) &&
- (0xFFFF != cfg[i].set)) {
- SAY("STK reg[0x%03X]=0x%02X expected 0x%02X\n",
- cfg[i].reg, ir, cfg[i].set);
- }
- continue;
- }
- if ((ir != (0xFF & cfg[i].set)) && (0xFFFF != cfg[i].set))
- SAY("STK register 0x%03X has 0x%02X,expected 0x%02X\n",
- cfg[i].reg, ir, cfg[i].set);
- }
- return 0;
-}
-/****************************************************************************/
-int read_saa(struct usb_device *p, u16 reg0)
-{
- u8 igot;
-
- if (!p)
- return -ENODEV;
- SET(p, 0x208, reg0);
- SET(p, 0x200, 0x20);
- if (0 != wait_i2c(p))
- return -1;
- igot = 0;
- GET(p, 0x0209, &igot);
- return igot;
-}
-/****************************************************************************/
-int read_stk(struct usb_device *p, u32 reg0)
-{
- u8 igot;
-
- if (!p)
- return -ENODEV;
- igot = 0;
- GET(p, reg0, &igot);
- return igot;
-}
-/****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * HARDWARE USERSPACE INPUT NUMBER PHYSICAL INPUT DRIVER input VALUE
- *
- * CVBS+S-VIDEO 0 or 1 CVBS 1
- * FOUR-CVBS 0 or 1 CVBS1 1
- * FOUR-CVBS 2 CVBS2 2
- * FOUR-CVBS 3 CVBS3 3
- * FOUR-CVBS 4 CVBS4 4
- * CVBS+S-VIDEO 5 S-VIDEO 5
- *
- * WHEN 5==input THE ARGUMENT mode MUST ALSO BE SUPPLIED:
- *
- * mode 7 => GAIN TO BE SET EXPLICITLY USING REGISTER 0x05 (UNTESTED)
- * mode 9 => USE AUTOMATIC GAIN CONTROL (DEFAULT)
- *
-*/
-/*---------------------------------------------------------------------------*/
-int
-select_input(struct usb_device *p, int input, int mode)
-{
- int ir;
-
- if (!p)
- return -ENODEV;
- stop_100(p);
- switch (input) {
- case 0:
- case 1: {
- if (0 != write_saa(p, 0x02, 0x80))
- SAY("ERROR: failed to set SAA register 0x02 "
- "for input %i\n", input);
-
- SET(p, 0x0000, 0x0098);
- SET(p, 0x0002, 0x0078);
- break;
- }
- case 2: {
- if (0 != write_saa(p, 0x02, 0x80))
- SAY("ERROR: failed to set SAA register 0x02 "
- "for input %i\n", input);
-
- SET(p, 0x0000, 0x0090);
- SET(p, 0x0002, 0x0078);
- break;
- }
- case 3: {
- if (0 != write_saa(p, 0x02, 0x80))
- SAY("ERROR: failed to set SAA register 0x02 "
- " for input %i\n", input);
-
- SET(p, 0x0000, 0x0088);
- SET(p, 0x0002, 0x0078);
- break;
- }
- case 4: {
- if (0 != write_saa(p, 0x02, 0x80)) {
- SAY("ERROR: failed to set SAA register 0x02 "
- "for input %i\n", input);
- }
- SET(p, 0x0000, 0x0080);
- SET(p, 0x0002, 0x0078);
- break;
- }
- case 5: {
- if (9 != mode)
- mode = 7;
- switch (mode) {
- case 7: {
- if (0 != write_saa(p, 0x02, 0x87))
- SAY("ERROR: failed to set SAA register 0x02 "
- "for input %i\n", input);
-
- if (0 != write_saa(p, 0x05, 0xFF))
- SAY("ERROR: failed to set SAA register 0x05 "
- "for input %i\n", input);
-
- break;
- }
- case 9: {
- if (0 != write_saa(p, 0x02, 0x89))
- SAY("ERROR: failed to set SAA register 0x02 "
- "for input %i\n", input);
-
- if (0 != write_saa(p, 0x05, 0x00))
- SAY("ERROR: failed to set SAA register 0x05 "
- "for input %i\n", input);
-
- break;
- }
- default:
- SAY("MISTAKE: bad mode: %i\n", mode);
- return -1;
- }
-
- if (0 != write_saa(p, 0x04, 0x00))
- SAY("ERROR: failed to set SAA register 0x04 "
- "for input %i\n", input);
-
- if (0 != write_saa(p, 0x09, 0x80))
- SAY("ERROR: failed to set SAA register 0x09 "
- "for input %i\n", input);
-
- SET(p, 0x0002, 0x0093);
- break;
- }
- default:
- SAY("ERROR: bad input: %i\n", input);
- return -1;
- }
-
- ir = read_stk(p, 0x00);
- JOT(8, "STK register 0x00 has 0x%02X\n", ir);
- ir = read_saa(p, 0x02);
- JOT(8, "SAA register 0x02 has 0x%02X\n", ir);
-
- start_100(p);
-
- return 0;
-}
-/****************************************************************************/
-int set_resolution(struct usb_device *p,
- u16 set0, u16 set1, u16 set2, u16 set3)
-{
- u16 u0x0111, u0x0113, u0x0115, u0x0117;
-
- if (!p)
- return -ENODEV;
- u0x0111 = ((0xFF00 & set0) >> 8);
- u0x0113 = ((0xFF00 & set1) >> 8);
- u0x0115 = ((0xFF00 & set2) >> 8);
- u0x0117 = ((0xFF00 & set3) >> 8);
-
- SET(p, 0x0110, (0x00FF & set0));
- SET(p, 0x0111, u0x0111);
- SET(p, 0x0112, (0x00FF & set1));
- SET(p, 0x0113, u0x0113);
- SET(p, 0x0114, (0x00FF & set2));
- SET(p, 0x0115, u0x0115);
- SET(p, 0x0116, (0x00FF & set3));
- SET(p, 0x0117, u0x0117);
-
- return 0;
-}
-/****************************************************************************/
-int start_100(struct usb_device *p)
-{
- u16 get116, get117, get0;
- u8 igot116, igot117, igot;
-
- if (!p)
- return -ENODEV;
- GET(p, 0x0116, &igot116);
- get116 = igot116;
- GET(p, 0x0117, &igot117);
- get117 = igot117;
- SET(p, 0x0116, 0x0000);
- SET(p, 0x0117, 0x0000);
-
- GET(p, 0x0100, &igot);
- get0 = igot;
- SET(p, 0x0100, (0x80 | get0));
-
- SET(p, 0x0116, get116);
- SET(p, 0x0117, get117);
-
- return 0;
-}
-/****************************************************************************/
-int stop_100(struct usb_device *p)
-{
- u16 get0;
- u8 igot;
-
- if (!p)
- return -ENODEV;
- GET(p, 0x0100, &igot);
- get0 = igot;
- SET(p, 0x0100, (0x7F & get0));
- return 0;
-}
-/****************************************************************************/
-/****************************************************************************/
-/*****************************************************************************/
-int wakeup_device(struct usb_device *pusb_device)
-{
- if (!pusb_device)
- return -ENODEV;
- return usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
- USB_REQ_SET_FEATURE,
- USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
- USB_DEVICE_REMOTE_WAKEUP,
- 0, NULL, 0, 50000);
-}
-/*****************************************************************************/
-int
-audio_setup(struct easycap *peasycap)
-{
- struct usb_device *pusb_device;
- u8 buffer[1];
- int rc, id1, id2;
-/*---------------------------------------------------------------------------*/
-/*
- * IMPORTANT:
- * THE MESSAGE OF TYPE (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
- * CAUSES MUTING IF THE VALUE 0x0100 IS SENT.
- * TO ENABLE AUDIO THE VALUE 0x0200 MUST BE SENT.
- */
-/*---------------------------------------------------------------------------*/
- const u8 request = 0x01;
- const u8 requesttype = USB_DIR_OUT |
- USB_TYPE_CLASS |
- USB_RECIP_INTERFACE;
- const u16 value_unmute = 0x0200;
- const u16 index = 0x0301;
- const u16 length = 1;
-
- if (!peasycap)
- return -EFAULT;
-
- pusb_device = peasycap->pusb_device;
- if (!pusb_device)
- return -ENODEV;
-
- JOM(8, "%02X %02X %02X %02X %02X %02X %02X %02X\n",
- requesttype, request,
- (0x00FF & value_unmute),
- (0xFF00 & value_unmute) >> 8,
- (0x00FF & index),
- (0xFF00 & index) >> 8,
- (0x00FF & length),
- (0xFF00 & length) >> 8);
-
- buffer[0] = 0x01;
-
- rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
- request, requesttype, value_unmute,
- index, &buffer[0], length, 50000);
-
- JOT(8, "0x%02X=buffer\n", buffer[0]);
- if (rc != (int)length) {
- switch (rc) {
- case -EPIPE:
- SAY("usb_control_msg returned -EPIPE\n");
- break;
- default:
- SAY("ERROR: usb_control_msg returned %i\n", rc);
- break;
- }
- }
-/*--------------------------------------------------------------------------*/
-/*
- * REGISTER 500: SETTING VALUE TO 0x0094 RESETS AUDIO CONFIGURATION ???
- * REGISTER 506: ANALOGUE AUDIO ATTENTUATOR ???
- * FOR THE CVBS+S-VIDEO HARDWARE:
- * SETTING VALUE TO 0x0000 GIVES QUIET SOUND.
- * THE UPPER BYTE SEEMS TO HAVE NO EFFECT.
- * FOR THE FOUR-CVBS HARDWARE:
- * SETTING VALUE TO 0x0000 SEEMS TO HAVE NO EFFECT.
- * REGISTER 507: ANALOGUE AUDIO PREAMPLIFIER ON/OFF ???
- * FOR THE CVBS-S-VIDEO HARDWARE:
- * SETTING VALUE TO 0x0001 GIVES VERY LOUD, DISTORTED SOUND.
- * THE UPPER BYTE SEEMS TO HAVE NO EFFECT.
- */
-/*--------------------------------------------------------------------------*/
- SET(pusb_device, 0x0500, 0x0094);
- SET(pusb_device, 0x0500, 0x008C);
- SET(pusb_device, 0x0506, 0x0001);
- SET(pusb_device, 0x0507, 0x0000);
- id1 = read_vt(pusb_device, 0x007C);
- id2 = read_vt(pusb_device, 0x007E);
- SAM("0x%04X:0x%04X is audio vendor id\n", id1, id2);
-/*---------------------------------------------------------------------------*/
-/*
- * SELECT AUDIO SOURCE "LINE IN" AND SET THE AUDIO GAIN.
-*/
-/*---------------------------------------------------------------------------*/
- if (0 != audio_gainset(pusb_device, peasycap->gain))
- SAY("ERROR: audio_gainset() failed\n");
- check_vt(pusb_device);
- return 0;
-}
-/*****************************************************************************/
-int check_vt(struct usb_device *pusb_device)
-{
- int igot;
-
- if (!pusb_device)
- return -ENODEV;
- igot = read_vt(pusb_device, 0x0002);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x02\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x02);
-
- igot = read_vt(pusb_device, 0x000E);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x0E\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x0E);
-
- igot = read_vt(pusb_device, 0x0010);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x10\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x10);
-
- igot = read_vt(pusb_device, 0x0012);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x12\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x12);
-
- igot = read_vt(pusb_device, 0x0014);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x14\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x14);
-
- igot = read_vt(pusb_device, 0x0016);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x16\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x16);
-
- igot = read_vt(pusb_device, 0x0018);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x18\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x18);
-
- igot = read_vt(pusb_device, 0x001C);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x1C\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x1C);
-
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/* NOTE: THIS DOES INCREASE THE VOLUME DRAMATICALLY:
- * audio_gainset(pusb_device, 0x000F);
- *
- * loud dB register 0x10 dB register 0x1C dB total
- * 0 -34.5 0 -34.5
- * .. .... . ....
- * 15 10.5 0 10.5
- * 16 12.0 0 12.0
- * 17 12.0 1.5 13.5
- * .. .... .... ....
- * 31 12.0 22.5 34.5
-*/
-/*---------------------------------------------------------------------------*/
-int audio_gainset(struct usb_device *pusb_device, s8 loud)
-{
- int igot;
- u8 tmp;
- u16 mute;
-
- if (!pusb_device)
- return -ENODEV;
- if (0 > loud)
- loud = 0;
- if (31 < loud)
- loud = 31;
-
- write_vt(pusb_device, 0x0002, 0x8000);
-/*---------------------------------------------------------------------------*/
- igot = read_vt(pusb_device, 0x000E);
- if (0 > igot) {
- SAY("ERROR: failed to read VT1612A register 0x0E\n");
- mute = 0x0000;
- } else
- mute = 0x8000 & ((unsigned int)igot);
- mute = 0;
-
- if (16 > loud)
- tmp = 0x01 | (0x001F & (((u8)(15 - loud)) << 1));
- else
- tmp = 0;
-
- JOT(8, "0x%04X=(mute|tmp) for VT1612A register 0x0E\n", mute | tmp);
- write_vt(pusb_device, 0x000E, (mute | tmp));
-/*---------------------------------------------------------------------------*/
- igot = read_vt(pusb_device, 0x0010);
- if (0 > igot) {
- SAY("ERROR: failed to read VT1612A register 0x10\n");
- mute = 0x0000;
- } else
- mute = 0x8000 & ((unsigned int)igot);
- mute = 0;
-
- JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x10,...0x18\n",
- mute | tmp | (tmp << 8));
- write_vt(pusb_device, 0x0010, (mute | tmp | (tmp << 8)));
- write_vt(pusb_device, 0x0012, (mute | tmp | (tmp << 8)));
- write_vt(pusb_device, 0x0014, (mute | tmp | (tmp << 8)));
- write_vt(pusb_device, 0x0016, (mute | tmp | (tmp << 8)));
- write_vt(pusb_device, 0x0018, (mute | tmp | (tmp << 8)));
-/*---------------------------------------------------------------------------*/
- igot = read_vt(pusb_device, 0x001C);
- if (0 > igot) {
- SAY("ERROR: failed to read VT1612A register 0x1C\n");
- mute = 0x0000;
- } else
- mute = 0x8000 & ((unsigned int)igot);
- mute = 0;
-
- if (16 <= loud)
- tmp = 0x000F & (u8)(loud - 16);
- else
- tmp = 0;
-
- JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x1C\n",
- mute | tmp | (tmp << 8));
- write_vt(pusb_device, 0x001C, (mute | tmp | (tmp << 8)));
- write_vt(pusb_device, 0x001A, 0x0404);
- write_vt(pusb_device, 0x0002, 0x0000);
- return 0;
-}
-/*****************************************************************************/
-int audio_gainget(struct usb_device *pusb_device)
-{
- int igot;
-
- if (!pusb_device)
- return -ENODEV;
- igot = read_vt(pusb_device, 0x001C);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x1C\n");
- return igot;
-}
-/*****************************************************************************/
+++ /dev/null
-/******************************************************************************
-* *
-* easycap_main.c *
-* *
-* Video driver for EasyCAP USB2.0 Video Capture Device DC60 *
-* *
-* *
-******************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This 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.
- *
- * The software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-
-#include "easycap.h"
-#include <linux/usb/audio.h>
-
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("R.M. Thomas <rmthomas@sciolus.org>");
-MODULE_DESCRIPTION(EASYCAP_DRIVER_DESCRIPTION);
-MODULE_VERSION(EASYCAP_DRIVER_VERSION);
-
-#ifdef CONFIG_EASYCAP_DEBUG
-int easycap_debug;
-module_param_named(debug, easycap_debug, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug level: 0(default),1,2,...,9");
-#endif /* CONFIG_EASYCAP_DEBUG */
-
-bool easycap_readback;
-module_param_named(readback, easycap_readback, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(readback, "read back written registers: (default false)");
-
-static int easycap_bars = 1;
-module_param_named(bars, easycap_bars, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(bars,
- "Testcard bars on input signal failure: 0=>no, 1=>yes(default)");
-
-static int easycap_gain = 16;
-module_param_named(gain, easycap_gain, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(gain, "Audio gain: 0,...,16(default),...31");
-
-static bool easycap_ntsc;
-module_param_named(ntsc, easycap_ntsc, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(ntsc, "NTSC default encoding (default PAL)");
-
-
-
-struct easycap_dongle easycapdc60_dongle[DONGLE_MANY];
-static struct mutex mutex_dongle;
-static void easycap_complete(struct urb *purb);
-static int reset(struct easycap *peasycap);
-
-const char *strerror(int err)
-{
-#define ERRNOSTR(_e) case _e: return # _e
- switch (err) {
- case 0: return "OK";
- ERRNOSTR(ENOMEM);
- ERRNOSTR(ENODEV);
- ERRNOSTR(ENXIO);
- ERRNOSTR(EINVAL);
- ERRNOSTR(EAGAIN);
- ERRNOSTR(EFBIG);
- ERRNOSTR(EPIPE);
- ERRNOSTR(EMSGSIZE);
- ERRNOSTR(ENOSPC);
- ERRNOSTR(EINPROGRESS);
- ERRNOSTR(ENOSR);
- ERRNOSTR(EOVERFLOW);
- ERRNOSTR(EPROTO);
- ERRNOSTR(EILSEQ);
- ERRNOSTR(ETIMEDOUT);
- ERRNOSTR(EOPNOTSUPP);
- ERRNOSTR(EPFNOSUPPORT);
- ERRNOSTR(EAFNOSUPPORT);
- ERRNOSTR(EADDRINUSE);
- ERRNOSTR(EADDRNOTAVAIL);
- ERRNOSTR(ENOBUFS);
- ERRNOSTR(EISCONN);
- ERRNOSTR(ENOTCONN);
- ERRNOSTR(ESHUTDOWN);
- ERRNOSTR(ENOENT);
- ERRNOSTR(ECONNRESET);
- ERRNOSTR(ETIME);
- ERRNOSTR(ECOMM);
- ERRNOSTR(EREMOTEIO);
- ERRNOSTR(EXDEV);
- ERRNOSTR(EPERM);
- default: return "unknown";
- }
-
-#undef ERRNOSTR
-}
-
-/*---------------------------------------------------------------------------*/
-/*
- * PARAMETERS USED WHEN REGISTERING THE VIDEO INTERFACE
- *
- * NOTE: SOME KERNELS IGNORE usb_class_driver.minor_base, AS MENTIONED BY
- * CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGE 253.
- * THIS IS THE CASE FOR OpenSUSE.
- */
-/*---------------------------------------------------------------------------*/
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-/****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * THIS ROUTINE DOES NOT DETECT DUPLICATE OCCURRENCES OF POINTER peasycap
-*/
-/*---------------------------------------------------------------------------*/
-int isdongle(struct easycap *peasycap)
-{
- int k;
- if (!peasycap)
- return -2;
- for (k = 0; k < DONGLE_MANY; k++) {
- if (easycapdc60_dongle[k].peasycap == peasycap) {
- peasycap->isdongle = k;
- return k;
- }
- }
- return -1;
-}
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-static int easycap_open(struct inode *inode, struct file *file)
-{
- struct video_device *pvideo_device;
- struct easycap *peasycap;
- int rc;
-
- JOT(4, "\n");
- SAY("==========OPEN=========\n");
-
- pvideo_device = video_devdata(file);
- if (!pvideo_device) {
- SAY("ERROR: pvideo_device is NULL.\n");
- return -EFAULT;
- }
- peasycap = (struct easycap *)video_get_drvdata(pvideo_device);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- } else {
- JOM(16, "peasycap->pusb_device=%p\n", peasycap->pusb_device);
- }
- file->private_data = peasycap;
- rc = wakeup_device(peasycap->pusb_device);
- if (0 == rc)
- JOM(8, "wakeup_device() OK\n");
- else {
- SAM("ERROR: wakeup_device() rc = %i\n", rc);
- if (-ENODEV == rc)
- SAM("ERROR: wakeup_device() returned -ENODEV\n");
- else
- SAM("ERROR: wakeup_device() rc = %i\n", rc);
- return rc;
- }
- peasycap->input = 0;
- rc = reset(peasycap);
- if (rc) {
- SAM("ERROR: reset() rc = %i\n", rc);
- return -EFAULT;
- }
- return 0;
-}
-
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * RESET THE HARDWARE TO ITS REFERENCE STATE.
- *
- * THIS ROUTINE MAY BE CALLED REPEATEDLY IF easycap_complete() DETECTS
- * A BAD VIDEO FRAME SIZE.
-*/
-/*---------------------------------------------------------------------------*/
-static int reset(struct easycap *peasycap)
-{
- struct easycap_standard const *peasycap_standard;
- int fmtidx, input, rate;
- bool ntsc, other;
- int rc;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- input = peasycap->input;
-
-/*---------------------------------------------------------------------------*/
-/*
- * IF THE SAA7113H HAS ALREADY ACQUIRED SYNC, USE ITS HARDWARE-DETECTED
- * FIELD FREQUENCY TO DISTINGUISH NTSC FROM PAL. THIS IS ESSENTIAL FOR
- * gstreamer AND OTHER USERSPACE PROGRAMS WHICH MAY NOT ATTEMPT TO INITIATE
- * A SWITCH BETWEEN PAL AND NTSC.
- *
- * FUNCTION ready_saa() MAY REQUIRE A SUBSTANTIAL FRACTION OF A SECOND TO
- * COMPLETE, SO SHOULD NOT BE INVOKED WITHOUT GOOD REASON.
-*/
-/*---------------------------------------------------------------------------*/
- other = false;
- JOM(8, "peasycap->ntsc=%d\n", peasycap->ntsc);
-
- rate = ready_saa(peasycap->pusb_device);
- if (rate < 0) {
- JOM(8, "not ready to capture after %i ms ...\n", PATIENCE);
- ntsc = !peasycap->ntsc;
- JOM(8, "... trying %s ..\n", ntsc ? "NTSC" : "PAL");
- rc = setup_stk(peasycap->pusb_device, ntsc);
- if (rc) {
- SAM("ERROR: setup_stk() rc = %i\n", rc);
- return -EFAULT;
- }
- rc = setup_saa(peasycap->pusb_device, ntsc);
- if (rc) {
- SAM("ERROR: setup_saa() rc = %i\n", rc);
- return -EFAULT;
- }
-
- rate = ready_saa(peasycap->pusb_device);
- if (rate < 0) {
- JOM(8, "not ready to capture after %i ms\n", PATIENCE);
- JOM(8, "... saa register 0x1F has 0x%02X\n",
- read_saa(peasycap->pusb_device, 0x1F));
- ntsc = peasycap->ntsc;
- } else {
- JOM(8, "... success at second try: %i=rate\n", rate);
- ntsc = (0 < (rate/2)) ? true : false ;
- other = true;
- }
- } else {
- JOM(8, "... success at first try: %i=rate\n", rate);
- ntsc = (0 < rate/2) ? true : false ;
- }
- JOM(8, "ntsc=%d\n", ntsc);
-/*---------------------------------------------------------------------------*/
-
- rc = setup_stk(peasycap->pusb_device, ntsc);
- if (rc) {
- SAM("ERROR: setup_stk() rc = %i\n", rc);
- return -EFAULT;
- }
- rc = setup_saa(peasycap->pusb_device, ntsc);
- if (rc) {
- SAM("ERROR: setup_saa() rc = %i\n", rc);
- return -EFAULT;
- }
-
- memset(peasycap->merit, 0, sizeof(peasycap->merit));
-
- peasycap->video_eof = 0;
- peasycap->audio_eof = 0;
-/*---------------------------------------------------------------------------*/
-/*
- * RESTORE INPUT AND FORCE REFRESH OF STANDARD, FORMAT, ETC.
- *
- * WHILE THIS PROCEDURE IS IN PROGRESS, SOME IOCTL COMMANDS WILL RETURN -EBUSY.
-*/
-/*---------------------------------------------------------------------------*/
- peasycap->input = -8192;
- peasycap->standard_offset = -8192;
- fmtidx = ntsc ? NTSC_M : PAL_BGHIN;
- if (other) {
- peasycap_standard = &easycap_standard[0];
- while (0xFFFF != peasycap_standard->mask) {
- if (fmtidx == peasycap_standard->v4l2_standard.index) {
- peasycap->inputset[input].standard_offset =
- peasycap_standard - easycap_standard;
- break;
- }
- peasycap_standard++;
- }
- if (0xFFFF == peasycap_standard->mask) {
- SAM("ERROR: standard not found\n");
- return -EINVAL;
- }
- JOM(8, "%i=peasycap->inputset[%i].standard_offset\n",
- peasycap->inputset[input].standard_offset, input);
- }
- peasycap->format_offset = -8192;
- peasycap->brightness = -8192;
- peasycap->contrast = -8192;
- peasycap->saturation = -8192;
- peasycap->hue = -8192;
-
- rc = newinput(peasycap, input);
-
- if (rc) {
- SAM("ERROR: newinput(.,%i) rc = %i\n", rc, input);
- return -EFAULT;
- }
- JOM(4, "restored input, standard and format\n");
-
- JOM(8, "true=peasycap->ntsc %d\n", peasycap->ntsc);
-
- if (0 > peasycap->input) {
- SAM("MISTAKE: %i=peasycap->input\n", peasycap->input);
- return -ENOENT;
- }
- if (0 > peasycap->standard_offset) {
- SAM("MISTAKE: %i=peasycap->standard_offset\n",
- peasycap->standard_offset);
- return -ENOENT;
- }
- if (0 > peasycap->format_offset) {
- SAM("MISTAKE: %i=peasycap->format_offset\n",
- peasycap->format_offset);
- return -ENOENT;
- }
- if (0 > peasycap->brightness) {
- SAM("MISTAKE: %i=peasycap->brightness\n",
- peasycap->brightness);
- return -ENOENT;
- }
- if (0 > peasycap->contrast) {
- SAM("MISTAKE: %i=peasycap->contrast\n", peasycap->contrast);
- return -ENOENT;
- }
- if (0 > peasycap->saturation) {
- SAM("MISTAKE: %i=peasycap->saturation\n",
- peasycap->saturation);
- return -ENOENT;
- }
- if (0 > peasycap->hue) {
- SAM("MISTAKE: %i=peasycap->hue\n", peasycap->hue);
- return -ENOENT;
- }
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * IF THE REQUESTED INPUT IS THE SAME AS THE EXISTING INPUT, DO NOTHING.
- * OTHERWISE:
- * KILL URBS, CLEAR FIELD AND FRAME BUFFERS AND RESET THEIR
- * _read AND _fill POINTERS.
- * SELECT THE NEW INPUT.
- * ADJUST THE STANDARD, FORMAT, BRIGHTNESS, CONTRAST, SATURATION AND HUE
- * ON THE BASIS OF INFORMATION IN STRUCTURE easycap.inputset[input].
- * RESUBMIT THE URBS IF STREAMING WAS ALREADY IN PROGRESS.
- *
- * NOTE:
- * THIS ROUTINE MAY BE CALLED FREQUENTLY BY ZONEMINDER VIA IOCTL,
- * SO IT SHOULD WRITE ONLY SPARINGLY TO THE LOGFILE.
-*/
-/*---------------------------------------------------------------------------*/
-int
-newinput(struct easycap *peasycap, int input)
-{
- int rc, k, m, mood, off;
- int inputnow, video_idlenow, audio_idlenow;
- bool resubmit;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- JOM(8, "%i=input sought\n", input);
-
- if (0 > input && INPUT_MANY <= input)
- return -ENOENT;
- inputnow = peasycap->input;
- if (input == inputnow)
- return 0;
-/*---------------------------------------------------------------------------*/
-/*
- * IF STREAMING IS IN PROGRESS THE URBS ARE KILLED AT THIS
- * STAGE AND WILL BE RESUBMITTED PRIOR TO EXIT FROM THE ROUTINE.
- * IF NO STREAMING IS IN PROGRESS NO URBS WILL BE SUBMITTED BY THE
- * ROUTINE.
-*/
-/*---------------------------------------------------------------------------*/
- video_idlenow = peasycap->video_idle;
- audio_idlenow = peasycap->audio_idle;
-
- peasycap->video_idle = 1;
- peasycap->audio_idle = 1;
- if (peasycap->video_isoc_streaming) {
- resubmit = true;
- kill_video_urbs(peasycap);
- } else {
- resubmit = false;
- }
-/*---------------------------------------------------------------------------*/
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -ENODEV;
- }
- rc = usb_set_interface(peasycap->pusb_device,
- peasycap->video_interface,
- peasycap->video_altsetting_off);
- if (rc) {
- SAM("ERROR: usb_set_interface() rc = %i\n", rc);
- return -EFAULT;
- }
- rc = stop_100(peasycap->pusb_device);
- if (rc) {
- SAM("ERROR: stop_100() rc = %i\n", rc);
- return -EFAULT;
- }
- for (k = 0; k < FIELD_BUFFER_MANY; k++) {
- for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++)
- memset(peasycap->field_buffer[k][m].pgo, 0, PAGE_SIZE);
- }
- for (k = 0; k < FRAME_BUFFER_MANY; k++) {
- for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++)
- memset(peasycap->frame_buffer[k][m].pgo, 0, PAGE_SIZE);
- }
- peasycap->field_page = 0;
- peasycap->field_read = 0;
- peasycap->field_fill = 0;
-
- peasycap->frame_read = 0;
- peasycap->frame_fill = 0;
- for (k = 0; k < peasycap->input; k++) {
- (peasycap->frame_fill)++;
- if (peasycap->frame_buffer_many <= peasycap->frame_fill)
- peasycap->frame_fill = 0;
- }
- peasycap->input = input;
- select_input(peasycap->pusb_device, peasycap->input, 9);
-/*---------------------------------------------------------------------------*/
- if (input == peasycap->inputset[input].input) {
- off = peasycap->inputset[input].standard_offset;
- if (off != peasycap->standard_offset) {
- rc = adjust_standard(peasycap,
- easycap_standard[off].v4l2_standard.id);
- if (rc) {
- SAM("ERROR: adjust_standard() rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->standard_offset\n",
- peasycap->standard_offset);
- } else {
- JOM(8, "%i=peasycap->standard_offset unchanged\n",
- peasycap->standard_offset);
- }
- off = peasycap->inputset[input].format_offset;
- if (off != peasycap->format_offset) {
- struct v4l2_pix_format *pix =
- &easycap_format[off].v4l2_format.fmt.pix;
- rc = adjust_format(peasycap,
- pix->width, pix->height,
- pix->pixelformat, pix->field, false);
- if (0 > rc) {
- SAM("ERROR: adjust_format() rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->format_offset\n",
- peasycap->format_offset);
- } else {
- JOM(8, "%i=peasycap->format_offset unchanged\n",
- peasycap->format_offset);
- }
- mood = peasycap->inputset[input].brightness;
- if (mood != peasycap->brightness) {
- rc = adjust_brightness(peasycap, mood);
- if (rc) {
- SAM("ERROR: adjust_brightness rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->brightness\n",
- peasycap->brightness);
- }
- mood = peasycap->inputset[input].contrast;
- if (mood != peasycap->contrast) {
- rc = adjust_contrast(peasycap, mood);
- if (rc) {
- SAM("ERROR: adjust_contrast rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->contrast\n", peasycap->contrast);
- }
- mood = peasycap->inputset[input].saturation;
- if (mood != peasycap->saturation) {
- rc = adjust_saturation(peasycap, mood);
- if (rc) {
- SAM("ERROR: adjust_saturation rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->saturation\n",
- peasycap->saturation);
- }
- mood = peasycap->inputset[input].hue;
- if (mood != peasycap->hue) {
- rc = adjust_hue(peasycap, mood);
- if (rc) {
- SAM("ERROR: adjust_hue rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->hue\n", peasycap->hue);
- }
- } else {
- SAM("MISTAKE: easycap.inputset[%i] unpopulated\n", input);
- return -ENOENT;
- }
-/*---------------------------------------------------------------------------*/
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -ENODEV;
- }
- rc = usb_set_interface(peasycap->pusb_device,
- peasycap->video_interface,
- peasycap->video_altsetting_on);
- if (rc) {
- SAM("ERROR: usb_set_interface() rc = %i\n", rc);
- return -EFAULT;
- }
- rc = start_100(peasycap->pusb_device);
- if (rc) {
- SAM("ERROR: start_100() rc = %i\n", rc);
- return -EFAULT;
- }
- if (resubmit)
- submit_video_urbs(peasycap);
-
- peasycap->video_isoc_sequence = VIDEO_ISOC_BUFFER_MANY - 1;
- peasycap->video_idle = video_idlenow;
- peasycap->audio_idle = audio_idlenow;
- peasycap->video_junk = 0;
-
- return 0;
-}
-/*****************************************************************************/
-int submit_video_urbs(struct easycap *peasycap)
-{
- struct data_urb *pdata_urb;
- struct urb *purb;
- struct list_head *plist_head;
- int j, isbad, nospc, m, rc;
- int isbuf;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
-
- if (!peasycap->purb_video_head) {
- SAY("ERROR: peasycap->urb_video_head uninitialized\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAY("ERROR: peasycap->pusb_device is NULL\n");
- return -ENODEV;
- }
- if (!peasycap->video_isoc_streaming) {
- JOM(4, "submission of all video urbs\n");
- isbad = 0; nospc = 0; m = 0;
- list_for_each(plist_head, (peasycap->purb_video_head)) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb) {
- purb = pdata_urb->purb;
- isbuf = pdata_urb->isbuf;
- purb->interval = 1;
- purb->dev = peasycap->pusb_device;
- purb->pipe =
- usb_rcvisocpipe(peasycap->pusb_device,
- peasycap->video_endpointnumber);
- purb->transfer_flags = URB_ISO_ASAP;
- purb->transfer_buffer =
- peasycap->video_isoc_buffer[isbuf].pgo;
- purb->transfer_buffer_length =
- peasycap->video_isoc_buffer_size;
- purb->complete = easycap_complete;
- purb->context = peasycap;
- purb->start_frame = 0;
- purb->number_of_packets =
- peasycap->video_isoc_framesperdesc;
-
- for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) {
- purb->iso_frame_desc[j]. offset =
- j * peasycap->video_isoc_maxframesize;
- purb->iso_frame_desc[j]. length =
- peasycap->video_isoc_maxframesize;
- }
-
- rc = usb_submit_urb(purb, GFP_KERNEL);
- if (rc) {
- isbad++;
- SAM("ERROR: usb_submit_urb() failed "
- "for urb with rc:-%s\n",
- strerror(rc));
- if (rc == -ENOSPC)
- nospc++;
- } else {
- m++;
- }
- } else {
- isbad++;
- }
- }
- if (nospc) {
- SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc);
- SAM("..... possibly inadequate USB bandwidth\n");
- peasycap->video_eof = 1;
- }
-
- if (isbad) {
- JOM(4, "attempting cleanup instead of submitting\n");
- list_for_each(plist_head, (peasycap->purb_video_head)) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb) {
- purb = pdata_urb->purb;
- if (purb)
- usb_kill_urb(purb);
- }
- }
- peasycap->video_isoc_streaming = 0;
- } else {
- peasycap->video_isoc_streaming = 1;
- JOM(4, "submitted %i video urbs\n", m);
- }
- } else {
- JOM(4, "already streaming video urbs\n");
- }
- return 0;
-}
-/*****************************************************************************/
-int kill_video_urbs(struct easycap *peasycap)
-{
- int m;
- struct list_head *plist_head;
- struct data_urb *pdata_urb;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->video_isoc_streaming) {
- JOM(8, "%i=video_isoc_streaming, no video urbs killed\n",
- peasycap->video_isoc_streaming);
- return 0;
- }
- if (!peasycap->purb_video_head) {
- SAM("ERROR: peasycap->purb_video_head is NULL\n");
- return -EFAULT;
- }
-
- peasycap->video_isoc_streaming = 0;
- JOM(4, "killing video urbs\n");
- m = 0;
- list_for_each(plist_head, (peasycap->purb_video_head)) {
- pdata_urb = list_entry(plist_head, struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb) {
- usb_kill_urb(pdata_urb->purb);
- m++;
- }
- }
- JOM(4, "%i video urbs killed\n", m);
-
- return 0;
-}
-/****************************************************************************/
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-/*--------------------------------------------------------------------------*/
-static int easycap_open_noinode(struct file *file)
-{
- return easycap_open(NULL, file);
-}
-
-static int videodev_release(struct video_device *pvideo_device)
-{
- struct easycap *peasycap;
-
- peasycap = video_get_drvdata(pvideo_device);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- SAY("ending unsuccessfully\n");
- return -EFAULT;
- }
- if (0 != kill_video_urbs(peasycap)) {
- SAM("ERROR: kill_video_urbs() failed\n");
- return -EFAULT;
- }
- JOM(4, "ending successfully\n");
- return 0;
-}
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-/*****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect() AND IS
- * PROTECTED BY SEMAPHORES SET AND CLEARED BY easycap_usb_disconnect().
- *
- * BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED, SO
- * peasycap->pusb_device IS NO LONGER VALID.
- */
-/*---------------------------------------------------------------------------*/
-static void easycap_delete(struct kref *pkref)
-{
- struct easycap *peasycap;
- struct data_urb *pdata_urb;
- struct list_head *plist_head, *plist_next;
- int k, m, gone, kd;
- int allocation_video_urb;
- int allocation_video_page;
- int allocation_video_struct;
- int allocation_audio_urb;
- int allocation_audio_page;
- int allocation_audio_struct;
- int registered_video, registered_audio;
-
- peasycap = container_of(pkref, struct easycap, kref);
- if (!peasycap) {
- SAM("ERROR: peasycap is NULL: cannot perform deletions\n");
- return;
- }
- kd = isdongle(peasycap);
-/*---------------------------------------------------------------------------*/
-/*
- * FREE VIDEO.
- */
-/*---------------------------------------------------------------------------*/
- if (peasycap->purb_video_head) {
- JOM(4, "freeing video urbs\n");
- m = 0;
- list_for_each(plist_head, (peasycap->purb_video_head)) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (!pdata_urb) {
- JOM(4, "ERROR: pdata_urb is NULL\n");
- } else {
- if (pdata_urb->purb) {
- usb_free_urb(pdata_urb->purb);
- pdata_urb->purb = NULL;
- peasycap->allocation_video_urb -= 1;
- m++;
- }
- }
- }
-
- JOM(4, "%i video urbs freed\n", m);
-/*---------------------------------------------------------------------------*/
- JOM(4, "freeing video data_urb structures.\n");
- m = 0;
- list_for_each_safe(plist_head, plist_next,
- peasycap->purb_video_head) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb) {
- peasycap->allocation_video_struct -=
- sizeof(struct data_urb);
- kfree(pdata_urb);
- pdata_urb = NULL;
- m++;
- }
- }
- JOM(4, "%i video data_urb structures freed\n", m);
- JOM(4, "setting peasycap->purb_video_head=NULL\n");
- peasycap->purb_video_head = NULL;
- }
-/*---------------------------------------------------------------------------*/
- JOM(4, "freeing video isoc buffers.\n");
- m = 0;
- for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
- if (peasycap->video_isoc_buffer[k].pgo) {
- free_pages((unsigned long)
- peasycap->video_isoc_buffer[k].pgo,
- VIDEO_ISOC_ORDER);
- peasycap->video_isoc_buffer[k].pgo = NULL;
- peasycap->allocation_video_page -=
- BIT(VIDEO_ISOC_ORDER);
- m++;
- }
- }
- JOM(4, "isoc video buffers freed: %i pages\n",
- m * (0x01 << VIDEO_ISOC_ORDER));
-/*---------------------------------------------------------------------------*/
- JOM(4, "freeing video field buffers.\n");
- gone = 0;
- for (k = 0; k < FIELD_BUFFER_MANY; k++) {
- for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) {
- if (peasycap->field_buffer[k][m].pgo) {
- free_page((unsigned long)
- peasycap->field_buffer[k][m].pgo);
- peasycap->field_buffer[k][m].pgo = NULL;
- peasycap->allocation_video_page -= 1;
- gone++;
- }
- }
- }
- JOM(4, "video field buffers freed: %i pages\n", gone);
-/*---------------------------------------------------------------------------*/
- JOM(4, "freeing video frame buffers.\n");
- gone = 0;
- for (k = 0; k < FRAME_BUFFER_MANY; k++) {
- for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) {
- if (peasycap->frame_buffer[k][m].pgo) {
- free_page((unsigned long)
- peasycap->frame_buffer[k][m].pgo);
- peasycap->frame_buffer[k][m].pgo = NULL;
- peasycap->allocation_video_page -= 1;
- gone++;
- }
- }
- }
- JOM(4, "video frame buffers freed: %i pages\n", gone);
-/*---------------------------------------------------------------------------*/
-/*
- * FREE AUDIO.
- */
-/*---------------------------------------------------------------------------*/
- if (peasycap->purb_audio_head) {
- JOM(4, "freeing audio urbs\n");
- m = 0;
- list_for_each(plist_head, (peasycap->purb_audio_head)) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (!pdata_urb)
- JOM(4, "ERROR: pdata_urb is NULL\n");
- else {
- if (pdata_urb->purb) {
- usb_free_urb(pdata_urb->purb);
- pdata_urb->purb = NULL;
- peasycap->allocation_audio_urb -= 1;
- m++;
- }
- }
- }
- JOM(4, "%i audio urbs freed\n", m);
-/*---------------------------------------------------------------------------*/
- JOM(4, "freeing audio data_urb structures.\n");
- m = 0;
- list_for_each_safe(plist_head, plist_next,
- peasycap->purb_audio_head) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb) {
- peasycap->allocation_audio_struct -=
- sizeof(struct data_urb);
- kfree(pdata_urb);
- pdata_urb = NULL;
- m++;
- }
- }
- JOM(4, "%i audio data_urb structures freed\n", m);
- JOM(4, "setting peasycap->purb_audio_head=NULL\n");
- peasycap->purb_audio_head = NULL;
- }
-/*---------------------------------------------------------------------------*/
- JOM(4, "freeing audio isoc buffers.\n");
- m = 0;
- for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
- if (peasycap->audio_isoc_buffer[k].pgo) {
- free_pages((unsigned long)
- (peasycap->audio_isoc_buffer[k].pgo),
- AUDIO_ISOC_ORDER);
- peasycap->audio_isoc_buffer[k].pgo = NULL;
- peasycap->allocation_audio_page -=
- BIT(AUDIO_ISOC_ORDER);
- m++;
- }
- }
- JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n",
- m * (0x01 << AUDIO_ISOC_ORDER));
-/*---------------------------------------------------------------------------*/
- JOM(4, "freeing easycap structure.\n");
- allocation_video_urb = peasycap->allocation_video_urb;
- allocation_video_page = peasycap->allocation_video_page;
- allocation_video_struct = peasycap->allocation_video_struct;
- registered_video = peasycap->registered_video;
- allocation_audio_urb = peasycap->allocation_audio_urb;
- allocation_audio_page = peasycap->allocation_audio_page;
- allocation_audio_struct = peasycap->allocation_audio_struct;
- registered_audio = peasycap->registered_audio;
-
- if (0 <= kd && DONGLE_MANY > kd) {
- if (mutex_lock_interruptible(&mutex_dongle)) {
- SAY("ERROR: cannot down mutex_dongle\n");
- } else {
- JOM(4, "locked mutex_dongle\n");
- easycapdc60_dongle[kd].peasycap = NULL;
- mutex_unlock(&mutex_dongle);
- JOM(4, "unlocked mutex_dongle\n");
- JOT(4, " null-->dongle[%i].peasycap\n", kd);
- allocation_video_struct -= sizeof(struct easycap);
- }
- } else {
- SAY("ERROR: cannot purge dongle[].peasycap");
- }
-
- kfree(peasycap);
-
-/*---------------------------------------------------------------------------*/
- SAY("%8i=video urbs after all deletions\n", allocation_video_urb);
- SAY("%8i=video pages after all deletions\n", allocation_video_page);
- SAY("%8i=video structs after all deletions\n", allocation_video_struct);
- SAY("%8i=video devices after all deletions\n", registered_video);
- SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb);
- SAY("%8i=audio pages after all deletions\n", allocation_audio_page);
- SAY("%8i=audio structs after all deletions\n", allocation_audio_struct);
- SAY("%8i=audio devices after all deletions\n", registered_audio);
-
- JOT(4, "ending.\n");
- return;
-}
-/*****************************************************************************/
-static unsigned int easycap_poll(struct file *file, poll_table *wait)
-{
- struct easycap *peasycap;
- int rc, kd;
-
- JOT(8, "\n");
-
- if (NULL == ((poll_table *)wait))
- JOT(8, "WARNING: poll table pointer is NULL ... continuing\n");
- if (!file) {
- SAY("ERROR: file pointer is NULL\n");
- return -ERESTARTSYS;
- }
- peasycap = file->private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAY("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
-/*---------------------------------------------------------------------------*/
- kd = isdongle(peasycap);
- if (0 <= kd && DONGLE_MANY > kd) {
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
- SAY("ERROR: cannot down dongle[%i].mutex_video\n", kd);
- return -ERESTARTSYS;
- }
- JOM(4, "locked dongle[%i].mutex_video\n", kd);
- /*
- * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER
- * peasycap, IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
- * IF NECESSARY, BAIL OUT.
- */
- if (kd != isdongle(peasycap)) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- if (!file) {
- SAY("ERROR: file is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- peasycap = file->private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- } else
- /*
- * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap
- * BEFORE THE ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL
- * HAVE FAILED. BAIL OUT.
- */
- return -ERESTARTSYS;
-/*---------------------------------------------------------------------------*/
- rc = easycap_dqbuf(peasycap, 0);
- peasycap->polled = 1;
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- if (0 == rc)
- return POLLIN | POLLRDNORM;
- else
- return POLLERR;
- }
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * IF mode IS NONZERO THIS ROUTINE RETURNS -EAGAIN RATHER THAN BLOCKING.
- */
-/*---------------------------------------------------------------------------*/
-int easycap_dqbuf(struct easycap *peasycap, int mode)
-{
- int input, ifield, miss, rc;
-
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAY("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- ifield = 0;
- JOM(8, "%i=ifield\n", ifield);
-/*---------------------------------------------------------------------------*/
-/*
- * CHECK FOR LOST INPUT SIGNAL.
- *
- * FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED.
- * IF INPUT 0 IS PRESENT AND SYNC ACQUIRED, UNPLUGGING INPUT 4 DOES NOT
- * RESULT IN SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE
- * IS FLYWHEELING ON INPUT 0. THE UPSHOT IS:
- *
- * INPUT 0 PLUGGED, INPUT 4 PLUGGED => SCREEN 0 OK, SCREEN 4 OK
- * INPUT 0 PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK, SCREEN 4 BLACK
- * INPUT 0 UNPLUGGED, INPUT 4 PLUGGED => SCREEN 0 BARS, SCREEN 4 OK
- * INPUT 0 UNPLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 BARS, SCREEN 4 BARS
-*/
-/*---------------------------------------------------------------------------*/
- input = peasycap->input;
- if (0 <= input && INPUT_MANY > input) {
- rc = read_saa(peasycap->pusb_device, 0x1F);
- if (0 <= rc) {
- if (rc & 0x40)
- peasycap->lost[input] += 1;
- else
- peasycap->lost[input] -= 2;
-
- if (0 > peasycap->lost[input])
- peasycap->lost[input] = 0;
- else if ((2 * VIDEO_LOST_TOLERATE) < peasycap->lost[input])
- peasycap->lost[input] = (2 * VIDEO_LOST_TOLERATE);
- }
- }
-/*---------------------------------------------------------------------------*/
-/*
- * WAIT FOR FIELD ifield (0 => TOP, 1 => BOTTOM)
- */
-/*---------------------------------------------------------------------------*/
- miss = 0;
- while ((peasycap->field_read == peasycap->field_fill) ||
- (0 != (0xFF00 & peasycap->field_buffer
- [peasycap->field_read][0].kount)) ||
- (ifield != (0x00FF & peasycap->field_buffer
- [peasycap->field_read][0].kount))) {
- if (mode)
- return -EAGAIN;
-
- JOM(8, "first wait on wq_video, %i=field_read %i=field_fill\n",
- peasycap->field_read, peasycap->field_fill);
-
- if (0 != (wait_event_interruptible(peasycap->wq_video,
- (peasycap->video_idle || peasycap->video_eof ||
- ((peasycap->field_read != peasycap->field_fill) &&
- (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) &&
- (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) {
- SAM("aborted by signal\n");
- return -EIO;
- }
- if (peasycap->video_idle) {
- JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n",
- peasycap->video_idle);
- return -EAGAIN;
- }
- if (peasycap->video_eof) {
- JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof);
- #if defined(PERSEVERE)
- if (1 == peasycap->status) {
- JOM(8, "persevering ...\n");
- peasycap->video_eof = 0;
- peasycap->audio_eof = 0;
- if (0 != reset(peasycap)) {
- JOM(8, " ... failed returning -EIO\n");
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- kill_video_urbs(peasycap);
- return -EIO;
- }
- peasycap->status = 0;
- JOM(8, " ... OK returning -EAGAIN\n");
- return -EAGAIN;
- }
- #endif /*PERSEVERE*/
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- kill_video_urbs(peasycap);
- JOM(8, "returning -EIO\n");
- return -EIO;
- }
- miss++;
- }
- JOM(8, "first awakening on wq_video after %i waits\n", miss);
-
- rc = field2frame(peasycap);
- if (rc)
- SAM("ERROR: field2frame() rc = %i\n", rc);
-/*---------------------------------------------------------------------------*/
-/*
- * WAIT FOR THE OTHER FIELD
- */
-/*---------------------------------------------------------------------------*/
- if (ifield)
- ifield = 0;
- else
- ifield = 1;
- miss = 0;
- while ((peasycap->field_read == peasycap->field_fill) ||
- (0 != (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) ||
- (ifield != (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))) {
- if (mode)
- return -EAGAIN;
-
- JOM(8, "second wait on wq_video %i=field_read %i=field_fill\n",
- peasycap->field_read, peasycap->field_fill);
- if (0 != (wait_event_interruptible(peasycap->wq_video,
- (peasycap->video_idle || peasycap->video_eof ||
- ((peasycap->field_read != peasycap->field_fill) &&
- (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) &&
- (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) {
- SAM("aborted by signal\n");
- return -EIO;
- }
- if (peasycap->video_idle) {
- JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n",
- peasycap->video_idle);
- return -EAGAIN;
- }
- if (peasycap->video_eof) {
- JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof);
-#if defined(PERSEVERE)
- if (1 == peasycap->status) {
- JOM(8, "persevering ...\n");
- peasycap->video_eof = 0;
- peasycap->audio_eof = 0;
- if (0 != reset(peasycap)) {
- JOM(8, " ... failed returning -EIO\n");
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- kill_video_urbs(peasycap);
- return -EIO;
- }
- peasycap->status = 0;
- JOM(8, " ... OK ... returning -EAGAIN\n");
- return -EAGAIN;
- }
-#endif /*PERSEVERE*/
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- kill_video_urbs(peasycap);
- JOM(8, "returning -EIO\n");
- return -EIO;
- }
- miss++;
- }
- JOM(8, "second awakening on wq_video after %i waits\n", miss);
-
- rc = field2frame(peasycap);
- if (rc)
- SAM("ERROR: field2frame() rc = %i\n", rc);
-/*---------------------------------------------------------------------------*/
-/*
- * WASTE THIS FRAME
-*/
-/*---------------------------------------------------------------------------*/
- if (peasycap->skip) {
- peasycap->skipped++;
- if (peasycap->skip != peasycap->skipped)
- return peasycap->skip - peasycap->skipped;
- else
- peasycap->skipped = 0;
- }
-/*---------------------------------------------------------------------------*/
- peasycap->frame_read = peasycap->frame_fill;
- peasycap->queued[peasycap->frame_read] = 0;
- peasycap->done[peasycap->frame_read] = V4L2_BUF_FLAG_DONE;
-
- peasycap->frame_fill++;
- if (peasycap->frame_buffer_many <= peasycap->frame_fill)
- peasycap->frame_fill = 0;
-
- if (0x01 & easycap_standard[peasycap->standard_offset].mask)
- peasycap->frame_buffer[peasycap->frame_read][0].kount =
- V4L2_FIELD_TOP;
- else
- peasycap->frame_buffer[peasycap->frame_read][0].kount =
- V4L2_FIELD_BOTTOM;
-
-
- JOM(8, "setting: %i=peasycap->frame_read\n", peasycap->frame_read);
- JOM(8, "bumped to: %i=peasycap->frame_fill\n", peasycap->frame_fill);
-
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * BY DEFINITION, odd IS true FOR THE FIELD OCCUPYING LINES 1,3,5,...,479
- * odd IS false FOR THE FIELD OCCUPYING LINES 0,2,4,...,478
- *
- * WHEN BOOLEAN PARAMETER decimatepixel IS true, ONLY THE FIELD FOR WHICH
- * odd==false IS TRANSFERRED TO THE FRAME BUFFER.
- *
- * THE BOOLEAN PARAMETER offerfields IS true ONLY WHEN THE USER PROGRAM
- * CHOOSES THE OPTION V4L2_FIELD_INTERLACED.
- */
-/*---------------------------------------------------------------------------*/
-int
-field2frame(struct easycap *peasycap)
-{
-
- void *pex, *pad;
- int kex, kad, mex, mad, rex, rad, rad2;
- int c2, c3, w2, w3, cz, wz;
- int rc, bytesperpixel, multiplier;
- int much, more, over, rump, caches, input;
- u8 mask, margin;
- bool odd, isuy, decimatepixel, offerfields, badinput;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
-
- badinput = false;
- input = 0x07 & peasycap->field_buffer[peasycap->field_read][0].input;
-
- JOM(8, "===== parity %i, input 0x%02X, field buffer %i --> "
- "frame buffer %i\n",
- peasycap->field_buffer[peasycap->field_read][0].kount,
- peasycap->field_buffer[peasycap->field_read][0].input,
- peasycap->field_read, peasycap->frame_fill);
- JOM(8, "===== %i=bytesperpixel\n", peasycap->bytesperpixel);
- if (peasycap->offerfields)
- JOM(8, "===== offerfields\n");
-
-/*---------------------------------------------------------------------------*/
-/*
- * REJECT OR CLEAN BAD FIELDS
- */
-/*---------------------------------------------------------------------------*/
- if (peasycap->field_read == peasycap->field_fill) {
- SAM("ERROR: on entry, still filling field buffer %i\n",
- peasycap->field_read);
- return 0;
- }
-#ifdef EASYCAP_TESTCARD
- easycap_testcard(peasycap, peasycap->field_read);
-#else
- if (0 <= input && INPUT_MANY > input) {
- if (easycap_bars && VIDEO_LOST_TOLERATE <= peasycap->lost[input])
- easycap_testcard(peasycap, peasycap->field_read);
- }
-#endif /*EASYCAP_TESTCARD*/
-/*---------------------------------------------------------------------------*/
-
- offerfields = peasycap->offerfields;
- bytesperpixel = peasycap->bytesperpixel;
- decimatepixel = peasycap->decimatepixel;
-
- if ((2 != bytesperpixel) &&
- (3 != bytesperpixel) &&
- (4 != bytesperpixel)) {
- SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel);
- return -EFAULT;
- }
- if (decimatepixel)
- multiplier = 2;
- else
- multiplier = 1;
-
- w2 = 2 * multiplier * (peasycap->width);
- w3 = bytesperpixel * multiplier * (peasycap->width);
- wz = multiplier * (peasycap->height) *
- multiplier * (peasycap->width);
-
- kex = peasycap->field_read; mex = 0;
- kad = peasycap->frame_fill; mad = 0;
-
- pex = peasycap->field_buffer[kex][0].pgo; rex = PAGE_SIZE;
- pad = peasycap->frame_buffer[kad][0].pgo; rad = PAGE_SIZE;
- odd = !!(peasycap->field_buffer[kex][0].kount);
-
- if (odd && (!decimatepixel)) {
- JOM(8, "initial skipping %4i bytes p.%4i\n",
- w3/multiplier, mad);
- pad += (w3 / multiplier); rad -= (w3 / multiplier);
- }
- isuy = true;
- mask = 0; rump = 0; caches = 0;
-
- cz = 0;
- while (cz < wz) {
- /*
- * PROCESS ONE LINE OF FRAME AT FULL RESOLUTION:
- * READ w2 BYTES FROM FIELD BUFFER,
- * WRITE w3 BYTES TO FRAME BUFFER
- */
- if (!decimatepixel) {
- over = w2;
- do {
- much = over; more = 0;
- margin = 0; mask = 0x00;
- if (rex < much)
- much = rex;
- rump = 0;
-
- if (much % 2) {
- SAM("MISTAKE: much is odd\n");
- return -EFAULT;
- }
-
- more = (bytesperpixel *
- much) / 2;
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- if (1 < bytesperpixel) {
- if (rad * 2 < much * bytesperpixel) {
- /*
- * INJUDICIOUS ALTERATION OF
- * THIS STATEMENT BLOCK WILL
- * CAUSE BREAKAGE. BEWARE.
- */
- rad2 = rad + bytesperpixel - 1;
- much = ((((2 * rad2)/bytesperpixel)/2) * 2);
- rump = ((bytesperpixel * much) / 2) - rad;
- more = rad;
- }
- mask = (u8)rump;
- margin = 0;
- if (much == rex) {
- mask |= 0x04;
- if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE)
- margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo));
- else
- mask |= 0x08;
- }
- } else {
- SAM("MISTAKE: %i=bytesperpixel\n",
- bytesperpixel);
- return -EFAULT;
- }
- if (rump)
- caches++;
- if (badinput) {
- JOM(8, "ERROR: 0x%02X=->field_buffer"
- "[%i][%i].input, "
- "0x%02X=(0x08|->input)\n",
- peasycap->field_buffer
- [kex][mex].input, kex, mex,
- (0x08|peasycap->input));
- }
- rc = redaub(peasycap, pad, pex, much, more,
- mask, margin, isuy);
- if (0 > rc) {
- SAM("ERROR: redaub() failed\n");
- return -EFAULT;
- }
- if (much % 4)
- isuy = !isuy;
-
- over -= much; cz += much;
- pex += much; rex -= much;
- if (!rex) {
- mex++;
- pex = peasycap->field_buffer[kex][mex].pgo;
- rex = PAGE_SIZE;
- if (peasycap->field_buffer[kex][mex].input != (0x08|peasycap->input))
- badinput = true;
- }
- pad += more;
- rad -= more;
- if (!rad) {
- mad++;
- pad = peasycap->frame_buffer[kad][mad].pgo;
- rad = PAGE_SIZE;
- if (rump) {
- pad += rump;
- rad -= rump;
- }
- }
- } while (over);
-/*---------------------------------------------------------------------------*/
-/*
- * SKIP w3 BYTES IN TARGET FRAME BUFFER,
- * UNLESS IT IS THE LAST LINE OF AN ODD FRAME
- */
-/*---------------------------------------------------------------------------*/
- if (!odd || (cz != wz)) {
- over = w3;
- do {
- if (!rad) {
- mad++;
- pad = peasycap->frame_buffer
- [kad][mad].pgo;
- rad = PAGE_SIZE;
- }
- more = over;
- if (rad < more)
- more = rad;
- over -= more;
- pad += more;
- rad -= more;
- } while (over);
- }
-/*---------------------------------------------------------------------------*/
-/*
- * PROCESS ONE LINE OF FRAME AT REDUCED RESOLUTION:
- * ONLY IF false==odd,
- * READ w2 BYTES FROM FIELD BUFFER,
- * WRITE w3 / 2 BYTES TO FRAME BUFFER
- */
-/*---------------------------------------------------------------------------*/
- } else if (!odd) {
- over = w2;
- do {
- much = over; more = 0; margin = 0; mask = 0x00;
- if (rex < much)
- much = rex;
- rump = 0;
-
- if (much % 2) {
- SAM("MISTAKE: much is odd\n");
- return -EFAULT;
- }
-
- more = (bytesperpixel * much) / 4;
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- if (1 < bytesperpixel) {
- if (rad * 4 < much * bytesperpixel) {
- /*
- * INJUDICIOUS ALTERATION OF
- * THIS STATEMENT BLOCK
- * WILL CAUSE BREAKAGE.
- * BEWARE.
- */
- rad2 = rad + bytesperpixel - 1;
- much = ((((2 * rad2) / bytesperpixel) / 2) * 4);
- rump = ((bytesperpixel * much) / 4) - rad;
- more = rad;
- }
- mask = (u8)rump;
- margin = 0;
- if (much == rex) {
- mask |= 0x04;
- if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE)
- margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo));
- else
- mask |= 0x08;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- } else {
- SAM("MISTAKE: %i=bytesperpixel\n",
- bytesperpixel);
- return -EFAULT;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- if (rump)
- caches++;
-
- if (badinput) {
- JOM(8, "ERROR: 0x%02X=->field_buffer"
- "[%i][%i].input, "
- "0x%02X=(0x08|->input)\n",
- peasycap->field_buffer
- [kex][mex].input, kex, mex,
- (0x08|peasycap->input));
- }
- rc = redaub(peasycap, pad, pex, much, more,
- mask, margin, isuy);
- if (0 > rc) {
- SAM("ERROR: redaub() failed\n");
- return -EFAULT;
- }
- over -= much; cz += much;
- pex += much; rex -= much;
- if (!rex) {
- mex++;
- pex = peasycap->field_buffer[kex][mex].pgo;
- rex = PAGE_SIZE;
- if (peasycap->field_buffer[kex][mex].input !=
- (0x08|peasycap->input))
- badinput = true;
- }
- pad += more;
- rad -= more;
- if (!rad) {
- mad++;
- pad = peasycap->frame_buffer[kad][mad].pgo;
- rad = PAGE_SIZE;
- if (rump) {
- pad += rump;
- rad -= rump;
- }
- }
- } while (over);
-/*---------------------------------------------------------------------------*/
-/*
- * OTHERWISE JUST
- * READ w2 BYTES FROM FIELD BUFFER AND DISCARD THEM
- */
-/*---------------------------------------------------------------------------*/
- } else {
- over = w2;
- do {
- if (!rex) {
- mex++;
- pex = peasycap->field_buffer[kex][mex].pgo;
- rex = PAGE_SIZE;
- if (peasycap->field_buffer[kex][mex].input !=
- (0x08|peasycap->input)) {
- JOM(8, "ERROR: 0x%02X=->field_buffer"
- "[%i][%i].input, "
- "0x%02X=(0x08|->input)\n",
- peasycap->field_buffer
- [kex][mex].input, kex, mex,
- (0x08|peasycap->input));
- badinput = true;
- }
- }
- much = over;
- if (rex < much)
- much = rex;
- over -= much;
- cz += much;
- pex += much;
- rex -= much;
- } while (over);
- }
- }
-/*---------------------------------------------------------------------------*/
-/*
- * SANITY CHECKS
- */
-/*---------------------------------------------------------------------------*/
- c2 = (mex + 1)*PAGE_SIZE - rex;
- if (cz != c2)
- SAM("ERROR: discrepancy %i in bytes read\n", c2 - cz);
- c3 = (mad + 1)*PAGE_SIZE - rad;
-
- if (!decimatepixel) {
- if (bytesperpixel * cz != c3)
- SAM("ERROR: discrepancy %i in bytes written\n",
- c3 - (bytesperpixel * cz));
- } else {
- if (!odd) {
- if (bytesperpixel *
- cz != (4 * c3))
- SAM("ERROR: discrepancy %i in bytes written\n",
- (2*c3)-(bytesperpixel * cz));
- } else {
- if (0 != c3)
- SAM("ERROR: discrepancy %i "
- "in bytes written\n", c3);
- }
- }
- if (rump)
- SAM("WORRY: undischarged cache at end of line in frame buffer\n");
-
- JOM(8, "===== field2frame(): %i bytes --> %i bytes (incl skip)\n", c2, c3);
- JOM(8, "===== field2frame(): %i=mad %i=rad\n", mad, rad);
-
- if (odd)
- JOM(8, "+++++ field2frame(): frame buffer %i is full\n", kad);
-
- if (peasycap->field_read == peasycap->field_fill)
- SAM("WARNING: on exit, filling field buffer %i\n",
- peasycap->field_read);
-
- if (caches)
- JOM(8, "%i=caches\n", caches);
- return 0;
-}
-/*---------------------------------------------------------------------------*/
-/*
- * DECIMATION AND COLOURSPACE CONVERSION.
- *
- * THIS ROUTINE REQUIRES THAT ALL THE DATA TO BE READ RESIDES ON ONE PAGE
- * AND THAT ALL THE DATA TO BE WRITTEN RESIDES ON ONE (DIFFERENT) PAGE.
- * THE CALLING ROUTINE MUST ENSURE THAT THIS REQUIREMENT IS MET, AND MUST
- * ALSO ENSURE THAT much IS EVEN.
- *
- * much BYTES ARE READ, AT LEAST (bytesperpixel * much)/2 BYTES ARE WRITTEN
- * IF THERE IS NO DECIMATION, HALF THIS AMOUNT IF THERE IS DECIMATION.
- *
- * mask IS ZERO WHEN NO SPECIAL BEHAVIOUR REQUIRED. OTHERWISE IT IS SET THUS:
- * 0x03 & mask = number of bytes to be written to cache instead of to
- * frame buffer
- * 0x04 & mask => use argument margin to set the chrominance for last pixel
- * 0x08 & mask => do not set the chrominance for last pixel
- *
- * YUV to RGB CONVERSION IS (OR SHOULD BE) ITU-R BT 601.
- *
- * THERE IS A LOT OF CODE REPETITION IN THIS ROUTINE IN ORDER TO AVOID
- * INEFFICIENT SWITCHING INSIDE INNER LOOPS. REARRANGING THE LOGIC TO
- * REDUCE CODE LENGTH WILL GENERALLY IMPAIR RUNTIME PERFORMANCE. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
-int
-redaub(struct easycap *peasycap, void *pad, void *pex, int much, int more,
- u8 mask, u8 margin, bool isuy)
-{
- static s32 ay[256], bu[256], rv[256], gu[256], gv[256];
- u8 *pcache;
- u8 r, g, b, y, u, v, c, *p2, *p3, *pz, *pr;
- int bytesperpixel;
- bool byteswaporder, decimatepixel, last;
- int j, rump;
- s32 tmp;
-
- if (much % 2) {
- SAM("MISTAKE: much is odd\n");
- return -EFAULT;
- }
- bytesperpixel = peasycap->bytesperpixel;
- byteswaporder = peasycap->byteswaporder;
- decimatepixel = peasycap->decimatepixel;
-
-/*---------------------------------------------------------------------------*/
- if (!bu[255]) {
- for (j = 0; j < 112; j++) {
- tmp = (0xFF00 & (453 * j)) >> 8;
- bu[j + 128] = tmp; bu[127 - j] = -tmp;
- tmp = (0xFF00 & (359 * j)) >> 8;
- rv[j + 128] = tmp; rv[127 - j] = -tmp;
- tmp = (0xFF00 & (88 * j)) >> 8;
- gu[j + 128] = tmp; gu[127 - j] = -tmp;
- tmp = (0xFF00 & (183 * j)) >> 8;
- gv[j + 128] = tmp; gv[127 - j] = -tmp;
- }
- for (j = 0; j < 16; j++) {
- bu[j] = bu[16]; rv[j] = rv[16];
- gu[j] = gu[16]; gv[j] = gv[16];
- }
- for (j = 240; j < 256; j++) {
- bu[j] = bu[239]; rv[j] = rv[239];
- gu[j] = gu[239]; gv[j] = gv[239];
- }
- for (j = 16; j < 236; j++)
- ay[j] = j;
- for (j = 0; j < 16; j++)
- ay[j] = ay[16];
- for (j = 236; j < 256; j++)
- ay[j] = ay[235];
- JOM(8, "lookup tables are prepared\n");
- }
- pcache = peasycap->pcache;
- if (!pcache)
- pcache = &peasycap->cache[0];
-/*---------------------------------------------------------------------------*/
-/*
- * TRANSFER CONTENTS OF CACHE TO THE FRAME BUFFER
- */
-/*---------------------------------------------------------------------------*/
- if (!pcache) {
- SAM("MISTAKE: pcache is NULL\n");
- return -EFAULT;
- }
-
- if (pcache != &peasycap->cache[0])
- JOM(16, "cache has %i bytes\n", (int)(pcache - &peasycap->cache[0]));
- p2 = &peasycap->cache[0];
- p3 = (u8 *)pad - (int)(pcache - &peasycap->cache[0]);
- while (p2 < pcache) {
- *p3++ = *p2; p2++;
- }
- pcache = &peasycap->cache[0];
- if (p3 != pad) {
- SAM("MISTAKE: pointer misalignment\n");
- return -EFAULT;
- }
-/*---------------------------------------------------------------------------*/
- rump = (int)(0x03 & mask);
- u = 0; v = 0;
- p2 = (u8 *)pex; pz = p2 + much; pr = p3 + more; last = false;
- p2++;
-
- if (isuy)
- u = *(p2 - 1);
- else
- v = *(p2 - 1);
-
- if (rump)
- JOM(16, "%4i=much %4i=more %i=rump\n", much, more, rump);
-
-/*---------------------------------------------------------------------------*/
- switch (bytesperpixel) {
- case 2: {
- if (!decimatepixel) {
- memcpy(pad, pex, (size_t)much);
- if (!byteswaporder) {
- /* UYVY */
- return 0;
- } else {
- /* YUYV */
- p3 = (u8 *)pad; pz = p3 + much;
- while (pz > p3) {
- c = *p3;
- *p3 = *(p3 + 1);
- *(p3 + 1) = c;
- p3 += 2;
- }
- return 0;
- }
- } else {
- if (!byteswaporder) {
- /* UYVY DECIMATED */
- p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much;
- while (pz > p2) {
- *p3 = *p2;
- *(p3 + 1) = *(p2 + 1);
- *(p3 + 2) = *(p2 + 2);
- *(p3 + 3) = *(p2 + 3);
- p3 += 4; p2 += 8;
- }
- return 0;
- } else {
- /* YUYV DECIMATED */
- p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much;
- while (pz > p2) {
- *p3 = *(p2 + 1);
- *(p3 + 1) = *p2;
- *(p3 + 2) = *(p2 + 3);
- *(p3 + 3) = *(p2 + 2);
- p3 += 4; p2 += 8;
- }
- return 0;
- }
- }
- break;
- }
- case 3:
- {
- if (!decimatepixel) {
- if (!byteswaporder) {
- /* RGB */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = r;
- *pcache++ = g;
- *pcache++ = b;
- break;
- }
- case 2: {
- *p3 = r;
- *(p3 + 1) = g;
- *pcache++ = b;
- break;
- }
- default: {
- SAM("MISTAKE: %i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- }
- p2 += 2;
- if (isuy)
- isuy = false;
- else
- isuy = true;
- p3 += bytesperpixel;
- }
- return 0;
- } else {
- /* BGR */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- }
- else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = b;
- *pcache++ = g;
- *pcache++ = r;
- break;
- }
- case 2: {
- *p3 = b;
- *(p3 + 1) = g;
- *pcache++ = r;
- break;
- }
- default: {
- SAM("MISTAKE: %i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- }
- p2 += 2;
- if (isuy)
- isuy = false;
- else
- isuy = true;
- p3 += bytesperpixel;
- }
- }
- return 0;
- } else {
- if (!byteswaporder) {
- /* RGB DECIMATED */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- if (isuy) {
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] -
- gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = r;
- *pcache++ = g;
- *pcache++ = b;
- break;
- }
- case 2: {
- *p3 = r;
- *(p3 + 1) = g;
- *pcache++ = b;
- break;
- }
- default: {
- SAM("MISTAKE: "
- "%i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- }
- isuy = false;
- p3 += bytesperpixel;
- } else {
- isuy = true;
- }
- p2 += 2;
- }
- return 0;
- } else {
- /* BGR DECIMATED */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- if (isuy) {
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] -
- gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = b;
- *pcache++ = g;
- *pcache++ = r;
- break;
- }
- case 2: {
- *p3 = b;
- *(p3 + 1) = g;
- *pcache++ = r;
- break;
- }
- default: {
- SAM("MISTAKE: "
- "%i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- }
- isuy = false;
- p3 += bytesperpixel;
- }
- else
- isuy = true;
- p2 += 2;
- }
- return 0;
- }
- }
- break;
- }
- case 4:
- {
- if (!decimatepixel) {
- if (!byteswaporder) {
- /* RGBA */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = r;
- *pcache++ = g;
- *pcache++ = b;
- *pcache++ = 0;
- break;
- }
- case 2: {
- *p3 = r;
- *(p3 + 1) = g;
- *pcache++ = b;
- *pcache++ = 0;
- break;
- }
- case 3: {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- *pcache++ = 0;
- break;
- }
- default: {
- SAM("MISTAKE: %i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- *(p3 + 3) = 0;
- }
- p2 += 2;
- if (isuy)
- isuy = false;
- else
- isuy = true;
- p3 += bytesperpixel;
- }
- return 0;
- } else {
- /*
- * BGRA
- */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = b;
- *pcache++ = g;
- *pcache++ = r;
- *pcache++ = 0;
- break;
- }
- case 2: {
- *p3 = b;
- *(p3 + 1) = g;
- *pcache++ = r;
- *pcache++ = 0;
- break;
- }
- case 3: {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- *pcache++ = 0;
- break;
- }
- default:
- SAM("MISTAKE: %i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- } else {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- *(p3 + 3) = 0;
- }
- p2 += 2;
- if (isuy)
- isuy = false;
- else
- isuy = true;
- p3 += bytesperpixel;
- }
- }
- return 0;
- } else {
- if (!byteswaporder) {
- /*
- * RGBA DECIMATED
- */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- if (isuy) {
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] -
- gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = r;
- *pcache++ = g;
- *pcache++ = b;
- *pcache++ = 0;
- break;
- }
- case 2: {
- *p3 = r;
- *(p3 + 1) = g;
- *pcache++ = b;
- *pcache++ = 0;
- break;
- }
- case 3: {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- *pcache++ = 0;
- break;
- }
- default: {
- SAM("MISTAKE: "
- "%i=rump\n",
- bytesperpixel -
- rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- *(p3 + 3) = 0;
- }
- isuy = false;
- p3 += bytesperpixel;
- } else
- isuy = true;
- p2 += 2;
- }
- return 0;
- } else {
- /*
- * BGRA DECIMATED
- */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- if (isuy) {
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] -
- gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = b;
- *pcache++ = g;
- *pcache++ = r;
- *pcache++ = 0;
- break;
- }
- case 2: {
- *p3 = b;
- *(p3 + 1) = g;
- *pcache++ = r;
- *pcache++ = 0;
- break;
- }
- case 3: {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- *pcache++ = 0;
- break;
- }
- default: {
- SAM("MISTAKE: "
- "%i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- *(p3 + 3) = 0;
- }
- isuy = false;
- p3 += bytesperpixel;
- } else
- isuy = true;
- p2 += 2;
- }
- return 0;
- }
- }
- break;
- }
- default: {
- SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel);
- return -EFAULT;
- }
- }
- return 0;
-}
-/*****************************************************************************/
-/*
- * SEE CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGES 430-434
- */
-/*****************************************************************************/
-static void easycap_vma_open(struct vm_area_struct *pvma)
-{
- struct easycap *peasycap;
-
- peasycap = pvma->vm_private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return;
- }
- peasycap->vma_many++;
- JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many);
- return;
-}
-/*****************************************************************************/
-static void easycap_vma_close(struct vm_area_struct *pvma)
-{
- struct easycap *peasycap;
-
- peasycap = pvma->vm_private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return;
- }
- peasycap->vma_many--;
- JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many);
- return;
-}
-/*****************************************************************************/
-static int easycap_vma_fault(struct vm_area_struct *pvma, struct vm_fault *pvmf)
-{
- int k, m, retcode;
- void *pbuf;
- struct page *page;
- struct easycap *peasycap;
-
- retcode = VM_FAULT_NOPAGE;
-
- if (!pvma) {
- SAY("pvma is NULL\n");
- return retcode;
- }
- if (!pvmf) {
- SAY("pvmf is NULL\n");
- return retcode;
- }
-
- k = (pvmf->pgoff) / (FRAME_BUFFER_SIZE/PAGE_SIZE);
- m = (pvmf->pgoff) % (FRAME_BUFFER_SIZE/PAGE_SIZE);
-
- if (!m)
- JOT(4, "%4i=k, %4i=m\n", k, m);
- else
- JOT(16, "%4i=k, %4i=m\n", k, m);
-
- if ((0 > k) || (FRAME_BUFFER_MANY <= k)) {
- SAY("ERROR: buffer index %i out of range\n", k);
- return retcode;
- }
- if ((0 > m) || (FRAME_BUFFER_SIZE/PAGE_SIZE <= m)) {
- SAY("ERROR: page number %i out of range\n", m);
- return retcode;
- }
- peasycap = pvma->vm_private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return retcode;
- }
-/*---------------------------------------------------------------------------*/
- pbuf = peasycap->frame_buffer[k][m].pgo;
- if (!pbuf) {
- SAM("ERROR: pbuf is NULL\n");
- return retcode;
- }
- page = virt_to_page(pbuf);
- if (!page) {
- SAM("ERROR: page is NULL\n");
- return retcode;
- }
- get_page(page);
-/*---------------------------------------------------------------------------*/
- if (!page) {
- SAM("ERROR: page is NULL after get_page(page)\n");
- } else {
- pvmf->page = page;
- retcode = VM_FAULT_MINOR;
- }
- return retcode;
-}
-
-static const struct vm_operations_struct easycap_vm_ops = {
- .open = easycap_vma_open,
- .close = easycap_vma_close,
- .fault = easycap_vma_fault,
-};
-
-static int easycap_mmap(struct file *file, struct vm_area_struct *pvma)
-{
- JOT(8, "\n");
-
- pvma->vm_ops = &easycap_vm_ops;
- pvma->vm_flags |= VM_RESERVED;
- if (file)
- pvma->vm_private_data = file->private_data;
- easycap_vma_open(pvma);
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * ON COMPLETION OF A VIDEO URB ITS DATA IS COPIED TO THE FIELD BUFFERS
- * PROVIDED peasycap->video_idle IS ZERO. REGARDLESS OF THIS BEING TRUE,
- * IT IS RESUBMITTED PROVIDED peasycap->video_isoc_streaming IS NOT ZERO.
- *
- * THIS FUNCTION IS AN INTERRUPT SERVICE ROUTINE AND MUST NOT SLEEP.
- *
- * INFORMATION ABOUT THE VALIDITY OF THE CONTENTS OF THE FIELD BUFFER ARE
- * STORED IN THE TWO-BYTE STATUS PARAMETER
- * peasycap->field_buffer[peasycap->field_fill][0].kount
- * NOTICE THAT THE INFORMATION IS STORED ONLY WITH PAGE 0 OF THE FIELD BUFFER.
- *
- * THE LOWER BYTE CONTAINS THE FIELD PARITY BYTE FURNISHED BY THE SAA7113H
- * CHIP.
- *
- * THE UPPER BYTE IS ZERO IF NO PROBLEMS, OTHERWISE:
- * 0 != (kount & 0x8000) => AT LEAST ONE URB COMPLETED WITH ERRORS
- * 0 != (kount & 0x4000) => BUFFER HAS TOO MUCH DATA
- * 0 != (kount & 0x2000) => BUFFER HAS NOT ENOUGH DATA
- * 0 != (kount & 0x1000) => BUFFER HAS DATA FROM DISPARATE INPUTS
- * 0 != (kount & 0x0400) => RESERVED
- * 0 != (kount & 0x0200) => FIELD BUFFER NOT YET CHECKED
- * 0 != (kount & 0x0100) => BUFFER HAS TWO EXTRA BYTES - WHY?
- */
-/*---------------------------------------------------------------------------*/
-static void easycap_complete(struct urb *purb)
-{
- struct easycap *peasycap;
- struct data_buffer *pfield_buffer;
- char errbuf[16];
- int i, more, much, leap, rc, last;
- int videofieldamount;
- unsigned int override, bad;
- int framestatus, framelength, frameactual, frameoffset;
- u8 *pu;
-
- if (!purb) {
- SAY("ERROR: easycap_complete(): purb is NULL\n");
- return;
- }
- peasycap = purb->context;
- if (!peasycap) {
- SAY("ERROR: easycap_complete(): peasycap is NULL\n");
- return;
- }
- if (peasycap->video_eof)
- return;
- for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++)
- if (purb->transfer_buffer == peasycap->video_isoc_buffer[i].pgo)
- break;
- JOM(16, "%2i=urb\n", i);
- last = peasycap->video_isoc_sequence;
- if ((((VIDEO_ISOC_BUFFER_MANY - 1) == last) && (0 != i)) ||
- (((VIDEO_ISOC_BUFFER_MANY - 1) != last) && ((last + 1) != i))) {
- JOM(16, "ERROR: out-of-order urbs %i,%i ... continuing\n",
- last, i);
- }
- peasycap->video_isoc_sequence = i;
-
- if (peasycap->video_idle) {
- JOM(16, "%i=video_idle %i=video_isoc_streaming\n",
- peasycap->video_idle, peasycap->video_isoc_streaming);
- if (peasycap->video_isoc_streaming) {
- rc = usb_submit_urb(purb, GFP_ATOMIC);
- if (rc) {
- SAM("%s:%d ENOMEM\n", strerror(rc), rc);
- if (-ENODEV != rc)
- SAM("ERROR: while %i=video_idle, "
- "usb_submit_urb() "
- "failed with rc:\n",
- peasycap->video_idle);
- }
- }
- return;
- }
- override = 0;
-/*---------------------------------------------------------------------------*/
- if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
- SAM("ERROR: bad peasycap->field_fill\n");
- return;
- }
- if (purb->status) {
- if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
- JOM(8, "urb status -ESHUTDOWN or -ENOENT\n");
- return;
- }
-
- (peasycap->field_buffer[peasycap->field_fill][0].kount) |= 0x8000 ;
- SAM("ERROR: bad urb status -%s: %d\n",
- strerror(purb->status), purb->status);
-/*---------------------------------------------------------------------------*/
- } else {
- for (i = 0; i < purb->number_of_packets; i++) {
- if (0 != purb->iso_frame_desc[i].status) {
- (peasycap->field_buffer
- [peasycap->field_fill][0].kount) |= 0x8000 ;
- /* FIXME: 1. missing '-' check boundaries */
- strcpy(&errbuf[0],
- strerror(purb->iso_frame_desc[i].status));
- }
- framestatus = purb->iso_frame_desc[i].status;
- framelength = purb->iso_frame_desc[i].length;
- frameactual = purb->iso_frame_desc[i].actual_length;
- frameoffset = purb->iso_frame_desc[i].offset;
-
- JOM(16, "frame[%2i]:"
- "%4i=status "
- "%4i=actual "
- "%4i=length "
- "%5i=offset\n",
- i, framestatus, frameactual, framelength, frameoffset);
- if (!purb->iso_frame_desc[i].status) {
- more = purb->iso_frame_desc[i].actual_length;
- pfield_buffer = &peasycap->field_buffer
- [peasycap->field_fill][peasycap->field_page];
- videofieldamount = (peasycap->field_page *
- PAGE_SIZE) +
- (int)(pfield_buffer->pto - pfield_buffer->pgo);
- if (4 == more)
- peasycap->video_mt++;
- if (4 < more) {
- if (peasycap->video_mt) {
- JOM(8, "%4i empty video urb frames\n",
- peasycap->video_mt);
- peasycap->video_mt = 0;
- }
- if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
- SAM("ERROR: bad peasycap->field_fill\n");
- return;
- }
- if (FIELD_BUFFER_SIZE/PAGE_SIZE <=
- peasycap->field_page) {
- SAM("ERROR: bad peasycap->field_page\n");
- return;
- }
- pfield_buffer = &peasycap->field_buffer
- [peasycap->field_fill][peasycap->field_page];
- pu = (u8 *)(purb->transfer_buffer +
- purb->iso_frame_desc[i].offset);
- if (0x80 & *pu)
- leap = 8;
- else
- leap = 4;
-/*--------------------------------------------------------------------------*/
-/*
- * EIGHT-BYTE END-OF-VIDEOFIELD MARKER.
- * NOTE: A SUCCESSION OF URB FRAMES FOLLOWING THIS ARE EMPTY,
- * CORRESPONDING TO THE FIELD FLYBACK (VERTICAL BLANKING) PERIOD.
- *
- * PROVIDED THE FIELD BUFFER CONTAINS GOOD DATA AS INDICATED BY A ZERO UPPER
- * BYTE OF
- * peasycap->field_buffer[peasycap->field_fill][0].kount
- * THE CONTENTS OF THE FIELD BUFFER ARE OFFERED TO dqbuf(), field_read IS
- * UPDATED AND field_fill IS BUMPED. IF THE FIELD BUFFER CONTAINS BAD DATA
- * NOTHING IS OFFERED TO dqbuf().
- *
- * THE DECISION ON WHETHER THE PARITY OF THE OFFERED FIELD BUFFER IS RIGHT
- * RESTS WITH dqbuf().
- */
-/*---------------------------------------------------------------------------*/
- if ((8 == more) || override) {
- if (videofieldamount >
- peasycap->videofieldamount) {
- if (2 == videofieldamount -
- peasycap->
- videofieldamount) {
- (peasycap->field_buffer
- [peasycap->field_fill]
- [0].kount) |= 0x0100;
- peasycap->video_junk += (1 +
- VIDEO_JUNK_TOLERATE);
- } else
- (peasycap->field_buffer
- [peasycap->field_fill]
- [0].kount) |= 0x4000;
- } else if (videofieldamount <
- peasycap->
- videofieldamount) {
- (peasycap->field_buffer
- [peasycap->field_fill]
- [0].kount) |= 0x2000;
- }
- bad = 0xFF00 & peasycap->field_buffer
- [peasycap->field_fill]
- [0].kount;
- if (!bad) {
- (peasycap->video_junk)--;
- if (-VIDEO_JUNK_TOLERATE >
- peasycap->video_junk)
- peasycap->video_junk =
- -VIDEO_JUNK_TOLERATE;
- peasycap->field_read =
- (peasycap->
- field_fill)++;
- if (FIELD_BUFFER_MANY <=
- peasycap->
- field_fill)
- peasycap->
- field_fill = 0;
- peasycap->field_page = 0;
- pfield_buffer = &peasycap->
- field_buffer
- [peasycap->
- field_fill]
- [peasycap->
- field_page];
- pfield_buffer->pto =
- pfield_buffer->pgo;
- JOM(8, "bumped to: %i="
- "peasycap->"
- "field_fill %i="
- "parity\n",
- peasycap->field_fill,
- 0x00FF &
- pfield_buffer->kount);
- JOM(8, "field buffer %i has "
- "%i bytes fit to be "
- "read\n",
- peasycap->field_read,
- videofieldamount);
- JOM(8, "wakeup call to "
- "wq_video, "
- "%i=field_read "
- "%i=field_fill "
- "%i=parity\n",
- peasycap->field_read,
- peasycap->field_fill,
- 0x00FF & peasycap->
- field_buffer
- [peasycap->
- field_read][0].kount);
- wake_up_interruptible
- (&(peasycap->
- wq_video));
- } else {
- peasycap->video_junk++;
- if (bad & 0x0010)
- peasycap->video_junk +=
- (1 + VIDEO_JUNK_TOLERATE/2);
- JOM(8, "field buffer %i had %i "
- "bytes, now discarded: "
- "0x%04X\n",
- peasycap->field_fill,
- videofieldamount,
- (0xFF00 &
- peasycap->field_buffer
- [peasycap->field_fill][0].
- kount));
- (peasycap->field_fill)++;
-
- if (FIELD_BUFFER_MANY <=
- peasycap->field_fill)
- peasycap->field_fill = 0;
- peasycap->field_page = 0;
- pfield_buffer =
- &peasycap->field_buffer
- [peasycap->field_fill]
- [peasycap->field_page];
- pfield_buffer->pto =
- pfield_buffer->pgo;
-
- JOM(8, "bumped to: %i=peasycap->"
- "field_fill %i=parity\n",
- peasycap->field_fill,
- 0x00FF & pfield_buffer->kount);
- }
- if (8 == more) {
- JOM(8, "end-of-field: received "
- "parity byte 0x%02X\n",
- (0xFF & *pu));
- if (0x40 & *pu)
- pfield_buffer->kount = 0x0000;
- else
- pfield_buffer->kount = 0x0001;
- pfield_buffer->input = 0x08 |
- (0x07 & peasycap->input);
- JOM(8, "end-of-field: 0x%02X=kount\n",
- 0xFF & pfield_buffer->kount);
- }
- }
-/*---------------------------------------------------------------------------*/
-/*
- * COPY more BYTES FROM ISOC BUFFER TO FIELD BUFFER
- */
-/*---------------------------------------------------------------------------*/
- pu += leap;
- more -= leap;
-
- if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
- SAM("ERROR: bad peasycap->field_fill\n");
- return;
- }
- if (FIELD_BUFFER_SIZE/PAGE_SIZE <= peasycap->field_page) {
- SAM("ERROR: bad peasycap->field_page\n");
- return;
- }
- pfield_buffer = &peasycap->field_buffer
- [peasycap->field_fill][peasycap->field_page];
- while (more) {
- pfield_buffer = &peasycap->field_buffer
- [peasycap->field_fill]
- [peasycap->field_page];
- if (PAGE_SIZE < (pfield_buffer->pto -
- pfield_buffer->pgo)) {
- SAM("ERROR: bad pfield_buffer->pto\n");
- return;
- }
- if (PAGE_SIZE == (pfield_buffer->pto -
- pfield_buffer->pgo)) {
- (peasycap->field_page)++;
- if (FIELD_BUFFER_SIZE/PAGE_SIZE <=
- peasycap->field_page) {
- JOM(16, "wrapping peasycap->"
- "field_page\n");
- peasycap->field_page = 0;
- }
- pfield_buffer = &peasycap->
- field_buffer
- [peasycap->field_fill]
- [peasycap->field_page];
- pfield_buffer->pto = pfield_buffer->pgo;
- pfield_buffer->input = 0x08 |
- (0x07 & peasycap->input);
- if ((peasycap->field_buffer[peasycap->
- field_fill][0]).
- input !=
- pfield_buffer->input)
- (peasycap->field_buffer
- [peasycap->field_fill]
- [0]).kount |= 0x1000;
- }
-
- much = PAGE_SIZE -
- (int)(pfield_buffer->pto -
- pfield_buffer->pgo);
-
- if (much > more)
- much = more;
- memcpy(pfield_buffer->pto, pu, much);
- pu += much;
- (pfield_buffer->pto) += much;
- more -= much;
- }
- }
- }
- }
- }
-/*---------------------------------------------------------------------------*/
-/*
- * RESUBMIT THIS URB, UNLESS A SEVERE PERSISTENT ERROR CONDITION EXISTS.
- *
- * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO AN ERROR CONDITION
- * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
- if (VIDEO_ISOC_BUFFER_MANY <= peasycap->video_junk) {
- SAM("easycap driver shutting down on condition green\n");
- peasycap->status = 1;
- peasycap->video_eof = 1;
- peasycap->video_junk = 0;
- wake_up_interruptible(&peasycap->wq_video);
-#if !defined(PERSEVERE)
- peasycap->audio_eof = 1;
- wake_up_interruptible(&peasycap->wq_audio);
-#endif /*PERSEVERE*/
- return;
- }
- if (peasycap->video_isoc_streaming) {
- rc = usb_submit_urb(purb, GFP_ATOMIC);
- if (rc) {
- SAM("%s: %d\n", strerror(rc), rc);
- if (-ENODEV != rc)
- SAM("ERROR: while %i=video_idle, "
- "usb_submit_urb() "
- "failed with rc:\n",
- peasycap->video_idle);
- }
- }
- return;
-}
-static const struct file_operations easycap_fops = {
- .owner = THIS_MODULE,
- .open = easycap_open,
- .unlocked_ioctl = easycap_unlocked_ioctl,
- .poll = easycap_poll,
- .mmap = easycap_mmap,
- .llseek = no_llseek,
-};
-static const struct usb_class_driver easycap_class = {
- .name = "usb/easycap%d",
- .fops = &easycap_fops,
- .minor_base = USB_SKEL_MINOR_BASE,
-};
-/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
-static const struct v4l2_file_operations v4l2_fops = {
- .owner = THIS_MODULE,
- .open = easycap_open_noinode,
- .unlocked_ioctl = easycap_unlocked_ioctl,
- .poll = easycap_poll,
- .mmap = easycap_mmap,
-};
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * WHEN THE EasyCAP IS PHYSICALLY PLUGGED IN, THIS FUNCTION IS CALLED THREE
- * TIMES, ONCE FOR EACH OF THE THREE INTERFACES. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
-static int easycap_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *usbdev;
- struct usb_host_interface *alt;
- struct usb_endpoint_descriptor *ep;
- struct usb_interface_descriptor *interface;
- struct urb *purb;
- struct easycap *peasycap;
- int ndong;
- struct data_urb *pdata_urb;
- int i, j, k, m, rc;
- u8 bInterfaceNumber;
- u8 bInterfaceClass;
- u8 bInterfaceSubClass;
- void *pbuf;
- int okalt[8], isokalt;
- int okepn[8];
- int okmps[8];
- int maxpacketsize;
- u16 mask;
- s32 value;
- struct easycap_format *peasycap_format;
- int fmtidx;
- struct inputset *inputset;
-
- usbdev = interface_to_usbdev(intf);
-
-/*---------------------------------------------------------------------------*/
- alt = usb_altnum_to_altsetting(intf, 0);
- if (!alt) {
- SAY("ERROR: usb_host_interface not found\n");
- return -EFAULT;
- }
- interface = &alt->desc;
- if (!interface) {
- SAY("ERROR: intf_descriptor is NULL\n");
- return -EFAULT;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * GET PROPERTIES OF PROBED INTERFACE
- */
-/*---------------------------------------------------------------------------*/
- bInterfaceNumber = interface->bInterfaceNumber;
- bInterfaceClass = interface->bInterfaceClass;
- bInterfaceSubClass = interface->bInterfaceSubClass;
-
- JOT(4, "intf[%i]: num_altsetting=%i\n",
- bInterfaceNumber, intf->num_altsetting);
- JOT(4, "intf[%i]: cur_altsetting - altsetting=%li\n",
- bInterfaceNumber,
- (long int)(intf->cur_altsetting - intf->altsetting));
- JOT(4, "intf[%i]: bInterfaceClass=0x%02X bInterfaceSubClass=0x%02X\n",
- bInterfaceNumber, bInterfaceClass, bInterfaceSubClass);
-/*---------------------------------------------------------------------------*/
-/*
- * A NEW struct easycap IS ALWAYS ALLOCATED WHEN INTERFACE 0 IS PROBED.
- * IT IS NOT POSSIBLE HERE TO FREE ANY EXISTING struct easycap. THIS
- * SHOULD HAVE BEEN DONE BY easycap_delete() WHEN THE EasyCAP WAS
- * PHYSICALLY UNPLUGGED.
- *
- * THE POINTER peasycap TO THE struct easycap IS REMEMBERED WHEN
- * INTERFACES 1 AND 2 ARE PROBED.
-*/
-/*---------------------------------------------------------------------------*/
- if (0 == bInterfaceNumber) {
- peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL);
- if (!peasycap) {
- SAY("ERROR: Could not allocate peasycap\n");
- return -ENOMEM;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * PERFORM URGENT INTIALIZATIONS ...
-*/
-/*---------------------------------------------------------------------------*/
- peasycap->minor = -1;
- kref_init(&peasycap->kref);
- JOM(8, "intf[%i]: after kref_init(..._video) "
- "%i=peasycap->kref.refcount.counter\n",
- bInterfaceNumber, peasycap->kref.refcount.counter);
-
- /* module params */
- peasycap->gain = (s8)clamp(easycap_gain, 0, 31);
-
- init_waitqueue_head(&peasycap->wq_video);
- init_waitqueue_head(&peasycap->wq_audio);
- init_waitqueue_head(&peasycap->wq_trigger);
-
- if (mutex_lock_interruptible(&mutex_dongle)) {
- SAY("ERROR: cannot down mutex_dongle\n");
- return -ERESTARTSYS;
- } else {
-/*---------------------------------------------------------------------------*/
- /*
- * FOR INTERFACES 1 AND 2 THE POINTER peasycap WILL NEED TO
- * TO BE THE SAME AS THAT ALLOCATED NOW FOR INTERFACE 0.
- *
- * NORMALLY ndong WILL NOT HAVE CHANGED SINCE INTERFACE 0 WAS
- * PROBED, BUT THIS MAY NOT BE THE CASE IF, FOR EXAMPLE, TWO
- * EASYCAPs ARE PLUGGED IN SIMULTANEOUSLY.
- */
-/*---------------------------------------------------------------------------*/
- for (ndong = 0; ndong < DONGLE_MANY; ndong++) {
- if ((!easycapdc60_dongle[ndong].peasycap) &&
- (!mutex_is_locked(&easycapdc60_dongle
- [ndong].mutex_video)) &&
- (!mutex_is_locked(&easycapdc60_dongle
- [ndong].mutex_audio))) {
- easycapdc60_dongle[ndong].peasycap = peasycap;
- peasycap->isdongle = ndong;
- JOM(8, "intf[%i]: peasycap-->easycap"
- "_dongle[%i].peasycap\n",
- bInterfaceNumber, ndong);
- break;
- }
- }
- if (DONGLE_MANY <= ndong) {
- SAM("ERROR: too many dongles\n");
- mutex_unlock(&mutex_dongle);
- return -ENOMEM;
- }
- mutex_unlock(&mutex_dongle);
- }
- peasycap->allocation_video_struct = sizeof(struct easycap);
- peasycap->allocation_video_page = 0;
- peasycap->allocation_video_urb = 0;
- peasycap->allocation_audio_struct = 0;
- peasycap->allocation_audio_page = 0;
- peasycap->allocation_audio_urb = 0;
-
-/*---------------------------------------------------------------------------*/
-/*
- * ... AND FURTHER INITIALIZE THE STRUCTURE
-*/
-/*---------------------------------------------------------------------------*/
- peasycap->pusb_device = usbdev;
- peasycap->pusb_interface = intf;
-
- peasycap->ilk = 0;
- peasycap->microphone = false;
-
- peasycap->video_interface = -1;
- peasycap->video_altsetting_on = -1;
- peasycap->video_altsetting_off = -1;
- peasycap->video_endpointnumber = -1;
- peasycap->video_isoc_maxframesize = -1;
- peasycap->video_isoc_buffer_size = -1;
-
- peasycap->audio_interface = -1;
- peasycap->audio_altsetting_on = -1;
- peasycap->audio_altsetting_off = -1;
- peasycap->audio_endpointnumber = -1;
- peasycap->audio_isoc_maxframesize = -1;
- peasycap->audio_isoc_buffer_size = -1;
-
- peasycap->frame_buffer_many = FRAME_BUFFER_MANY;
-
- for (k = 0; k < INPUT_MANY; k++)
- peasycap->lost[k] = 0;
- peasycap->skip = 0;
- peasycap->skipped = 0;
- peasycap->offerfields = 0;
-/*---------------------------------------------------------------------------*/
-/*
- * DYNAMICALLY FILL IN THE AVAILABLE FORMATS ...
- */
-/*---------------------------------------------------------------------------*/
- rc = fillin_formats();
- if (0 > rc) {
- SAM("ERROR: fillin_formats() rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(4, "%i formats available\n", rc);
-/*---------------------------------------------------------------------------*/
-/*
- * ... AND POPULATE easycap.inputset[]
-*/
-/*---------------------------------------------------------------------------*/
- /* FIXME: maybe we just use memset 0 */
- inputset = peasycap->inputset;
- for (k = 0; k < INPUT_MANY; k++) {
- inputset[k].input_ok = 0;
- inputset[k].standard_offset_ok = 0;
- inputset[k].format_offset_ok = 0;
- inputset[k].brightness_ok = 0;
- inputset[k].contrast_ok = 0;
- inputset[k].saturation_ok = 0;
- inputset[k].hue_ok = 0;
- }
-
- fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN;
- m = 0;
- mask = 0;
- for (i = 0; 0xFFFF != easycap_standard[i].mask; i++) {
- if (fmtidx == easycap_standard[i].v4l2_standard.index) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].standard_offset = i;
-
- mask = easycap_standard[i].mask;
- }
- }
-
- if (1 != m) {
- SAM("ERROR: "
- "inputset->standard_offset unpopulated, %i=m\n", m);
- return -ENOENT;
- }
-
- peasycap_format = &easycap_format[0];
- m = 0;
- for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) {
- struct v4l2_pix_format *pix =
- &peasycap_format->v4l2_format.fmt.pix;
- if (((peasycap_format->mask & 0x0F) == (mask & 0x0F)) &&
- pix->field == V4L2_FIELD_NONE &&
- pix->pixelformat == V4L2_PIX_FMT_UYVY &&
- pix->width == 640 && pix->height == 480) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].format_offset = i;
- break;
- }
- peasycap_format++;
- }
- if (1 != m) {
- SAM("ERROR: inputset[]->format_offset unpopulated\n");
- return -ENOENT;
- }
-
- m = 0;
- for (i = 0; 0xFFFFFFFF != easycap_control[i].id; i++) {
- value = easycap_control[i].default_value;
- if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].brightness = value;
- } else if (V4L2_CID_CONTRAST == easycap_control[i].id) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].contrast = value;
- } else if (V4L2_CID_SATURATION == easycap_control[i].id) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].saturation = value;
- } else if (V4L2_CID_HUE == easycap_control[i].id) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].hue = value;
- }
- }
-
- if (4 != m) {
- SAM("ERROR: inputset[]->brightness underpopulated\n");
- return -ENOENT;
- }
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].input = k;
- JOM(4, "populated inputset[]\n");
- JOM(4, "finished initialization\n");
- } else {
-/*---------------------------------------------------------------------------*/
-/*
- * FIXME
- *
- * IDENTIFY THE APPROPRIATE POINTER peasycap FOR INTERFACES 1 AND 2.
- * THE ADDRESS OF peasycap->pusb_device IS RELUCTANTLY USED FOR THIS PURPOSE.
- */
-/*---------------------------------------------------------------------------*/
- for (ndong = 0; ndong < DONGLE_MANY; ndong++) {
- if (usbdev == easycapdc60_dongle[ndong].peasycap->
- pusb_device) {
- peasycap = easycapdc60_dongle[ndong].peasycap;
- JOT(8, "intf[%i]: dongle[%i].peasycap\n",
- bInterfaceNumber, ndong);
- break;
- }
- }
- if (DONGLE_MANY <= ndong) {
- SAY("ERROR: peasycap is unknown when probing interface %i\n",
- bInterfaceNumber);
- return -ENODEV;
- }
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL when probing interface %i\n",
- bInterfaceNumber);
- return -ENODEV;
- }
- }
-/*---------------------------------------------------------------------------*/
- if ((USB_CLASS_VIDEO == bInterfaceClass) ||
- (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) {
- if (-1 == peasycap->video_interface) {
- peasycap->video_interface = bInterfaceNumber;
- JOM(4, "setting peasycap->video_interface=%i\n",
- peasycap->video_interface);
- } else {
- if (peasycap->video_interface != bInterfaceNumber) {
- SAM("ERROR: attempting to reset "
- "peasycap->video_interface\n");
- SAM("...... continuing with "
- "%i=peasycap->video_interface\n",
- peasycap->video_interface);
- }
- }
- } else if ((USB_CLASS_AUDIO == bInterfaceClass) &&
- (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) {
- if (-1 == peasycap->audio_interface) {
- peasycap->audio_interface = bInterfaceNumber;
- JOM(4, "setting peasycap->audio_interface=%i\n",
- peasycap->audio_interface);
- } else {
- if (peasycap->audio_interface != bInterfaceNumber) {
- SAM("ERROR: attempting to reset "
- "peasycap->audio_interface\n");
- SAM("...... continuing with "
- "%i=peasycap->audio_interface\n",
- peasycap->audio_interface);
- }
- }
- }
-/*---------------------------------------------------------------------------*/
-/*
- * INVESTIGATE ALL ALTSETTINGS.
- * DONE IN DETAIL BECAUSE USB DEVICE 05e1:0408 HAS DISPARATE INCARNATIONS.
- */
-/*---------------------------------------------------------------------------*/
- isokalt = 0;
-
- for (i = 0; i < intf->num_altsetting; i++) {
- alt = usb_altnum_to_altsetting(intf, i);
- if (!alt) {
- SAM("ERROR: alt is NULL\n");
- return -EFAULT;
- }
- interface = &alt->desc;
- if (!interface) {
- SAM("ERROR: intf_descriptor is NULL\n");
- return -EFAULT;
- }
-
- if (0 == interface->bNumEndpoints)
- JOM(4, "intf[%i]alt[%i] has no endpoints\n",
- bInterfaceNumber, i);
-/*---------------------------------------------------------------------------*/
- for (j = 0; j < interface->bNumEndpoints; j++) {
- ep = &alt->endpoint[j].desc;
- if (!ep) {
- SAM("ERROR: ep is NULL.\n");
- SAM("...... skipping\n");
- continue;
- }
-
- if (!usb_endpoint_is_isoc_in(ep)) {
- JOM(4, "intf[%i]alt[%i]end[%i] is a %d endpoint\n",
- bInterfaceNumber,
- i, j, ep->bmAttributes);
- if (usb_endpoint_dir_out(ep)) {
- SAM("ERROR: OUT endpoint unexpected\n");
- SAM("...... continuing\n");
- }
- continue;
- }
- switch (bInterfaceClass) {
- case USB_CLASS_VIDEO:
- case USB_CLASS_VENDOR_SPEC: {
- if (ep->wMaxPacketSize) {
- if (8 > isokalt) {
- okalt[isokalt] = i;
- JOM(4,
- "%i=okalt[%i]\n",
- okalt[isokalt],
- isokalt);
- okepn[isokalt] =
- ep->
- bEndpointAddress &
- 0x0F;
- JOM(4,
- "%i=okepn[%i]\n",
- okepn[isokalt],
- isokalt);
- okmps[isokalt] =
- le16_to_cpu(ep->
- wMaxPacketSize);
- JOM(4,
- "%i=okmps[%i]\n",
- okmps[isokalt],
- isokalt);
- isokalt++;
- }
- } else {
- if (-1 == peasycap->
- video_altsetting_off) {
- peasycap->
- video_altsetting_off =
- i;
- JOM(4, "%i=video_"
- "altsetting_off "
- "<====\n",
- peasycap->
- video_altsetting_off);
- } else {
- SAM("ERROR: peasycap"
- "->video_altsetting_"
- "off already set\n");
- SAM("...... "
- "continuing with "
- "%i=peasycap->video_"
- "altsetting_off\n",
- peasycap->
- video_altsetting_off);
- }
- }
- break;
- }
- case USB_CLASS_AUDIO: {
- if (bInterfaceSubClass !=
- USB_SUBCLASS_AUDIOSTREAMING)
- break;
- if (!peasycap) {
- SAM("MISTAKE: "
- "peasycap is NULL\n");
- return -EFAULT;
- }
- if (ep->wMaxPacketSize) {
- if (8 > isokalt) {
- okalt[isokalt] = i ;
- JOM(4,
- "%i=okalt[%i]\n",
- okalt[isokalt],
- isokalt);
- okepn[isokalt] =
- ep->
- bEndpointAddress &
- 0x0F;
- JOM(4,
- "%i=okepn[%i]\n",
- okepn[isokalt],
- isokalt);
- okmps[isokalt] =
- le16_to_cpu(ep->
- wMaxPacketSize);
- JOM(4,
- "%i=okmps[%i]\n",
- okmps[isokalt],
- isokalt);
- isokalt++;
- }
- } else {
- if (-1 == peasycap->
- audio_altsetting_off) {
- peasycap->
- audio_altsetting_off =
- i;
- JOM(4, "%i=audio_"
- "altsetting_off "
- "<====\n",
- peasycap->
- audio_altsetting_off);
- } else {
- SAM("ERROR: peasycap"
- "->audio_altsetting_"
- "off already set\n");
- SAM("...... "
- "continuing with "
- "%i=peasycap->"
- "audio_altsetting_"
- "off\n",
- peasycap->
- audio_altsetting_off);
- }
- }
- break;
- }
- default:
- break;
- }
- if (0 == ep->wMaxPacketSize) {
- JOM(4, "intf[%i]alt[%i]end[%i] "
- "has zero packet size\n",
- bInterfaceNumber, i, j);
- }
- }
- }
-/*---------------------------------------------------------------------------*/
-/*
- * PERFORM INITIALIZATION OF THE PROBED INTERFACE
- */
-/*---------------------------------------------------------------------------*/
- JOM(4, "initialization begins for interface %i\n",
- interface->bInterfaceNumber);
- switch (bInterfaceNumber) {
-/*---------------------------------------------------------------------------*/
-/*
- * INTERFACE 0 IS THE VIDEO INTERFACE
- */
-/*---------------------------------------------------------------------------*/
- case 0: {
- if (!peasycap) {
- SAM("MISTAKE: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!isokalt) {
- SAM("ERROR: no viable video_altsetting_on\n");
- return -ENOENT;
- } else {
- peasycap->video_altsetting_on = okalt[isokalt - 1];
- JOM(4, "%i=video_altsetting_on <====\n",
- peasycap->video_altsetting_on);
- }
-/*---------------------------------------------------------------------------*/
-/*
- * DECIDE THE VIDEO STREAMING PARAMETERS
- */
-/*---------------------------------------------------------------------------*/
- peasycap->video_endpointnumber = okepn[isokalt - 1];
- JOM(4, "%i=video_endpointnumber\n", peasycap->video_endpointnumber);
- maxpacketsize = okmps[isokalt - 1];
-
- peasycap->video_isoc_maxframesize =
- min(maxpacketsize, USB_2_0_MAXPACKETSIZE);
- if (0 >= peasycap->video_isoc_maxframesize) {
- SAM("ERROR: bad video_isoc_maxframesize\n");
- SAM(" possibly because port is USB 1.1\n");
- return -ENOENT;
- }
- JOM(4, "%i=video_isoc_maxframesize\n",
- peasycap->video_isoc_maxframesize);
-
- peasycap->video_isoc_framesperdesc = VIDEO_ISOC_FRAMESPERDESC;
- JOM(4, "%i=video_isoc_framesperdesc\n",
- peasycap->video_isoc_framesperdesc);
- if (0 >= peasycap->video_isoc_framesperdesc) {
- SAM("ERROR: bad video_isoc_framesperdesc\n");
- return -ENOENT;
- }
- peasycap->video_isoc_buffer_size =
- peasycap->video_isoc_maxframesize *
- peasycap->video_isoc_framesperdesc;
- JOM(4, "%i=video_isoc_buffer_size\n",
- peasycap->video_isoc_buffer_size);
- if ((PAGE_SIZE << VIDEO_ISOC_ORDER) <
- peasycap->video_isoc_buffer_size) {
- SAM("MISTAKE: peasycap->video_isoc_buffer_size too big\n");
- return -EFAULT;
- }
-/*---------------------------------------------------------------------------*/
- if (-1 == peasycap->video_interface) {
- SAM("MISTAKE: video_interface is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_altsetting_on) {
- SAM("MISTAKE: video_altsetting_on is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_altsetting_off) {
- SAM("MISTAKE: video_interface_off is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_endpointnumber) {
- SAM("MISTAKE: video_endpointnumber is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_isoc_maxframesize) {
- SAM("MISTAKE: video_isoc_maxframesize is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_isoc_buffer_size) {
- SAM("MISTAKE: video_isoc_buffer_size is unset\n");
- return -EFAULT;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * ALLOCATE MEMORY FOR VIDEO BUFFERS. LISTS MUST BE INITIALIZED FIRST.
- */
-/*---------------------------------------------------------------------------*/
- INIT_LIST_HEAD(&(peasycap->urb_video_head));
- peasycap->purb_video_head = &(peasycap->urb_video_head);
-/*---------------------------------------------------------------------------*/
- JOM(4, "allocating %i frame buffers of size %li\n",
- FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE);
- JOM(4, ".... each scattered over %li pages\n",
- FRAME_BUFFER_SIZE/PAGE_SIZE);
-
- for (k = 0; k < FRAME_BUFFER_MANY; k++) {
- for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) {
- if (peasycap->frame_buffer[k][m].pgo)
- SAM("attempting to reallocate frame "
- " buffers\n");
- else {
- pbuf = (void *)__get_free_page(GFP_KERNEL);
- if (!pbuf) {
- SAM("ERROR: Could not allocate frame "
- "buffer %i page %i\n", k, m);
- return -ENOMEM;
- } else
- peasycap->allocation_video_page += 1;
- peasycap->frame_buffer[k][m].pgo = pbuf;
- }
- peasycap->frame_buffer[k][m].pto =
- peasycap->frame_buffer[k][m].pgo;
- }
- }
-
- peasycap->frame_fill = 0;
- peasycap->frame_read = 0;
- JOM(4, "allocation of frame buffers done: %i pages\n", k *
- m);
-/*---------------------------------------------------------------------------*/
- JOM(4, "allocating %i field buffers of size %li\n",
- FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE);
- JOM(4, ".... each scattered over %li pages\n",
- FIELD_BUFFER_SIZE/PAGE_SIZE);
-
- for (k = 0; k < FIELD_BUFFER_MANY; k++) {
- for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) {
- if (peasycap->field_buffer[k][m].pgo) {
- SAM("ERROR: attempting to reallocate "
- "field buffers\n");
- } else {
- pbuf = (void *) __get_free_page(GFP_KERNEL);
- if (!pbuf) {
- SAM("ERROR: Could not allocate field"
- " buffer %i page %i\n", k, m);
- return -ENOMEM;
- }
- else
- peasycap->allocation_video_page += 1;
- peasycap->field_buffer[k][m].pgo = pbuf;
- }
- peasycap->field_buffer[k][m].pto =
- peasycap->field_buffer[k][m].pgo;
- }
- peasycap->field_buffer[k][0].kount = 0x0200;
- }
- peasycap->field_fill = 0;
- peasycap->field_page = 0;
- peasycap->field_read = 0;
- JOM(4, "allocation of field buffers done: %i pages\n", k *
- m);
-/*---------------------------------------------------------------------------*/
- JOM(4, "allocating %i isoc video buffers of size %i\n",
- VIDEO_ISOC_BUFFER_MANY,
- peasycap->video_isoc_buffer_size);
- JOM(4, ".... each occupying contiguous memory pages\n");
-
- for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
- pbuf = (void *)__get_free_pages(GFP_KERNEL,
- VIDEO_ISOC_ORDER);
- if (!pbuf) {
- SAM("ERROR: Could not allocate isoc video buffer "
- "%i\n", k);
- return -ENOMEM;
- } else
- peasycap->allocation_video_page +=
- BIT(VIDEO_ISOC_ORDER);
-
- peasycap->video_isoc_buffer[k].pgo = pbuf;
- peasycap->video_isoc_buffer[k].pto =
- pbuf + peasycap->video_isoc_buffer_size;
- peasycap->video_isoc_buffer[k].kount = k;
- }
- JOM(4, "allocation of isoc video buffers done: %i pages\n",
- k * (0x01 << VIDEO_ISOC_ORDER));
-/*---------------------------------------------------------------------------*/
-/*
- * ALLOCATE AND INITIALIZE MULTIPLE struct urb ...
- */
-/*---------------------------------------------------------------------------*/
- JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY);
- JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n",
- peasycap->video_isoc_framesperdesc);
- JOM(4, "using %i=peasycap->video_isoc_maxframesize\n",
- peasycap->video_isoc_maxframesize);
- JOM(4, "using %i=peasycap->video_isoc_buffer_sizen",
- peasycap->video_isoc_buffer_size);
-
- for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
- purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc,
- GFP_KERNEL);
- if (!purb) {
- SAM("ERROR: usb_alloc_urb returned NULL for buffer "
- "%i\n", k);
- return -ENOMEM;
- } else
- peasycap->allocation_video_urb += 1;
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
- if (!pdata_urb) {
- SAM("ERROR: Could not allocate struct data_urb.\n");
- return -ENOMEM;
- } else
- peasycap->allocation_video_struct +=
- sizeof(struct data_urb);
-
- pdata_urb->purb = purb;
- pdata_urb->isbuf = k;
- pdata_urb->length = 0;
- list_add_tail(&(pdata_urb->list_head),
- peasycap->purb_video_head);
-/*---------------------------------------------------------------------------*/
-/*
- * ... AND INITIALIZE THEM
- */
-/*---------------------------------------------------------------------------*/
- if (!k) {
- JOM(4, "initializing video urbs thus:\n");
- JOM(4, " purb->interval = 1;\n");
- JOM(4, " purb->dev = peasycap->pusb_device;\n");
- JOM(4, " purb->pipe = usb_rcvisocpipe"
- "(peasycap->pusb_device,%i);\n",
- peasycap->video_endpointnumber);
- JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n");
- JOM(4, " purb->transfer_buffer = peasycap->"
- "video_isoc_buffer[.].pgo;\n");
- JOM(4, " purb->transfer_buffer_length = %i;\n",
- peasycap->video_isoc_buffer_size);
- JOM(4, " purb->complete = easycap_complete;\n");
- JOM(4, " purb->context = peasycap;\n");
- JOM(4, " purb->start_frame = 0;\n");
- JOM(4, " purb->number_of_packets = %i;\n",
- peasycap->video_isoc_framesperdesc);
- JOM(4, " for (j = 0; j < %i; j++)\n",
- peasycap->video_isoc_framesperdesc);
- JOM(4, " {\n");
- JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n",
- peasycap->video_isoc_maxframesize);
- JOM(4, " purb->iso_frame_desc[j].length = %i;\n",
- peasycap->video_isoc_maxframesize);
- JOM(4, " }\n");
- }
-
- purb->interval = 1;
- purb->dev = peasycap->pusb_device;
- purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
- peasycap->video_endpointnumber);
- purb->transfer_flags = URB_ISO_ASAP;
- purb->transfer_buffer = peasycap->video_isoc_buffer[k].pgo;
- purb->transfer_buffer_length =
- peasycap->video_isoc_buffer_size;
- purb->complete = easycap_complete;
- purb->context = peasycap;
- purb->start_frame = 0;
- purb->number_of_packets = peasycap->video_isoc_framesperdesc;
- for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) {
- purb->iso_frame_desc[j].offset = j *
- peasycap->video_isoc_maxframesize;
- purb->iso_frame_desc[j].length =
- peasycap->video_isoc_maxframesize;
- }
- }
- JOM(4, "allocation of %i struct urb done.\n", k);
-/*--------------------------------------------------------------------------*/
-/*
- * SAVE POINTER peasycap IN THIS INTERFACE.
- */
-/*--------------------------------------------------------------------------*/
- usb_set_intfdata(intf, peasycap);
-/*---------------------------------------------------------------------------*/
-/*
- * IT IS ESSENTIAL TO INITIALIZE THE HARDWARE BEFORE, RATHER THAN AFTER,
- * THE DEVICE IS REGISTERED, BECAUSE SOME VERSIONS OF THE videodev MODULE
- * CALL easycap_open() IMMEDIATELY AFTER REGISTRATION, CAUSING A CLASH.
- * BEWARE.
-*/
-/*---------------------------------------------------------------------------*/
- peasycap->ntsc = easycap_ntsc;
- JOM(8, "defaulting initially to %s\n",
- easycap_ntsc ? "NTSC" : "PAL");
- rc = reset(peasycap);
- if (rc) {
- SAM("ERROR: reset() rc = %i\n", rc);
- return -EFAULT;
- }
-/*--------------------------------------------------------------------------*/
-/*
- * THE VIDEO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
- */
-/*--------------------------------------------------------------------------*/
- if (v4l2_device_register(&intf->dev, &peasycap->v4l2_device)) {
- SAM("v4l2_device_register() failed\n");
- return -ENODEV;
- }
- JOM(4, "registered device instance: %s\n",
- peasycap->v4l2_device.name);
-/*---------------------------------------------------------------------------*/
-/*
- * FIXME
- *
- *
- * THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG:
-*/
-/*---------------------------------------------------------------------------*/
- peasycap->video_device.v4l2_dev = NULL;
-/*---------------------------------------------------------------------------*/
-
-
- strcpy(&peasycap->video_device.name[0], "easycapdc60");
- peasycap->video_device.fops = &v4l2_fops;
- peasycap->video_device.minor = -1;
- peasycap->video_device.release = (void *)(&videodev_release);
-
- video_set_drvdata(&(peasycap->video_device), (void *)peasycap);
-
- if (0 != (video_register_device(&(peasycap->video_device),
- VFL_TYPE_GRABBER, -1))) {
- err("Not able to register with videodev");
- videodev_release(&(peasycap->video_device));
- return -ENODEV;
- } else {
- (peasycap->registered_video)++;
- SAM("registered with videodev: %i=minor\n",
- peasycap->video_device.minor);
- peasycap->minor = peasycap->video_device.minor;
- }
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-
- break;
- }
-/*--------------------------------------------------------------------------*/
-/*
- * INTERFACE 1 IS THE AUDIO CONTROL INTERFACE
- * INTERFACE 2 IS THE AUDIO STREAMING INTERFACE
- */
-/*--------------------------------------------------------------------------*/
- case 1: {
- if (!peasycap) {
- SAM("MISTAKE: peasycap is NULL\n");
- return -EFAULT;
- }
-/*--------------------------------------------------------------------------*/
-/*
- * SAVE POINTER peasycap IN INTERFACE 1
- */
-/*--------------------------------------------------------------------------*/
- usb_set_intfdata(intf, peasycap);
- JOM(4, "no initialization required for interface %i\n",
- interface->bInterfaceNumber);
- break;
- }
-/*--------------------------------------------------------------------------*/
- case 2: {
- if (!peasycap) {
- SAM("MISTAKE: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!isokalt) {
- SAM("ERROR: no viable audio_altsetting_on\n");
- return -ENOENT;
- } else {
- peasycap->audio_altsetting_on = okalt[isokalt - 1];
- JOM(4, "%i=audio_altsetting_on <====\n",
- peasycap->audio_altsetting_on);
- }
-
- peasycap->audio_endpointnumber = okepn[isokalt - 1];
- JOM(4, "%i=audio_endpointnumber\n", peasycap->audio_endpointnumber);
-
- peasycap->audio_isoc_maxframesize = okmps[isokalt - 1];
- JOM(4, "%i=audio_isoc_maxframesize\n",
- peasycap->audio_isoc_maxframesize);
- if (0 >= peasycap->audio_isoc_maxframesize) {
- SAM("ERROR: bad audio_isoc_maxframesize\n");
- return -ENOENT;
- }
- if (9 == peasycap->audio_isoc_maxframesize) {
- peasycap->ilk |= 0x02;
- SAM("audio hardware is microphone\n");
- peasycap->microphone = true;
- peasycap->audio_pages_per_fragment =
- PAGES_PER_AUDIO_FRAGMENT;
- } else if (256 == peasycap->audio_isoc_maxframesize) {
- peasycap->ilk &= ~0x02;
- SAM("audio hardware is AC'97\n");
- peasycap->microphone = false;
- peasycap->audio_pages_per_fragment =
- PAGES_PER_AUDIO_FRAGMENT;
- } else {
- SAM("hardware is unidentified:\n");
- SAM("%i=audio_isoc_maxframesize\n",
- peasycap->audio_isoc_maxframesize);
- return -ENOENT;
- }
-
- peasycap->audio_bytes_per_fragment =
- peasycap->audio_pages_per_fragment * PAGE_SIZE;
- peasycap->audio_buffer_page_many = (AUDIO_FRAGMENT_MANY *
- peasycap->audio_pages_per_fragment);
-
- JOM(4, "%6i=AUDIO_FRAGMENT_MANY\n", AUDIO_FRAGMENT_MANY);
- JOM(4, "%6i=audio_pages_per_fragment\n",
- peasycap->audio_pages_per_fragment);
- JOM(4, "%6i=audio_bytes_per_fragment\n",
- peasycap->audio_bytes_per_fragment);
- JOM(4, "%6i=audio_buffer_page_many\n",
- peasycap->audio_buffer_page_many);
-
- peasycap->audio_isoc_framesperdesc = AUDIO_ISOC_FRAMESPERDESC;
-
- JOM(4, "%i=audio_isoc_framesperdesc\n",
- peasycap->audio_isoc_framesperdesc);
- if (0 >= peasycap->audio_isoc_framesperdesc) {
- SAM("ERROR: bad audio_isoc_framesperdesc\n");
- return -ENOENT;
- }
-
- peasycap->audio_isoc_buffer_size =
- peasycap->audio_isoc_maxframesize *
- peasycap->audio_isoc_framesperdesc;
- JOM(4, "%i=audio_isoc_buffer_size\n",
- peasycap->audio_isoc_buffer_size);
- if (AUDIO_ISOC_BUFFER_SIZE < peasycap->audio_isoc_buffer_size) {
- SAM("MISTAKE: audio_isoc_buffer_size bigger "
- "than %li=AUDIO_ISOC_BUFFER_SIZE\n",
- AUDIO_ISOC_BUFFER_SIZE);
- return -EFAULT;
- }
- if (-1 == peasycap->audio_interface) {
- SAM("MISTAKE: audio_interface is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->audio_altsetting_on) {
- SAM("MISTAKE: audio_altsetting_on is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->audio_altsetting_off) {
- SAM("MISTAKE: audio_interface_off is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->audio_endpointnumber) {
- SAM("MISTAKE: audio_endpointnumber is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->audio_isoc_maxframesize) {
- SAM("MISTAKE: audio_isoc_maxframesize is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->audio_isoc_buffer_size) {
- SAM("MISTAKE: audio_isoc_buffer_size is unset\n");
- return -EFAULT;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * ALLOCATE MEMORY FOR AUDIO BUFFERS. LISTS MUST BE INITIALIZED FIRST.
- */
-/*---------------------------------------------------------------------------*/
- INIT_LIST_HEAD(&(peasycap->urb_audio_head));
- peasycap->purb_audio_head = &(peasycap->urb_audio_head);
-
-/*---------------------------------------------------------------------------*/
- JOM(4, "allocating %i isoc audio buffers of size %i\n",
- AUDIO_ISOC_BUFFER_MANY,
- peasycap->audio_isoc_buffer_size);
- JOM(4, ".... each occupying contiguous memory pages\n");
-
- for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
- pbuf = (void *)__get_free_pages(GFP_KERNEL,
- AUDIO_ISOC_ORDER);
- if (!pbuf) {
- SAM("ERROR: Could not allocate isoc audio buffer "
- "%i\n", k);
- return -ENOMEM;
- } else
- peasycap->allocation_audio_page +=
- BIT(AUDIO_ISOC_ORDER);
-
- peasycap->audio_isoc_buffer[k].pgo = pbuf;
- peasycap->audio_isoc_buffer[k].pto = pbuf +
- peasycap->audio_isoc_buffer_size;
- peasycap->audio_isoc_buffer[k].kount = k;
- }
- JOM(4, "allocation of isoc audio buffers done.\n");
-/*---------------------------------------------------------------------------*/
-/*
- * ALLOCATE AND INITIALIZE MULTIPLE struct urb ...
- */
-/*---------------------------------------------------------------------------*/
- JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY);
- JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n",
- peasycap->audio_isoc_framesperdesc);
- JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n",
- peasycap->audio_isoc_maxframesize);
- JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n",
- peasycap->audio_isoc_buffer_size);
-
- for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
- purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc,
- GFP_KERNEL);
- if (!purb) {
- SAM("ERROR: usb_alloc_urb returned NULL for buffer "
- "%i\n", k);
- return -ENOMEM;
- }
- peasycap->allocation_audio_urb += 1 ;
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
- if (!pdata_urb) {
- SAM("ERROR: Could not allocate struct data_urb.\n");
- return -ENOMEM;
- }
- peasycap->allocation_audio_struct +=
- sizeof(struct data_urb);
-
- pdata_urb->purb = purb;
- pdata_urb->isbuf = k;
- pdata_urb->length = 0;
- list_add_tail(&(pdata_urb->list_head),
- peasycap->purb_audio_head);
-/*---------------------------------------------------------------------------*/
-/*
- * ... AND INITIALIZE THEM
- */
-/*---------------------------------------------------------------------------*/
- if (!k) {
- JOM(4, "initializing audio urbs thus:\n");
- JOM(4, " purb->interval = 1;\n");
- JOM(4, " purb->dev = peasycap->pusb_device;\n");
- JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->"
- "pusb_device,%i);\n",
- peasycap->audio_endpointnumber);
- JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n");
- JOM(4, " purb->transfer_buffer = "
- "peasycap->audio_isoc_buffer[.].pgo;\n");
- JOM(4, " purb->transfer_buffer_length = %i;\n",
- peasycap->audio_isoc_buffer_size);
- JOM(4, " purb->complete = easycap_alsa_complete;\n");
- JOM(4, " purb->context = peasycap;\n");
- JOM(4, " purb->start_frame = 0;\n");
- JOM(4, " purb->number_of_packets = %i;\n",
- peasycap->audio_isoc_framesperdesc);
- JOM(4, " for (j = 0; j < %i; j++)\n",
- peasycap->audio_isoc_framesperdesc);
- JOM(4, " {\n");
- JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n",
- peasycap->audio_isoc_maxframesize);
- JOM(4, " purb->iso_frame_desc[j].length = %i;\n",
- peasycap->audio_isoc_maxframesize);
- JOM(4, " }\n");
- }
-
- purb->interval = 1;
- purb->dev = peasycap->pusb_device;
- purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
- peasycap->audio_endpointnumber);
- purb->transfer_flags = URB_ISO_ASAP;
- purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo;
- purb->transfer_buffer_length =
- peasycap->audio_isoc_buffer_size;
- purb->complete = easycap_alsa_complete;
- purb->context = peasycap;
- purb->start_frame = 0;
- purb->number_of_packets = peasycap->audio_isoc_framesperdesc;
- for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) {
- purb->iso_frame_desc[j].offset = j *
- peasycap->audio_isoc_maxframesize;
- purb->iso_frame_desc[j].length =
- peasycap->audio_isoc_maxframesize;
- }
- }
- JOM(4, "allocation of %i struct urb done.\n", k);
-/*---------------------------------------------------------------------------*/
-/*
- * SAVE POINTER peasycap IN THIS INTERFACE.
- */
-/*---------------------------------------------------------------------------*/
- usb_set_intfdata(intf, peasycap);
-/*---------------------------------------------------------------------------*/
-/*
- * THE AUDIO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
- */
-/*---------------------------------------------------------------------------*/
- JOM(4, "initializing ALSA card\n");
-
- rc = easycap_alsa_probe(peasycap);
- if (rc) {
- err("easycap_alsa_probe() rc = %i\n", rc);
- return -ENODEV;
- }
-
-
- JOM(8, "kref_get() with %i=kref.refcount.counter\n",
- peasycap->kref.refcount.counter);
- kref_get(&peasycap->kref);
- peasycap->registered_audio++;
- break;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * INTERFACES OTHER THAN 0, 1 AND 2 ARE UNEXPECTED
- */
-/*---------------------------------------------------------------------------*/
- default:
- JOM(4, "ERROR: unexpected interface %i\n", bInterfaceNumber);
- return -EINVAL;
- }
- SAM("ends successfully for interface %i\n", bInterfaceNumber);
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * WHEN THIS FUNCTION IS CALLED THE EasyCAP HAS ALREADY BEEN PHYSICALLY
- * UNPLUGGED. HENCE peasycap->pusb_device IS NO LONGER VALID.
- *
- * THIS FUNCTION AFFECTS ALSA. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
-static void easycap_usb_disconnect(struct usb_interface *pusb_interface)
-{
- struct usb_host_interface *pusb_host_interface;
- struct usb_interface_descriptor *pusb_interface_descriptor;
- u8 bInterfaceNumber;
- struct easycap *peasycap;
-
- struct list_head *plist_head;
- struct data_urb *pdata_urb;
- int minor, m, kd;
-
- JOT(4, "\n");
-
- pusb_host_interface = pusb_interface->cur_altsetting;
- if (!pusb_host_interface) {
- JOT(4, "ERROR: pusb_host_interface is NULL\n");
- return;
- }
- pusb_interface_descriptor = &(pusb_host_interface->desc);
- if (!pusb_interface_descriptor) {
- JOT(4, "ERROR: pusb_interface_descriptor is NULL\n");
- return;
- }
- bInterfaceNumber = pusb_interface_descriptor->bInterfaceNumber;
- minor = pusb_interface->minor;
- JOT(4, "intf[%i]: minor=%i\n", bInterfaceNumber, minor);
-
- if (1 == bInterfaceNumber)
- return;
-
- peasycap = usb_get_intfdata(pusb_interface);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * IF THE WAIT QUEUES ARE NOT CLEARED A DEADLOCK IS POSSIBLE. BEWARE.
-*/
-/*---------------------------------------------------------------------------*/
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- wake_up_interruptible(&(peasycap->wq_video));
- wake_up_interruptible(&(peasycap->wq_audio));
-/*---------------------------------------------------------------------------*/
- switch (bInterfaceNumber) {
- case 0: {
- if (peasycap->purb_video_head) {
- JOM(4, "killing video urbs\n");
- m = 0;
- list_for_each(plist_head, peasycap->purb_video_head) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb) {
- if (pdata_urb->purb) {
- usb_kill_urb(pdata_urb->purb);
- m++;
- }
- }
- }
- JOM(4, "%i video urbs killed\n", m);
- }
- break;
- }
-/*---------------------------------------------------------------------------*/
- case 2: {
- if (peasycap->purb_audio_head) {
- JOM(4, "killing audio urbs\n");
- m = 0;
- list_for_each(plist_head, peasycap->purb_audio_head) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb) {
- if (pdata_urb->purb) {
- usb_kill_urb(pdata_urb->purb);
- m++;
- }
- }
- }
- JOM(4, "%i audio urbs killed\n", m);
- }
- break;
- }
- default:
- break;
- }
-/*--------------------------------------------------------------------------*/
-/*
- * DEREGISTER
- *
- * THIS PROCEDURE WILL BLOCK UNTIL easycap_poll(), VIDEO IOCTL AND AUDIO
- * IOCTL ARE ALL UNLOCKED. IF THIS IS NOT DONE AN Oops CAN OCCUR WHEN
- * AN EasyCAP IS UNPLUGGED WHILE THE URBS ARE RUNNING. BEWARE.
- */
-/*--------------------------------------------------------------------------*/
- kd = isdongle(peasycap);
- switch (bInterfaceNumber) {
- case 0: {
- if (0 <= kd && DONGLE_MANY > kd) {
- wake_up_interruptible(&peasycap->wq_video);
- JOM(4, "about to lock dongle[%i].mutex_video\n", kd);
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].
- mutex_video)) {
- SAY("ERROR: "
- "cannot lock dongle[%i].mutex_video\n", kd);
- return;
- }
- JOM(4, "locked dongle[%i].mutex_video\n", kd);
- } else {
- SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
- }
-/*---------------------------------------------------------------------------*/
- if (!peasycap->v4l2_device.name[0]) {
- SAM("ERROR: peasycap->v4l2_device.name is empty\n");
- if (0 <= kd && DONGLE_MANY > kd)
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return;
- }
- v4l2_device_disconnect(&peasycap->v4l2_device);
- JOM(4, "v4l2_device_disconnect() OK\n");
- v4l2_device_unregister(&peasycap->v4l2_device);
- JOM(4, "v4l2_device_unregister() OK\n");
-
- video_unregister_device(&peasycap->video_device);
- JOM(4, "intf[%i]: video_unregister_device() minor=%i\n",
- bInterfaceNumber, minor);
- peasycap->registered_video--;
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-
- if (0 <= kd && DONGLE_MANY > kd) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- JOM(4, "unlocked dongle[%i].mutex_video\n", kd);
- }
- break;
- }
- case 2: {
- if (0 <= kd && DONGLE_MANY > kd) {
- wake_up_interruptible(&peasycap->wq_audio);
- JOM(4, "about to lock dongle[%i].mutex_audio\n", kd);
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].
- mutex_audio)) {
- SAY("ERROR: "
- "cannot lock dongle[%i].mutex_audio\n", kd);
- return;
- }
- JOM(4, "locked dongle[%i].mutex_audio\n", kd);
- } else
- SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
- if (0 != snd_card_free(peasycap->psnd_card)) {
- SAY("ERROR: snd_card_free() failed\n");
- } else {
- peasycap->psnd_card = NULL;
- (peasycap->registered_audio)--;
- }
- if (0 <= kd && DONGLE_MANY > kd) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
- JOM(4, "unlocked dongle[%i].mutex_audio\n", kd);
- }
- break;
- }
- default:
- break;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap
- * (ALSO WHEN ALSA HAS BEEN IN USE)
- */
-/*---------------------------------------------------------------------------*/
- if (!peasycap->kref.refcount.counter) {
- SAM("ERROR: peasycap->kref.refcount.counter is zero "
- "so cannot call kref_put()\n");
- SAM("ending unsuccessfully: may cause memory leak\n");
- return;
- }
- if (0 <= kd && DONGLE_MANY > kd) {
- JOM(4, "about to lock dongle[%i].mutex_video\n", kd);
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
- SAY("ERROR: cannot lock dongle[%i].mutex_video\n", kd);
- SAM("ending unsuccessfully: may cause memory leak\n");
- return;
- }
- JOM(4, "locked dongle[%i].mutex_video\n", kd);
- JOM(4, "about to lock dongle[%i].mutex_audio\n", kd);
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_audio)) {
- SAY("ERROR: cannot lock dongle[%i].mutex_audio\n", kd);
- mutex_unlock(&(easycapdc60_dongle[kd].mutex_video));
- JOM(4, "unlocked dongle[%i].mutex_video\n", kd);
- SAM("ending unsuccessfully: may cause memory leak\n");
- return;
- }
- JOM(4, "locked dongle[%i].mutex_audio\n", kd);
- }
- JOM(4, "intf[%i]: %i=peasycap->kref.refcount.counter\n",
- bInterfaceNumber, (int)peasycap->kref.refcount.counter);
- kref_put(&peasycap->kref, easycap_delete);
- JOT(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber);
- if (0 <= kd && DONGLE_MANY > kd) {
- mutex_unlock(&(easycapdc60_dongle[kd].mutex_audio));
- JOT(4, "unlocked dongle[%i].mutex_audio\n", kd);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- JOT(4, "unlocked dongle[%i].mutex_video\n", kd);
- }
-/*---------------------------------------------------------------------------*/
- JOM(4, "ends\n");
- return;
-}
-/*****************************************************************************/
-
-/*---------------------------------------------------------------------------*/
-/*
- * PARAMETERS APPLICABLE TO ENTIRE DRIVER, I.E. BOTH VIDEO AND AUDIO
- */
-/*---------------------------------------------------------------------------*/
-static struct usb_device_id easycap_usb_device_id_table[] = {
- {USB_DEVICE(USB_EASYCAP_VENDOR_ID, USB_EASYCAP_PRODUCT_ID)},
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, easycap_usb_device_id_table);
-struct usb_driver easycap_usb_driver = {
- .name = "easycap",
- .id_table = easycap_usb_device_id_table,
- .probe = easycap_usb_probe,
- .disconnect = easycap_usb_disconnect,
-};
-
-static int __init easycap_module_init(void)
-{
- int k, rc;
-
- printk(KERN_INFO "Easycap version: "EASYCAP_DRIVER_VERSION "\n");
-
- JOT(4, "begins. %i=debug %i=bars %i=gain\n",
- easycap_debug, easycap_bars, easycap_gain);
-
- mutex_init(&mutex_dongle);
- for (k = 0; k < DONGLE_MANY; k++) {
- easycapdc60_dongle[k].peasycap = NULL;
- mutex_init(&easycapdc60_dongle[k].mutex_video);
- mutex_init(&easycapdc60_dongle[k].mutex_audio);
- }
- rc = usb_register(&easycap_usb_driver);
- if (rc)
- printk(KERN_ERR "Easycap: usb_register failed rc=%d\n", rc);
-
- return rc;
-}
-/*****************************************************************************/
-static void __exit easycap_module_exit(void)
-{
- usb_deregister(&easycap_usb_driver);
-}
-/*****************************************************************************/
-
-module_init(easycap_module_init);
-module_exit(easycap_module_exit);
-
-/*****************************************************************************/
+++ /dev/null
-/******************************************************************************
-* *
-* easycap_settings.c *
-* *
-******************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This 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.
- *
- * The software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-
-#include "easycap.h"
-
-/*---------------------------------------------------------------------------*/
-/*
- * THE LEAST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING:
- * 0 => 25 fps
- * 1 => 30 fps
- *
- * THE MOST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING:
- * 0 => full framerate
- * 1 => 20% framerate
- */
-/*---------------------------------------------------------------------------*/
-const struct easycap_standard easycap_standard[] = {
- {
- .mask = 0x00FF & PAL_BGHIN ,
- .v4l2_standard = {
- .index = PAL_BGHIN,
- .id = (V4L2_STD_PAL_B |
- V4L2_STD_PAL_G | V4L2_STD_PAL_H |
- V4L2_STD_PAL_I | V4L2_STD_PAL_N),
- .name = "PAL_BGHIN",
- .frameperiod = {1, 25},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & NTSC_N_443 ,
- .v4l2_standard = {
- .index = NTSC_N_443,
- .id = V4L2_STD_UNKNOWN,
- .name = "NTSC_N_443",
- .frameperiod = {1, 25},
- .framelines = 480,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & PAL_Nc ,
- .v4l2_standard = {
- .index = PAL_Nc,
- .id = V4L2_STD_PAL_Nc,
- .name = "PAL_Nc",
- .frameperiod = {1, 25},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & NTSC_N ,
- .v4l2_standard = {
- .index = NTSC_N,
- .id = V4L2_STD_UNKNOWN,
- .name = "NTSC_N",
- .frameperiod = {1, 25},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & SECAM ,
- .v4l2_standard = {
- .index = SECAM,
- .id = V4L2_STD_SECAM,
- .name = "SECAM",
- .frameperiod = {1, 25},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & NTSC_M ,
- .v4l2_standard = {
- .index = NTSC_M,
- .id = V4L2_STD_NTSC_M,
- .name = "NTSC_M",
- .frameperiod = {1, 30},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & NTSC_M_JP ,
- .v4l2_standard = {
- .index = NTSC_M_JP,
- .id = V4L2_STD_NTSC_M_JP,
- .name = "NTSC_M_JP",
- .frameperiod = {1, 30},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & PAL_60 ,
- .v4l2_standard = {
- .index = PAL_60,
- .id = V4L2_STD_PAL_60,
- .name = "PAL_60",
- .frameperiod = {1, 30},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & NTSC_443 ,
- .v4l2_standard = {
- .index = NTSC_443,
- .id = V4L2_STD_NTSC_443,
- .name = "NTSC_443",
- .frameperiod = {1, 30},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & PAL_M ,
- .v4l2_standard = {
- .index = PAL_M,
- .id = V4L2_STD_PAL_M,
- .name = "PAL_M",
- .frameperiod = {1, 30},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & PAL_BGHIN_SLOW),
- .v4l2_standard = {
- .index = PAL_BGHIN_SLOW,
- .id = (V4L2_STD_PAL_B | V4L2_STD_PAL_G |
- V4L2_STD_PAL_H |
- V4L2_STD_PAL_I | V4L2_STD_PAL_N |
- (((v4l2_std_id)0x01) << 32)),
- .name = "PAL_BGHIN_SLOW",
- .frameperiod = {1, 5},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & NTSC_N_443_SLOW),
- .v4l2_standard = {
- .index = NTSC_N_443_SLOW,
- .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x11) << 32)),
- .name = "NTSC_N_443_SLOW",
- .frameperiod = {1, 5},
- .framelines = 480,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & PAL_Nc_SLOW),
- .v4l2_standard = {
- .index = PAL_Nc_SLOW,
- .id = (V4L2_STD_PAL_Nc | (((v4l2_std_id)0x01) << 32)),
- .name = "PAL_Nc_SLOW",
- .frameperiod = {1, 5},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & NTSC_N_SLOW),
- .v4l2_standard = {
- .index = NTSC_N_SLOW,
- .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x21) << 32)),
- .name = "NTSC_N_SLOW",
- .frameperiod = {1, 5},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & SECAM_SLOW),
- .v4l2_standard = {
- .index = SECAM_SLOW,
- .id = (V4L2_STD_SECAM | (((v4l2_std_id)0x01) << 32)),
- .name = "SECAM_SLOW",
- .frameperiod = {1, 5},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & NTSC_M_SLOW),
- .v4l2_standard = {
- .index = NTSC_M_SLOW,
- .id = (V4L2_STD_NTSC_M | (((v4l2_std_id)0x01) << 32)),
- .name = "NTSC_M_SLOW",
- .frameperiod = {1, 6},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & NTSC_M_JP_SLOW),
- .v4l2_standard = {
- .index = NTSC_M_JP_SLOW,
- .id = (V4L2_STD_NTSC_M_JP |
- (((v4l2_std_id)0x01) << 32)),
- .name = "NTSC_M_JP_SLOW",
- .frameperiod = {1, 6},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & PAL_60_SLOW),
- .v4l2_standard = {
- .index = PAL_60_SLOW,
- .id = (V4L2_STD_PAL_60 | (((v4l2_std_id)0x01) << 32)),
- .name = "PAL_60_SLOW",
- .frameperiod = {1, 6},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & NTSC_443_SLOW),
- .v4l2_standard = {
- .index = NTSC_443_SLOW,
- .id = (V4L2_STD_NTSC_443 | (((v4l2_std_id)0x01) << 32)),
- .name = "NTSC_443_SLOW",
- .frameperiod = {1, 6},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & PAL_M_SLOW),
- .v4l2_standard = {
- .index = PAL_M_SLOW,
- .id = (V4L2_STD_PAL_M | (((v4l2_std_id)0x01) << 32)),
- .name = "PAL_M_SLOW",
- .frameperiod = {1, 6},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0xFFFF
- }
-};
-/*---------------------------------------------------------------------------*/
-/*
- * THE 16-BIT easycap_format.mask HAS MEANING:
- * (least significant) BIT 0: 0 => PAL, 25 FPS; 1 => NTSC, 30 FPS
- * BITS 2-4: RESERVED FOR DIFFERENTIATING STANDARDS
- * BITS 5-7: NUMBER OF BYTES PER PIXEL
- * BIT 8: 0 => NATIVE BYTE ORDER; 1 => SWAPPED
- * BITS 9-10: RESERVED FOR OTHER BYTE PERMUTATIONS
- * BIT 11: 0 => UNDECIMATED; 1 => DECIMATED
- * BIT 12: 0 => OFFER FRAMES; 1 => OFFER FIELDS
- * BIT 13: 0 => FULL FRAMERATE; 1 => REDUCED
- * (most significant) BITS 14-15: RESERVED FOR OTHER FIELD/FRAME OPTIONS
- * IT FOLLOWS THAT:
- * bytesperpixel IS ((0x00E0 & easycap_format.mask) >> 5)
- * byteswaporder IS true IF (0 != (0x0100 & easycap_format.mask))
- *
- * decimatepixel IS true IF (0 != (0x0800 & easycap_format.mask))
- *
- * offerfields IS true IF (0 != (0x1000 & easycap_format.mask))
- */
-/*---------------------------------------------------------------------------*/
-
-struct easycap_format easycap_format[1 + SETTINGS_MANY];
-
-int fillin_formats(void)
-{
- const char *name1, *name2, *name3, *name4;
- struct v4l2_format *fmt;
- int i, j, k, m, n;
- u32 width, height, pixelformat, bytesperline, sizeimage;
- u16 mask1, mask2, mask3, mask4;
- enum v4l2_field field;
- enum v4l2_colorspace colorspace;
-
- for (i = 0, n = 0; i < STANDARD_MANY; i++) {
- mask1 = 0x0000;
- switch (i) {
- case PAL_BGHIN: {
- mask1 = 0x1F & PAL_BGHIN;
- name1 = "PAL_BGHIN";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case SECAM: {
- mask1 = 0x1F & SECAM;
- name1 = "SECAM";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_Nc: {
- mask1 = 0x1F & PAL_Nc;
- name1 = "PAL_Nc";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_60: {
- mask1 = 0x1F & PAL_60;
- name1 = "PAL_60";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_M: {
- mask1 = 0x1F & PAL_M;
- name1 = "PAL_M";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case NTSC_M: {
- mask1 = 0x1F & NTSC_M;
- name1 = "NTSC_M";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_443: {
- mask1 = 0x1F & NTSC_443;
- name1 = "NTSC_443";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_M_JP: {
- mask1 = 0x1F & NTSC_M_JP;
- name1 = "NTSC_M_JP";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_N: {
- mask1 = 0x1F & NTSC_M;
- name1 = "NTSC_N";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_N_443: {
- mask1 = 0x1F & NTSC_N_443;
- name1 = "NTSC_N_443";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case PAL_BGHIN_SLOW: {
- mask1 = 0x001F & PAL_BGHIN_SLOW;
- mask1 |= 0x0200;
- name1 = "PAL_BGHIN_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case SECAM_SLOW: {
- mask1 = 0x001F & SECAM_SLOW;
- mask1 |= 0x0200;
- name1 = "SECAM_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_Nc_SLOW: {
- mask1 = 0x001F & PAL_Nc_SLOW;
- mask1 |= 0x0200;
- name1 = "PAL_Nc_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_60_SLOW: {
- mask1 = 0x001F & PAL_60_SLOW;
- mask1 |= 0x0200;
- name1 = "PAL_60_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_M_SLOW: {
- mask1 = 0x001F & PAL_M_SLOW;
- mask1 |= 0x0200;
- name1 = "PAL_M_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case NTSC_M_SLOW: {
- mask1 = 0x001F & NTSC_M_SLOW;
- mask1 |= 0x0200;
- name1 = "NTSC_M_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_443_SLOW: {
- mask1 = 0x001F & NTSC_443_SLOW;
- mask1 |= 0x0200;
- name1 = "NTSC_443_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_M_JP_SLOW: {
- mask1 = 0x001F & NTSC_M_JP_SLOW;
- mask1 |= 0x0200;
- name1 = "NTSC_M_JP_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_N_SLOW: {
- mask1 = 0x001F & NTSC_N_SLOW;
- mask1 |= 0x0200;
- name1 = "NTSC_N_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_N_443_SLOW: {
- mask1 = 0x001F & NTSC_N_443_SLOW;
- mask1 |= 0x0200;
- name1 = "NTSC_N_443_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- default:
- return -1;
- }
-
- for (j = 0; j < RESOLUTION_MANY; j++) {
- mask2 = 0x0000;
- switch (j) {
- case AT_720x576: {
- if (0x1 & mask1)
- continue;
- name2 = "_AT_720x576";
- width = 720;
- height = 576;
- break;
- }
- case AT_704x576: {
- if (0x1 & mask1)
- continue;
- name2 = "_AT_704x576";
- width = 704;
- height = 576;
- break;
- }
- case AT_640x480: {
- name2 = "_AT_640x480";
- width = 640;
- height = 480;
- break;
- }
- case AT_720x480: {
- if (!(0x1 & mask1))
- continue;
- name2 = "_AT_720x480";
- width = 720;
- height = 480;
- break;
- }
- case AT_360x288: {
- if (0x1 & mask1)
- continue;
- name2 = "_AT_360x288";
- width = 360;
- height = 288;
- mask2 = 0x0800;
- break;
- }
- case AT_320x240: {
- name2 = "_AT_320x240";
- width = 320;
- height = 240;
- mask2 = 0x0800;
- break;
- }
- case AT_360x240: {
- if (!(0x1 & mask1))
- continue;
- name2 = "_AT_360x240";
- width = 360;
- height = 240;
- mask2 = 0x0800;
- break;
- }
- default:
- return -2;
- }
-
- for (k = 0; k < PIXELFORMAT_MANY; k++) {
- mask3 = 0x0000;
- switch (k) {
- case FMT_UYVY: {
- name3 = __stringify(FMT_UYVY);
- pixelformat = V4L2_PIX_FMT_UYVY;
- mask3 |= (0x02 << 5);
- break;
- }
- case FMT_YUY2: {
- name3 = __stringify(FMT_YUY2);
- pixelformat = V4L2_PIX_FMT_YUYV;
- mask3 |= (0x02 << 5);
- mask3 |= 0x0100;
- break;
- }
- case FMT_RGB24: {
- name3 = __stringify(FMT_RGB24);
- pixelformat = V4L2_PIX_FMT_RGB24;
- mask3 |= (0x03 << 5);
- break;
- }
- case FMT_RGB32: {
- name3 = __stringify(FMT_RGB32);
- pixelformat = V4L2_PIX_FMT_RGB32;
- mask3 |= (0x04 << 5);
- break;
- }
- case FMT_BGR24: {
- name3 = __stringify(FMT_BGR24);
- pixelformat = V4L2_PIX_FMT_BGR24;
- mask3 |= (0x03 << 5);
- mask3 |= 0x0100;
- break;
- }
- case FMT_BGR32: {
- name3 = __stringify(FMT_BGR32);
- pixelformat = V4L2_PIX_FMT_BGR32;
- mask3 |= (0x04 << 5);
- mask3 |= 0x0100;
- break;
- }
- default:
- return -3;
- }
- bytesperline = width * ((mask3 & 0x00E0) >> 5);
- sizeimage = bytesperline * height;
-
- for (m = 0; m < INTERLACE_MANY; m++) {
- mask4 = 0x0000;
- switch (m) {
- case FIELD_NONE: {
- name4 = "-n";
- field = V4L2_FIELD_NONE;
- break;
- }
- case FIELD_INTERLACED: {
- name4 = "-i";
- mask4 |= 0x1000;
- field = V4L2_FIELD_INTERLACED;
- break;
- }
- default:
- return -4;
- }
- if (SETTINGS_MANY <= n)
- return -5;
-
- strcpy(easycap_format[n].name, name1);
- strcat(easycap_format[n].name, name2);
- strcat(easycap_format[n].name, "_");
- strcat(easycap_format[n].name, name3);
- strcat(easycap_format[n].name, name4);
- easycap_format[n].mask =
- mask1 | mask2 | mask3 | mask4;
- fmt = &easycap_format[n].v4l2_format;
-
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- fmt->fmt.pix.width = width;
- fmt->fmt.pix.height = height;
- fmt->fmt.pix.pixelformat = pixelformat;
- fmt->fmt.pix.field = field;
- fmt->fmt.pix.bytesperline = bytesperline;
- fmt->fmt.pix.sizeimage = sizeimage;
- fmt->fmt.pix.colorspace = colorspace;
- fmt->fmt.pix.priv = 0;
- n++;
- }
- }
- }
- }
- if ((1 + SETTINGS_MANY) <= n)
- return -6;
- easycap_format[n].mask = 0xFFFF;
- return n;
-}
-/*---------------------------------------------------------------------------*/
-struct v4l2_queryctrl easycap_control[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = SAA_0A_DEFAULT,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = SAA_0B_DEFAULT + 128,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = SAA_0C_DEFAULT + 128,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = SAA_0D_DEFAULT + 128,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = V4L2_CID_AUDIO_VOLUME,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Volume",
- .minimum = 0,
- .maximum = 31,
- .step = 1,
- .default_value = 16,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mute",
- .default_value = true,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = 0xFFFFFFFF
- }
-};
-/*****************************************************************************/
+++ /dev/null
-/******************************************************************************
-* *
-* easycap_sound.c *
-* *
-* Audio driver for EasyCAP USB2.0 Video Capture Device DC60 *
-* *
-* *
-******************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This 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.
- *
- * The software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-
-#include "easycap.h"
-
-/*--------------------------------------------------------------------------*/
-/*
- * PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE
- */
-/*--------------------------------------------------------------------------*/
-static const struct snd_pcm_hardware alsa_hardware = {
- .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
- .rate_min = 32000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = PAGE_SIZE *
- PAGES_PER_AUDIO_FRAGMENT *
- AUDIO_FRAGMENT_MANY,
- .period_bytes_min = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT,
- .period_bytes_max = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT * 2,
- .periods_min = AUDIO_FRAGMENT_MANY,
- .periods_max = AUDIO_FRAGMENT_MANY * 2,
-};
-
-
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE DAM BUFFER
- * PROVIDED peasycap->audio_idle IS ZERO. REGARDLESS OF THIS BEING TRUE,
- * IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO.
- */
-/*---------------------------------------------------------------------------*/
-void
-easycap_alsa_complete(struct urb *purb)
-{
- struct easycap *peasycap;
- struct snd_pcm_substream *pss;
- struct snd_pcm_runtime *prt;
- int dma_bytes, fragment_bytes;
- int isfragment;
- u8 *p1, *p2;
- s16 tmp;
- int i, j, more, much, rc;
-#ifdef UPSAMPLE
- int k;
- s16 oldaudio, newaudio, delta;
-#endif /*UPSAMPLE*/
-
- JOT(16, "\n");
-
- if (!purb) {
- SAY("ERROR: purb is NULL\n");
- return;
- }
- peasycap = purb->context;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return;
- }
- much = 0;
- if (peasycap->audio_idle) {
- JOM(16, "%i=audio_idle %i=audio_isoc_streaming\n",
- peasycap->audio_idle, peasycap->audio_isoc_streaming);
- if (peasycap->audio_isoc_streaming)
- goto resubmit;
- }
-/*---------------------------------------------------------------------------*/
- pss = peasycap->psubstream;
- if (!pss)
- goto resubmit;
- prt = pss->runtime;
- if (!prt)
- goto resubmit;
- dma_bytes = (int)prt->dma_bytes;
- if (0 == dma_bytes)
- goto resubmit;
- fragment_bytes = 4 * ((int)prt->period_size);
- if (0 == fragment_bytes)
- goto resubmit;
-/* -------------------------------------------------------------------------*/
- if (purb->status) {
- if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
- JOM(16, "urb status -ESHUTDOWN or -ENOENT\n");
- return;
- }
- SAM("ERROR: non-zero urb status: -%s: %d\n",
- strerror(purb->status), purb->status);
- goto resubmit;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * PROCEED HERE WHEN NO ERROR
- */
-/*---------------------------------------------------------------------------*/
-
-#ifdef UPSAMPLE
- oldaudio = peasycap->oldaudio;
-#endif /*UPSAMPLE*/
-
- for (i = 0; i < purb->number_of_packets; i++) {
- if (purb->iso_frame_desc[i].status < 0) {
- SAM("-%s: %d\n",
- strerror(purb->iso_frame_desc[i].status),
- purb->iso_frame_desc[i].status);
- }
- if (purb->iso_frame_desc[i].status) {
- JOM(12, "discarding audio samples because "
- "%i=purb->iso_frame_desc[i].status\n",
- purb->iso_frame_desc[i].status);
- continue;
- }
- more = purb->iso_frame_desc[i].actual_length;
- if (more == 0) {
- peasycap->audio_mt++;
- continue;
- }
- if (0 > more) {
- SAM("MISTAKE: more is negative\n");
- return;
- }
-
- if (peasycap->audio_mt) {
- JOM(12, "%4i empty audio urb frames\n",
- peasycap->audio_mt);
- peasycap->audio_mt = 0;
- }
-
- p1 = (u8 *)(purb->transfer_buffer +
- purb->iso_frame_desc[i].offset);
-
- /*
- * COPY more BYTES FROM ISOC BUFFER
- * TO THE DMA BUFFER, CONVERTING
- * 8-BIT MONO TO 16-BIT SIGNED
- * LITTLE-ENDIAN SAMPLES IF NECESSARY
- */
- while (more) {
- much = dma_bytes - peasycap->dma_fill;
- if (0 > much) {
- SAM("MISTAKE: much is negative\n");
- return;
- }
- if (0 == much) {
- peasycap->dma_fill = 0;
- peasycap->dma_next = fragment_bytes;
- JOM(8, "wrapped dma buffer\n");
- }
- if (!peasycap->microphone) {
- if (much > more)
- much = more;
- memcpy(prt->dma_area + peasycap->dma_fill,
- p1, much);
- p1 += much;
- more -= much;
- } else {
-#ifdef UPSAMPLE
- if (much % 16)
- JOM(8, "MISTAKE? much"
- " is not divisible by 16\n");
- if (much > (16 * more))
- much = 16 * more;
- p2 = (u8 *)(prt->dma_area + peasycap->dma_fill);
-
- for (j = 0; j < (much / 16); j++) {
- newaudio = ((int) *p1) - 128;
- newaudio = 128 * newaudio;
-
- delta = (newaudio - oldaudio) / 4;
- tmp = oldaudio + delta;
-
- for (k = 0; k < 4; k++) {
- *p2 = (0x00FF & tmp);
- *(p2 + 1) = (0xFF00 & tmp) >> 8;
- p2 += 2;
- *p2 = (0x00FF & tmp);
- *(p2 + 1) = (0xFF00 & tmp) >> 8;
- p2 += 2;
- tmp += delta;
- }
- p1++;
- more--;
- oldaudio = tmp;
- }
-#else /*!UPSAMPLE*/
- if (much > (2 * more))
- much = 2 * more;
- p2 = (u8 *)(prt->dma_area + peasycap->dma_fill);
-
- for (j = 0; j < (much / 2); j++) {
- tmp = ((int) *p1) - 128;
- tmp = 128 * tmp;
- *p2 = (0x00FF & tmp);
- *(p2 + 1) = (0xFF00 & tmp) >> 8;
- p1++;
- p2 += 2;
- more--;
- }
-#endif /*UPSAMPLE*/
- }
- peasycap->dma_fill += much;
- if (peasycap->dma_fill >= peasycap->dma_next) {
- isfragment = peasycap->dma_fill / fragment_bytes;
- if (0 > isfragment) {
- SAM("MISTAKE: isfragment is negative\n");
- return;
- }
- peasycap->dma_read = (isfragment - 1) * fragment_bytes;
- peasycap->dma_next = (isfragment + 1) * fragment_bytes;
- if (dma_bytes < peasycap->dma_next)
- peasycap->dma_next = fragment_bytes;
-
- if (0 <= peasycap->dma_read) {
- JOM(8, "snd_pcm_period_elapsed(), %i="
- "isfragment\n", isfragment);
- snd_pcm_period_elapsed(pss);
- }
- }
- }
-
-#ifdef UPSAMPLE
- peasycap->oldaudio = oldaudio;
-#endif /*UPSAMPLE*/
-
- }
-/*---------------------------------------------------------------------------*/
-/*
- * RESUBMIT THIS URB
- */
-/*---------------------------------------------------------------------------*/
-resubmit:
- if (peasycap->audio_isoc_streaming == 0)
- return;
-
- rc = usb_submit_urb(purb, GFP_ATOMIC);
- if (rc) {
- if ((-ENODEV != rc) && (-ENOENT != rc)) {
- SAM("ERROR: while %i=audio_idle, usb_submit_urb failed "
- "with rc: -%s :%d\n",
- peasycap->audio_idle, strerror(rc), rc);
- }
- if (0 < peasycap->audio_isoc_streaming)
- peasycap->audio_isoc_streaming--;
- }
- return;
-}
-/*****************************************************************************/
-static int easycap_alsa_open(struct snd_pcm_substream *pss)
-{
- struct snd_pcm *psnd_pcm;
- struct snd_card *psnd_card;
- struct easycap *peasycap;
-
- JOT(4, "\n");
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- psnd_pcm = pss->pcm;
- if (!psnd_pcm) {
- SAY("ERROR: psnd_pcm is NULL\n");
- return -EFAULT;
- }
- psnd_card = psnd_pcm->card;
- if (!psnd_card) {
- SAY("ERROR: psnd_card is NULL\n");
- return -EFAULT;
- }
-
- peasycap = psnd_card->private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (peasycap->psnd_card != psnd_card) {
- SAM("ERROR: bad peasycap->psnd_card\n");
- return -EFAULT;
- }
- if (peasycap->psubstream) {
- SAM("ERROR: bad peasycap->psubstream\n");
- return -EFAULT;
- }
- pss->private_data = peasycap;
- peasycap->psubstream = pss;
- pss->runtime->hw = peasycap->alsa_hardware;
- pss->runtime->private_data = peasycap;
- pss->private_data = peasycap;
-
- if (0 != easycap_sound_setup(peasycap)) {
- JOM(4, "ending unsuccessfully\n");
- return -EFAULT;
- }
- JOM(4, "ending successfully\n");
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_close(struct snd_pcm_substream *pss)
-{
- struct easycap *peasycap;
-
- JOT(4, "\n");
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- peasycap = snd_pcm_substream_chip(pss);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- pss->private_data = NULL;
- peasycap->psubstream = NULL;
- JOT(4, "ending successfully\n");
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_vmalloc(struct snd_pcm_substream *pss, size_t sz)
-{
- struct snd_pcm_runtime *prt;
- JOT(4, "\n");
-
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- prt = pss->runtime;
- if (!prt) {
- SAY("ERROR: substream.runtime is NULL\n");
- return -EFAULT;
- }
- if (prt->dma_area) {
- if (prt->dma_bytes > sz)
- return 0;
- vfree(prt->dma_area);
- }
- prt->dma_area = vmalloc(sz);
- if (!prt->dma_area)
- return -ENOMEM;
- prt->dma_bytes = sz;
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_hw_params(struct snd_pcm_substream *pss,
- struct snd_pcm_hw_params *phw)
-{
- int rc;
-
- JOT(4, "%i\n", (params_buffer_bytes(phw)));
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- rc = easycap_alsa_vmalloc(pss, params_buffer_bytes(phw));
- if (rc)
- return rc;
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_hw_free(struct snd_pcm_substream *pss)
-{
- struct snd_pcm_runtime *prt;
- JOT(4, "\n");
-
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- prt = pss->runtime;
- if (!prt) {
- SAY("ERROR: substream.runtime is NULL\n");
- return -EFAULT;
- }
- if (prt->dma_area) {
- JOT(8, "prt->dma_area = %p\n", prt->dma_area);
- vfree(prt->dma_area);
- prt->dma_area = NULL;
- } else
- JOT(8, "dma_area already freed\n");
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_prepare(struct snd_pcm_substream *pss)
-{
- struct easycap *peasycap;
- struct snd_pcm_runtime *prt;
-
- JOT(4, "\n");
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- prt = pss->runtime;
- peasycap = snd_pcm_substream_chip(pss);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
-
- JOM(16, "ALSA decides %8i Hz=rate\n", pss->runtime->rate);
- JOM(16, "ALSA decides %8ld =period_size\n", pss->runtime->period_size);
- JOM(16, "ALSA decides %8i =periods\n", pss->runtime->periods);
- JOM(16, "ALSA decides %8ld =buffer_size\n", pss->runtime->buffer_size);
- JOM(16, "ALSA decides %8zd =dma_bytes\n", pss->runtime->dma_bytes);
- JOM(16, "ALSA decides %8ld =boundary\n", pss->runtime->boundary);
- JOM(16, "ALSA decides %8i =period_step\n", pss->runtime->period_step);
- JOM(16, "ALSA decides %8i =sample_bits\n", pss->runtime->sample_bits);
- JOM(16, "ALSA decides %8i =frame_bits\n", pss->runtime->frame_bits);
- JOM(16, "ALSA decides %8ld =min_align\n", pss->runtime->min_align);
- JOM(12, "ALSA decides %8ld =hw_ptr_base\n", pss->runtime->hw_ptr_base);
- JOM(12, "ALSA decides %8ld =hw_ptr_interrupt\n",
- pss->runtime->hw_ptr_interrupt);
-
- if (prt->dma_bytes != 4 * ((int)prt->period_size) * ((int)prt->periods)) {
- SAY("MISTAKE: unexpected ALSA parameters\n");
- return -ENOENT;
- }
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_ack(struct snd_pcm_substream *pss)
-{
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_trigger(struct snd_pcm_substream *pss, int cmd)
-{
- struct easycap *peasycap;
- int retval;
-
- JOT(4, "%i=cmd cf %i=START %i=STOP\n", cmd, SNDRV_PCM_TRIGGER_START,
- SNDRV_PCM_TRIGGER_STOP);
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- peasycap = snd_pcm_substream_chip(pss);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START: {
- peasycap->audio_idle = 0;
- break;
- }
- case SNDRV_PCM_TRIGGER_STOP: {
- peasycap->audio_idle = 1;
- break;
- }
- default:
- retval = -EINVAL;
- }
- return 0;
-}
-/*****************************************************************************/
-static snd_pcm_uframes_t easycap_alsa_pointer(struct snd_pcm_substream *pss)
-{
- struct easycap *peasycap;
- snd_pcm_uframes_t offset;
-
- JOT(16, "\n");
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- peasycap = snd_pcm_substream_chip(pss);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if ((0 != peasycap->audio_eof) || (0 != peasycap->audio_idle)) {
- JOM(8, "returning -EIO because "
- "%i=audio_idle %i=audio_eof\n",
- peasycap->audio_idle, peasycap->audio_eof);
- return -EIO;
- }
-/*---------------------------------------------------------------------------*/
- if (0 > peasycap->dma_read) {
- JOM(8, "returning -EBUSY\n");
- return -EBUSY;
- }
- offset = ((snd_pcm_uframes_t)peasycap->dma_read)/4;
- JOM(8, "ALSA decides %8i =hw_ptr_base\n", (int)pss->runtime->hw_ptr_base);
- JOM(8, "ALSA decides %8i =hw_ptr_interrupt\n",
- (int)pss->runtime->hw_ptr_interrupt);
- JOM(8, "%7i=offset %7i=dma_read %7i=dma_next\n",
- (int)offset, peasycap->dma_read, peasycap->dma_next);
- return offset;
-}
-/*****************************************************************************/
-static struct page *
-easycap_alsa_page(struct snd_pcm_substream *pss, unsigned long offset)
-{
- return vmalloc_to_page(pss->runtime->dma_area + offset);
-}
-/*****************************************************************************/
-
-static struct snd_pcm_ops easycap_alsa_pcm_ops = {
- .open = easycap_alsa_open,
- .close = easycap_alsa_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = easycap_alsa_hw_params,
- .hw_free = easycap_alsa_hw_free,
- .prepare = easycap_alsa_prepare,
- .ack = easycap_alsa_ack,
- .trigger = easycap_alsa_trigger,
- .pointer = easycap_alsa_pointer,
- .page = easycap_alsa_page,
-};
-
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * THE FUNCTION snd_card_create() HAS THIS_MODULE AS AN ARGUMENT. THIS
- * MEANS MODULE easycap. BEWARE.
-*/
-/*---------------------------------------------------------------------------*/
-int easycap_alsa_probe(struct easycap *peasycap)
-{
- int rc;
- struct snd_card *psnd_card;
- struct snd_pcm *psnd_pcm;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -ENODEV;
- }
- if (0 > peasycap->minor) {
- SAY("ERROR: no minor\n");
- return -ENODEV;
- }
-
- peasycap->alsa_hardware = alsa_hardware;
- if (peasycap->microphone) {
- peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_32000;
- peasycap->alsa_hardware.rate_min = 32000;
- peasycap->alsa_hardware.rate_max = 32000;
- } else {
- peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_48000;
- peasycap->alsa_hardware.rate_min = 48000;
- peasycap->alsa_hardware.rate_max = 48000;
- }
-
- if (0 != snd_card_create(SNDRV_DEFAULT_IDX1, "easycap_alsa",
- THIS_MODULE, 0, &psnd_card)) {
- SAY("ERROR: Cannot do ALSA snd_card_create()\n");
- return -EFAULT;
- }
-
- sprintf(&psnd_card->id[0], "EasyALSA%i", peasycap->minor);
- strcpy(&psnd_card->driver[0], EASYCAP_DRIVER_DESCRIPTION);
- strcpy(&psnd_card->shortname[0], "easycap_alsa");
- sprintf(&psnd_card->longname[0], "%s", &psnd_card->shortname[0]);
-
- psnd_card->dev = &peasycap->pusb_device->dev;
- psnd_card->private_data = peasycap;
- peasycap->psnd_card = psnd_card;
-
- rc = snd_pcm_new(psnd_card, "easycap_pcm", 0, 0, 1, &psnd_pcm);
- if (rc) {
- SAM("ERROR: Cannot do ALSA snd_pcm_new()\n");
- snd_card_free(psnd_card);
- return -EFAULT;
- }
-
- snd_pcm_set_ops(psnd_pcm, SNDRV_PCM_STREAM_CAPTURE,
- &easycap_alsa_pcm_ops);
- psnd_pcm->info_flags = 0;
- strcpy(&psnd_pcm->name[0], &psnd_card->id[0]);
- psnd_pcm->private_data = peasycap;
- peasycap->psnd_pcm = psnd_pcm;
- peasycap->psubstream = NULL;
-
- rc = snd_card_register(psnd_card);
- if (rc) {
- SAM("ERROR: Cannot do ALSA snd_card_register()\n");
- snd_card_free(psnd_card);
- return -EFAULT;
- }
-
- SAM("registered %s\n", &psnd_card->id[0]);
- return 0;
-}
-
-/*****************************************************************************/
-/*****************************************************************************/
-/*****************************************************************************/
-/*****************************************************************************/
-/*****************************************************************************/
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * COMMON AUDIO INITIALIZATION
- */
-/*---------------------------------------------------------------------------*/
-int
-easycap_sound_setup(struct easycap *peasycap)
-{
- int rc;
-
- JOM(4, "starting initialization\n");
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL.\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -ENODEV;
- }
- JOM(16, "0x%08lX=peasycap->pusb_device\n", (long int)peasycap->pusb_device);
-
- rc = audio_setup(peasycap);
- JOM(8, "audio_setup() returned %i\n", rc);
-
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device has become NULL\n");
- return -ENODEV;
- }
-/*---------------------------------------------------------------------------*/
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device has become NULL\n");
- return -ENODEV;
- }
- rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface,
- peasycap->audio_altsetting_on);
- JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface,
- peasycap->audio_altsetting_on, rc);
-
- rc = wakeup_device(peasycap->pusb_device);
- JOM(8, "wakeup_device() returned %i\n", rc);
-
- peasycap->audio_eof = 0;
- peasycap->audio_idle = 0;
-
- submit_audio_urbs(peasycap);
-
- JOM(4, "finished initialization\n");
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * SUBMIT ALL AUDIO URBS.
- */
-/*---------------------------------------------------------------------------*/
-int
-submit_audio_urbs(struct easycap *peasycap)
-{
- struct data_urb *pdata_urb;
- struct urb *purb;
- struct list_head *plist_head;
- int j, isbad, nospc, m, rc;
- int isbuf;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->purb_audio_head) {
- SAM("ERROR: peasycap->urb_audio_head uninitialized\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
-
- if (peasycap->audio_isoc_streaming) {
- JOM(4, "already streaming audio urbs\n");
- return 0;
- }
-
- JOM(4, "initial submission of all audio urbs\n");
- rc = usb_set_interface(peasycap->pusb_device,
- peasycap->audio_interface,
- peasycap->audio_altsetting_on);
- JOM(8, "usb_set_interface(.,%i,%i) returned %i\n",
- peasycap->audio_interface,
- peasycap->audio_altsetting_on, rc);
-
- isbad = 0;
- nospc = 0;
- m = 0;
- list_for_each(plist_head, peasycap->purb_audio_head) {
- pdata_urb = list_entry(plist_head, struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb) {
- purb = pdata_urb->purb;
- isbuf = pdata_urb->isbuf;
-
- purb->interval = 1;
- purb->dev = peasycap->pusb_device;
- purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
- peasycap->audio_endpointnumber);
- purb->transfer_flags = URB_ISO_ASAP;
- purb->transfer_buffer = peasycap->audio_isoc_buffer[isbuf].pgo;
- purb->transfer_buffer_length = peasycap->audio_isoc_buffer_size;
- purb->complete = easycap_alsa_complete;
- purb->context = peasycap;
- purb->start_frame = 0;
- purb->number_of_packets = peasycap->audio_isoc_framesperdesc;
- for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) {
- purb->iso_frame_desc[j].offset = j * peasycap->audio_isoc_maxframesize;
- purb->iso_frame_desc[j].length = peasycap->audio_isoc_maxframesize;
- }
-
- rc = usb_submit_urb(purb, GFP_KERNEL);
- if (rc) {
- isbad++;
- SAM("ERROR: usb_submit_urb() failed"
- " for urb with rc: -%s: %d\n",
- strerror(rc), rc);
- } else {
- m++;
- }
- } else {
- isbad++;
- }
- }
- if (nospc) {
- SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc);
- SAM("..... possibly inadequate USB bandwidth\n");
- peasycap->audio_eof = 1;
- }
- if (isbad) {
- JOM(4, "attempting cleanup instead of submitting\n");
- list_for_each(plist_head, (peasycap->purb_audio_head)) {
- pdata_urb = list_entry(plist_head, struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb)
- usb_kill_urb(pdata_urb->purb);
- }
- peasycap->audio_isoc_streaming = 0;
- } else {
- peasycap->audio_isoc_streaming = m;
- JOM(4, "submitted %i audio urbs\n", m);
- }
-
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * KILL ALL AUDIO URBS.
- */
-/*---------------------------------------------------------------------------*/
-int
-kill_audio_urbs(struct easycap *peasycap)
-{
- int m;
- struct list_head *plist_head;
- struct data_urb *pdata_urb;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
-
- if (!peasycap->audio_isoc_streaming) {
- JOM(8, "%i=audio_isoc_streaming, no audio urbs killed\n",
- peasycap->audio_isoc_streaming);
- return 0;
- }
-
- if (!peasycap->purb_audio_head) {
- SAM("ERROR: peasycap->purb_audio_head is NULL\n");
- return -EFAULT;
- }
-
- peasycap->audio_isoc_streaming = 0;
- JOM(4, "killing audio urbs\n");
- m = 0;
- list_for_each(plist_head, (peasycap->purb_audio_head)) {
- pdata_urb = list_entry(plist_head, struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb) {
- usb_kill_urb(pdata_urb->purb);
- m++;
- }
- }
- JOM(4, "%i audio urbs killed\n", m);
-
- return 0;
-}
-/*****************************************************************************/
+++ /dev/null
-/******************************************************************************
-* *
-* easycap_testcard.c *
-* *
-******************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This 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.
- *
- * The software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-
-#include "easycap.h"
-
-/*****************************************************************************/
-#define TESTCARD_BYTESPERLINE (2 * 720)
-void
-easycap_testcard(struct easycap *peasycap, int field)
-{
- int total;
- int y, u, v, r, g, b;
- unsigned char uyvy[4];
- int i1, line, k, m, n, more, much, barwidth, barheight;
- unsigned char bfbar[TESTCARD_BYTESPERLINE / 8], *p1, *p2;
- struct data_buffer *pfield_buffer;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return;
- }
- JOM(8, "%i=field\n", field);
- switch (peasycap->width) {
- case 720:
- case 360: {
- barwidth = (2 * 720) / 8;
- break;
- }
- case 704:
- case 352: {
- barwidth = (2 * 704) / 8;
- break;
- }
- case 640:
- case 320: {
- barwidth = (2 * 640) / 8;
- break;
- }
- default: {
- SAM("ERROR: cannot set barwidth\n");
- return;
- }
- }
- if (TESTCARD_BYTESPERLINE < barwidth) {
- SAM("ERROR: barwidth is too large\n");
- return;
- }
- switch (peasycap->height) {
- case 576:
- case 288: {
- barheight = 576;
- break;
- }
- case 480:
- case 240: {
- barheight = 480;
- break;
- }
- default: {
- SAM("ERROR: cannot set barheight\n");
- return;
- }
- }
- total = 0;
- k = field;
- m = 0;
- n = 0;
-
- for (line = 0; line < (barheight / 2); line++) {
- for (i1 = 0; i1 < 8; i1++) {
- r = (i1 * 256)/8;
- g = (i1 * 256)/8;
- b = (i1 * 256)/8;
-
- y = 299*r/1000 + 587*g/1000 + 114*b/1000 ;
- u = -147*r/1000 - 289*g/1000 + 436*b/1000 ;
- u = u + 128;
- v = 615*r/1000 - 515*g/1000 - 100*b/1000 ;
- v = v + 128;
-
- uyvy[0] = 0xFF & u ;
- uyvy[1] = 0xFF & y ;
- uyvy[2] = 0xFF & v ;
- uyvy[3] = 0xFF & y ;
-
- p1 = &bfbar[0];
- while (p1 < &bfbar[barwidth]) {
- *p1++ = uyvy[0] ;
- *p1++ = uyvy[1] ;
- *p1++ = uyvy[2] ;
- *p1++ = uyvy[3] ;
- total += 4;
- }
-
- p1 = &bfbar[0];
- more = barwidth;
-
- while (more) {
- if ((FIELD_BUFFER_SIZE/PAGE_SIZE) <= m) {
- SAM("ERROR: bad m reached\n");
- return;
- }
- if (PAGE_SIZE < n) {
- SAM("ERROR: bad n reached\n");
- return;
- }
-
- if (0 > more) {
- SAM("ERROR: internal fault\n");
- return;
- }
-
- much = PAGE_SIZE - n;
- if (much > more)
- much = more;
- pfield_buffer = &peasycap->field_buffer[k][m];
- p2 = pfield_buffer->pgo + n;
- memcpy(p2, p1, much);
-
- p1 += much;
- n += much;
- more -= much;
- if (PAGE_SIZE == n) {
- m++;
- n = 0;
- }
- }
- }
- }
- return;
-}
+++ /dev/null
-config VIDEO_GO7007
- tristate "WIS GO7007 MPEG encoder support"
- depends on VIDEO_DEV && PCI && I2C
- depends on SND
- select VIDEOBUF_DMA_SG
- depends on RC_CORE
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- select SND_PCM
- select CRC32
- default N
- ---help---
- This is a video4linux driver for the WIS GO7007 MPEG
- encoder chip.
-
- To compile this driver as a module, choose M here: the
- module will be called go7007
-
-config VIDEO_GO7007_USB
- tristate "WIS GO7007 USB support"
- depends on VIDEO_GO7007 && USB
- default N
- ---help---
- This is a video4linux driver for the WIS GO7007 MPEG
- encoder chip over USB.
-
- To compile this driver as a module, choose M here: the
- module will be called go7007-usb
-
-config VIDEO_GO7007_USB_S2250_BOARD
- tristate "Sensoray 2250/2251 support"
- depends on VIDEO_GO7007_USB && DVB_USB
- default N
- ---help---
- This is a video4linux driver for the Sensoray 2250/2251 device.
-
- To compile this driver as a module, choose M here: the
- module will be called s2250
-
-config VIDEO_GO7007_OV7640
- tristate "OV7640 subdev support"
- depends on VIDEO_GO7007
- default N
- ---help---
- This is a video4linux driver for the OV7640 sub-device.
-
- To compile this driver as a module, choose M here: the
- module will be called wis-ov7640
-
-config VIDEO_GO7007_SAA7113
- tristate "SAA7113 subdev support"
- depends on VIDEO_GO7007
- default N
- ---help---
- This is a video4linux driver for the SAA7113 sub-device.
-
- To compile this driver as a module, choose M here: the
- module will be called wis-saa7113
-
-config VIDEO_GO7007_SAA7115
- tristate "SAA7115 subdev support"
- depends on VIDEO_GO7007
- default N
- ---help---
- This is a video4linux driver for the SAA7115 sub-device.
-
- To compile this driver as a module, choose M here: the
- module will be called wis-saa7115
-
-config VIDEO_GO7007_TW9903
- tristate "TW9903 subdev support"
- depends on VIDEO_GO7007
- default N
- ---help---
- This is a video4linux driver for the TW9903 sub-device.
-
- To compile this driver as a module, choose M here: the
- module will be called wis-tw9903
-
-config VIDEO_GO7007_UDA1342
- tristate "UDA1342 subdev support"
- depends on VIDEO_GO7007
- default N
- ---help---
- This is a video4linux driver for the UDA1342 sub-device.
-
- To compile this driver as a module, choose M here: the
- module will be called wis-uda1342
-
-config VIDEO_GO7007_SONY_TUNER
- tristate "Sony tuner subdev support"
- depends on VIDEO_GO7007
- default N
- ---help---
- This is a video4linux driver for the Sony Tuner sub-device.
-
- To compile this driver as a module, choose M here: the
- module will be called wis-sony-tuner
-
-config VIDEO_GO7007_TW2804
- tristate "TW2804 subdev support"
- depends on VIDEO_GO7007
- default N
- ---help---
- This is a video4linux driver for the TW2804 sub-device.
-
- To compile this driver as a module, choose M here: the
- module will be called wis-tw2804
-
+++ /dev/null
-#obj-m += go7007.o go7007-usb.o snd-go7007.o wis-saa7115.o wis-tw9903.o \
- wis-uda1342.o wis-sony-tuner.o wis-saa7113.o wis-ov7640.o \
- wis-tw2804.o
-
-
-obj-$(CONFIG_VIDEO_GO7007) += go7007.o
-obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o
-obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o s2250-loader.o
-obj-$(CONFIG_VIDEO_GO7007_SAA7113) += wis-saa7113.o
-obj-$(CONFIG_VIDEO_GO7007_OV7640) += wis-ov7640.o
-obj-$(CONFIG_VIDEO_GO7007_SAA7115) += wis-saa7115.o
-obj-$(CONFIG_VIDEO_GO7007_TW9903) += wis-tw9903.o
-obj-$(CONFIG_VIDEO_GO7007_UDA1342) += wis-uda1342.o
-obj-$(CONFIG_VIDEO_GO7007_SONY_TUNER) += wis-sony-tuner.o
-obj-$(CONFIG_VIDEO_GO7007_TW2804) += wis-tw2804.o
-
-go7007-y := go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \
- snd-go7007.o
-
-s2250-y := s2250-board.o
-
-# Uncomment when the saa7134 patches get into upstream
-#obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o
-#ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/video/saa7134 -DSAA7134_MPEG_GO7007=3
-
-# S2250 needs cypress ezusb loader from dvb-usb
-ccflags-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD:m=y) += -Idrivers/media/dvb/dvb-usb
-
-ccflags-y += -Idrivers/media/dvb/frontends
-ccflags-y += -Idrivers/media/dvb/dvb-core
+++ /dev/null
-Todo:
- - checkpatch.pl cleanups
- - sparse cleanups
- - lots of little modules, should be merged together
- and added to the build.
- - testing?
- - handle churn in v4l layer.
-
-Please send patchs to Greg Kroah-Hartman <greg@kroah.com> and Cc: Ross
-Cohen <rcohen@snurgle.org> as well.
-
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/unistd.h>
-#include <linux/time.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/firmware.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-#include <linux/slab.h>
-#include <asm/system.h>
-#include <linux/videodev2.h>
-#include <media/tuner.h>
-#include <media/v4l2-common.h>
-
-#include "go7007-priv.h"
-#include "wis-i2c.h"
-
-/*
- * Wait for an interrupt to be delivered from the GO7007SB and return
- * the associated value and data.
- *
- * Must be called with the hw_lock held.
- */
-int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data)
-{
- go->interrupt_available = 0;
- go->hpi_ops->read_interrupt(go);
- if (wait_event_timeout(go->interrupt_waitq,
- go->interrupt_available, 5*HZ) < 0) {
- v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n");
- return -1;
- }
- if (!go->interrupt_available)
- return -1;
- go->interrupt_available = 0;
- *value = go->interrupt_value & 0xfffe;
- *data = go->interrupt_data;
- return 0;
-}
-EXPORT_SYMBOL(go7007_read_interrupt);
-
-/*
- * Read a register/address on the GO7007SB.
- *
- * Must be called with the hw_lock held.
- */
-int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data)
-{
- int count = 100;
- u16 value;
-
- if (go7007_write_interrupt(go, 0x0010, addr) < 0)
- return -EIO;
- while (count-- > 0) {
- if (go7007_read_interrupt(go, &value, data) == 0 &&
- value == 0xa000)
- return 0;
- }
- return -EIO;
-}
-EXPORT_SYMBOL(go7007_read_addr);
-
-/*
- * Send the boot firmware to the encoder, which just wakes it up and lets
- * us talk to the GPIO pins and on-board I2C adapter.
- *
- * Must be called with the hw_lock held.
- */
-static int go7007_load_encoder(struct go7007 *go)
-{
- const struct firmware *fw_entry;
- char fw_name[] = "go7007fw.bin";
- void *bounce;
- int fw_len, rv = 0;
- u16 intr_val, intr_data;
-
- if (request_firmware(&fw_entry, fw_name, go->dev)) {
- v4l2_err(go, "unable to load firmware from file "
- "\"%s\"\n", fw_name);
- return -1;
- }
- if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) {
- v4l2_err(go, "file \"%s\" does not appear to be "
- "go7007 firmware\n", fw_name);
- release_firmware(fw_entry);
- return -1;
- }
- fw_len = fw_entry->size - 16;
- bounce = kmalloc(fw_len, GFP_KERNEL);
- if (bounce == NULL) {
- v4l2_err(go, "unable to allocate %d bytes for "
- "firmware transfer\n", fw_len);
- release_firmware(fw_entry);
- return -1;
- }
- memcpy(bounce, fw_entry->data + 16, fw_len);
- release_firmware(fw_entry);
- if (go7007_interface_reset(go) < 0 ||
- go7007_send_firmware(go, bounce, fw_len) < 0 ||
- go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
- (intr_val & ~0x1) != 0x5a5a) {
- v4l2_err(go, "error transferring firmware\n");
- rv = -1;
- }
- kfree(bounce);
- return rv;
-}
-
-MODULE_FIRMWARE("go7007fw.bin");
-
-/*
- * Boot the encoder and register the I2C adapter if requested. Do the
- * minimum initialization necessary, since the board-specific code may
- * still need to probe the board ID.
- *
- * Must NOT be called with the hw_lock held.
- */
-int go7007_boot_encoder(struct go7007 *go, int init_i2c)
-{
- int ret;
-
- mutex_lock(&go->hw_lock);
- ret = go7007_load_encoder(go);
- mutex_unlock(&go->hw_lock);
- if (ret < 0)
- return -1;
- if (!init_i2c)
- return 0;
- if (go7007_i2c_init(go) < 0)
- return -1;
- go->i2c_adapter_online = 1;
- return 0;
-}
-EXPORT_SYMBOL(go7007_boot_encoder);
-
-/*
- * Configure any hardware-related registers in the GO7007, such as GPIO
- * pins and bus parameters, which are board-specific. This assumes
- * the boot firmware has already been downloaded.
- *
- * Must be called with the hw_lock held.
- */
-static int go7007_init_encoder(struct go7007 *go)
-{
- if (go->board_info->audio_flags & GO7007_AUDIO_I2S_MASTER) {
- go7007_write_addr(go, 0x1000, 0x0811);
- go7007_write_addr(go, 0x1000, 0x0c11);
- }
- if (go->board_id == GO7007_BOARDID_MATRIX_REV) {
- /* Set GPIO pin 0 to be an output (audio clock control) */
- go7007_write_addr(go, 0x3c82, 0x0001);
- go7007_write_addr(go, 0x3c80, 0x00fe);
- }
- return 0;
-}
-
-/*
- * Send the boot firmware to the GO7007 and configure the registers. This
- * is the only way to stop the encoder once it has started streaming video.
- *
- * Must be called with the hw_lock held.
- */
-int go7007_reset_encoder(struct go7007 *go)
-{
- if (go7007_load_encoder(go) < 0)
- return -1;
- return go7007_init_encoder(go);
-}
-
-/*
- * Attempt to instantiate an I2C client by ID, probably loading a module.
- */
-static int init_i2c_module(struct i2c_adapter *adapter, const char *type,
- int addr)
-{
- struct go7007 *go = i2c_get_adapdata(adapter);
- struct v4l2_device *v4l2_dev = &go->v4l2_dev;
-
- if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL))
- return 0;
-
- printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type);
- return -1;
-}
-
-/*
- * Finalize the GO7007 hardware setup, register the on-board I2C adapter
- * (if used on this board), load the I2C client driver for the sensor
- * (SAA7115 or whatever) and other devices, and register the ALSA and V4L2
- * interfaces.
- *
- * Must NOT be called with the hw_lock held.
- */
-int go7007_register_encoder(struct go7007 *go)
-{
- int i, ret;
-
- printk(KERN_INFO "go7007: registering new %s\n", go->name);
-
- mutex_lock(&go->hw_lock);
- ret = go7007_init_encoder(go);
- mutex_unlock(&go->hw_lock);
- if (ret < 0)
- return -1;
-
- /* v4l2 init must happen before i2c subdevs */
- ret = go7007_v4l2_init(go);
- if (ret < 0)
- return ret;
-
- if (!go->i2c_adapter_online &&
- go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) {
- if (go7007_i2c_init(go) < 0)
- return -1;
- go->i2c_adapter_online = 1;
- }
- if (go->i2c_adapter_online) {
- for (i = 0; i < go->board_info->num_i2c_devs; ++i)
- init_i2c_module(&go->i2c_adapter,
- go->board_info->i2c_devs[i].type,
- go->board_info->i2c_devs[i].addr);
- if (go->board_id == GO7007_BOARDID_ADLINK_MPG24)
- i2c_clients_command(&go->i2c_adapter,
- DECODER_SET_CHANNEL, &go->channel_number);
- }
- if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) {
- go->audio_enabled = 1;
- go7007_snd_init(go);
- }
- return 0;
-}
-EXPORT_SYMBOL(go7007_register_encoder);
-
-/*
- * Send the encode firmware to the encoder, which will cause it
- * to immediately start delivering the video and audio streams.
- *
- * Must be called with the hw_lock held.
- */
-int go7007_start_encoder(struct go7007 *go)
-{
- u8 *fw;
- int fw_len, rv = 0, i;
- u16 intr_val, intr_data;
-
- go->modet_enable = 0;
- if (!go->dvd_mode)
- for (i = 0; i < 4; ++i) {
- if (go->modet[i].enable) {
- go->modet_enable = 1;
- continue;
- }
- go->modet[i].pixel_threshold = 32767;
- go->modet[i].motion_threshold = 32767;
- go->modet[i].mb_threshold = 32767;
- }
-
- if (go7007_construct_fw_image(go, &fw, &fw_len) < 0)
- return -1;
-
- if (go7007_send_firmware(go, fw, fw_len) < 0 ||
- go7007_read_interrupt(go, &intr_val, &intr_data) < 0) {
- v4l2_err(&go->v4l2_dev, "error transferring firmware\n");
- rv = -1;
- goto start_error;
- }
-
- go->state = STATE_DATA;
- go->parse_length = 0;
- go->seen_frame = 0;
- if (go7007_stream_start(go) < 0) {
- v4l2_err(&go->v4l2_dev, "error starting stream transfer\n");
- rv = -1;
- goto start_error;
- }
-
-start_error:
- kfree(fw);
- return rv;
-}
-
-/*
- * Store a byte in the current video buffer, if there is one.
- */
-static inline void store_byte(struct go7007_buffer *gobuf, u8 byte)
-{
- if (gobuf != NULL && gobuf->bytesused < GO7007_BUF_SIZE) {
- unsigned int pgidx = gobuf->offset >> PAGE_SHIFT;
- unsigned int pgoff = gobuf->offset & ~PAGE_MASK;
-
- *((u8 *)page_address(gobuf->pages[pgidx]) + pgoff) = byte;
- ++gobuf->offset;
- ++gobuf->bytesused;
- }
-}
-
-/*
- * Deliver the last video buffer and get a new one to start writing to.
- */
-static void frame_boundary(struct go7007 *go)
-{
- struct go7007_buffer *gobuf;
- int i;
-
- if (go->active_buf) {
- if (go->active_buf->modet_active) {
- if (go->active_buf->bytesused + 216 < GO7007_BUF_SIZE) {
- for (i = 0; i < 216; ++i)
- store_byte(go->active_buf,
- go->active_map[i]);
- go->active_buf->bytesused -= 216;
- } else
- go->active_buf->modet_active = 0;
- }
- go->active_buf->state = BUF_STATE_DONE;
- wake_up_interruptible(&go->frame_waitq);
- go->active_buf = NULL;
- }
- list_for_each_entry(gobuf, &go->stream, stream)
- if (gobuf->state == BUF_STATE_QUEUED) {
- gobuf->seq = go->next_seq;
- do_gettimeofday(&gobuf->timestamp);
- go->active_buf = gobuf;
- break;
- }
- ++go->next_seq;
-}
-
-static void write_bitmap_word(struct go7007 *go)
-{
- int x, y, i, stride = ((go->width >> 4) + 7) >> 3;
-
- for (i = 0; i < 16; ++i) {
- y = (((go->parse_length - 1) << 3) + i) / (go->width >> 4);
- x = (((go->parse_length - 1) << 3) + i) % (go->width >> 4);
- if (stride * y + (x >> 3) < sizeof(go->active_map))
- go->active_map[stride * y + (x >> 3)] |=
- (go->modet_word & 1) << (x & 0x7);
- go->modet_word >>= 1;
- }
-}
-
-/*
- * Parse a chunk of the video stream into frames. The frames are not
- * delimited by the hardware, so we have to parse the frame boundaries
- * based on the type of video stream we're receiving.
- */
-void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length)
-{
- int i, seq_start_code = -1, frame_start_code = -1;
-
- spin_lock(&go->spinlock);
-
- switch (go->format) {
- case GO7007_FORMAT_MPEG4:
- seq_start_code = 0xB0;
- frame_start_code = 0xB6;
- break;
- case GO7007_FORMAT_MPEG1:
- case GO7007_FORMAT_MPEG2:
- seq_start_code = 0xB3;
- frame_start_code = 0x00;
- break;
- }
-
- for (i = 0; i < length; ++i) {
- if (go->active_buf != NULL &&
- go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) {
- v4l2_info(&go->v4l2_dev, "dropping oversized frame\n");
- go->active_buf->offset -= go->active_buf->bytesused;
- go->active_buf->bytesused = 0;
- go->active_buf->modet_active = 0;
- go->active_buf = NULL;
- }
-
- switch (go->state) {
- case STATE_DATA:
- switch (buf[i]) {
- case 0x00:
- go->state = STATE_00;
- break;
- case 0xFF:
- go->state = STATE_FF;
- break;
- default:
- store_byte(go->active_buf, buf[i]);
- break;
- }
- break;
- case STATE_00:
- switch (buf[i]) {
- case 0x00:
- go->state = STATE_00_00;
- break;
- case 0xFF:
- store_byte(go->active_buf, 0x00);
- go->state = STATE_FF;
- break;
- default:
- store_byte(go->active_buf, 0x00);
- store_byte(go->active_buf, buf[i]);
- go->state = STATE_DATA;
- break;
- }
- break;
- case STATE_00_00:
- switch (buf[i]) {
- case 0x00:
- store_byte(go->active_buf, 0x00);
- /* go->state remains STATE_00_00 */
- break;
- case 0x01:
- go->state = STATE_00_00_01;
- break;
- case 0xFF:
- store_byte(go->active_buf, 0x00);
- store_byte(go->active_buf, 0x00);
- go->state = STATE_FF;
- break;
- default:
- store_byte(go->active_buf, 0x00);
- store_byte(go->active_buf, 0x00);
- store_byte(go->active_buf, buf[i]);
- go->state = STATE_DATA;
- break;
- }
- break;
- case STATE_00_00_01:
- if (buf[i] == 0xF8 && go->modet_enable == 0) {
- /* MODET start code, but MODET not enabled */
- store_byte(go->active_buf, 0x00);
- store_byte(go->active_buf, 0x00);
- store_byte(go->active_buf, 0x01);
- store_byte(go->active_buf, 0xF8);
- go->state = STATE_DATA;
- break;
- }
- /* If this is the start of a new MPEG frame,
- * get a new buffer */
- if ((go->format == GO7007_FORMAT_MPEG1 ||
- go->format == GO7007_FORMAT_MPEG2 ||
- go->format == GO7007_FORMAT_MPEG4) &&
- (buf[i] == seq_start_code ||
- buf[i] == 0xB8 || /* GOP code */
- buf[i] == frame_start_code)) {
- if (go->active_buf == NULL || go->seen_frame)
- frame_boundary(go);
- if (buf[i] == frame_start_code) {
- if (go->active_buf != NULL)
- go->active_buf->frame_offset =
- go->active_buf->offset;
- go->seen_frame = 1;
- } else {
- go->seen_frame = 0;
- }
- }
- /* Handle any special chunk types, or just write the
- * start code to the (potentially new) buffer */
- switch (buf[i]) {
- case 0xF5: /* timestamp */
- go->parse_length = 12;
- go->state = STATE_UNPARSED;
- break;
- case 0xF6: /* vbi */
- go->state = STATE_VBI_LEN_A;
- break;
- case 0xF8: /* MD map */
- go->parse_length = 0;
- memset(go->active_map, 0,
- sizeof(go->active_map));
- go->state = STATE_MODET_MAP;
- break;
- case 0xFF: /* Potential JPEG start code */
- store_byte(go->active_buf, 0x00);
- store_byte(go->active_buf, 0x00);
- store_byte(go->active_buf, 0x01);
- go->state = STATE_FF;
- break;
- default:
- store_byte(go->active_buf, 0x00);
- store_byte(go->active_buf, 0x00);
- store_byte(go->active_buf, 0x01);
- store_byte(go->active_buf, buf[i]);
- go->state = STATE_DATA;
- break;
- }
- break;
- case STATE_FF:
- switch (buf[i]) {
- case 0x00:
- store_byte(go->active_buf, 0xFF);
- go->state = STATE_00;
- break;
- case 0xFF:
- store_byte(go->active_buf, 0xFF);
- /* go->state remains STATE_FF */
- break;
- case 0xD8:
- if (go->format == GO7007_FORMAT_MJPEG)
- frame_boundary(go);
- /* fall through */
- default:
- store_byte(go->active_buf, 0xFF);
- store_byte(go->active_buf, buf[i]);
- go->state = STATE_DATA;
- break;
- }
- break;
- case STATE_VBI_LEN_A:
- go->parse_length = buf[i] << 8;
- go->state = STATE_VBI_LEN_B;
- break;
- case STATE_VBI_LEN_B:
- go->parse_length |= buf[i];
- if (go->parse_length > 0)
- go->state = STATE_UNPARSED;
- else
- go->state = STATE_DATA;
- break;
- case STATE_MODET_MAP:
- if (go->parse_length < 204) {
- if (go->parse_length & 1) {
- go->modet_word |= buf[i];
- write_bitmap_word(go);
- } else
- go->modet_word = buf[i] << 8;
- } else if (go->parse_length == 207 && go->active_buf) {
- go->active_buf->modet_active = buf[i];
- }
- if (++go->parse_length == 208)
- go->state = STATE_DATA;
- break;
- case STATE_UNPARSED:
- if (--go->parse_length == 0)
- go->state = STATE_DATA;
- break;
- }
- }
-
- spin_unlock(&go->spinlock);
-}
-EXPORT_SYMBOL(go7007_parse_video_stream);
-
-/*
- * Allocate a new go7007 struct. Used by the hardware-specific probe.
- */
-struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev)
-{
- struct go7007 *go;
- int i;
-
- go = kmalloc(sizeof(struct go7007), GFP_KERNEL);
- if (go == NULL)
- return NULL;
- go->dev = dev;
- go->board_info = board;
- go->board_id = 0;
- go->tuner_type = -1;
- go->channel_number = 0;
- go->name[0] = 0;
- mutex_init(&go->hw_lock);
- init_waitqueue_head(&go->frame_waitq);
- spin_lock_init(&go->spinlock);
- go->video_dev = NULL;
- go->ref_count = 0;
- go->status = STATUS_INIT;
- memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter));
- go->i2c_adapter_online = 0;
- go->interrupt_available = 0;
- init_waitqueue_head(&go->interrupt_waitq);
- go->in_use = 0;
- go->input = 0;
- if (board->sensor_flags & GO7007_SENSOR_TV) {
- go->standard = GO7007_STD_NTSC;
- go->width = 720;
- go->height = 480;
- go->sensor_framerate = 30000;
- } else {
- go->standard = GO7007_STD_OTHER;
- go->width = board->sensor_width;
- go->height = board->sensor_height;
- go->sensor_framerate = board->sensor_framerate;
- }
- go->encoder_v_offset = board->sensor_v_offset;
- go->encoder_h_offset = board->sensor_h_offset;
- go->encoder_h_halve = 0;
- go->encoder_v_halve = 0;
- go->encoder_subsample = 0;
- go->streaming = 0;
- go->format = GO7007_FORMAT_MJPEG;
- go->bitrate = 1500000;
- go->fps_scale = 1;
- go->pali = 0;
- go->aspect_ratio = GO7007_RATIO_1_1;
- go->gop_size = 0;
- go->ipb = 0;
- go->closed_gop = 0;
- go->repeat_seqhead = 0;
- go->seq_header_enable = 0;
- go->gop_header_enable = 0;
- go->dvd_mode = 0;
- go->interlace_coding = 0;
- for (i = 0; i < 4; ++i)
- go->modet[i].enable = 0;
- for (i = 0; i < 1624; ++i)
- go->modet_map[i] = 0;
- go->audio_deliver = NULL;
- go->audio_enabled = 0;
- INIT_LIST_HEAD(&go->stream);
-
- return go;
-}
-EXPORT_SYMBOL(go7007_alloc);
-
-/*
- * Detach and unregister the encoder. The go7007 struct won't be freed
- * until v4l2 finishes releasing its resources and all associated fds are
- * closed by applications.
- */
-void go7007_remove(struct go7007 *go)
-{
- if (go->i2c_adapter_online) {
- if (i2c_del_adapter(&go->i2c_adapter) == 0)
- go->i2c_adapter_online = 0;
- else
- v4l2_err(&go->v4l2_dev,
- "error removing I2C adapter!\n");
- }
-
- if (go->audio_enabled)
- go7007_snd_remove(go);
- go7007_v4l2_remove(go);
-}
-EXPORT_SYMBOL(go7007_remove);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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.
- */
-
-/*
- * This file contains code to generate a firmware image for the GO7007SB
- * encoder. Much of the firmware is read verbatim from a file, but some of
- * it concerning bitrate control and other things that can be configured at
- * run-time are generated dynamically. Note that the format headers
- * generated here do not affect the functioning of the encoder; they are
- * merely parroted back to the host at the start of each frame.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/time.h>
-#include <linux/mm.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/firmware.h>
-#include <linux/slab.h>
-#include <asm/byteorder.h>
-
-#include "go7007-priv.h"
-
-/* Constants used in the source firmware image to describe code segments */
-
-#define FLAG_MODE_MJPEG (1)
-#define FLAG_MODE_MPEG1 (1<<1)
-#define FLAG_MODE_MPEG2 (1<<2)
-#define FLAG_MODE_MPEG4 (1<<3)
-#define FLAG_MODE_H263 (1<<4)
-#define FLAG_MODE_ALL (FLAG_MODE_MJPEG | FLAG_MODE_MPEG1 | \
- FLAG_MODE_MPEG2 | FLAG_MODE_MPEG4 | \
- FLAG_MODE_H263)
-#define FLAG_SPECIAL (1<<8)
-
-#define SPECIAL_FRM_HEAD 0
-#define SPECIAL_BRC_CTRL 1
-#define SPECIAL_CONFIG 2
-#define SPECIAL_SEQHEAD 3
-#define SPECIAL_AV_SYNC 4
-#define SPECIAL_FINAL 5
-#define SPECIAL_AUDIO 6
-#define SPECIAL_MODET 7
-
-/* Little data class for creating MPEG headers bit-by-bit */
-
-struct code_gen {
- unsigned char *p; /* destination */
- u32 a; /* collects bits at the top of the variable */
- int b; /* bit position of most recently-written bit */
- int len; /* written out so far */
-};
-
-#define CODE_GEN(name, dest) struct code_gen name = { dest, 0, 32, 0 }
-
-#define CODE_ADD(name, val, length) do { \
- name.b -= (length); \
- name.a |= (val) << name.b; \
- while (name.b <= 24) { \
- *name.p = name.a >> 24; \
- ++name.p; \
- name.a <<= 8; \
- name.b += 8; \
- name.len += 8; \
- } \
-} while (0)
-
-#define CODE_LENGTH(name) (name.len + (32 - name.b))
-
-/* Tables for creating the bitrate control data */
-
-static const s16 converge_speed_ip[101] = {
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
- 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
- 5, 5, 5, 6, 6, 6, 7, 7, 8, 8,
- 9, 10, 10, 11, 12, 13, 14, 15, 16, 17,
- 19, 20, 22, 23, 25, 27, 30, 32, 35, 38,
- 41, 45, 49, 53, 58, 63, 69, 76, 83, 91,
- 100
-};
-
-static const s16 converge_speed_ipb[101] = {
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 5, 5, 5, 5, 5, 6,
- 6, 6, 6, 7, 7, 7, 7, 8, 8, 9,
- 9, 9, 10, 10, 11, 12, 12, 13, 14, 14,
- 15, 16, 17, 18, 19, 20, 22, 23, 25, 26,
- 28, 30, 32, 34, 37, 40, 42, 46, 49, 53,
- 57, 61, 66, 71, 77, 83, 90, 97, 106, 115,
- 125, 135, 147, 161, 175, 191, 209, 228, 249, 273,
- 300
-};
-
-static const s16 LAMBDA_table[4][101] = {
- { 16, 16, 16, 16, 17, 17, 17, 18, 18, 18,
- 19, 19, 19, 20, 20, 20, 21, 21, 22, 22,
- 22, 23, 23, 24, 24, 25, 25, 25, 26, 26,
- 27, 27, 28, 28, 29, 29, 30, 31, 31, 32,
- 32, 33, 33, 34, 35, 35, 36, 37, 37, 38,
- 39, 39, 40, 41, 42, 42, 43, 44, 45, 46,
- 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
- 67, 68, 69, 70, 72, 73, 74, 76, 77, 78,
- 80, 81, 83, 84, 86, 87, 89, 90, 92, 94,
- 96
- },
- {
- 20, 20, 20, 21, 21, 21, 22, 22, 23, 23,
- 23, 24, 24, 25, 25, 26, 26, 27, 27, 28,
- 28, 29, 29, 30, 30, 31, 31, 32, 33, 33,
- 34, 34, 35, 36, 36, 37, 38, 38, 39, 40,
- 40, 41, 42, 43, 43, 44, 45, 46, 47, 48,
- 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- 58, 59, 60, 61, 62, 64, 65, 66, 67, 68,
- 70, 71, 72, 73, 75, 76, 78, 79, 80, 82,
- 83, 85, 86, 88, 90, 91, 93, 95, 96, 98,
- 100, 102, 103, 105, 107, 109, 111, 113, 115, 117,
- 120
- },
- {
- 24, 24, 24, 25, 25, 26, 26, 27, 27, 28,
- 28, 29, 29, 30, 30, 31, 31, 32, 33, 33,
- 34, 34, 35, 36, 36, 37, 38, 38, 39, 40,
- 41, 41, 42, 43, 44, 44, 45, 46, 47, 48,
- 49, 50, 50, 51, 52, 53, 54, 55, 56, 57,
- 58, 59, 60, 62, 63, 64, 65, 66, 67, 69,
- 70, 71, 72, 74, 75, 76, 78, 79, 81, 82,
- 84, 85, 87, 88, 90, 92, 93, 95, 97, 98,
- 100, 102, 104, 106, 108, 110, 112, 114, 116, 118,
- 120, 122, 124, 127, 129, 131, 134, 136, 138, 141,
- 144
- },
- {
- 32, 32, 33, 33, 34, 34, 35, 36, 36, 37,
- 38, 38, 39, 40, 41, 41, 42, 43, 44, 44,
- 45, 46, 47, 48, 49, 50, 50, 51, 52, 53,
- 54, 55, 56, 57, 58, 59, 60, 62, 63, 64,
- 65, 66, 67, 69, 70, 71, 72, 74, 75, 76,
- 78, 79, 81, 82, 84, 85, 87, 88, 90, 92,
- 93, 95, 97, 98, 100, 102, 104, 106, 108, 110,
- 112, 114, 116, 118, 120, 122, 124, 127, 129, 131,
- 134, 136, 139, 141, 144, 146, 149, 152, 154, 157,
- 160, 163, 166, 169, 172, 175, 178, 181, 185, 188,
- 192
- }
-};
-
-/* MPEG blank frame generation tables */
-
-enum mpeg_frame_type {
- PFRAME,
- BFRAME_PRE,
- BFRAME_POST,
- BFRAME_BIDIR,
- BFRAME_EMPTY
-};
-
-static const u32 addrinctab[33][2] = {
- { 0x01, 1 }, { 0x03, 3 }, { 0x02, 3 }, { 0x03, 4 },
- { 0x02, 4 }, { 0x03, 5 }, { 0x02, 5 }, { 0x07, 7 },
- { 0x06, 7 }, { 0x0b, 8 }, { 0x0a, 8 }, { 0x09, 8 },
- { 0x08, 8 }, { 0x07, 8 }, { 0x06, 8 }, { 0x17, 10 },
- { 0x16, 10 }, { 0x15, 10 }, { 0x14, 10 }, { 0x13, 10 },
- { 0x12, 10 }, { 0x23, 11 }, { 0x22, 11 }, { 0x21, 11 },
- { 0x20, 11 }, { 0x1f, 11 }, { 0x1e, 11 }, { 0x1d, 11 },
- { 0x1c, 11 }, { 0x1b, 11 }, { 0x1a, 11 }, { 0x19, 11 },
- { 0x18, 11 }
-};
-
-/* Standard JPEG tables */
-
-static const u8 default_intra_quant_table[] = {
- 8, 16, 19, 22, 26, 27, 29, 34,
- 16, 16, 22, 24, 27, 29, 34, 37,
- 19, 22, 26, 27, 29, 34, 34, 38,
- 22, 22, 26, 27, 29, 34, 37, 40,
- 22, 26, 27, 29, 32, 35, 40, 48,
- 26, 27, 29, 32, 35, 40, 48, 58,
- 26, 27, 29, 34, 38, 46, 56, 69,
- 27, 29, 35, 38, 46, 56, 69, 83
-};
-
-static const u8 bits_dc_luminance[] = {
- 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
-};
-
-static const u8 val_dc_luminance[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
-};
-
-static const u8 bits_dc_chrominance[] = {
- 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
-};
-
-static const u8 val_dc_chrominance[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
-};
-
-static const u8 bits_ac_luminance[] = {
- 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
-};
-
-static const u8 val_ac_luminance[] = {
- 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
-};
-
-static const u8 bits_ac_chrominance[] = {
- 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
-};
-
-static const u8 val_ac_chrominance[] = {
- 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
-};
-
-/* Zig-zag mapping for quant table
- *
- * OK, let's do this mapping on the actual table above so it doesn't have
- * to be done on the fly.
- */
-static const int zz[64] = {
- 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
- 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
- 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
- 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
-};
-
-static int copy_packages(__le16 *dest, u16 *src, int pkg_cnt, int space)
-{
- int i, cnt = pkg_cnt * 32;
-
- if (space < cnt)
- return -1;
-
- for (i = 0; i < cnt; ++i)
- dest[i] = cpu_to_le16p(src + i);
-
- return cnt;
-}
-
-static int mjpeg_frame_header(struct go7007 *go, unsigned char *buf, int q)
-{
- int i, p = 0;
-
- buf[p++] = 0xff;
- buf[p++] = 0xd8;
- buf[p++] = 0xff;
- buf[p++] = 0xdb;
- buf[p++] = 0;
- buf[p++] = 2 + 65;
- buf[p++] = 0;
- buf[p++] = default_intra_quant_table[0];
- for (i = 1; i < 64; ++i)
- /* buf[p++] = (default_intra_quant_table[i] * q) >> 3; */
- buf[p++] = (default_intra_quant_table[zz[i]] * q) >> 3;
- buf[p++] = 0xff;
- buf[p++] = 0xc0;
- buf[p++] = 0;
- buf[p++] = 17;
- buf[p++] = 8;
- buf[p++] = go->height >> 8;
- buf[p++] = go->height & 0xff;
- buf[p++] = go->width >> 8;
- buf[p++] = go->width & 0xff;
- buf[p++] = 3;
- buf[p++] = 1;
- buf[p++] = 0x22;
- buf[p++] = 0;
- buf[p++] = 2;
- buf[p++] = 0x11;
- buf[p++] = 0;
- buf[p++] = 3;
- buf[p++] = 0x11;
- buf[p++] = 0;
- buf[p++] = 0xff;
- buf[p++] = 0xc4;
- buf[p++] = 418 >> 8;
- buf[p++] = 418 & 0xff;
- buf[p++] = 0x00;
- memcpy(buf + p, bits_dc_luminance + 1, 16);
- p += 16;
- memcpy(buf + p, val_dc_luminance, sizeof(val_dc_luminance));
- p += sizeof(val_dc_luminance);
- buf[p++] = 0x01;
- memcpy(buf + p, bits_dc_chrominance + 1, 16);
- p += 16;
- memcpy(buf + p, val_dc_chrominance, sizeof(val_dc_chrominance));
- p += sizeof(val_dc_chrominance);
- buf[p++] = 0x10;
- memcpy(buf + p, bits_ac_luminance + 1, 16);
- p += 16;
- memcpy(buf + p, val_ac_luminance, sizeof(val_ac_luminance));
- p += sizeof(val_ac_luminance);
- buf[p++] = 0x11;
- memcpy(buf + p, bits_ac_chrominance + 1, 16);
- p += 16;
- memcpy(buf + p, val_ac_chrominance, sizeof(val_ac_chrominance));
- p += sizeof(val_ac_chrominance);
- buf[p++] = 0xff;
- buf[p++] = 0xda;
- buf[p++] = 0;
- buf[p++] = 12;
- buf[p++] = 3;
- buf[p++] = 1;
- buf[p++] = 0x00;
- buf[p++] = 2;
- buf[p++] = 0x11;
- buf[p++] = 3;
- buf[p++] = 0x11;
- buf[p++] = 0;
- buf[p++] = 63;
- buf[p++] = 0;
- return p;
-}
-
-static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space)
-{
- u8 *buf;
- u16 mem = 0x3e00;
- unsigned int addr = 0x19;
- int size = 0, i, off = 0, chunk;
-
- buf = kzalloc(4096, GFP_KERNEL);
- if (buf == NULL) {
- printk(KERN_ERR "go7007: unable to allocate 4096 bytes for "
- "firmware construction\n");
- return -1;
- }
-
- for (i = 1; i < 32; ++i) {
- mjpeg_frame_header(go, buf + size, i);
- size += 80;
- }
- chunk = mjpeg_frame_header(go, buf + size, 1);
- memmove(buf + size, buf + size + 80, chunk - 80);
- size += chunk - 80;
-
- for (i = 0; i < size; i += chunk * 2) {
- if (space - off < 32) {
- off = -1;
- goto done;
- }
-
- code[off + 1] = __cpu_to_le16(0x8000 | mem);
-
- chunk = 28;
- if (mem + chunk > 0x4000)
- chunk = 0x4000 - mem;
- if (i + 2 * chunk > size)
- chunk = (size - i) / 2;
-
- if (chunk < 28) {
- code[off] = __cpu_to_le16(0x4000 | chunk);
- code[off + 31] = __cpu_to_le16(addr++);
- mem = 0x3e00;
- } else {
- code[off] = __cpu_to_le16(0x1000 | 28);
- code[off + 31] = 0;
- mem += 28;
- }
-
- memcpy(&code[off + 2], buf + i, chunk * 2);
- off += 32;
- }
-done:
- kfree(buf);
- return off;
-}
-
-static int mpeg1_frame_header(struct go7007 *go, unsigned char *buf,
- int modulo, int pict_struct, enum mpeg_frame_type frame)
-{
- int i, j, mb_code, mb_len;
- int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
- CODE_GEN(c, buf + 6);
-
- switch (frame) {
- case PFRAME:
- mb_code = 0x1;
- mb_len = 3;
- break;
- case BFRAME_PRE:
- mb_code = 0x2;
- mb_len = 4;
- break;
- case BFRAME_POST:
- mb_code = 0x2;
- mb_len = 3;
- break;
- case BFRAME_BIDIR:
- mb_code = 0x2;
- mb_len = 2;
- break;
- default: /* keep the compiler happy */
- mb_code = mb_len = 0;
- break;
- }
-
- CODE_ADD(c, frame == PFRAME ? 0x2 : 0x3, 13);
- CODE_ADD(c, 0xffff, 16);
- CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4);
- if (frame != PFRAME)
- CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4);
- else
- CODE_ADD(c, 0, 4); /* Is this supposed to be here?? */
- CODE_ADD(c, 0, 3); /* What is this?? */
- /* Byte-align with zeros */
- j = 8 - (CODE_LENGTH(c) % 8);
- if (j != 8)
- CODE_ADD(c, 0, j);
-
- if (go->format == GO7007_FORMAT_MPEG2) {
- CODE_ADD(c, 0x1, 24);
- CODE_ADD(c, 0xb5, 8);
- CODE_ADD(c, 0x844, 12);
- CODE_ADD(c, frame == PFRAME ? 0xff : 0x44, 8);
- if (go->interlace_coding) {
- CODE_ADD(c, pict_struct, 4);
- if (go->dvd_mode)
- CODE_ADD(c, 0x000, 11);
- else
- CODE_ADD(c, 0x200, 11);
- } else {
- CODE_ADD(c, 0x3, 4);
- CODE_ADD(c, 0x20c, 11);
- }
- /* Byte-align with zeros */
- j = 8 - (CODE_LENGTH(c) % 8);
- if (j != 8)
- CODE_ADD(c, 0, j);
- }
-
- for (i = 0; i < rows; ++i) {
- CODE_ADD(c, 1, 24);
- CODE_ADD(c, i + 1, 8);
- CODE_ADD(c, 0x2, 6);
- CODE_ADD(c, 0x1, 1);
- CODE_ADD(c, mb_code, mb_len);
- if (go->interlace_coding) {
- CODE_ADD(c, 0x1, 2);
- CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
- }
- if (frame == BFRAME_BIDIR) {
- CODE_ADD(c, 0x3, 2);
- if (go->interlace_coding)
- CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
- }
- CODE_ADD(c, 0x3, 2);
- for (j = (go->width >> 4) - 2; j >= 33; j -= 33)
- CODE_ADD(c, 0x8, 11);
- CODE_ADD(c, addrinctab[j][0], addrinctab[j][1]);
- CODE_ADD(c, mb_code, mb_len);
- if (go->interlace_coding) {
- CODE_ADD(c, 0x1, 2);
- CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
- }
- if (frame == BFRAME_BIDIR) {
- CODE_ADD(c, 0x3, 2);
- if (go->interlace_coding)
- CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
- }
- CODE_ADD(c, 0x3, 2);
-
- /* Byte-align with zeros */
- j = 8 - (CODE_LENGTH(c) % 8);
- if (j != 8)
- CODE_ADD(c, 0, j);
- }
-
- i = CODE_LENGTH(c) + 4 * 8;
- buf[2] = 0x00;
- buf[3] = 0x00;
- buf[4] = 0x01;
- buf[5] = 0x00;
- return i;
-}
-
-static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext)
-{
- int i, aspect_ratio, picture_rate;
- CODE_GEN(c, buf + 6);
-
- if (go->format == GO7007_FORMAT_MPEG1) {
- switch (go->aspect_ratio) {
- case GO7007_RATIO_4_3:
- aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2;
- break;
- case GO7007_RATIO_16_9:
- aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4;
- break;
- default:
- aspect_ratio = 1;
- break;
- }
- } else {
- switch (go->aspect_ratio) {
- case GO7007_RATIO_4_3:
- aspect_ratio = 2;
- break;
- case GO7007_RATIO_16_9:
- aspect_ratio = 3;
- break;
- default:
- aspect_ratio = 1;
- break;
- }
- }
- switch (go->sensor_framerate) {
- case 24000:
- picture_rate = 1;
- break;
- case 24024:
- picture_rate = 2;
- break;
- case 25025:
- picture_rate = go->interlace_coding ? 6 : 3;
- break;
- case 30000:
- picture_rate = go->interlace_coding ? 7 : 4;
- break;
- case 30030:
- picture_rate = go->interlace_coding ? 8 : 5;
- break;
- default:
- picture_rate = 5; /* 30 fps seems like a reasonable default */
- break;
- }
-
- CODE_ADD(c, go->width, 12);
- CODE_ADD(c, go->height, 12);
- CODE_ADD(c, aspect_ratio, 4);
- CODE_ADD(c, picture_rate, 4);
- CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 20000 : 0x3ffff, 18);
- CODE_ADD(c, 1, 1);
- CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 112 : 20, 10);
- CODE_ADD(c, 0, 3);
-
- /* Byte-align with zeros */
- i = 8 - (CODE_LENGTH(c) % 8);
- if (i != 8)
- CODE_ADD(c, 0, i);
-
- if (go->format == GO7007_FORMAT_MPEG2) {
- CODE_ADD(c, 0x1, 24);
- CODE_ADD(c, 0xb5, 8);
- CODE_ADD(c, 0x148, 12);
- if (go->interlace_coding)
- CODE_ADD(c, 0x20001, 20);
- else
- CODE_ADD(c, 0xa0001, 20);
- CODE_ADD(c, 0, 16);
-
- /* Byte-align with zeros */
- i = 8 - (CODE_LENGTH(c) % 8);
- if (i != 8)
- CODE_ADD(c, 0, i);
-
- if (ext) {
- CODE_ADD(c, 0x1, 24);
- CODE_ADD(c, 0xb52, 12);
- CODE_ADD(c, go->standard == GO7007_STD_NTSC ? 2 : 1, 3);
- CODE_ADD(c, 0x105, 9);
- CODE_ADD(c, 0x505, 16);
- CODE_ADD(c, go->width, 14);
- CODE_ADD(c, 1, 1);
- CODE_ADD(c, go->height, 14);
-
- /* Byte-align with zeros */
- i = 8 - (CODE_LENGTH(c) % 8);
- if (i != 8)
- CODE_ADD(c, 0, i);
- }
- }
-
- i = CODE_LENGTH(c) + 4 * 8;
- buf[0] = i & 0xff;
- buf[1] = i >> 8;
- buf[2] = 0x00;
- buf[3] = 0x00;
- buf[4] = 0x01;
- buf[5] = 0xb3;
- return i;
-}
-
-static int gen_mpeg1hdr_to_package(struct go7007 *go,
- __le16 *code, int space, int *framelen)
-{
- u8 *buf;
- u16 mem = 0x3e00;
- unsigned int addr = 0x19;
- int i, off = 0, chunk;
-
- buf = kzalloc(5120, GFP_KERNEL);
- if (buf == NULL) {
- printk(KERN_ERR "go7007: unable to allocate 5120 bytes for "
- "firmware construction\n");
- return -1;
- }
- framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME);
- if (go->interlace_coding)
- framelen[0] += mpeg1_frame_header(go, buf + framelen[0] / 8,
- 0, 2, PFRAME);
- buf[0] = framelen[0] & 0xff;
- buf[1] = framelen[0] >> 8;
- i = 368;
- framelen[1] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_PRE);
- if (go->interlace_coding)
- framelen[1] += mpeg1_frame_header(go, buf + i + framelen[1] / 8,
- 0, 2, BFRAME_PRE);
- buf[i] = framelen[1] & 0xff;
- buf[i + 1] = framelen[1] >> 8;
- i += 1632;
- framelen[2] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_POST);
- if (go->interlace_coding)
- framelen[2] += mpeg1_frame_header(go, buf + i + framelen[2] / 8,
- 0, 2, BFRAME_POST);
- buf[i] = framelen[2] & 0xff;
- buf[i + 1] = framelen[2] >> 8;
- i += 1432;
- framelen[3] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_BIDIR);
- if (go->interlace_coding)
- framelen[3] += mpeg1_frame_header(go, buf + i + framelen[3] / 8,
- 0, 2, BFRAME_BIDIR);
- buf[i] = framelen[3] & 0xff;
- buf[i + 1] = framelen[3] >> 8;
- i += 1632 + 16;
- mpeg1_sequence_header(go, buf + i, 0);
- i += 40;
- for (i = 0; i < 5120; i += chunk * 2) {
- if (space - off < 32) {
- off = -1;
- goto done;
- }
-
- code[off + 1] = __cpu_to_le16(0x8000 | mem);
-
- chunk = 28;
- if (mem + chunk > 0x4000)
- chunk = 0x4000 - mem;
- if (i + 2 * chunk > 5120)
- chunk = (5120 - i) / 2;
-
- if (chunk < 28) {
- code[off] = __cpu_to_le16(0x4000 | chunk);
- code[off + 31] = __cpu_to_le16(addr);
- if (mem + chunk == 0x4000) {
- mem = 0x3e00;
- ++addr;
- }
- } else {
- code[off] = __cpu_to_le16(0x1000 | 28);
- code[off + 31] = 0;
- mem += 28;
- }
-
- memcpy(&code[off + 2], buf + i, chunk * 2);
- off += 32;
- }
-done:
- kfree(buf);
- return off;
-}
-
-static int vti_bitlen(struct go7007 *go)
-{
- unsigned int i, max_time_incr = go->sensor_framerate / go->fps_scale;
-
- for (i = 31; (max_time_incr & ((1 << i) - 1)) == max_time_incr; --i);
- return i + 1;
-}
-
-static int mpeg4_frame_header(struct go7007 *go, unsigned char *buf,
- int modulo, enum mpeg_frame_type frame)
-{
- int i;
- CODE_GEN(c, buf + 6);
- int mb_count = (go->width >> 4) * (go->height >> 4);
-
- CODE_ADD(c, frame == PFRAME ? 0x1 : 0x2, 2);
- if (modulo)
- CODE_ADD(c, 0x1, 1);
- CODE_ADD(c, 0x1, 2);
- CODE_ADD(c, 0, vti_bitlen(go));
- CODE_ADD(c, 0x3, 2);
- if (frame == PFRAME)
- CODE_ADD(c, 0, 1);
- CODE_ADD(c, 0xc, 11);
- if (frame != PFRAME)
- CODE_ADD(c, 0x4, 3);
- if (frame != BFRAME_EMPTY) {
- for (i = 0; i < mb_count; ++i) {
- switch (frame) {
- case PFRAME:
- CODE_ADD(c, 0x1, 1);
- break;
- case BFRAME_PRE:
- CODE_ADD(c, 0x47, 8);
- break;
- case BFRAME_POST:
- CODE_ADD(c, 0x27, 7);
- break;
- case BFRAME_BIDIR:
- CODE_ADD(c, 0x5f, 8);
- break;
- case BFRAME_EMPTY: /* keep compiler quiet */
- break;
- }
- }
- }
-
- /* Byte-align with a zero followed by ones */
- i = 8 - (CODE_LENGTH(c) % 8);
- CODE_ADD(c, 0, 1);
- CODE_ADD(c, (1 << (i - 1)) - 1, i - 1);
-
- i = CODE_LENGTH(c) + 4 * 8;
- buf[0] = i & 0xff;
- buf[1] = i >> 8;
- buf[2] = 0x00;
- buf[3] = 0x00;
- buf[4] = 0x01;
- buf[5] = 0xb6;
- return i;
-}
-
-static int mpeg4_sequence_header(struct go7007 *go, unsigned char *buf, int ext)
-{
- const unsigned char head[] = { 0x00, 0x00, 0x01, 0xb0, go->pali,
- 0x00, 0x00, 0x01, 0xb5, 0x09,
- 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x01, 0x20, };
- int i, aspect_ratio;
- int fps = go->sensor_framerate / go->fps_scale;
- CODE_GEN(c, buf + 2 + sizeof(head));
-
- switch (go->aspect_ratio) {
- case GO7007_RATIO_4_3:
- aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2;
- break;
- case GO7007_RATIO_16_9:
- aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4;
- break;
- default:
- aspect_ratio = 1;
- break;
- }
-
- memcpy(buf + 2, head, sizeof(head));
- CODE_ADD(c, 0x191, 17);
- CODE_ADD(c, aspect_ratio, 4);
- CODE_ADD(c, 0x1, 4);
- CODE_ADD(c, fps, 16);
- CODE_ADD(c, 0x3, 2);
- CODE_ADD(c, 1001, vti_bitlen(go));
- CODE_ADD(c, 1, 1);
- CODE_ADD(c, go->width, 13);
- CODE_ADD(c, 1, 1);
- CODE_ADD(c, go->height, 13);
- CODE_ADD(c, 0x2830, 14);
-
- /* Byte-align */
- i = 8 - (CODE_LENGTH(c) % 8);
- CODE_ADD(c, 0, 1);
- CODE_ADD(c, (1 << (i - 1)) - 1, i - 1);
-
- i = CODE_LENGTH(c) + sizeof(head) * 8;
- buf[0] = i & 0xff;
- buf[1] = i >> 8;
- return i;
-}
-
-static int gen_mpeg4hdr_to_package(struct go7007 *go,
- __le16 *code, int space, int *framelen)
-{
- u8 *buf;
- u16 mem = 0x3e00;
- unsigned int addr = 0x19;
- int i, off = 0, chunk;
-
- buf = kzalloc(5120, GFP_KERNEL);
- if (buf == NULL) {
- printk(KERN_ERR "go7007: unable to allocate 5120 bytes for "
- "firmware construction\n");
- return -1;
- }
- framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME);
- i = 368;
- framelen[1] = mpeg4_frame_header(go, buf + i, 0, BFRAME_PRE);
- i += 1632;
- framelen[2] = mpeg4_frame_header(go, buf + i, 0, BFRAME_POST);
- i += 1432;
- framelen[3] = mpeg4_frame_header(go, buf + i, 0, BFRAME_BIDIR);
- i += 1632;
- mpeg4_frame_header(go, buf + i, 0, BFRAME_EMPTY);
- i += 16;
- mpeg4_sequence_header(go, buf + i, 0);
- i += 40;
- for (i = 0; i < 5120; i += chunk * 2) {
- if (space - off < 32) {
- off = -1;
- goto done;
- }
-
- code[off + 1] = __cpu_to_le16(0x8000 | mem);
-
- chunk = 28;
- if (mem + chunk > 0x4000)
- chunk = 0x4000 - mem;
- if (i + 2 * chunk > 5120)
- chunk = (5120 - i) / 2;
-
- if (chunk < 28) {
- code[off] = __cpu_to_le16(0x4000 | chunk);
- code[off + 31] = __cpu_to_le16(addr);
- if (mem + chunk == 0x4000) {
- mem = 0x3e00;
- ++addr;
- }
- } else {
- code[off] = __cpu_to_le16(0x1000 | 28);
- code[off + 31] = 0;
- mem += 28;
- }
-
- memcpy(&code[off + 2], buf + i, chunk * 2);
- off += 32;
- }
- mem = 0x3e00;
- addr = go->ipb ? 0x14f9 : 0x0af9;
- memset(buf, 0, 5120);
- framelen[4] = mpeg4_frame_header(go, buf, 1, PFRAME);
- i = 368;
- framelen[5] = mpeg4_frame_header(go, buf + i, 1, BFRAME_PRE);
- i += 1632;
- framelen[6] = mpeg4_frame_header(go, buf + i, 1, BFRAME_POST);
- i += 1432;
- framelen[7] = mpeg4_frame_header(go, buf + i, 1, BFRAME_BIDIR);
- i += 1632;
- mpeg4_frame_header(go, buf + i, 1, BFRAME_EMPTY);
- i += 16;
- for (i = 0; i < 5120; i += chunk * 2) {
- if (space - off < 32) {
- off = -1;
- goto done;
- }
-
- code[off + 1] = __cpu_to_le16(0x8000 | mem);
-
- chunk = 28;
- if (mem + chunk > 0x4000)
- chunk = 0x4000 - mem;
- if (i + 2 * chunk > 5120)
- chunk = (5120 - i) / 2;
-
- if (chunk < 28) {
- code[off] = __cpu_to_le16(0x4000 | chunk);
- code[off + 31] = __cpu_to_le16(addr);
- if (mem + chunk == 0x4000) {
- mem = 0x3e00;
- ++addr;
- }
- } else {
- code[off] = __cpu_to_le16(0x1000 | 28);
- code[off + 31] = 0;
- mem += 28;
- }
-
- memcpy(&code[off + 2], buf + i, chunk * 2);
- off += 32;
- }
-done:
- kfree(buf);
- return off;
-}
-
-static int brctrl_to_package(struct go7007 *go,
- __le16 *code, int space, int *framelen)
-{
- int converge_speed = 0;
- int lambda = (go->format == GO7007_FORMAT_MJPEG || go->dvd_mode) ?
- 100 : 0;
- int peak_rate = 6 * go->bitrate / 5;
- int vbv_buffer = go->format == GO7007_FORMAT_MJPEG ?
- go->bitrate :
- (go->dvd_mode ? 900000 : peak_rate);
- int fps = go->sensor_framerate / go->fps_scale;
- int q = 0;
- /* Bizarre math below depends on rounding errors in division */
- u32 sgop_expt_addr = go->bitrate / 32 * (go->ipb ? 3 : 1) * 1001 / fps;
- u32 sgop_peak_addr = peak_rate / 32 * 1001 / fps;
- u32 total_expt_addr = go->bitrate / 32 * 1000 / fps * (fps / 1000);
- u32 vbv_alert_addr = vbv_buffer * 3 / (4 * 32);
- u32 cplx[] = {
- q > 0 ? sgop_expt_addr * q :
- 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
- q > 0 ? sgop_expt_addr * q :
- 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
- q > 0 ? sgop_expt_addr * q :
- 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
- q > 0 ? sgop_expt_addr * q :
- 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
- };
- u32 calc_q = q > 0 ? q : cplx[0] / sgop_expt_addr;
- u16 pack[] = {
- 0x200e, 0x0000,
- 0xBF20, go->ipb ? converge_speed_ipb[converge_speed]
- : converge_speed_ip[converge_speed],
- 0xBF21, go->ipb ? 2 : 0,
- 0xBF22, go->ipb ? LAMBDA_table[0][lambda / 2 + 50]
- : 32767,
- 0xBF23, go->ipb ? LAMBDA_table[1][lambda] : 32767,
- 0xBF24, 32767,
- 0xBF25, lambda > 99 ? 32767 : LAMBDA_table[3][lambda],
- 0xBF26, sgop_expt_addr & 0x0000FFFF,
- 0xBF27, sgop_expt_addr >> 16,
- 0xBF28, sgop_peak_addr & 0x0000FFFF,
- 0xBF29, sgop_peak_addr >> 16,
- 0xBF2A, vbv_alert_addr & 0x0000FFFF,
- 0xBF2B, vbv_alert_addr >> 16,
- 0xBF2C, 0,
- 0xBF2D, 0,
- 0, 0,
-
- 0x200e, 0x0000,
- 0xBF2E, vbv_alert_addr & 0x0000FFFF,
- 0xBF2F, vbv_alert_addr >> 16,
- 0xBF30, cplx[0] & 0x0000FFFF,
- 0xBF31, cplx[0] >> 16,
- 0xBF32, cplx[1] & 0x0000FFFF,
- 0xBF33, cplx[1] >> 16,
- 0xBF34, cplx[2] & 0x0000FFFF,
- 0xBF35, cplx[2] >> 16,
- 0xBF36, cplx[3] & 0x0000FFFF,
- 0xBF37, cplx[3] >> 16,
- 0xBF38, 0,
- 0xBF39, 0,
- 0xBF3A, total_expt_addr & 0x0000FFFF,
- 0xBF3B, total_expt_addr >> 16,
- 0, 0,
-
- 0x200e, 0x0000,
- 0xBF3C, total_expt_addr & 0x0000FFFF,
- 0xBF3D, total_expt_addr >> 16,
- 0xBF3E, 0,
- 0xBF3F, 0,
- 0xBF48, 0,
- 0xBF49, 0,
- 0xBF4A, calc_q < 4 ? 4 : (calc_q > 124 ? 124 : calc_q),
- 0xBF4B, 4,
- 0xBF4C, 0,
- 0xBF4D, 0,
- 0xBF4E, 0,
- 0xBF4F, 0,
- 0xBF50, 0,
- 0xBF51, 0,
- 0, 0,
-
- 0x200e, 0x0000,
- 0xBF40, sgop_expt_addr & 0x0000FFFF,
- 0xBF41, sgop_expt_addr >> 16,
- 0xBF42, 0,
- 0xBF43, 0,
- 0xBF44, 0,
- 0xBF45, 0,
- 0xBF46, (go->width >> 4) * (go->height >> 4),
- 0xBF47, 0,
- 0xBF64, 0,
- 0xBF65, 0,
- 0xBF18, framelen[4],
- 0xBF19, framelen[5],
- 0xBF1A, framelen[6],
- 0xBF1B, framelen[7],
- 0, 0,
-
-#if 0
- /* Remove once we don't care about matching */
- 0x200e, 0x0000,
- 0xBF56, 4,
- 0xBF57, 0,
- 0xBF58, 5,
- 0xBF59, 0,
- 0xBF5A, 6,
- 0xBF5B, 0,
- 0xBF5C, 8,
- 0xBF5D, 0,
- 0xBF5E, 1,
- 0xBF5F, 0,
- 0xBF60, 1,
- 0xBF61, 0,
- 0xBF62, 0,
- 0xBF63, 0,
- 0, 0,
-#else
- 0x2008, 0x0000,
- 0xBF56, 4,
- 0xBF57, 0,
- 0xBF58, 5,
- 0xBF59, 0,
- 0xBF5A, 6,
- 0xBF5B, 0,
- 0xBF5C, 8,
- 0xBF5D, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
-#endif
-
- 0x200e, 0x0000,
- 0xBF10, 0,
- 0xBF11, 0,
- 0xBF12, 0,
- 0xBF13, 0,
- 0xBF14, 0,
- 0xBF15, 0,
- 0xBF16, 0,
- 0xBF17, 0,
- 0xBF7E, 0,
- 0xBF7F, 1,
- 0xBF52, framelen[0],
- 0xBF53, framelen[1],
- 0xBF54, framelen[2],
- 0xBF55, framelen[3],
- 0, 0,
- };
-
- return copy_packages(code, pack, 6, space);
-}
-
-static int config_package(struct go7007 *go, __le16 *code, int space)
-{
- int fps = go->sensor_framerate / go->fps_scale / 1000;
- int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
- int brc_window_size = fps;
- int q_min = 2, q_max = 31;
- int THACCoeffSet0 = 0;
- u16 pack[] = {
- 0x200e, 0x0000,
- 0xc002, 0x14b4,
- 0xc003, 0x28b4,
- 0xc004, 0x3c5a,
- 0xdc05, 0x2a77,
- 0xc6c3, go->format == GO7007_FORMAT_MPEG4 ? 0 :
- (go->format == GO7007_FORMAT_H263 ? 0 : 1),
- 0xc680, go->format == GO7007_FORMAT_MPEG4 ? 0xf1 :
- (go->format == GO7007_FORMAT_H263 ? 0x61 :
- 0xd3),
- 0xc780, 0x0140,
- 0xe009, 0x0001,
- 0xc60f, 0x0008,
- 0xd4ff, 0x0002,
- 0xe403, 2340,
- 0xe406, 75,
- 0xd411, 0x0001,
- 0xd410, 0xa1d6,
- 0x0001, 0x2801,
-
- 0x200d, 0x0000,
- 0xe402, 0x018b,
- 0xe401, 0x8b01,
- 0xd472, (go->board_info->sensor_flags &
- GO7007_SENSOR_TV) &&
- (!go->interlace_coding) ?
- 0x01b0 : 0x0170,
- 0xd475, (go->board_info->sensor_flags &
- GO7007_SENSOR_TV) &&
- (!go->interlace_coding) ?
- 0x0008 : 0x0009,
- 0xc404, go->interlace_coding ? 0x44 :
- (go->format == GO7007_FORMAT_MPEG4 ? 0x11 :
- (go->format == GO7007_FORMAT_MPEG1 ? 0x02 :
- (go->format == GO7007_FORMAT_MPEG2 ? 0x04 :
- (go->format == GO7007_FORMAT_H263 ? 0x08 :
- 0x20)))),
- 0xbf0a, (go->format == GO7007_FORMAT_MPEG4 ? 8 :
- (go->format == GO7007_FORMAT_MPEG1 ? 1 :
- (go->format == GO7007_FORMAT_MPEG2 ? 2 :
- (go->format == GO7007_FORMAT_H263 ? 4 : 16)))) |
- ((go->repeat_seqhead ? 1 : 0) << 6) |
- ((go->dvd_mode ? 1 : 0) << 9) |
- ((go->gop_header_enable ? 1 : 0) << 10),
- 0xbf0b, 0,
- 0xdd5a, go->ipb ? 0x14 : 0x0a,
- 0xbf0c, 0,
- 0xbf0d, 0,
- 0xc683, THACCoeffSet0,
- 0xc40a, (go->width << 4) | rows,
- 0xe01a, go->board_info->hpi_buffer_cap,
- 0, 0,
- 0, 0,
-
- 0x2008, 0,
- 0xe402, 0x88,
- 0xe401, 0x8f01,
- 0xbf6a, 0,
- 0xbf6b, 0,
- 0xbf6c, 0,
- 0xbf6d, 0,
- 0xbf6e, 0,
- 0xbf6f, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
-
- 0x200e, 0,
- 0xbf66, brc_window_size,
- 0xbf67, 0,
- 0xbf68, q_min,
- 0xbf69, q_max,
- 0xbfe0, 0,
- 0xbfe1, 0,
- 0xbfe2, 0,
- 0xbfe3, go->ipb ? 3 : 1,
- 0xc031, go->board_info->sensor_flags &
- GO7007_SENSOR_VBI ? 1 : 0,
- 0xc01c, 0x1f,
- 0xdd8c, 0x15,
- 0xdd94, 0x15,
- 0xdd88, go->ipb ? 0x1401 : 0x0a01,
- 0xdd90, go->ipb ? 0x1401 : 0x0a01,
- 0, 0,
-
- 0x200e, 0,
- 0xbfe4, 0,
- 0xbfe5, 0,
- 0xbfe6, 0,
- 0xbfe7, fps << 8,
- 0xbfe8, 0x3a00,
- 0xbfe9, 0,
- 0xbfea, 0,
- 0xbfeb, 0,
- 0xbfec, (go->interlace_coding ? 1 << 15 : 0) |
- (go->modet_enable ? 0xa : 0) |
- (go->board_info->sensor_flags &
- GO7007_SENSOR_VBI ? 1 : 0),
- 0xbfed, 0,
- 0xbfee, 0,
- 0xbfef, 0,
- 0xbff0, go->board_info->sensor_flags &
- GO7007_SENSOR_TV ? 0xf060 : 0xb060,
- 0xbff1, 0,
- 0, 0,
- };
-
- return copy_packages(code, pack, 5, space);
-}
-
-static int seqhead_to_package(struct go7007 *go, __le16 *code, int space,
- int (*sequence_header_func)(struct go7007 *go,
- unsigned char *buf, int ext))
-{
- int vop_time_increment_bitlength = vti_bitlen(go);
- int fps = go->sensor_framerate / go->fps_scale *
- (go->interlace_coding ? 2 : 1);
- unsigned char buf[40] = { };
- int len = sequence_header_func(go, buf, 1);
- u16 pack[] = {
- 0x2006, 0,
- 0xbf08, fps,
- 0xbf09, 0,
- 0xbff2, vop_time_increment_bitlength,
- 0xbff3, (1 << vop_time_increment_bitlength) - 1,
- 0xbfe6, 0,
- 0xbfe7, (fps / 1000) << 8,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
-
- 0x2007, 0,
- 0xc800, buf[2] << 8 | buf[3],
- 0xc801, buf[4] << 8 | buf[5],
- 0xc802, buf[6] << 8 | buf[7],
- 0xc803, buf[8] << 8 | buf[9],
- 0xc406, 64,
- 0xc407, len - 64,
- 0xc61b, 1,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
-
- 0x200e, 0,
- 0xc808, buf[10] << 8 | buf[11],
- 0xc809, buf[12] << 8 | buf[13],
- 0xc80a, buf[14] << 8 | buf[15],
- 0xc80b, buf[16] << 8 | buf[17],
- 0xc80c, buf[18] << 8 | buf[19],
- 0xc80d, buf[20] << 8 | buf[21],
- 0xc80e, buf[22] << 8 | buf[23],
- 0xc80f, buf[24] << 8 | buf[25],
- 0xc810, buf[26] << 8 | buf[27],
- 0xc811, buf[28] << 8 | buf[29],
- 0xc812, buf[30] << 8 | buf[31],
- 0xc813, buf[32] << 8 | buf[33],
- 0xc814, buf[34] << 8 | buf[35],
- 0xc815, buf[36] << 8 | buf[37],
- 0, 0,
- 0, 0,
- 0, 0,
- };
-
- return copy_packages(code, pack, 3, space);
-}
-
-static int relative_prime(int big, int little)
-{
- int remainder;
-
- while (little != 0) {
- remainder = big % little;
- big = little;
- little = remainder;
- }
- return big;
-}
-
-static int avsync_to_package(struct go7007 *go, __le16 *code, int space)
-{
- int arate = go->board_info->audio_rate * 1001 * go->fps_scale;
- int ratio = arate / go->sensor_framerate;
- int adjratio = ratio * 215 / 100;
- int rprime = relative_prime(go->sensor_framerate,
- arate % go->sensor_framerate);
- int f1 = (arate % go->sensor_framerate) / rprime;
- int f2 = (go->sensor_framerate - arate % go->sensor_framerate) / rprime;
- u16 pack[] = {
- 0x200e, 0,
- 0xbf98, (u16)((-adjratio) & 0xffff),
- 0xbf99, (u16)((-adjratio) >> 16),
- 0xbf92, 0,
- 0xbf93, 0,
- 0xbff4, f1 > f2 ? f1 : f2,
- 0xbff5, f1 < f2 ? f1 : f2,
- 0xbff6, f1 < f2 ? ratio : ratio + 1,
- 0xbff7, f1 > f2 ? ratio : ratio + 1,
- 0xbff8, 0,
- 0xbff9, 0,
- 0xbffa, adjratio & 0xffff,
- 0xbffb, adjratio >> 16,
- 0xbf94, 0,
- 0xbf95, 0,
- 0, 0,
- };
-
- return copy_packages(code, pack, 1, space);
-}
-
-static int final_package(struct go7007 *go, __le16 *code, int space)
-{
- int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
- u16 pack[] = {
- 0x8000,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 2,
- ((go->board_info->sensor_flags & GO7007_SENSOR_TV) &&
- (!go->interlace_coding) ?
- (1 << 14) | (1 << 9) : 0) |
- ((go->encoder_subsample ? 1 : 0) << 8) |
- (go->board_info->sensor_flags &
- GO7007_SENSOR_CONFIG_MASK),
- ((go->encoder_v_halve ? 1 : 0) << 14) |
- (go->encoder_v_halve ? rows << 9 : rows << 8) |
- (go->encoder_h_halve ? 1 << 6 : 0) |
- (go->encoder_h_halve ? go->width >> 3 : go->width >> 4),
- (1 << 15) | (go->encoder_v_offset << 6) |
- (1 << 7) | (go->encoder_h_offset >> 2),
- (1 << 6),
- 0,
- 0,
- ((go->fps_scale - 1) << 8) |
- (go->board_info->sensor_flags & GO7007_SENSOR_TV ?
- (1 << 7) : 0) |
- 0x41,
- go->ipb ? 0xd4c : 0x36b,
- (rows << 8) | (go->width >> 4),
- go->format == GO7007_FORMAT_MPEG4 ? 0x0404 : 0,
- (1 << 15) | ((go->interlace_coding ? 1 : 0) << 13) |
- ((go->closed_gop ? 1 : 0) << 12) |
- ((go->format == GO7007_FORMAT_MPEG4 ? 1 : 0) << 11) |
- /* (1 << 9) | */
- ((go->ipb ? 3 : 0) << 7) |
- ((go->modet_enable ? 1 : 0) << 2) |
- ((go->dvd_mode ? 1 : 0) << 1) | 1,
- (go->format == GO7007_FORMAT_MPEG1 ? 0x89a0 :
- (go->format == GO7007_FORMAT_MPEG2 ? 0x89a0 :
- (go->format == GO7007_FORMAT_MJPEG ? 0x89a0 :
- (go->format == GO7007_FORMAT_MPEG4 ? 0x8920 :
- (go->format == GO7007_FORMAT_H263 ? 0x8920 : 0))))),
- go->ipb ? 0x1f15 : 0x1f0b,
- go->ipb ? 0x0015 : 0x000b,
- go->ipb ? 0xa800 : 0x5800,
- 0xffff,
- 0x0020 + 0x034b * 0,
- 0x0020 + 0x034b * 1,
- 0x0020 + 0x034b * 2,
- 0x0020 + 0x034b * 3,
- 0x0020 + 0x034b * 4,
- 0x0020 + 0x034b * 5,
- go->ipb ? (go->gop_size / 3) : go->gop_size,
- (go->height >> 4) * (go->width >> 4) * 110 / 100,
- };
-
- return copy_packages(code, pack, 1, space);
-}
-
-static int audio_to_package(struct go7007 *go, __le16 *code, int space)
-{
- int clock_config = ((go->board_info->audio_flags &
- GO7007_AUDIO_I2S_MASTER ? 1 : 0) << 11) |
- ((go->board_info->audio_flags &
- GO7007_AUDIO_OKI_MODE ? 1 : 0) << 8) |
- (((go->board_info->audio_bclk_div / 4) - 1) << 4) |
- (go->board_info->audio_main_div - 1);
- u16 pack[] = {
- 0x200d, 0,
- 0x9002, 0,
- 0x9002, 0,
- 0x9031, 0,
- 0x9032, 0,
- 0x9033, 0,
- 0x9034, 0,
- 0x9035, 0,
- 0x9036, 0,
- 0x9037, 0,
- 0x9040, 0,
- 0x9000, clock_config,
- 0x9001, (go->board_info->audio_flags & 0xffff) |
- (1 << 9),
- 0x9000, ((go->board_info->audio_flags &
- GO7007_AUDIO_I2S_MASTER ?
- 1 : 0) << 10) |
- clock_config,
- 0, 0,
- 0, 0,
- 0x2005, 0,
- 0x9041, 0,
- 0x9042, 256,
- 0x9043, 0,
- 0x9044, 16,
- 0x9045, 16,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- };
-
- return copy_packages(code, pack, 2, space);
-}
-
-static int modet_to_package(struct go7007 *go, __le16 *code, int space)
-{
- int ret, mb, i, addr, cnt = 0;
- u16 pack[32];
- u16 thresholds[] = {
- 0x200e, 0,
- 0xbf82, go->modet[0].pixel_threshold,
- 0xbf83, go->modet[1].pixel_threshold,
- 0xbf84, go->modet[2].pixel_threshold,
- 0xbf85, go->modet[3].pixel_threshold,
- 0xbf86, go->modet[0].motion_threshold,
- 0xbf87, go->modet[1].motion_threshold,
- 0xbf88, go->modet[2].motion_threshold,
- 0xbf89, go->modet[3].motion_threshold,
- 0xbf8a, go->modet[0].mb_threshold,
- 0xbf8b, go->modet[1].mb_threshold,
- 0xbf8c, go->modet[2].mb_threshold,
- 0xbf8d, go->modet[3].mb_threshold,
- 0xbf8e, 0,
- 0xbf8f, 0,
- 0, 0,
- };
-
- ret = copy_packages(code, thresholds, 1, space);
- if (ret < 0)
- return -1;
- cnt += ret;
-
- addr = 0xbac0;
- memset(pack, 0, 64);
- i = 0;
- for (mb = 0; mb < 1624; ++mb) {
- pack[i * 2 + 3] <<= 2;
- pack[i * 2 + 3] |= go->modet_map[mb];
- if (mb % 8 != 7)
- continue;
- pack[i * 2 + 2] = addr++;
- ++i;
- if (i == 10 || mb == 1623) {
- pack[0] = 0x2000 | i;
- ret = copy_packages(code + cnt, pack, 1, space - cnt);
- if (ret < 0)
- return -1;
- cnt += ret;
- i = 0;
- memset(pack, 0, 64);
- }
- pack[i * 2 + 3] = 0;
- }
-
- memset(pack, 0, 64);
- i = 0;
- for (addr = 0xbb90; addr < 0xbbfa; ++addr) {
- pack[i * 2 + 2] = addr;
- pack[i * 2 + 3] = 0;
- ++i;
- if (i == 10 || addr == 0xbbf9) {
- pack[0] = 0x2000 | i;
- ret = copy_packages(code + cnt, pack, 1, space - cnt);
- if (ret < 0)
- return -1;
- cnt += ret;
- i = 0;
- memset(pack, 0, 64);
- }
- }
- return cnt;
-}
-
-static int do_special(struct go7007 *go, u16 type, __le16 *code, int space,
- int *framelen)
-{
- switch (type) {
- case SPECIAL_FRM_HEAD:
- switch (go->format) {
- case GO7007_FORMAT_MJPEG:
- return gen_mjpeghdr_to_package(go, code, space);
- case GO7007_FORMAT_MPEG1:
- case GO7007_FORMAT_MPEG2:
- return gen_mpeg1hdr_to_package(go, code, space,
- framelen);
- case GO7007_FORMAT_MPEG4:
- return gen_mpeg4hdr_to_package(go, code, space,
- framelen);
- }
- case SPECIAL_BRC_CTRL:
- return brctrl_to_package(go, code, space, framelen);
- case SPECIAL_CONFIG:
- return config_package(go, code, space);
- case SPECIAL_SEQHEAD:
- switch (go->format) {
- case GO7007_FORMAT_MPEG1:
- case GO7007_FORMAT_MPEG2:
- return seqhead_to_package(go, code, space,
- mpeg1_sequence_header);
- case GO7007_FORMAT_MPEG4:
- return seqhead_to_package(go, code, space,
- mpeg4_sequence_header);
- default:
- return 0;
- }
- case SPECIAL_AV_SYNC:
- return avsync_to_package(go, code, space);
- case SPECIAL_FINAL:
- return final_package(go, code, space);
- case SPECIAL_AUDIO:
- return audio_to_package(go, code, space);
- case SPECIAL_MODET:
- return modet_to_package(go, code, space);
- }
- printk(KERN_ERR
- "go7007: firmware file contains unsupported feature %04x\n",
- type);
- return -1;
-}
-
-int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen)
-{
- const struct firmware *fw_entry;
- __le16 *code, *src;
- int framelen[8] = { }; /* holds the lengths of empty frame templates */
- int codespace = 64 * 1024, i = 0, srclen, chunk_len, chunk_flags;
- int mode_flag;
- int ret;
-
- switch (go->format) {
- case GO7007_FORMAT_MJPEG:
- mode_flag = FLAG_MODE_MJPEG;
- break;
- case GO7007_FORMAT_MPEG1:
- mode_flag = FLAG_MODE_MPEG1;
- break;
- case GO7007_FORMAT_MPEG2:
- mode_flag = FLAG_MODE_MPEG2;
- break;
- case GO7007_FORMAT_MPEG4:
- mode_flag = FLAG_MODE_MPEG4;
- break;
- default:
- return -1;
- }
- if (request_firmware(&fw_entry, go->board_info->firmware, go->dev)) {
- printk(KERN_ERR
- "go7007: unable to load firmware from file \"%s\"\n",
- go->board_info->firmware);
- return -1;
- }
- code = kzalloc(codespace * 2, GFP_KERNEL);
- if (code == NULL) {
- printk(KERN_ERR "go7007: unable to allocate %d bytes for "
- "firmware construction\n", codespace * 2);
- goto fw_failed;
- }
- src = (__le16 *)fw_entry->data;
- srclen = fw_entry->size / 2;
- while (srclen >= 2) {
- chunk_flags = __le16_to_cpu(src[0]);
- chunk_len = __le16_to_cpu(src[1]);
- if (chunk_len + 2 > srclen) {
- printk(KERN_ERR "go7007: firmware file \"%s\" "
- "appears to be corrupted\n",
- go->board_info->firmware);
- goto fw_failed;
- }
- if (chunk_flags & mode_flag) {
- if (chunk_flags & FLAG_SPECIAL) {
- ret = do_special(go, __le16_to_cpu(src[2]),
- &code[i], codespace - i, framelen);
- if (ret < 0) {
- printk(KERN_ERR "go7007: insufficient "
- "memory for firmware "
- "construction\n");
- goto fw_failed;
- }
- i += ret;
- } else {
- if (codespace - i < chunk_len) {
- printk(KERN_ERR "go7007: insufficient "
- "memory for firmware "
- "construction\n");
- goto fw_failed;
- }
- memcpy(&code[i], &src[2], chunk_len * 2);
- i += chunk_len;
- }
- }
- srclen -= chunk_len + 2;
- src += chunk_len + 2;
- }
- release_firmware(fw_entry);
- *fw = (u8 *)code;
- *fwlen = i * 2;
- return 0;
-
-fw_failed:
- kfree(code);
- release_firmware(fw_entry);
- return -1;
-}
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/list.h>
-#include <linux/unistd.h>
-#include <linux/time.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-#include <asm/system.h>
-
-#include "go7007-priv.h"
-#include "wis-i2c.h"
-
-/********************* Driver for on-board I2C adapter *********************/
-
-/* #define GO7007_I2C_DEBUG */
-
-#define SPI_I2C_ADDR_BASE 0x1400
-#define STATUS_REG_ADDR (SPI_I2C_ADDR_BASE + 0x2)
-#define I2C_CTRL_REG_ADDR (SPI_I2C_ADDR_BASE + 0x6)
-#define I2C_DEV_UP_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x7)
-#define I2C_LO_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x8)
-#define I2C_DATA_REG_ADDR (SPI_I2C_ADDR_BASE + 0x9)
-#define I2C_CLKFREQ_REG_ADDR (SPI_I2C_ADDR_BASE + 0xa)
-
-#define I2C_STATE_MASK 0x0007
-#define I2C_READ_READY_MASK 0x0008
-
-/* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs
- * on the Adlink PCI-MPG24, so access is shared between all of them. */
-static DEFINE_MUTEX(adlink_mpg24_i2c_lock);
-
-static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read,
- u16 command, int flags, u8 *data)
-{
- int i, ret = -1;
- u16 val;
-
- if (go->status == STATUS_SHUTDOWN)
- return -1;
-
-#ifdef GO7007_I2C_DEBUG
- if (read)
- printk(KERN_DEBUG "go7007-i2c: reading 0x%02x on 0x%02x\n",
- command, addr);
- else
- printk(KERN_DEBUG
- "go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n",
- *data, command, addr);
-#endif
-
- mutex_lock(&go->hw_lock);
-
- if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) {
- /* Bridge the I2C port on this GO7007 to the shared bus */
- mutex_lock(&adlink_mpg24_i2c_lock);
- go7007_write_addr(go, 0x3c82, 0x0020);
- }
-
- /* Wait for I2C adapter to be ready */
- for (i = 0; i < 10; ++i) {
- if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0)
- goto i2c_done;
- if (!(val & I2C_STATE_MASK))
- break;
- msleep(100);
- }
- if (i == 10) {
- printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n");
- goto i2c_done;
- }
-
- /* Set target register (command) */
- go7007_write_addr(go, I2C_CTRL_REG_ADDR, flags);
- go7007_write_addr(go, I2C_LO_ADDR_REG_ADDR, command);
-
- /* If we're writing, send the data and target address and we're done */
- if (!read) {
- go7007_write_addr(go, I2C_DATA_REG_ADDR, *data);
- go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR,
- (addr << 9) | (command >> 8));
- ret = 0;
- goto i2c_done;
- }
-
- /* Otherwise, we're reading. First clear i2c_rx_data_rdy. */
- if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0)
- goto i2c_done;
-
- /* Send the target address plus read flag */
- go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR,
- (addr << 9) | 0x0100 | (command >> 8));
-
- /* Wait for i2c_rx_data_rdy */
- for (i = 0; i < 10; ++i) {
- if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0)
- goto i2c_done;
- if (val & I2C_READ_READY_MASK)
- break;
- msleep(100);
- }
- if (i == 10) {
- printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n");
- goto i2c_done;
- }
-
- /* Retrieve the read byte */
- if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0)
- goto i2c_done;
- *data = val;
- ret = 0;
-
-i2c_done:
- if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) {
- /* Isolate the I2C port on this GO7007 from the shared bus */
- go7007_write_addr(go, 0x3c82, 0x0000);
- mutex_unlock(&adlink_mpg24_i2c_lock);
- }
- mutex_unlock(&go->hw_lock);
- return ret;
-}
-
-static int go7007_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data *data)
-{
- struct go7007 *go = i2c_get_adapdata(adapter);
-
- if (size != I2C_SMBUS_BYTE_DATA)
- return -1;
- return go7007_i2c_xfer(go, addr, read_write == I2C_SMBUS_READ, command,
- flags & I2C_CLIENT_SCCB ? 0x10 : 0x00, &data->byte);
-}
-
-/* VERY LIMITED I2C master xfer function -- only needed because the
- * SMBus functions only support 8-bit commands and the SAA7135 uses
- * 16-bit commands. The I2C interface on the GO7007, as limited as
- * it is, does support this mode. */
-
-static int go7007_i2c_master_xfer(struct i2c_adapter *adapter,
- struct i2c_msg msgs[], int num)
-{
- struct go7007 *go = i2c_get_adapdata(adapter);
- int i;
-
- for (i = 0; i < num; ++i) {
- /* We can only do two things here -- write three bytes, or
- * write two bytes and read one byte. */
- if (msgs[i].len == 2) {
- if (i + 1 == num || msgs[i].addr != msgs[i + 1].addr ||
- (msgs[i].flags & I2C_M_RD) ||
- !(msgs[i + 1].flags & I2C_M_RD) ||
- msgs[i + 1].len != 1)
- return -1;
- if (go7007_i2c_xfer(go, msgs[i].addr, 1,
- (msgs[i].buf[0] << 8) | msgs[i].buf[1],
- 0x01, &msgs[i + 1].buf[0]) < 0)
- return -1;
- ++i;
- } else if (msgs[i].len == 3) {
- if (msgs[i].flags & I2C_M_RD)
- return -1;
- if (msgs[i].len != 3)
- return -1;
- if (go7007_i2c_xfer(go, msgs[i].addr, 0,
- (msgs[i].buf[0] << 8) | msgs[i].buf[1],
- 0x01, &msgs[i].buf[2]) < 0)
- return -1;
- } else
- return -1;
- }
-
- return 0;
-}
-
-static u32 go7007_functionality(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_SMBUS_BYTE_DATA;
-}
-
-static struct i2c_algorithm go7007_algo = {
- .smbus_xfer = go7007_smbus_xfer,
- .master_xfer = go7007_i2c_master_xfer,
- .functionality = go7007_functionality,
-};
-
-static struct i2c_adapter go7007_adap_templ = {
- .owner = THIS_MODULE,
- .name = "WIS GO7007SB",
- .algo = &go7007_algo,
-};
-
-int go7007_i2c_init(struct go7007 *go)
-{
- memcpy(&go->i2c_adapter, &go7007_adap_templ,
- sizeof(go7007_adap_templ));
- go->i2c_adapter.dev.parent = go->dev;
- i2c_set_adapdata(&go->i2c_adapter, go);
- if (i2c_add_adapter(&go->i2c_adapter) < 0) {
- printk(KERN_ERR
- "go7007-i2c: error: i2c_add_adapter failed\n");
- return -1;
- }
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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.
- */
-
-/*
- * This is the private include file for the go7007 driver. It should not
- * be included by anybody but the driver itself, and especially not by
- * user-space applications.
- */
-
-#include <media/v4l2-device.h>
-
-struct go7007;
-
-/* IDs to activate board-specific support code */
-#define GO7007_BOARDID_MATRIX_II 0
-#define GO7007_BOARDID_MATRIX_RELOAD 1
-#define GO7007_BOARDID_STAR_TREK 2
-#define GO7007_BOARDID_PCI_VOYAGER 3
-#define GO7007_BOARDID_XMEN 4
-#define GO7007_BOARDID_XMEN_II 5
-#define GO7007_BOARDID_XMEN_III 6
-#define GO7007_BOARDID_MATRIX_REV 7
-#define GO7007_BOARDID_PX_M402U 16
-#define GO7007_BOARDID_PX_TV402U_ANY 17 /* need to check tuner model */
-#define GO7007_BOARDID_PX_TV402U_NA 18 /* detected NTSC tuner */
-#define GO7007_BOARDID_PX_TV402U_EU 19 /* detected PAL tuner */
-#define GO7007_BOARDID_PX_TV402U_JP 20 /* detected NTSC-J tuner */
-#define GO7007_BOARDID_LIFEVIEW_LR192 21 /* TV Walker Ultra */
-#define GO7007_BOARDID_ENDURA 22
-#define GO7007_BOARDID_ADLINK_MPG24 23
-#define GO7007_BOARDID_SENSORAY_2250 24 /* Sensoray 2250/2251 */
-
-/* Various characteristics of each board */
-#define GO7007_BOARD_HAS_AUDIO (1<<0)
-#define GO7007_BOARD_USE_ONBOARD_I2C (1<<1)
-#define GO7007_BOARD_HAS_TUNER (1<<2)
-
-/* Characteristics of sensor devices */
-#define GO7007_SENSOR_VALID_POLAR (1<<0)
-#define GO7007_SENSOR_HREF_POLAR (1<<1)
-#define GO7007_SENSOR_VREF_POLAR (1<<2)
-#define GO7007_SENSOR_FIELD_ID_POLAR (1<<3)
-#define GO7007_SENSOR_BIT_WIDTH (1<<4)
-#define GO7007_SENSOR_VALID_ENABLE (1<<5)
-#define GO7007_SENSOR_656 (1<<6)
-#define GO7007_SENSOR_CONFIG_MASK 0x7f
-#define GO7007_SENSOR_TV (1<<7)
-#define GO7007_SENSOR_VBI (1<<8)
-#define GO7007_SENSOR_SCALING (1<<9)
-
-/* Characteristics of audio sensor devices */
-#define GO7007_AUDIO_I2S_MODE_1 (1)
-#define GO7007_AUDIO_I2S_MODE_2 (2)
-#define GO7007_AUDIO_I2S_MODE_3 (3)
-#define GO7007_AUDIO_BCLK_POLAR (1<<2)
-#define GO7007_AUDIO_WORD_14 (14<<4)
-#define GO7007_AUDIO_WORD_16 (16<<4)
-#define GO7007_AUDIO_ONE_CHANNEL (1<<11)
-#define GO7007_AUDIO_I2S_MASTER (1<<16)
-#define GO7007_AUDIO_OKI_MODE (1<<17)
-
-struct go7007_board_info {
- char *firmware;
- unsigned int flags;
- int hpi_buffer_cap;
- unsigned int sensor_flags;
- int sensor_width;
- int sensor_height;
- int sensor_framerate;
- int sensor_h_offset;
- int sensor_v_offset;
- unsigned int audio_flags;
- int audio_rate;
- int audio_bclk_div;
- int audio_main_div;
- int num_i2c_devs;
- struct {
- const char *type;
- int id;
- int addr;
- } i2c_devs[4];
- int num_inputs;
- struct {
- int video_input;
- int audio_input;
- char *name;
- } inputs[4];
-};
-
-struct go7007_hpi_ops {
- int (*interface_reset)(struct go7007 *go);
- int (*write_interrupt)(struct go7007 *go, int addr, int data);
- int (*read_interrupt)(struct go7007 *go);
- int (*stream_start)(struct go7007 *go);
- int (*stream_stop)(struct go7007 *go);
- int (*send_firmware)(struct go7007 *go, u8 *data, int len);
- int (*send_command)(struct go7007 *go, unsigned int cmd, void *arg);
-};
-
-/* The video buffer size must be a multiple of PAGE_SIZE */
-#define GO7007_BUF_PAGES (128 * 1024 / PAGE_SIZE)
-#define GO7007_BUF_SIZE (GO7007_BUF_PAGES << PAGE_SHIFT)
-
-struct go7007_buffer {
- struct go7007 *go; /* Reverse reference for VMA ops */
- int index; /* Reverse reference for DQBUF */
- enum { BUF_STATE_IDLE, BUF_STATE_QUEUED, BUF_STATE_DONE } state;
- u32 seq;
- struct timeval timestamp;
- struct list_head stream;
- struct page *pages[GO7007_BUF_PAGES + 1]; /* extra for straddling */
- unsigned long user_addr;
- unsigned int page_count;
- unsigned int offset;
- unsigned int bytesused;
- unsigned int frame_offset;
- u32 modet_active;
- int mapped;
-};
-
-struct go7007_file {
- struct go7007 *go;
- struct mutex lock;
- int buf_count;
- struct go7007_buffer *bufs;
-};
-
-#define GO7007_FORMAT_MJPEG 0
-#define GO7007_FORMAT_MPEG4 1
-#define GO7007_FORMAT_MPEG1 2
-#define GO7007_FORMAT_MPEG2 3
-#define GO7007_FORMAT_H263 4
-
-#define GO7007_RATIO_1_1 0
-#define GO7007_RATIO_4_3 1
-#define GO7007_RATIO_16_9 2
-
-enum go7007_parser_state {
- STATE_DATA,
- STATE_00,
- STATE_00_00,
- STATE_00_00_01,
- STATE_FF,
- STATE_VBI_LEN_A,
- STATE_VBI_LEN_B,
- STATE_MODET_MAP,
- STATE_UNPARSED,
-};
-
-struct go7007 {
- struct device *dev;
- struct go7007_board_info *board_info;
- unsigned int board_id;
- int tuner_type;
- int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */
- char name[64];
- struct video_device *video_dev;
- struct v4l2_device v4l2_dev;
- int ref_count;
- enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status;
- spinlock_t spinlock;
- struct mutex hw_lock;
- int streaming;
- int in_use;
- int audio_enabled;
-
- /* Video input */
- int input;
- enum { GO7007_STD_NTSC, GO7007_STD_PAL, GO7007_STD_OTHER } standard;
- int sensor_framerate;
- int width;
- int height;
- int encoder_h_offset;
- int encoder_v_offset;
- unsigned int encoder_h_halve:1;
- unsigned int encoder_v_halve:1;
- unsigned int encoder_subsample:1;
-
- /* Encoder config */
- int format;
- int bitrate;
- int fps_scale;
- int pali;
- int aspect_ratio;
- int gop_size;
- unsigned int ipb:1;
- unsigned int closed_gop:1;
- unsigned int repeat_seqhead:1;
- unsigned int seq_header_enable:1;
- unsigned int gop_header_enable:1;
- unsigned int dvd_mode:1;
- unsigned int interlace_coding:1;
-
- /* Motion detection */
- unsigned int modet_enable:1;
- struct {
- unsigned int enable:1;
- int pixel_threshold;
- int motion_threshold;
- int mb_threshold;
- } modet[4];
- unsigned char modet_map[1624];
- unsigned char active_map[216];
-
- /* Video streaming */
- struct go7007_buffer *active_buf;
- enum go7007_parser_state state;
- int parse_length;
- u16 modet_word;
- int seen_frame;
- u32 next_seq;
- struct list_head stream;
- wait_queue_head_t frame_waitq;
-
- /* Audio streaming */
- void (*audio_deliver)(struct go7007 *go, u8 *buf, int length);
- void *snd_context;
-
- /* I2C */
- int i2c_adapter_online;
- struct i2c_adapter i2c_adapter;
-
- /* HPI driver */
- struct go7007_hpi_ops *hpi_ops;
- void *hpi_context;
- int interrupt_available;
- wait_queue_head_t interrupt_waitq;
- unsigned short interrupt_value;
- unsigned short interrupt_data;
-};
-
-static inline struct go7007 *to_go7007(struct v4l2_device *v4l2_dev)
-{
- return container_of(v4l2_dev, struct go7007, v4l2_dev);
-}
-
-/* All of these must be called with the hpi_lock mutex held! */
-#define go7007_interface_reset(go) \
- ((go)->hpi_ops->interface_reset(go))
-#define go7007_write_interrupt(go, x, y) \
- ((go)->hpi_ops->write_interrupt)((go), (x), (y))
-#define go7007_stream_start(go) \
- ((go)->hpi_ops->stream_start(go))
-#define go7007_stream_stop(go) \
- ((go)->hpi_ops->stream_stop(go))
-#define go7007_send_firmware(go, x, y) \
- ((go)->hpi_ops->send_firmware)((go), (x), (y))
-#define go7007_write_addr(go, x, y) \
- ((go)->hpi_ops->write_interrupt)((go), (x)|0x8000, (y))
-
-/* go7007-driver.c */
-int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data);
-int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data);
-int go7007_boot_encoder(struct go7007 *go, int init_i2c);
-int go7007_reset_encoder(struct go7007 *go);
-int go7007_register_encoder(struct go7007 *go);
-int go7007_start_encoder(struct go7007 *go);
-void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length);
-struct go7007 *go7007_alloc(struct go7007_board_info *board,
- struct device *dev);
-void go7007_remove(struct go7007 *go);
-
-/* go7007-fw.c */
-int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen);
-
-/* go7007-i2c.c */
-int go7007_i2c_init(struct go7007 *go);
-int go7007_i2c_remove(struct go7007 *go);
-
-/* go7007-v4l2.c */
-int go7007_v4l2_init(struct go7007 *go);
-void go7007_v4l2_remove(struct go7007 *go);
-
-/* snd-go7007.c */
-int go7007_snd_init(struct go7007 *go);
-int go7007_snd_remove(struct go7007 *go);
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/wait.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/mm.h>
-#include <linux/usb.h>
-#include <linux/i2c.h>
-#include <asm/byteorder.h>
-#include <media/tvaudio.h>
-
-#include "go7007-priv.h"
-#include "wis-i2c.h"
-
-static unsigned int assume_endura;
-module_param(assume_endura, int, 0644);
-MODULE_PARM_DESC(assume_endura, "when probing fails, "
- "hardware is a Pelco Endura");
-
-/* #define GO7007_USB_DEBUG */
-/* #define GO7007_I2C_DEBUG */ /* for debugging the EZ-USB I2C adapter */
-
-#define HPI_STATUS_ADDR 0xFFF4
-#define INT_PARAM_ADDR 0xFFF6
-#define INT_INDEX_ADDR 0xFFF8
-
-/*
- * Pipes on EZ-USB interface:
- * 0 snd - Control
- * 0 rcv - Control
- * 2 snd - Download firmware (control)
- * 4 rcv - Read Interrupt (interrupt)
- * 6 rcv - Read Video (bulk)
- * 8 rcv - Read Audio (bulk)
- */
-
-#define GO7007_USB_EZUSB (1<<0)
-#define GO7007_USB_EZUSB_I2C (1<<1)
-
-struct go7007_usb_board {
- unsigned int flags;
- struct go7007_board_info main_info;
-};
-
-struct go7007_usb {
- struct go7007_usb_board *board;
- struct mutex i2c_lock;
- struct usb_device *usbdev;
- struct urb *video_urbs[8];
- struct urb *audio_urbs[8];
- struct urb *intr_urb;
-};
-
-/*********************** Product specification data ***********************/
-
-static struct go7007_usb_board board_matrix_ii = {
- .flags = GO7007_USB_EZUSB,
- .main_info = {
- .firmware = "go7007tv.bin",
- .flags = GO7007_BOARD_HAS_AUDIO |
- GO7007_BOARD_USE_ONBOARD_I2C,
- .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
- GO7007_AUDIO_WORD_16,
- .audio_rate = 48000,
- .audio_bclk_div = 8,
- .audio_main_div = 2,
- .hpi_buffer_cap = 7,
- .sensor_flags = GO7007_SENSOR_656 |
- GO7007_SENSOR_VALID_ENABLE |
- GO7007_SENSOR_TV |
- GO7007_SENSOR_VBI |
- GO7007_SENSOR_SCALING,
- .num_i2c_devs = 1,
- .i2c_devs = {
- {
- .type = "wis_saa7115",
- .id = I2C_DRIVERID_WIS_SAA7115,
- .addr = 0x20,
- },
- },
- .num_inputs = 2,
- .inputs = {
- {
- .video_input = 0,
- .name = "Composite",
- },
- {
- .video_input = 9,
- .name = "S-Video",
- },
- },
- },
-};
-
-static struct go7007_usb_board board_matrix_reload = {
- .flags = GO7007_USB_EZUSB,
- .main_info = {
- .firmware = "go7007tv.bin",
- .flags = GO7007_BOARD_HAS_AUDIO |
- GO7007_BOARD_USE_ONBOARD_I2C,
- .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
- GO7007_AUDIO_I2S_MASTER |
- GO7007_AUDIO_WORD_16,
- .audio_rate = 48000,
- .audio_bclk_div = 8,
- .audio_main_div = 2,
- .hpi_buffer_cap = 7,
- .sensor_flags = GO7007_SENSOR_656 |
- GO7007_SENSOR_TV,
- .num_i2c_devs = 1,
- .i2c_devs = {
- {
- .type = "wis_saa7113",
- .id = I2C_DRIVERID_WIS_SAA7113,
- .addr = 0x25,
- },
- },
- .num_inputs = 2,
- .inputs = {
- {
- .video_input = 0,
- .name = "Composite",
- },
- {
- .video_input = 9,
- .name = "S-Video",
- },
- },
- },
-};
-
-static struct go7007_usb_board board_star_trek = {
- .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
- .main_info = {
- .firmware = "go7007tv.bin",
- .flags = GO7007_BOARD_HAS_AUDIO, /* |
- GO7007_BOARD_HAS_TUNER, */
- .sensor_flags = GO7007_SENSOR_656 |
- GO7007_SENSOR_VALID_ENABLE |
- GO7007_SENSOR_TV |
- GO7007_SENSOR_VBI |
- GO7007_SENSOR_SCALING,
- .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
- GO7007_AUDIO_WORD_16,
- .audio_bclk_div = 8,
- .audio_main_div = 2,
- .hpi_buffer_cap = 7,
- .num_i2c_devs = 1,
- .i2c_devs = {
- {
- .type = "wis_saa7115",
- .id = I2C_DRIVERID_WIS_SAA7115,
- .addr = 0x20,
- },
- },
- .num_inputs = 2,
- .inputs = {
- {
- .video_input = 1,
- /* .audio_input = AUDIO_EXTERN, */
- .name = "Composite",
- },
- {
- .video_input = 8,
- /* .audio_input = AUDIO_EXTERN, */
- .name = "S-Video",
- },
- /* {
- * .video_input = 3,
- * .audio_input = AUDIO_TUNER,
- * .name = "Tuner",
- * },
- */
- },
- },
-};
-
-static struct go7007_usb_board board_px_tv402u = {
- .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
- .main_info = {
- .firmware = "go7007tv.bin",
- .flags = GO7007_BOARD_HAS_AUDIO |
- GO7007_BOARD_HAS_TUNER,
- .sensor_flags = GO7007_SENSOR_656 |
- GO7007_SENSOR_VALID_ENABLE |
- GO7007_SENSOR_TV |
- GO7007_SENSOR_VBI |
- GO7007_SENSOR_SCALING,
- .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
- GO7007_AUDIO_WORD_16,
- .audio_bclk_div = 8,
- .audio_main_div = 2,
- .hpi_buffer_cap = 7,
- .num_i2c_devs = 3,
- .i2c_devs = {
- {
- .type = "wis_saa7115",
- .id = I2C_DRIVERID_WIS_SAA7115,
- .addr = 0x20,
- },
- {
- .type = "wis_uda1342",
- .id = I2C_DRIVERID_WIS_UDA1342,
- .addr = 0x1a,
- },
- {
- .type = "wis_sony_tuner",
- .id = I2C_DRIVERID_WIS_SONY_TUNER,
- .addr = 0x60,
- },
- },
- .num_inputs = 3,
- .inputs = {
- {
- .video_input = 1,
- .audio_input = TVAUDIO_INPUT_EXTERN,
- .name = "Composite",
- },
- {
- .video_input = 8,
- .audio_input = TVAUDIO_INPUT_EXTERN,
- .name = "S-Video",
- },
- {
- .video_input = 3,
- .audio_input = TVAUDIO_INPUT_TUNER,
- .name = "Tuner",
- },
- },
- },
-};
-
-static struct go7007_usb_board board_xmen = {
- .flags = 0,
- .main_info = {
- .firmware = "go7007tv.bin",
- .flags = GO7007_BOARD_USE_ONBOARD_I2C,
- .hpi_buffer_cap = 0,
- .sensor_flags = GO7007_SENSOR_VREF_POLAR,
- .sensor_width = 320,
- .sensor_height = 240,
- .sensor_framerate = 30030,
- .audio_flags = GO7007_AUDIO_ONE_CHANNEL |
- GO7007_AUDIO_I2S_MODE_3 |
- GO7007_AUDIO_WORD_14 |
- GO7007_AUDIO_I2S_MASTER |
- GO7007_AUDIO_BCLK_POLAR |
- GO7007_AUDIO_OKI_MODE,
- .audio_rate = 8000,
- .audio_bclk_div = 48,
- .audio_main_div = 1,
- .num_i2c_devs = 1,
- .i2c_devs = {
- {
- .type = "wis_ov7640",
- .id = I2C_DRIVERID_WIS_OV7640,
- .addr = 0x21,
- },
- },
- .num_inputs = 1,
- .inputs = {
- {
- .name = "Camera",
- },
- },
- },
-};
-
-static struct go7007_usb_board board_matrix_revolution = {
- .flags = GO7007_USB_EZUSB,
- .main_info = {
- .firmware = "go7007tv.bin",
- .flags = GO7007_BOARD_HAS_AUDIO |
- GO7007_BOARD_USE_ONBOARD_I2C,
- .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
- GO7007_AUDIO_I2S_MASTER |
- GO7007_AUDIO_WORD_16,
- .audio_rate = 48000,
- .audio_bclk_div = 8,
- .audio_main_div = 2,
- .hpi_buffer_cap = 7,
- .sensor_flags = GO7007_SENSOR_656 |
- GO7007_SENSOR_TV |
- GO7007_SENSOR_VBI,
- .num_i2c_devs = 1,
- .i2c_devs = {
- {
- .type = "wis_tw9903",
- .id = I2C_DRIVERID_WIS_TW9903,
- .addr = 0x44,
- },
- },
- .num_inputs = 2,
- .inputs = {
- {
- .video_input = 2,
- .name = "Composite",
- },
- {
- .video_input = 8,
- .name = "S-Video",
- },
- },
- },
-};
-
-static struct go7007_usb_board board_lifeview_lr192 = {
- .flags = GO7007_USB_EZUSB,
- .main_info = {
- .firmware = "go7007tv.bin",
- .flags = GO7007_BOARD_HAS_AUDIO |
- GO7007_BOARD_USE_ONBOARD_I2C,
- .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
- GO7007_AUDIO_WORD_16,
- .audio_rate = 48000,
- .audio_bclk_div = 8,
- .audio_main_div = 2,
- .hpi_buffer_cap = 7,
- .sensor_flags = GO7007_SENSOR_656 |
- GO7007_SENSOR_VALID_ENABLE |
- GO7007_SENSOR_TV |
- GO7007_SENSOR_VBI |
- GO7007_SENSOR_SCALING,
- .num_i2c_devs = 0,
- .num_inputs = 1,
- .inputs = {
- {
- .video_input = 0,
- .name = "Composite",
- },
- },
- },
-};
-
-static struct go7007_usb_board board_endura = {
- .flags = 0,
- .main_info = {
- .firmware = "go7007tv.bin",
- .flags = 0,
- .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
- GO7007_AUDIO_I2S_MASTER |
- GO7007_AUDIO_WORD_16,
- .audio_rate = 8000,
- .audio_bclk_div = 48,
- .audio_main_div = 8,
- .hpi_buffer_cap = 0,
- .sensor_flags = GO7007_SENSOR_656 |
- GO7007_SENSOR_TV,
- .sensor_h_offset = 8,
- .num_i2c_devs = 0,
- .num_inputs = 1,
- .inputs = {
- {
- .name = "Camera",
- },
- },
- },
-};
-
-static struct go7007_usb_board board_adlink_mpg24 = {
- .flags = 0,
- .main_info = {
- .firmware = "go7007tv.bin",
- .flags = GO7007_BOARD_USE_ONBOARD_I2C,
- .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
- GO7007_AUDIO_I2S_MASTER |
- GO7007_AUDIO_WORD_16,
- .audio_rate = 48000,
- .audio_bclk_div = 8,
- .audio_main_div = 2,
- .hpi_buffer_cap = 0,
- .sensor_flags = GO7007_SENSOR_656 |
- GO7007_SENSOR_TV |
- GO7007_SENSOR_VBI,
- .num_i2c_devs = 1,
- .i2c_devs = {
- {
- .type = "wis_tw2804",
- .id = I2C_DRIVERID_WIS_TW2804,
- .addr = 0x00, /* yes, really */
- },
- },
- .num_inputs = 1,
- .inputs = {
- {
- .name = "Composite",
- },
- },
- },
-};
-
-static struct go7007_usb_board board_sensoray_2250 = {
- .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
- .main_info = {
- .firmware = "go7007tv.bin",
- .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
- GO7007_AUDIO_I2S_MASTER |
- GO7007_AUDIO_WORD_16,
- .flags = GO7007_BOARD_HAS_AUDIO,
- .audio_rate = 48000,
- .audio_bclk_div = 8,
- .audio_main_div = 2,
- .hpi_buffer_cap = 7,
- .sensor_flags = GO7007_SENSOR_656 |
- GO7007_SENSOR_TV,
- .num_i2c_devs = 1,
- .i2c_devs = {
- {
- .type = "s2250",
- .id = I2C_DRIVERID_S2250,
- .addr = 0x43,
- },
- },
- .num_inputs = 2,
- .inputs = {
- {
- .video_input = 0,
- .name = "Composite",
- },
- {
- .video_input = 1,
- .name = "S-Video",
- },
- },
- },
-};
-
-MODULE_FIRMWARE("go7007tv.bin");
-
-static const struct usb_device_id go7007_usb_id_table[] = {
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
- USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
- .idProduct = 0x7007, /* Product ID of GO7007SB chip */
- .bcdDevice_lo = 0x200, /* Revision number of XMen */
- .bcdDevice_hi = 0x200,
- .bInterfaceClass = 255,
- .bInterfaceSubClass = 0,
- .bInterfaceProtocol = 255,
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN,
- },
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
- .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
- .idProduct = 0x7007, /* Product ID of GO7007SB chip */
- .bcdDevice_lo = 0x202, /* Revision number of Matrix II */
- .bcdDevice_hi = 0x202,
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_II,
- },
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
- .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
- .idProduct = 0x7007, /* Product ID of GO7007SB chip */
- .bcdDevice_lo = 0x204, /* Revision number of Matrix */
- .bcdDevice_hi = 0x204, /* Reloaded */
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_RELOAD,
- },
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
- USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
- .idProduct = 0x7007, /* Product ID of GO7007SB chip */
- .bcdDevice_lo = 0x205, /* Revision number of XMen-II */
- .bcdDevice_hi = 0x205,
- .bInterfaceClass = 255,
- .bInterfaceSubClass = 0,
- .bInterfaceProtocol = 255,
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_II,
- },
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
- .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
- .idProduct = 0x7007, /* Product ID of GO7007SB chip */
- .bcdDevice_lo = 0x208, /* Revision number of Star Trek */
- .bcdDevice_hi = 0x208,
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_STAR_TREK,
- },
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
- USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
- .idProduct = 0x7007, /* Product ID of GO7007SB chip */
- .bcdDevice_lo = 0x209, /* Revision number of XMen-III */
- .bcdDevice_hi = 0x209,
- .bInterfaceClass = 255,
- .bInterfaceSubClass = 0,
- .bInterfaceProtocol = 255,
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_III,
- },
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
- .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
- .idProduct = 0x7007, /* Product ID of GO7007SB chip */
- .bcdDevice_lo = 0x210, /* Revision number of Matrix */
- .bcdDevice_hi = 0x210, /* Revolution */
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_REV,
- },
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
- .idVendor = 0x093b, /* Vendor ID of Plextor */
- .idProduct = 0xa102, /* Product ID of M402U */
- .bcdDevice_lo = 0x1, /* revision number of Blueberry */
- .bcdDevice_hi = 0x1,
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_M402U,
- },
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
- .idVendor = 0x093b, /* Vendor ID of Plextor */
- .idProduct = 0xa104, /* Product ID of TV402U */
- .bcdDevice_lo = 0x1,
- .bcdDevice_hi = 0x1,
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_TV402U_ANY,
- },
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
- .idVendor = 0x10fd, /* Vendor ID of Anubis Electronics */
- .idProduct = 0xde00, /* Product ID of Lifeview LR192 */
- .bcdDevice_lo = 0x1,
- .bcdDevice_hi = 0x1,
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192,
- },
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
- .idVendor = 0x1943, /* Vendor ID Sensoray */
- .idProduct = 0x2250, /* Product ID of 2250/2251 */
- .bcdDevice_lo = 0x1,
- .bcdDevice_hi = 0x1,
- .driver_info = (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250,
- },
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, go7007_usb_id_table);
-
-/********************* Driver for EZ-USB HPI interface *********************/
-
-static int go7007_usb_vendor_request(struct go7007 *go, int request,
- int value, int index, void *transfer_buffer, int length, int in)
-{
- struct go7007_usb *usb = go->hpi_context;
- int timeout = 5000;
-
- if (in) {
- return usb_control_msg(usb->usbdev,
- usb_rcvctrlpipe(usb->usbdev, 0), request,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- value, index, transfer_buffer, length, timeout);
- } else {
- return usb_control_msg(usb->usbdev,
- usb_sndctrlpipe(usb->usbdev, 0), request,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, transfer_buffer, length, timeout);
- }
-}
-
-static int go7007_usb_interface_reset(struct go7007 *go)
-{
- struct go7007_usb *usb = go->hpi_context;
- u16 intr_val, intr_data;
-
- /* Reset encoder */
- if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0)
- return -1;
- msleep(100);
-
- if (usb->board->flags & GO7007_USB_EZUSB) {
- /* Reset buffer in EZ-USB */
-#ifdef GO7007_USB_DEBUG
- printk(KERN_DEBUG "go7007-usb: resetting EZ-USB buffers\n");
-#endif
- if (go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0 ||
- go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0)
- return -1;
-
- /* Reset encoder again */
- if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0)
- return -1;
- msleep(100);
- }
-
- /* Wait for an interrupt to indicate successful hardware reset */
- if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
- (intr_val & ~0x1) != 0x55aa) {
- printk(KERN_ERR
- "go7007-usb: unable to reset the USB interface\n");
- return -1;
- }
- return 0;
-}
-
-static int go7007_usb_ezusb_write_interrupt(struct go7007 *go,
- int addr, int data)
-{
- struct go7007_usb *usb = go->hpi_context;
- int i, r;
- u16 status_reg;
- int timeout = 500;
-
-#ifdef GO7007_USB_DEBUG
- printk(KERN_DEBUG
- "go7007-usb: WriteInterrupt: %04x %04x\n", addr, data);
-#endif
-
- for (i = 0; i < 100; ++i) {
- r = usb_control_msg(usb->usbdev,
- usb_rcvctrlpipe(usb->usbdev, 0), 0x14,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0, HPI_STATUS_ADDR, &status_reg,
- sizeof(status_reg), timeout);
- if (r < 0)
- goto write_int_error;
- __le16_to_cpus(&status_reg);
- if (!(status_reg & 0x0010))
- break;
- msleep(10);
- }
- if (i == 100) {
- printk(KERN_ERR
- "go7007-usb: device is hung, status reg = 0x%04x\n",
- status_reg);
- return -1;
- }
- r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), 0x12,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE, data,
- INT_PARAM_ADDR, NULL, 0, timeout);
- if (r < 0)
- goto write_int_error;
- r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0),
- 0x12, USB_TYPE_VENDOR | USB_RECIP_DEVICE, addr,
- INT_INDEX_ADDR, NULL, 0, timeout);
- if (r < 0)
- goto write_int_error;
- return 0;
-
-write_int_error:
- printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r);
- return r;
-}
-
-static int go7007_usb_onboard_write_interrupt(struct go7007 *go,
- int addr, int data)
-{
- struct go7007_usb *usb = go->hpi_context;
- u8 *tbuf;
- int r;
- int timeout = 500;
-
-#ifdef GO7007_USB_DEBUG
- printk(KERN_DEBUG
- "go7007-usb: WriteInterrupt: %04x %04x\n", addr, data);
-#endif
-
- tbuf = kzalloc(8, GFP_KERNEL);
- if (tbuf == NULL)
- return -ENOMEM;
- tbuf[0] = data & 0xff;
- tbuf[1] = data >> 8;
- tbuf[2] = addr & 0xff;
- tbuf[3] = addr >> 8;
- r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 2), 0x00,
- USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x55aa,
- 0xf0f0, tbuf, 8, timeout);
- kfree(tbuf);
- if (r < 0) {
- printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r);
- return r;
- }
- return 0;
-}
-
-static void go7007_usb_readinterrupt_complete(struct urb *urb)
-{
- struct go7007 *go = (struct go7007 *)urb->context;
- u16 *regs = (u16 *)urb->transfer_buffer;
- int status = urb->status;
-
- if (status) {
- if (status != -ESHUTDOWN &&
- go->status != STATUS_SHUTDOWN) {
- printk(KERN_ERR
- "go7007-usb: error in read interrupt: %d\n",
- urb->status);
- } else {
- wake_up(&go->interrupt_waitq);
- return;
- }
- } else if (urb->actual_length != urb->transfer_buffer_length) {
- printk(KERN_ERR "go7007-usb: short read in interrupt pipe!\n");
- } else {
- go->interrupt_available = 1;
- go->interrupt_data = __le16_to_cpu(regs[0]);
- go->interrupt_value = __le16_to_cpu(regs[1]);
-#ifdef GO7007_USB_DEBUG
- printk(KERN_DEBUG "go7007-usb: ReadInterrupt: %04x %04x\n",
- go->interrupt_value, go->interrupt_data);
-#endif
- }
-
- wake_up(&go->interrupt_waitq);
-}
-
-static int go7007_usb_read_interrupt(struct go7007 *go)
-{
- struct go7007_usb *usb = go->hpi_context;
- int r;
-
- r = usb_submit_urb(usb->intr_urb, GFP_KERNEL);
- if (r < 0) {
- printk(KERN_ERR
- "go7007-usb: unable to submit interrupt urb: %d\n", r);
- return r;
- }
- return 0;
-}
-
-static void go7007_usb_read_video_pipe_complete(struct urb *urb)
-{
- struct go7007 *go = (struct go7007 *)urb->context;
- int r, status = urb->status;
-
- if (!go->streaming) {
- wake_up_interruptible(&go->frame_waitq);
- return;
- }
- if (status) {
- printk(KERN_ERR "go7007-usb: error in video pipe: %d\n",
- status);
- return;
- }
- if (urb->actual_length != urb->transfer_buffer_length) {
- printk(KERN_ERR "go7007-usb: short read in video pipe!\n");
- return;
- }
- go7007_parse_video_stream(go, urb->transfer_buffer, urb->actual_length);
- r = usb_submit_urb(urb, GFP_ATOMIC);
- if (r < 0)
- printk(KERN_ERR "go7007-usb: error in video pipe: %d\n", r);
-}
-
-static void go7007_usb_read_audio_pipe_complete(struct urb *urb)
-{
- struct go7007 *go = (struct go7007 *)urb->context;
- int r, status = urb->status;
-
- if (!go->streaming)
- return;
- if (status) {
- printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n",
- status);
- return;
- }
- if (urb->actual_length != urb->transfer_buffer_length) {
- printk(KERN_ERR "go7007-usb: short read in audio pipe!\n");
- return;
- }
- if (go->audio_deliver != NULL)
- go->audio_deliver(go, urb->transfer_buffer, urb->actual_length);
- r = usb_submit_urb(urb, GFP_ATOMIC);
- if (r < 0)
- printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", r);
-}
-
-static int go7007_usb_stream_start(struct go7007 *go)
-{
- struct go7007_usb *usb = go->hpi_context;
- int i, r;
-
- for (i = 0; i < 8; ++i) {
- r = usb_submit_urb(usb->video_urbs[i], GFP_KERNEL);
- if (r < 0) {
- printk(KERN_ERR "go7007-usb: error submitting video "
- "urb %d: %d\n", i, r);
- goto video_submit_failed;
- }
- }
- if (!go->audio_enabled)
- return 0;
-
- for (i = 0; i < 8; ++i) {
- r = usb_submit_urb(usb->audio_urbs[i], GFP_KERNEL);
- if (r < 0) {
- printk(KERN_ERR "go7007-usb: error submitting audio "
- "urb %d: %d\n", i, r);
- goto audio_submit_failed;
- }
- }
- return 0;
-
-audio_submit_failed:
- for (i = 0; i < 7; ++i)
- usb_kill_urb(usb->audio_urbs[i]);
-video_submit_failed:
- for (i = 0; i < 8; ++i)
- usb_kill_urb(usb->video_urbs[i]);
- return -1;
-}
-
-static int go7007_usb_stream_stop(struct go7007 *go)
-{
- struct go7007_usb *usb = go->hpi_context;
- int i;
-
- if (go->status == STATUS_SHUTDOWN)
- return 0;
- for (i = 0; i < 8; ++i)
- usb_kill_urb(usb->video_urbs[i]);
- if (go->audio_enabled)
- for (i = 0; i < 8; ++i)
- usb_kill_urb(usb->audio_urbs[i]);
- return 0;
-}
-
-static int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len)
-{
- struct go7007_usb *usb = go->hpi_context;
- int transferred, pipe;
- int timeout = 500;
-
-#ifdef GO7007_USB_DEBUG
- printk(KERN_DEBUG "go7007-usb: DownloadBuffer sending %d bytes\n", len);
-#endif
-
- if (usb->board->flags & GO7007_USB_EZUSB)
- pipe = usb_sndbulkpipe(usb->usbdev, 2);
- else
- pipe = usb_sndbulkpipe(usb->usbdev, 3);
-
- return usb_bulk_msg(usb->usbdev, pipe, data, len,
- &transferred, timeout);
-}
-
-static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = {
- .interface_reset = go7007_usb_interface_reset,
- .write_interrupt = go7007_usb_ezusb_write_interrupt,
- .read_interrupt = go7007_usb_read_interrupt,
- .stream_start = go7007_usb_stream_start,
- .stream_stop = go7007_usb_stream_stop,
- .send_firmware = go7007_usb_send_firmware,
-};
-
-static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = {
- .interface_reset = go7007_usb_interface_reset,
- .write_interrupt = go7007_usb_onboard_write_interrupt,
- .read_interrupt = go7007_usb_read_interrupt,
- .stream_start = go7007_usb_stream_start,
- .stream_stop = go7007_usb_stream_stop,
- .send_firmware = go7007_usb_send_firmware,
-};
-
-/********************* Driver for EZ-USB I2C adapter *********************/
-
-static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter,
- struct i2c_msg msgs[], int num)
-{
- struct go7007 *go = i2c_get_adapdata(adapter);
- struct go7007_usb *usb = go->hpi_context;
- u8 buf[16];
- int buf_len, i;
- int ret = -1;
-
- if (go->status == STATUS_SHUTDOWN)
- return -1;
-
- mutex_lock(&usb->i2c_lock);
-
- for (i = 0; i < num; ++i) {
- /* The hardware command is "write some bytes then read some
- * bytes", so we try to coalesce a write followed by a read
- * into a single USB transaction */
- if (i + 1 < num && msgs[i].addr == msgs[i + 1].addr &&
- !(msgs[i].flags & I2C_M_RD) &&
- (msgs[i + 1].flags & I2C_M_RD)) {
-#ifdef GO7007_I2C_DEBUG
- printk(KERN_DEBUG "go7007-usb: i2c write/read %d/%d "
- "bytes on %02x\n", msgs[i].len,
- msgs[i + 1].len, msgs[i].addr);
-#endif
- buf[0] = 0x01;
- buf[1] = msgs[i].len + 1;
- buf[2] = msgs[i].addr << 1;
- memcpy(&buf[3], msgs[i].buf, msgs[i].len);
- buf_len = msgs[i].len + 3;
- buf[buf_len++] = msgs[++i].len;
- } else if (msgs[i].flags & I2C_M_RD) {
-#ifdef GO7007_I2C_DEBUG
- printk(KERN_DEBUG "go7007-usb: i2c read %d "
- "bytes on %02x\n", msgs[i].len,
- msgs[i].addr);
-#endif
- buf[0] = 0x01;
- buf[1] = 1;
- buf[2] = msgs[i].addr << 1;
- buf[3] = msgs[i].len;
- buf_len = 4;
- } else {
-#ifdef GO7007_I2C_DEBUG
- printk(KERN_DEBUG "go7007-usb: i2c write %d "
- "bytes on %02x\n", msgs[i].len,
- msgs[i].addr);
-#endif
- buf[0] = 0x00;
- buf[1] = msgs[i].len + 1;
- buf[2] = msgs[i].addr << 1;
- memcpy(&buf[3], msgs[i].buf, msgs[i].len);
- buf_len = msgs[i].len + 3;
- buf[buf_len++] = 0;
- }
- if (go7007_usb_vendor_request(go, 0x24, 0, 0,
- buf, buf_len, 0) < 0)
- goto i2c_done;
- if (msgs[i].flags & I2C_M_RD) {
- memset(buf, 0, sizeof(buf));
- if (go7007_usb_vendor_request(go, 0x25, 0, 0, buf,
- msgs[i].len + 1, 1) < 0)
- goto i2c_done;
- memcpy(msgs[i].buf, buf + 1, msgs[i].len);
- }
- }
- ret = 0;
-
-i2c_done:
- mutex_unlock(&usb->i2c_lock);
- return ret;
-}
-
-static u32 go7007_usb_functionality(struct i2c_adapter *adapter)
-{
- /* No errors are reported by the hardware, so we don't bother
- * supporting quick writes to avoid confusing probing */
- return (I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK;
-}
-
-static struct i2c_algorithm go7007_usb_algo = {
- .master_xfer = go7007_usb_i2c_master_xfer,
- .functionality = go7007_usb_functionality,
-};
-
-static struct i2c_adapter go7007_usb_adap_templ = {
- .owner = THIS_MODULE,
- .name = "WIS GO7007SB EZ-USB",
- .algo = &go7007_usb_algo,
-};
-
-/********************* USB add/remove functions *********************/
-
-static int go7007_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct go7007 *go;
- struct go7007_usb *usb;
- struct go7007_usb_board *board;
- struct usb_device *usbdev = interface_to_usbdev(intf);
- char *name;
- int video_pipe, i, v_urb_len;
-
- printk(KERN_DEBUG "go7007-usb: probing new GO7007 USB board\n");
-
- switch (id->driver_info) {
- case GO7007_BOARDID_MATRIX_II:
- name = "WIS Matrix II or compatible";
- board = &board_matrix_ii;
- break;
- case GO7007_BOARDID_MATRIX_RELOAD:
- name = "WIS Matrix Reloaded or compatible";
- board = &board_matrix_reload;
- break;
- case GO7007_BOARDID_MATRIX_REV:
- name = "WIS Matrix Revolution or compatible";
- board = &board_matrix_revolution;
- break;
- case GO7007_BOARDID_STAR_TREK:
- name = "WIS Star Trek or compatible";
- board = &board_star_trek;
- break;
- case GO7007_BOARDID_XMEN:
- name = "WIS XMen or compatible";
- board = &board_xmen;
- break;
- case GO7007_BOARDID_XMEN_II:
- name = "WIS XMen II or compatible";
- board = &board_xmen;
- break;
- case GO7007_BOARDID_XMEN_III:
- name = "WIS XMen III or compatible";
- board = &board_xmen;
- break;
- case GO7007_BOARDID_PX_M402U:
- name = "Plextor PX-M402U";
- board = &board_matrix_ii;
- break;
- case GO7007_BOARDID_PX_TV402U_ANY:
- name = "Plextor PX-TV402U (unknown tuner)";
- board = &board_px_tv402u;
- break;
- case GO7007_BOARDID_LIFEVIEW_LR192:
- printk(KERN_ERR "go7007-usb: The Lifeview TV Walker Ultra "
- "is not supported. Sorry!\n");
- return 0;
- name = "Lifeview TV Walker Ultra";
- board = &board_lifeview_lr192;
- break;
- case GO7007_BOARDID_SENSORAY_2250:
- printk(KERN_INFO "Sensoray 2250 found\n");
- name = "Sensoray 2250/2251";
- board = &board_sensoray_2250;
- break;
- default:
- printk(KERN_ERR "go7007-usb: unknown board ID %d!\n",
- (unsigned int)id->driver_info);
- return 0;
- }
-
- usb = kzalloc(sizeof(struct go7007_usb), GFP_KERNEL);
- if (usb == NULL)
- return -ENOMEM;
-
- /* Allocate the URB and buffer for receiving incoming interrupts */
- usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (usb->intr_urb == NULL)
- goto allocfail;
- usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL);
- if (usb->intr_urb->transfer_buffer == NULL)
- goto allocfail;
-
- go = go7007_alloc(&board->main_info, &intf->dev);
- if (go == NULL)
- goto allocfail;
- usb->board = board;
- usb->usbdev = usbdev;
- go->board_id = id->driver_info;
- strncpy(go->name, name, sizeof(go->name));
- if (board->flags & GO7007_USB_EZUSB)
- go->hpi_ops = &go7007_usb_ezusb_hpi_ops;
- else
- go->hpi_ops = &go7007_usb_onboard_hpi_ops;
- go->hpi_context = usb;
- usb_fill_int_urb(usb->intr_urb, usb->usbdev,
- usb_rcvintpipe(usb->usbdev, 4),
- usb->intr_urb->transfer_buffer, 2*sizeof(u16),
- go7007_usb_readinterrupt_complete, go, 8);
- usb_set_intfdata(intf, &go->v4l2_dev);
-
- /* Boot the GO7007 */
- if (go7007_boot_encoder(go, go->board_info->flags &
- GO7007_BOARD_USE_ONBOARD_I2C) < 0)
- goto initfail;
-
- /* Register the EZ-USB I2C adapter, if we're using it */
- if (board->flags & GO7007_USB_EZUSB_I2C) {
- memcpy(&go->i2c_adapter, &go7007_usb_adap_templ,
- sizeof(go7007_usb_adap_templ));
- mutex_init(&usb->i2c_lock);
- go->i2c_adapter.dev.parent = go->dev;
- i2c_set_adapdata(&go->i2c_adapter, go);
- if (i2c_add_adapter(&go->i2c_adapter) < 0) {
- printk(KERN_ERR
- "go7007-usb: error: i2c_add_adapter failed\n");
- goto initfail;
- }
- go->i2c_adapter_online = 1;
- }
-
- /* Pelco and Adlink reused the XMen and XMen-III vendor and product
- * IDs for their own incompatible designs. We can detect XMen boards
- * by probing the sensor, but there is no way to probe the sensors on
- * the Pelco and Adlink designs so we default to the Adlink. If it
- * is actually a Pelco, the user must set the assume_endura module
- * parameter. */
- if ((go->board_id == GO7007_BOARDID_XMEN ||
- go->board_id == GO7007_BOARDID_XMEN_III) &&
- go->i2c_adapter_online) {
- union i2c_smbus_data data;
-
- /* Check to see if register 0x0A is 0x76 */
- i2c_smbus_xfer(&go->i2c_adapter, 0x21, I2C_CLIENT_SCCB,
- I2C_SMBUS_READ, 0x0A, I2C_SMBUS_BYTE_DATA, &data);
- if (data.byte != 0x76) {
- if (assume_endura) {
- go->board_id = GO7007_BOARDID_ENDURA;
- usb->board = board = &board_endura;
- go->board_info = &board->main_info;
- strncpy(go->name, "Pelco Endura",
- sizeof(go->name));
- } else {
- u16 channel;
-
- /* set GPIO5 to be an output, currently low */
- go7007_write_addr(go, 0x3c82, 0x0000);
- go7007_write_addr(go, 0x3c80, 0x00df);
- /* read channel number from GPIO[1:0] */
- go7007_read_addr(go, 0x3c81, &channel);
- channel &= 0x3;
- go->board_id = GO7007_BOARDID_ADLINK_MPG24;
- usb->board = board = &board_adlink_mpg24;
- go->board_info = &board->main_info;
- go->channel_number = channel;
- snprintf(go->name, sizeof(go->name),
- "Adlink PCI-MPG24, channel #%d",
- channel);
- }
- }
- }
-
- /* Probe the tuner model on the TV402U */
- if (go->board_id == GO7007_BOARDID_PX_TV402U_ANY) {
- u8 data[3];
-
- /* Board strapping indicates tuner model */
- if (go7007_usb_vendor_request(go, 0x41, 0, 0, data, 3, 1) < 0) {
- printk(KERN_ERR "go7007-usb: GPIO read failed!\n");
- goto initfail;
- }
- switch (data[0] >> 6) {
- case 1:
- go->board_id = GO7007_BOARDID_PX_TV402U_EU;
- go->tuner_type = TUNER_SONY_BTF_PG472Z;
- strncpy(go->name, "Plextor PX-TV402U-EU",
- sizeof(go->name));
- break;
- case 2:
- go->board_id = GO7007_BOARDID_PX_TV402U_JP;
- go->tuner_type = TUNER_SONY_BTF_PK467Z;
- strncpy(go->name, "Plextor PX-TV402U-JP",
- sizeof(go->name));
- break;
- case 3:
- go->board_id = GO7007_BOARDID_PX_TV402U_NA;
- go->tuner_type = TUNER_SONY_BTF_PB463Z;
- strncpy(go->name, "Plextor PX-TV402U-NA",
- sizeof(go->name));
- break;
- default:
- printk(KERN_DEBUG "go7007-usb: unable to detect "
- "tuner type!\n");
- break;
- }
- /* Configure tuner mode selection inputs connected
- * to the EZ-USB GPIO output pins */
- if (go7007_usb_vendor_request(go, 0x40, 0x7f02, 0,
- NULL, 0, 0) < 0) {
- printk(KERN_ERR "go7007-usb: GPIO write failed!\n");
- goto initfail;
- }
- }
-
- /* Print a nasty message if the user attempts to use a USB2.0 device in
- * a USB1.1 port. There will be silent corruption of the stream. */
- if ((board->flags & GO7007_USB_EZUSB) &&
- usbdev->speed != USB_SPEED_HIGH)
- printk(KERN_ERR "go7007-usb: *** WARNING *** This device "
- "must be connected to a USB 2.0 port! "
- "Attempting to capture video through a USB 1.1 "
- "port will result in stream corruption, even "
- "at low bitrates!\n");
-
- /* Do any final GO7007 initialization, then register the
- * V4L2 and ALSA interfaces */
- if (go7007_register_encoder(go) < 0)
- goto initfail;
-
- /* Allocate the URBs and buffers for receiving the video stream */
- if (board->flags & GO7007_USB_EZUSB) {
- v_urb_len = 1024;
- video_pipe = usb_rcvbulkpipe(usb->usbdev, 6);
- } else {
- v_urb_len = 512;
- video_pipe = usb_rcvbulkpipe(usb->usbdev, 1);
- }
- for (i = 0; i < 8; ++i) {
- usb->video_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
- if (usb->video_urbs[i] == NULL)
- goto initfail;
- usb->video_urbs[i]->transfer_buffer =
- kmalloc(v_urb_len, GFP_KERNEL);
- if (usb->video_urbs[i]->transfer_buffer == NULL)
- goto initfail;
- usb_fill_bulk_urb(usb->video_urbs[i], usb->usbdev, video_pipe,
- usb->video_urbs[i]->transfer_buffer, v_urb_len,
- go7007_usb_read_video_pipe_complete, go);
- }
-
- /* Allocate the URBs and buffers for receiving the audio stream */
- if ((board->flags & GO7007_USB_EZUSB) && go->audio_enabled)
- for (i = 0; i < 8; ++i) {
- usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
- if (usb->audio_urbs[i] == NULL)
- goto initfail;
- usb->audio_urbs[i]->transfer_buffer = kmalloc(4096,
- GFP_KERNEL);
- if (usb->audio_urbs[i]->transfer_buffer == NULL)
- goto initfail;
- usb_fill_bulk_urb(usb->audio_urbs[i], usb->usbdev,
- usb_rcvbulkpipe(usb->usbdev, 8),
- usb->audio_urbs[i]->transfer_buffer, 4096,
- go7007_usb_read_audio_pipe_complete, go);
- }
-
-
- go->status = STATUS_ONLINE;
- return 0;
-
-initfail:
- go->status = STATUS_SHUTDOWN;
- return 0;
-
-allocfail:
- if (usb->intr_urb) {
- kfree(usb->intr_urb->transfer_buffer);
- usb_free_urb(usb->intr_urb);
- }
- kfree(usb);
- return -ENOMEM;
-}
-
-static void go7007_usb_disconnect(struct usb_interface *intf)
-{
- struct go7007 *go = to_go7007(usb_get_intfdata(intf));
- struct go7007_usb *usb = go->hpi_context;
- struct urb *vurb, *aurb;
- int i;
-
- go->status = STATUS_SHUTDOWN;
- usb_kill_urb(usb->intr_urb);
-
- /* Free USB-related structs */
- for (i = 0; i < 8; ++i) {
- vurb = usb->video_urbs[i];
- if (vurb) {
- usb_kill_urb(vurb);
- kfree(vurb->transfer_buffer);
- usb_free_urb(vurb);
- }
- aurb = usb->audio_urbs[i];
- if (aurb) {
- usb_kill_urb(aurb);
- kfree(aurb->transfer_buffer);
- usb_free_urb(aurb);
- }
- }
- kfree(usb->intr_urb->transfer_buffer);
- usb_free_urb(usb->intr_urb);
-
- kfree(go->hpi_context);
-
- go7007_remove(go);
-}
-
-static struct usb_driver go7007_usb_driver = {
- .name = "go7007",
- .probe = go7007_usb_probe,
- .disconnect = go7007_usb_disconnect,
- .id_table = go7007_usb_id_table,
-};
-
-static int __init go7007_usb_init(void)
-{
- return usb_register(&go7007_usb_driver);
-}
-
-static void __exit go7007_usb_cleanup(void)
-{
- usb_deregister(&go7007_usb_driver);
-}
-
-module_init(go7007_usb_init);
-module_exit(go7007_usb_cleanup);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/version.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/unistd.h>
-#include <linux/time.h>
-#include <linux/vmalloc.h>
-#include <linux/pagemap.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-subdev.h>
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-#include <asm/system.h>
-
-#include "go7007.h"
-#include "go7007-priv.h"
-#include "wis-i2c.h"
-
-/* Temporary defines until accepted in v4l-dvb */
-#ifndef V4L2_MPEG_STREAM_TYPE_MPEG_ELEM
-#define V4L2_MPEG_STREAM_TYPE_MPEG_ELEM 6 /* MPEG elementary stream */
-#endif
-#ifndef V4L2_MPEG_VIDEO_ENCODING_MPEG_4
-#define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 3
-#endif
-
-#define call_all(dev, o, f, args...) \
- v4l2_device_call_until_err(dev, 0, o, f, ##args)
-
-static void deactivate_buffer(struct go7007_buffer *gobuf)
-{
- int i;
-
- if (gobuf->state != BUF_STATE_IDLE) {
- list_del(&gobuf->stream);
- gobuf->state = BUF_STATE_IDLE;
- }
- if (gobuf->page_count > 0) {
- for (i = 0; i < gobuf->page_count; ++i)
- page_cache_release(gobuf->pages[i]);
- gobuf->page_count = 0;
- }
-}
-
-static void abort_queued(struct go7007 *go)
-{
- struct go7007_buffer *gobuf, *next;
-
- list_for_each_entry_safe(gobuf, next, &go->stream, stream) {
- deactivate_buffer(gobuf);
- }
-}
-
-static int go7007_streamoff(struct go7007 *go)
-{
- int retval = -EINVAL;
- unsigned long flags;
-
- mutex_lock(&go->hw_lock);
- if (go->streaming) {
- go->streaming = 0;
- go7007_stream_stop(go);
- spin_lock_irqsave(&go->spinlock, flags);
- abort_queued(go);
- spin_unlock_irqrestore(&go->spinlock, flags);
- go7007_reset_encoder(go);
- retval = 0;
- }
- mutex_unlock(&go->hw_lock);
- return 0;
-}
-
-static int go7007_open(struct file *file)
-{
- struct go7007 *go = video_get_drvdata(video_devdata(file));
- struct go7007_file *gofh;
-
- if (go->status != STATUS_ONLINE)
- return -EBUSY;
- gofh = kmalloc(sizeof(struct go7007_file), GFP_KERNEL);
- if (gofh == NULL)
- return -ENOMEM;
- ++go->ref_count;
- gofh->go = go;
- mutex_init(&gofh->lock);
- gofh->buf_count = 0;
- file->private_data = gofh;
- return 0;
-}
-
-static int go7007_release(struct file *file)
-{
- struct go7007_file *gofh = file->private_data;
- struct go7007 *go = gofh->go;
-
- if (gofh->buf_count > 0) {
- go7007_streamoff(go);
- go->in_use = 0;
- kfree(gofh->bufs);
- gofh->buf_count = 0;
- }
- kfree(gofh);
- if (--go->ref_count == 0)
- kfree(go);
- file->private_data = NULL;
- return 0;
-}
-
-static u32 get_frame_type_flag(struct go7007_buffer *gobuf, int format)
-{
- u8 *f = page_address(gobuf->pages[0]);
-
- switch (format) {
- case GO7007_FORMAT_MJPEG:
- return V4L2_BUF_FLAG_KEYFRAME;
- case GO7007_FORMAT_MPEG4:
- switch ((f[gobuf->frame_offset + 4] >> 6) & 0x3) {
- case 0:
- return V4L2_BUF_FLAG_KEYFRAME;
- case 1:
- return V4L2_BUF_FLAG_PFRAME;
- case 2:
- return V4L2_BUF_FLAG_BFRAME;
- default:
- return 0;
- }
- case GO7007_FORMAT_MPEG1:
- case GO7007_FORMAT_MPEG2:
- switch ((f[gobuf->frame_offset + 5] >> 3) & 0x7) {
- case 1:
- return V4L2_BUF_FLAG_KEYFRAME;
- case 2:
- return V4L2_BUF_FLAG_PFRAME;
- case 3:
- return V4L2_BUF_FLAG_BFRAME;
- default:
- return 0;
- }
- }
-
- return 0;
-}
-
-static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try)
-{
- int sensor_height = 0, sensor_width = 0;
- int width, height, i;
-
- if (fmt != NULL && fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG &&
- fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG &&
- fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG4)
- return -EINVAL;
-
- switch (go->standard) {
- case GO7007_STD_NTSC:
- sensor_width = 720;
- sensor_height = 480;
- break;
- case GO7007_STD_PAL:
- sensor_width = 720;
- sensor_height = 576;
- break;
- case GO7007_STD_OTHER:
- sensor_width = go->board_info->sensor_width;
- sensor_height = go->board_info->sensor_height;
- break;
- }
-
- if (fmt == NULL) {
- width = sensor_width;
- height = sensor_height;
- } else if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
- if (fmt->fmt.pix.width > sensor_width)
- width = sensor_width;
- else if (fmt->fmt.pix.width < 144)
- width = 144;
- else
- width = fmt->fmt.pix.width & ~0x0f;
-
- if (fmt->fmt.pix.height > sensor_height)
- height = sensor_height;
- else if (fmt->fmt.pix.height < 96)
- height = 96;
- else
- height = fmt->fmt.pix.height & ~0x0f;
- } else {
- int requested_size = fmt->fmt.pix.width * fmt->fmt.pix.height;
- int sensor_size = sensor_width * sensor_height;
-
- if (64 * requested_size < 9 * sensor_size) {
- width = sensor_width / 4;
- height = sensor_height / 4;
- } else if (64 * requested_size < 36 * sensor_size) {
- width = sensor_width / 2;
- height = sensor_height / 2;
- } else {
- width = sensor_width;
- height = sensor_height;
- }
- width &= ~0xf;
- height &= ~0xf;
- }
-
- if (fmt != NULL) {
- u32 pixelformat = fmt->fmt.pix.pixelformat;
-
- memset(fmt, 0, sizeof(*fmt));
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- fmt->fmt.pix.width = width;
- fmt->fmt.pix.height = height;
- fmt->fmt.pix.pixelformat = pixelformat;
- fmt->fmt.pix.field = V4L2_FIELD_NONE;
- fmt->fmt.pix.bytesperline = 0;
- fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE;
- fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* ?? */
- }
-
- if (try)
- return 0;
-
- go->width = width;
- go->height = height;
- go->encoder_h_offset = go->board_info->sensor_h_offset;
- go->encoder_v_offset = go->board_info->sensor_v_offset;
- for (i = 0; i < 4; ++i)
- go->modet[i].enable = 0;
- for (i = 0; i < 1624; ++i)
- go->modet_map[i] = 0;
-
- if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
- struct v4l2_mbus_framefmt mbus_fmt;
-
- mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
- if (fmt != NULL)
- mbus_fmt.width = fmt->fmt.pix.width;
- else
- mbus_fmt.width = width;
-
- if (height > sensor_height / 2) {
- mbus_fmt.height = height / 2;
- go->encoder_v_halve = 0;
- } else {
- mbus_fmt.height = height;
- go->encoder_v_halve = 1;
- }
- call_all(&go->v4l2_dev, video, s_mbus_fmt, &mbus_fmt);
- } else {
- if (width <= sensor_width / 4) {
- go->encoder_h_halve = 1;
- go->encoder_v_halve = 1;
- go->encoder_subsample = 1;
- } else if (width <= sensor_width / 2) {
- go->encoder_h_halve = 1;
- go->encoder_v_halve = 1;
- go->encoder_subsample = 0;
- } else {
- go->encoder_h_halve = 0;
- go->encoder_v_halve = 0;
- go->encoder_subsample = 0;
- }
- }
-
- if (fmt == NULL)
- return 0;
-
- switch (fmt->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_MPEG:
- if (go->format == GO7007_FORMAT_MPEG1 ||
- go->format == GO7007_FORMAT_MPEG2 ||
- go->format == GO7007_FORMAT_MPEG4)
- break;
- go->format = GO7007_FORMAT_MPEG1;
- go->pali = 0;
- go->aspect_ratio = GO7007_RATIO_1_1;
- go->gop_size = go->sensor_framerate / 1000;
- go->ipb = 0;
- go->closed_gop = 1;
- go->repeat_seqhead = 1;
- go->seq_header_enable = 1;
- go->gop_header_enable = 1;
- go->dvd_mode = 0;
- break;
- /* Backwards compatibility only! */
- case V4L2_PIX_FMT_MPEG4:
- if (go->format == GO7007_FORMAT_MPEG4)
- break;
- go->format = GO7007_FORMAT_MPEG4;
- go->pali = 0xf5;
- go->aspect_ratio = GO7007_RATIO_1_1;
- go->gop_size = go->sensor_framerate / 1000;
- go->ipb = 0;
- go->closed_gop = 1;
- go->repeat_seqhead = 1;
- go->seq_header_enable = 1;
- go->gop_header_enable = 1;
- go->dvd_mode = 0;
- break;
- case V4L2_PIX_FMT_MJPEG:
- go->format = GO7007_FORMAT_MJPEG;
- go->pali = 0;
- go->aspect_ratio = GO7007_RATIO_1_1;
- go->gop_size = 0;
- go->ipb = 0;
- go->closed_gop = 0;
- go->repeat_seqhead = 0;
- go->seq_header_enable = 0;
- go->gop_header_enable = 0;
- go->dvd_mode = 0;
- break;
- }
- return 0;
-}
-
-#if 0
-static int clip_to_modet_map(struct go7007 *go, int region,
- struct v4l2_clip *clip_list)
-{
- struct v4l2_clip clip, *clip_ptr;
- int x, y, mbnum;
-
- /* Check if coordinates are OK and if any macroblocks are already
- * used by other regions (besides 0) */
- clip_ptr = clip_list;
- while (clip_ptr) {
- if (copy_from_user(&clip, clip_ptr, sizeof(clip)))
- return -EFAULT;
- if (clip.c.left < 0 || (clip.c.left & 0xF) ||
- clip.c.width <= 0 || (clip.c.width & 0xF))
- return -EINVAL;
- if (clip.c.left + clip.c.width > go->width)
- return -EINVAL;
- if (clip.c.top < 0 || (clip.c.top & 0xF) ||
- clip.c.height <= 0 || (clip.c.height & 0xF))
- return -EINVAL;
- if (clip.c.top + clip.c.height > go->height)
- return -EINVAL;
- for (y = 0; y < clip.c.height; y += 16)
- for (x = 0; x < clip.c.width; x += 16) {
- mbnum = (go->width >> 4) *
- ((clip.c.top + y) >> 4) +
- ((clip.c.left + x) >> 4);
- if (go->modet_map[mbnum] != 0 &&
- go->modet_map[mbnum] != region)
- return -EBUSY;
- }
- clip_ptr = clip.next;
- }
-
- /* Clear old region macroblocks */
- for (mbnum = 0; mbnum < 1624; ++mbnum)
- if (go->modet_map[mbnum] == region)
- go->modet_map[mbnum] = 0;
-
- /* Claim macroblocks in this list */
- clip_ptr = clip_list;
- while (clip_ptr) {
- if (copy_from_user(&clip, clip_ptr, sizeof(clip)))
- return -EFAULT;
- for (y = 0; y < clip.c.height; y += 16)
- for (x = 0; x < clip.c.width; x += 16) {
- mbnum = (go->width >> 4) *
- ((clip.c.top + y) >> 4) +
- ((clip.c.left + x) >> 4);
- go->modet_map[mbnum] = region;
- }
- clip_ptr = clip.next;
- }
- return 0;
-}
-#endif
-
-static int mpeg_query_ctrl(struct v4l2_queryctrl *ctrl)
-{
- static const u32 mpeg_ctrls[] = {
- V4L2_CID_MPEG_CLASS,
- V4L2_CID_MPEG_STREAM_TYPE,
- V4L2_CID_MPEG_VIDEO_ENCODING,
- V4L2_CID_MPEG_VIDEO_ASPECT,
- V4L2_CID_MPEG_VIDEO_GOP_SIZE,
- V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
- V4L2_CID_MPEG_VIDEO_BITRATE,
- 0
- };
- static const u32 *ctrl_classes[] = {
- mpeg_ctrls,
- NULL
- };
-
- ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id);
-
- switch (ctrl->id) {
- case V4L2_CID_MPEG_CLASS:
- return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0);
- case V4L2_CID_MPEG_STREAM_TYPE:
- return v4l2_ctrl_query_fill(ctrl,
- V4L2_MPEG_STREAM_TYPE_MPEG2_DVD,
- V4L2_MPEG_STREAM_TYPE_MPEG_ELEM, 1,
- V4L2_MPEG_STREAM_TYPE_MPEG_ELEM);
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- return v4l2_ctrl_query_fill(ctrl,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4, 1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- return v4l2_ctrl_query_fill(ctrl,
- V4L2_MPEG_VIDEO_ASPECT_1x1,
- V4L2_MPEG_VIDEO_ASPECT_16x9, 1,
- V4L2_MPEG_VIDEO_ASPECT_1x1);
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- return v4l2_ctrl_query_fill(ctrl, 0, 34, 1, 15);
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
- return v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- return v4l2_ctrl_query_fill(ctrl,
- 64000,
- 10000000, 1,
- 1500000);
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int mpeg_s_ctrl(struct v4l2_control *ctrl, struct go7007 *go)
-{
- /* pretty sure we can't change any of these while streaming */
- if (go->streaming)
- return -EBUSY;
-
- switch (ctrl->id) {
- case V4L2_CID_MPEG_STREAM_TYPE:
- switch (ctrl->value) {
- case V4L2_MPEG_STREAM_TYPE_MPEG2_DVD:
- go->format = GO7007_FORMAT_MPEG2;
- go->bitrate = 9800000;
- go->gop_size = 15;
- go->pali = 0x48;
- go->closed_gop = 1;
- go->repeat_seqhead = 0;
- go->seq_header_enable = 1;
- go->gop_header_enable = 1;
- go->dvd_mode = 1;
- break;
- case V4L2_MPEG_STREAM_TYPE_MPEG_ELEM:
- /* todo: */
- break;
- default:
- return -EINVAL;
- }
- break;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- switch (ctrl->value) {
- case V4L2_MPEG_VIDEO_ENCODING_MPEG_1:
- go->format = GO7007_FORMAT_MPEG1;
- go->pali = 0;
- break;
- case V4L2_MPEG_VIDEO_ENCODING_MPEG_2:
- go->format = GO7007_FORMAT_MPEG2;
- /*if (mpeg->pali >> 24 == 2)
- go->pali = mpeg->pali & 0xff;
- else*/
- go->pali = 0x48;
- break;
- case V4L2_MPEG_VIDEO_ENCODING_MPEG_4:
- go->format = GO7007_FORMAT_MPEG4;
- /*if (mpeg->pali >> 24 == 4)
- go->pali = mpeg->pali & 0xff;
- else*/
- go->pali = 0xf5;
- break;
- default:
- return -EINVAL;
- }
- go->gop_header_enable =
- /*mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER
- ? 0 :*/ 1;
- /*if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER)
- go->repeat_seqhead = 1;
- else*/
- go->repeat_seqhead = 0;
- go->dvd_mode = 0;
- break;
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- if (go->format == GO7007_FORMAT_MJPEG)
- return -EINVAL;
- switch (ctrl->value) {
- case V4L2_MPEG_VIDEO_ASPECT_1x1:
- go->aspect_ratio = GO7007_RATIO_1_1;
- break;
- case V4L2_MPEG_VIDEO_ASPECT_4x3:
- go->aspect_ratio = GO7007_RATIO_4_3;
- break;
- case V4L2_MPEG_VIDEO_ASPECT_16x9:
- go->aspect_ratio = GO7007_RATIO_16_9;
- break;
- case V4L2_MPEG_VIDEO_ASPECT_221x100:
- default:
- return -EINVAL;
- }
- break;
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- if (ctrl->value < 0 || ctrl->value > 34)
- return -EINVAL;
- go->gop_size = ctrl->value;
- break;
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
- if (ctrl->value != 0 && ctrl->value != 1)
- return -EINVAL;
- go->closed_gop = ctrl->value;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- /* Upper bound is kind of arbitrary here */
- if (ctrl->value < 64000 || ctrl->value > 10000000)
- return -EINVAL;
- go->bitrate = ctrl->value;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int mpeg_g_ctrl(struct v4l2_control *ctrl, struct go7007 *go)
-{
- switch (ctrl->id) {
- case V4L2_CID_MPEG_STREAM_TYPE:
- if (go->dvd_mode)
- ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_DVD;
- else
- ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG_ELEM;
- break;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- switch (go->format) {
- case GO7007_FORMAT_MPEG1:
- ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
- break;
- case GO7007_FORMAT_MPEG2:
- ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
- break;
- case GO7007_FORMAT_MPEG4:
- ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4;
- break;
- default:
- return -EINVAL;
- }
- break;
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- switch (go->aspect_ratio) {
- case GO7007_RATIO_1_1:
- ctrl->value = V4L2_MPEG_VIDEO_ASPECT_1x1;
- break;
- case GO7007_RATIO_4_3:
- ctrl->value = V4L2_MPEG_VIDEO_ASPECT_4x3;
- break;
- case GO7007_RATIO_16_9:
- ctrl->value = V4L2_MPEG_VIDEO_ASPECT_16x9;
- break;
- default:
- return -EINVAL;
- }
- break;
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- ctrl->value = go->gop_size;
- break;
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
- ctrl->value = go->closed_gop;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- ctrl->value = go->bitrate;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- strlcpy(cap->driver, "go7007", sizeof(cap->driver));
- strlcpy(cap->card, go->name, sizeof(cap->card));
-#if 0
- strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
-#endif
-
- cap->version = KERNEL_VERSION(0, 9, 8);
-
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_STREAMING; /* | V4L2_CAP_AUDIO; */
-
- if (go->board_info->flags & GO7007_BOARD_HAS_TUNER)
- cap->capabilities |= V4L2_CAP_TUNER;
-
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmt)
-{
- char *desc = NULL;
-
- switch (fmt->index) {
- case 0:
- fmt->pixelformat = V4L2_PIX_FMT_MJPEG;
- desc = "Motion-JPEG";
- break;
- case 1:
- fmt->pixelformat = V4L2_PIX_FMT_MPEG;
- desc = "MPEG1/MPEG2/MPEG4";
- break;
- default:
- return -EINVAL;
- }
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- fmt->flags = V4L2_FMT_FLAG_COMPRESSED;
-
- strncpy(fmt->description, desc, sizeof(fmt->description));
-
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- fmt->fmt.pix.width = go->width;
- fmt->fmt.pix.height = go->height;
- fmt->fmt.pix.pixelformat = (go->format == GO7007_FORMAT_MJPEG) ?
- V4L2_PIX_FMT_MJPEG : V4L2_PIX_FMT_MPEG;
- fmt->fmt.pix.field = V4L2_FIELD_NONE;
- fmt->fmt.pix.bytesperline = 0;
- fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE;
- fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- return set_capture_size(go, fmt, 1);
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (go->streaming)
- return -EBUSY;
-
- return set_capture_size(go, fmt, 0);
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req)
-{
- struct go7007_file *gofh = priv;
- struct go7007 *go = gofh->go;
- int retval = -EBUSY;
- unsigned int count, i;
-
- if (go->streaming)
- return retval;
-
- if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- req->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
-
- mutex_lock(&gofh->lock);
- for (i = 0; i < gofh->buf_count; ++i)
- if (gofh->bufs[i].mapped > 0)
- goto unlock_and_return;
-
- mutex_lock(&go->hw_lock);
- if (go->in_use > 0 && gofh->buf_count == 0) {
- mutex_unlock(&go->hw_lock);
- goto unlock_and_return;
- }
-
- if (gofh->buf_count > 0)
- kfree(gofh->bufs);
-
- retval = -ENOMEM;
- count = req->count;
- if (count > 0) {
- if (count < 2)
- count = 2;
- if (count > 32)
- count = 32;
-
- gofh->bufs = kcalloc(count, sizeof(struct go7007_buffer),
- GFP_KERNEL);
-
- if (!gofh->bufs) {
- mutex_unlock(&go->hw_lock);
- goto unlock_and_return;
- }
-
- for (i = 0; i < count; ++i) {
- gofh->bufs[i].go = go;
- gofh->bufs[i].index = i;
- gofh->bufs[i].state = BUF_STATE_IDLE;
- gofh->bufs[i].mapped = 0;
- }
-
- go->in_use = 1;
- } else {
- go->in_use = 0;
- }
-
- gofh->buf_count = count;
- mutex_unlock(&go->hw_lock);
- mutex_unlock(&gofh->lock);
-
- memset(req, 0, sizeof(*req));
-
- req->count = count;
- req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- req->memory = V4L2_MEMORY_MMAP;
-
- return 0;
-
-unlock_and_return:
- mutex_unlock(&gofh->lock);
- return retval;
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct go7007_file *gofh = priv;
- int retval = -EINVAL;
- unsigned int index;
-
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return retval;
-
- index = buf->index;
-
- mutex_lock(&gofh->lock);
- if (index >= gofh->buf_count)
- goto unlock_and_return;
-
- memset(buf, 0, sizeof(*buf));
- buf->index = index;
- buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- switch (gofh->bufs[index].state) {
- case BUF_STATE_QUEUED:
- buf->flags = V4L2_BUF_FLAG_QUEUED;
- break;
- case BUF_STATE_DONE:
- buf->flags = V4L2_BUF_FLAG_DONE;
- break;
- default:
- buf->flags = 0;
- }
-
- if (gofh->bufs[index].mapped)
- buf->flags |= V4L2_BUF_FLAG_MAPPED;
- buf->memory = V4L2_MEMORY_MMAP;
- buf->m.offset = index * GO7007_BUF_SIZE;
- buf->length = GO7007_BUF_SIZE;
- mutex_unlock(&gofh->lock);
-
- return 0;
-
-unlock_and_return:
- mutex_unlock(&gofh->lock);
- return retval;
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct go7007_file *gofh = priv;
- struct go7007 *go = gofh->go;
- struct go7007_buffer *gobuf;
- unsigned long flags;
- int retval = -EINVAL;
- int ret;
-
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- buf->memory != V4L2_MEMORY_MMAP)
- return retval;
-
- mutex_lock(&gofh->lock);
- if (buf->index < 0 || buf->index >= gofh->buf_count)
- goto unlock_and_return;
-
- gobuf = &gofh->bufs[buf->index];
- if (!gobuf->mapped)
- goto unlock_and_return;
-
- retval = -EBUSY;
- if (gobuf->state != BUF_STATE_IDLE)
- goto unlock_and_return;
-
- /* offset will be 0 until we really support USERPTR streaming */
- gobuf->offset = gobuf->user_addr & ~PAGE_MASK;
- gobuf->bytesused = 0;
- gobuf->frame_offset = 0;
- gobuf->modet_active = 0;
- if (gobuf->offset > 0)
- gobuf->page_count = GO7007_BUF_PAGES + 1;
- else
- gobuf->page_count = GO7007_BUF_PAGES;
-
- retval = -ENOMEM;
- down_read(¤t->mm->mmap_sem);
- ret = get_user_pages(current, current->mm,
- gobuf->user_addr & PAGE_MASK, gobuf->page_count,
- 1, 1, gobuf->pages, NULL);
- up_read(¤t->mm->mmap_sem);
-
- if (ret != gobuf->page_count) {
- int i;
- for (i = 0; i < ret; ++i)
- page_cache_release(gobuf->pages[i]);
- gobuf->page_count = 0;
- goto unlock_and_return;
- }
-
- gobuf->state = BUF_STATE_QUEUED;
- spin_lock_irqsave(&go->spinlock, flags);
- list_add_tail(&gobuf->stream, &go->stream);
- spin_unlock_irqrestore(&go->spinlock, flags);
- mutex_unlock(&gofh->lock);
-
- return 0;
-
-unlock_and_return:
- mutex_unlock(&gofh->lock);
- return retval;
-}
-
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct go7007_file *gofh = priv;
- struct go7007 *go = gofh->go;
- struct go7007_buffer *gobuf;
- int retval = -EINVAL;
- unsigned long flags;
- u32 frame_type_flag;
- DEFINE_WAIT(wait);
-
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return retval;
- if (buf->memory != V4L2_MEMORY_MMAP)
- return retval;
-
- mutex_lock(&gofh->lock);
- if (list_empty(&go->stream))
- goto unlock_and_return;
- gobuf = list_entry(go->stream.next,
- struct go7007_buffer, stream);
-
- retval = -EAGAIN;
- if (gobuf->state != BUF_STATE_DONE &&
- !(file->f_flags & O_NONBLOCK)) {
- for (;;) {
- prepare_to_wait(&go->frame_waitq, &wait,
- TASK_INTERRUPTIBLE);
- if (gobuf->state == BUF_STATE_DONE)
- break;
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
- schedule();
- }
- finish_wait(&go->frame_waitq, &wait);
- }
- if (gobuf->state != BUF_STATE_DONE)
- goto unlock_and_return;
-
- spin_lock_irqsave(&go->spinlock, flags);
- deactivate_buffer(gobuf);
- spin_unlock_irqrestore(&go->spinlock, flags);
- frame_type_flag = get_frame_type_flag(gobuf, go->format);
- gobuf->state = BUF_STATE_IDLE;
-
- memset(buf, 0, sizeof(*buf));
- buf->index = gobuf->index;
- buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf->bytesused = gobuf->bytesused;
- buf->flags = V4L2_BUF_FLAG_MAPPED | frame_type_flag;
- buf->field = V4L2_FIELD_NONE;
- buf->timestamp = gobuf->timestamp;
- buf->sequence = gobuf->seq;
- buf->memory = V4L2_MEMORY_MMAP;
- buf->m.offset = gobuf->index * GO7007_BUF_SIZE;
- buf->length = GO7007_BUF_SIZE;
- buf->reserved = gobuf->modet_active;
-
- mutex_unlock(&gofh->lock);
- return 0;
-
-unlock_and_return:
- mutex_unlock(&gofh->lock);
- return retval;
-}
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct go7007_file *gofh = priv;
- struct go7007 *go = gofh->go;
- int retval = 0;
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- mutex_lock(&gofh->lock);
- mutex_lock(&go->hw_lock);
-
- if (!go->streaming) {
- go->streaming = 1;
- go->next_seq = 0;
- go->active_buf = NULL;
- if (go7007_start_encoder(go) < 0)
- retval = -EIO;
- else
- retval = 0;
- }
- mutex_unlock(&go->hw_lock);
- mutex_unlock(&gofh->lock);
-
- return retval;
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct go7007_file *gofh = priv;
- struct go7007 *go = gofh->go;
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- mutex_lock(&gofh->lock);
- go7007_streamoff(go);
- mutex_unlock(&gofh->lock);
-
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *query)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
- int id = query->id;
-
- if (0 == call_all(&go->v4l2_dev, core, queryctrl, query))
- return 0;
-
- query->id = id;
- return mpeg_query_ctrl(query);
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (0 == call_all(&go->v4l2_dev, core, g_ctrl, ctrl))
- return 0;
-
- return mpeg_g_ctrl(ctrl, go);
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (0 == call_all(&go->v4l2_dev, core, s_ctrl, ctrl))
- return 0;
-
- return mpeg_s_ctrl(ctrl, go);
-}
-
-static int vidioc_g_parm(struct file *filp, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
- struct v4l2_fract timeperframe = {
- .numerator = 1001 * go->fps_scale,
- .denominator = go->sensor_framerate,
- };
-
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME;
- parm->parm.capture.timeperframe = timeperframe;
-
- return 0;
-}
-
-static int vidioc_s_parm(struct file *filp, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
- unsigned int n, d;
-
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (parm->parm.capture.capturemode != 0)
- return -EINVAL;
-
- n = go->sensor_framerate *
- parm->parm.capture.timeperframe.numerator;
- d = 1001 * parm->parm.capture.timeperframe.denominator;
- if (n != 0 && d != 0 && n > d)
- go->fps_scale = (n + d/2) / d;
- else
- go->fps_scale = 1;
-
- return 0;
-}
-
-/* VIDIOC_ENUMSTD on go7007 were used for enumberating the supported fps and
- its resolution, when the device is not connected to TV.
- This were an API abuse, probably used by the lack of specific IOCTL's to
- enumberate it, by the time the driver were written.
-
- However, since kernel 2.6.19, two new ioctls (VIDIOC_ENUM_FRAMEINTERVALS
- and VIDIOC_ENUM_FRAMESIZES) were added for this purpose.
-
- The two functions bellow implements the newer ioctls
-*/
-static int vidioc_enum_framesizes(struct file *filp, void *priv,
- struct v4l2_frmsizeenum *fsize)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- /* Return -EINVAL, if it is a TV board */
- if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) ||
- (go->board_info->sensor_flags & GO7007_SENSOR_TV))
- return -EINVAL;
-
- if (fsize->index > 0)
- return -EINVAL;
-
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = go->board_info->sensor_width;
- fsize->discrete.height = go->board_info->sensor_height;
-
- return 0;
-}
-
-static int vidioc_enum_frameintervals(struct file *filp, void *priv,
- struct v4l2_frmivalenum *fival)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- /* Return -EINVAL, if it is a TV board */
- if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) ||
- (go->board_info->sensor_flags & GO7007_SENSOR_TV))
- return -EINVAL;
-
- if (fival->index > 0)
- return -EINVAL;
-
- fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- fival->discrete.numerator = 1001;
- fival->discrete.denominator = go->board_info->sensor_framerate;
-
- return 0;
-}
-
-static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- switch (go->standard) {
- case GO7007_STD_NTSC:
- *std = V4L2_STD_NTSC;
- break;
- case GO7007_STD_PAL:
- *std = V4L2_STD_PAL;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (go->streaming)
- return -EBUSY;
-
- if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && *std != 0)
- return -EINVAL;
-
- if (*std == 0)
- return -EINVAL;
-
- if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
- go->input == go->board_info->num_inputs - 1) {
- if (!go->i2c_adapter_online)
- return -EIO;
- if (call_all(&go->v4l2_dev, core, s_std, *std) < 0)
- return -EINVAL;
- }
-
- if (*std & V4L2_STD_NTSC) {
- go->standard = GO7007_STD_NTSC;
- go->sensor_framerate = 30000;
- } else if (*std & V4L2_STD_PAL) {
- go->standard = GO7007_STD_PAL;
- go->sensor_framerate = 25025;
- } else if (*std & V4L2_STD_SECAM) {
- go->standard = GO7007_STD_PAL;
- go->sensor_framerate = 25025;
- } else
- return -EINVAL;
-
- call_all(&go->v4l2_dev, core, s_std, *std);
- set_capture_size(go, NULL, 0);
-
- return 0;
-}
-
-static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
- go->input == go->board_info->num_inputs - 1) {
- if (!go->i2c_adapter_online)
- return -EIO;
- return call_all(&go->v4l2_dev, video, querystd, std);
- } else if (go->board_info->sensor_flags & GO7007_SENSOR_TV)
- *std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
- else
- *std = 0;
-
- return 0;
-}
-
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (inp->index >= go->board_info->num_inputs)
- return -EINVAL;
-
- strncpy(inp->name, go->board_info->inputs[inp->index].name,
- sizeof(inp->name));
-
- /* If this board has a tuner, it will be the last input */
- if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
- inp->index == go->board_info->num_inputs - 1)
- inp->type = V4L2_INPUT_TYPE_TUNER;
- else
- inp->type = V4L2_INPUT_TYPE_CAMERA;
-
- inp->audioset = 0;
- inp->tuner = 0;
- if (go->board_info->sensor_flags & GO7007_SENSOR_TV)
- inp->std = V4L2_STD_NTSC | V4L2_STD_PAL |
- V4L2_STD_SECAM;
- else
- inp->std = 0;
-
- return 0;
-}
-
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *input)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- *input = go->input;
-
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int input)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (input >= go->board_info->num_inputs)
- return -EINVAL;
- if (go->streaming)
- return -EBUSY;
-
- go->input = input;
-
- return call_all(&go->v4l2_dev, video, s_routing, input, 0, 0);
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
- return -EINVAL;
- if (t->index != 0)
- return -EINVAL;
- if (!go->i2c_adapter_online)
- return -EIO;
-
- return call_all(&go->v4l2_dev, tuner, g_tuner, t);
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
- return -EINVAL;
- if (t->index != 0)
- return -EINVAL;
- if (!go->i2c_adapter_online)
- return -EIO;
-
- switch (go->board_id) {
- case GO7007_BOARDID_PX_TV402U_NA:
- case GO7007_BOARDID_PX_TV402U_JP:
- /* No selectable options currently */
- if (t->audmode != V4L2_TUNER_MODE_STEREO)
- return -EINVAL;
- break;
- }
-
- return call_all(&go->v4l2_dev, tuner, s_tuner, t);
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
- return -EINVAL;
- if (!go->i2c_adapter_online)
- return -EIO;
-
- f->type = V4L2_TUNER_ANALOG_TV;
-
- return call_all(&go->v4l2_dev, tuner, g_frequency, f);
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
- return -EINVAL;
- if (!go->i2c_adapter_online)
- return -EIO;
-
- return call_all(&go->v4l2_dev, tuner, s_frequency, f);
-}
-
-static int vidioc_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *cropcap)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- /* These specify the raw input of the sensor */
- switch (go->standard) {
- case GO7007_STD_NTSC:
- cropcap->bounds.top = 0;
- cropcap->bounds.left = 0;
- cropcap->bounds.width = 720;
- cropcap->bounds.height = 480;
- cropcap->defrect.top = 0;
- cropcap->defrect.left = 0;
- cropcap->defrect.width = 720;
- cropcap->defrect.height = 480;
- break;
- case GO7007_STD_PAL:
- cropcap->bounds.top = 0;
- cropcap->bounds.left = 0;
- cropcap->bounds.width = 720;
- cropcap->bounds.height = 576;
- cropcap->defrect.top = 0;
- cropcap->defrect.left = 0;
- cropcap->defrect.width = 720;
- cropcap->defrect.height = 576;
- break;
- case GO7007_STD_OTHER:
- cropcap->bounds.top = 0;
- cropcap->bounds.left = 0;
- cropcap->bounds.width = go->board_info->sensor_width;
- cropcap->bounds.height = go->board_info->sensor_height;
- cropcap->defrect.top = 0;
- cropcap->defrect.left = 0;
- cropcap->defrect.width = go->board_info->sensor_width;
- cropcap->defrect.height = go->board_info->sensor_height;
- break;
- }
-
- return 0;
-}
-
-static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
-{
- struct go7007 *go = ((struct go7007_file *) priv)->go;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- /* These specify the raw input of the sensor */
- switch (go->standard) {
- case GO7007_STD_NTSC:
- crop->c.top = 0;
- crop->c.left = 0;
- crop->c.width = 720;
- crop->c.height = 480;
- break;
- case GO7007_STD_PAL:
- crop->c.top = 0;
- crop->c.left = 0;
- crop->c.width = 720;
- crop->c.height = 576;
- break;
- case GO7007_STD_OTHER:
- crop->c.top = 0;
- crop->c.left = 0;
- crop->c.width = go->board_info->sensor_width;
- crop->c.height = go->board_info->sensor_height;
- break;
- }
-
- return 0;
-}
-
-/* FIXME: vidioc_s_crop is not really implemented!!!
- */
-static int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop)
-{
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return 0;
-}
-
-static int vidioc_g_jpegcomp(struct file *file, void *priv,
- struct v4l2_jpegcompression *params)
-{
- memset(params, 0, sizeof(*params));
- params->quality = 50; /* ?? */
- params->jpeg_markers = V4L2_JPEG_MARKER_DHT |
- V4L2_JPEG_MARKER_DQT;
-
- return 0;
-}
-
-static int vidioc_s_jpegcomp(struct file *file, void *priv,
- struct v4l2_jpegcompression *params)
-{
- if (params->quality != 50 ||
- params->jpeg_markers != (V4L2_JPEG_MARKER_DHT |
- V4L2_JPEG_MARKER_DQT))
- return -EINVAL;
-
- return 0;
-}
-
-/* FIXME:
- Those ioctls are private, and not needed, since several standard
- extended controls already provide streaming control.
- So, those ioctls should be converted into vidioc_g_ext_ctrls()
- and vidioc_s_ext_ctrls()
- */
-
-#if 0
- /* Temporary ioctls for controlling compression characteristics */
- case GO7007IOC_S_BITRATE:
- {
- int *bitrate = arg;
-
- if (go->streaming)
- return -EINVAL;
- /* Upper bound is kind of arbitrary here */
- if (*bitrate < 64000 || *bitrate > 10000000)
- return -EINVAL;
- go->bitrate = *bitrate;
- return 0;
- }
- case GO7007IOC_G_BITRATE:
- {
- int *bitrate = arg;
-
- *bitrate = go->bitrate;
- return 0;
- }
- case GO7007IOC_S_COMP_PARAMS:
- {
- struct go7007_comp_params *comp = arg;
-
- if (go->format == GO7007_FORMAT_MJPEG)
- return -EINVAL;
- if (comp->gop_size > 0)
- go->gop_size = comp->gop_size;
- else
- go->gop_size = go->sensor_framerate / 1000;
- if (go->gop_size != 15)
- go->dvd_mode = 0;
- /*go->ipb = comp->max_b_frames > 0;*/ /* completely untested */
- if (go->board_info->sensor_flags & GO7007_SENSOR_TV) {
- switch (comp->aspect_ratio) {
- case GO7007_ASPECT_RATIO_4_3_NTSC:
- case GO7007_ASPECT_RATIO_4_3_PAL:
- go->aspect_ratio = GO7007_RATIO_4_3;
- break;
- case GO7007_ASPECT_RATIO_16_9_NTSC:
- case GO7007_ASPECT_RATIO_16_9_PAL:
- go->aspect_ratio = GO7007_RATIO_16_9;
- break;
- default:
- go->aspect_ratio = GO7007_RATIO_1_1;
- break;
- }
- }
- if (comp->flags & GO7007_COMP_OMIT_SEQ_HEADER) {
- go->dvd_mode = 0;
- go->seq_header_enable = 0;
- } else {
- go->seq_header_enable = 1;
- }
- /* fall-through */
- }
- case GO7007IOC_G_COMP_PARAMS:
- {
- struct go7007_comp_params *comp = arg;
-
- if (go->format == GO7007_FORMAT_MJPEG)
- return -EINVAL;
- memset(comp, 0, sizeof(*comp));
- comp->gop_size = go->gop_size;
- comp->max_b_frames = go->ipb ? 2 : 0;
- switch (go->aspect_ratio) {
- case GO7007_RATIO_4_3:
- if (go->standard == GO7007_STD_NTSC)
- comp->aspect_ratio =
- GO7007_ASPECT_RATIO_4_3_NTSC;
- else
- comp->aspect_ratio =
- GO7007_ASPECT_RATIO_4_3_PAL;
- break;
- case GO7007_RATIO_16_9:
- if (go->standard == GO7007_STD_NTSC)
- comp->aspect_ratio =
- GO7007_ASPECT_RATIO_16_9_NTSC;
- else
- comp->aspect_ratio =
- GO7007_ASPECT_RATIO_16_9_PAL;
- break;
- default:
- comp->aspect_ratio = GO7007_ASPECT_RATIO_1_1;
- break;
- }
- if (go->closed_gop)
- comp->flags |= GO7007_COMP_CLOSED_GOP;
- if (!go->seq_header_enable)
- comp->flags |= GO7007_COMP_OMIT_SEQ_HEADER;
- return 0;
- }
- case GO7007IOC_S_MPEG_PARAMS:
- {
- struct go7007_mpeg_params *mpeg = arg;
-
- if (go->format != GO7007_FORMAT_MPEG1 &&
- go->format != GO7007_FORMAT_MPEG2 &&
- go->format != GO7007_FORMAT_MPEG4)
- return -EINVAL;
-
- if (mpeg->flags & GO7007_MPEG_FORCE_DVD_MODE) {
- go->format = GO7007_FORMAT_MPEG2;
- go->bitrate = 9800000;
- go->gop_size = 15;
- go->pali = 0x48;
- go->closed_gop = 1;
- go->repeat_seqhead = 0;
- go->seq_header_enable = 1;
- go->gop_header_enable = 1;
- go->dvd_mode = 1;
- } else {
- switch (mpeg->mpeg_video_standard) {
- case GO7007_MPEG_VIDEO_MPEG1:
- go->format = GO7007_FORMAT_MPEG1;
- go->pali = 0;
- break;
- case GO7007_MPEG_VIDEO_MPEG2:
- go->format = GO7007_FORMAT_MPEG2;
- if (mpeg->pali >> 24 == 2)
- go->pali = mpeg->pali & 0xff;
- else
- go->pali = 0x48;
- break;
- case GO7007_MPEG_VIDEO_MPEG4:
- go->format = GO7007_FORMAT_MPEG4;
- if (mpeg->pali >> 24 == 4)
- go->pali = mpeg->pali & 0xff;
- else
- go->pali = 0xf5;
- break;
- default:
- return -EINVAL;
- }
- go->gop_header_enable =
- mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER
- ? 0 : 1;
- if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER)
- go->repeat_seqhead = 1;
- else
- go->repeat_seqhead = 0;
- go->dvd_mode = 0;
- }
- /* fall-through */
- }
- case GO7007IOC_G_MPEG_PARAMS:
- {
- struct go7007_mpeg_params *mpeg = arg;
-
- memset(mpeg, 0, sizeof(*mpeg));
- switch (go->format) {
- case GO7007_FORMAT_MPEG1:
- mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG1;
- mpeg->pali = 0;
- break;
- case GO7007_FORMAT_MPEG2:
- mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG2;
- mpeg->pali = GO7007_MPEG_PROFILE(2, go->pali);
- break;
- case GO7007_FORMAT_MPEG4:
- mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG4;
- mpeg->pali = GO7007_MPEG_PROFILE(4, go->pali);
- break;
- default:
- return -EINVAL;
- }
- if (!go->gop_header_enable)
- mpeg->flags |= GO7007_MPEG_OMIT_GOP_HEADER;
- if (go->repeat_seqhead)
- mpeg->flags |= GO7007_MPEG_REPEAT_SEQHEADER;
- if (go->dvd_mode)
- mpeg->flags |= GO7007_MPEG_FORCE_DVD_MODE;
- return 0;
- }
- case GO7007IOC_S_MD_PARAMS:
- {
- struct go7007_md_params *mdp = arg;
-
- if (mdp->region > 3)
- return -EINVAL;
- if (mdp->trigger > 0) {
- go->modet[mdp->region].pixel_threshold =
- mdp->pixel_threshold >> 1;
- go->modet[mdp->region].motion_threshold =
- mdp->motion_threshold >> 1;
- go->modet[mdp->region].mb_threshold =
- mdp->trigger >> 1;
- go->modet[mdp->region].enable = 1;
- } else
- go->modet[mdp->region].enable = 0;
- /* fall-through */
- }
- case GO7007IOC_G_MD_PARAMS:
- {
- struct go7007_md_params *mdp = arg;
- int region = mdp->region;
-
- if (mdp->region > 3)
- return -EINVAL;
- memset(mdp, 0, sizeof(struct go7007_md_params));
- mdp->region = region;
- if (!go->modet[region].enable)
- return 0;
- mdp->pixel_threshold =
- (go->modet[region].pixel_threshold << 1) + 1;
- mdp->motion_threshold =
- (go->modet[region].motion_threshold << 1) + 1;
- mdp->trigger =
- (go->modet[region].mb_threshold << 1) + 1;
- return 0;
- }
- case GO7007IOC_S_MD_REGION:
- {
- struct go7007_md_region *region = arg;
-
- if (region->region < 1 || region->region > 3)
- return -EINVAL;
- return clip_to_modet_map(go, region->region, region->clips);
- }
-#endif
-
-static ssize_t go7007_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- return -EINVAL;
-}
-
-static void go7007_vm_open(struct vm_area_struct *vma)
-{
- struct go7007_buffer *gobuf = vma->vm_private_data;
-
- ++gobuf->mapped;
-}
-
-static void go7007_vm_close(struct vm_area_struct *vma)
-{
- struct go7007_buffer *gobuf = vma->vm_private_data;
- unsigned long flags;
-
- if (--gobuf->mapped == 0) {
- spin_lock_irqsave(&gobuf->go->spinlock, flags);
- deactivate_buffer(gobuf);
- spin_unlock_irqrestore(&gobuf->go->spinlock, flags);
- }
-}
-
-/* Copied from videobuf-dma-sg.c */
-static int go7007_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
-{
- struct page *page;
-
- page = alloc_page(GFP_USER | __GFP_DMA32);
- if (!page)
- return VM_FAULT_OOM;
- clear_user_highpage(page, (unsigned long)vmf->virtual_address);
- vmf->page = page;
- return 0;
-}
-
-static struct vm_operations_struct go7007_vm_ops = {
- .open = go7007_vm_open,
- .close = go7007_vm_close,
- .fault = go7007_vm_fault,
-};
-
-static int go7007_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct go7007_file *gofh = file->private_data;
- unsigned int index;
-
- if (gofh->go->status != STATUS_ONLINE)
- return -EIO;
- if (!(vma->vm_flags & VM_SHARED))
- return -EINVAL; /* only support VM_SHARED mapping */
- if (vma->vm_end - vma->vm_start != GO7007_BUF_SIZE)
- return -EINVAL; /* must map exactly one full buffer */
- mutex_lock(&gofh->lock);
- index = vma->vm_pgoff / GO7007_BUF_PAGES;
- if (index >= gofh->buf_count) {
- mutex_unlock(&gofh->lock);
- return -EINVAL; /* trying to map beyond requested buffers */
- }
- if (index * GO7007_BUF_PAGES != vma->vm_pgoff) {
- mutex_unlock(&gofh->lock);
- return -EINVAL; /* offset is not aligned on buffer boundary */
- }
- if (gofh->bufs[index].mapped > 0) {
- mutex_unlock(&gofh->lock);
- return -EBUSY;
- }
- gofh->bufs[index].mapped = 1;
- gofh->bufs[index].user_addr = vma->vm_start;
- vma->vm_ops = &go7007_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND;
- vma->vm_flags &= ~VM_IO;
- vma->vm_private_data = &gofh->bufs[index];
- mutex_unlock(&gofh->lock);
- return 0;
-}
-
-static unsigned int go7007_poll(struct file *file, poll_table *wait)
-{
- struct go7007_file *gofh = file->private_data;
- struct go7007_buffer *gobuf;
-
- if (list_empty(&gofh->go->stream))
- return POLLERR;
- gobuf = list_entry(gofh->go->stream.next, struct go7007_buffer, stream);
- poll_wait(file, &gofh->go->frame_waitq, wait);
- if (gobuf->state == BUF_STATE_DONE)
- return POLLIN | POLLRDNORM;
- return 0;
-}
-
-static void go7007_vfl_release(struct video_device *vfd)
-{
- struct go7007 *go = video_get_drvdata(vfd);
-
- video_device_release(vfd);
- if (--go->ref_count == 0)
- kfree(go);
-}
-
-static struct v4l2_file_operations go7007_fops = {
- .owner = THIS_MODULE,
- .open = go7007_open,
- .release = go7007_release,
- .ioctl = video_ioctl2,
- .read = go7007_read,
- .mmap = go7007_mmap,
- .poll = go7007_poll,
-};
-
-static const struct v4l2_ioctl_ops video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_querystd = vidioc_querystd,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_s_parm = vidioc_s_parm,
- .vidioc_enum_framesizes = vidioc_enum_framesizes,
- .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
- .vidioc_cropcap = vidioc_cropcap,
- .vidioc_g_crop = vidioc_g_crop,
- .vidioc_s_crop = vidioc_s_crop,
- .vidioc_g_jpegcomp = vidioc_g_jpegcomp,
- .vidioc_s_jpegcomp = vidioc_s_jpegcomp,
-};
-
-static struct video_device go7007_template = {
- .name = "go7007",
- .fops = &go7007_fops,
- .release = go7007_vfl_release,
- .ioctl_ops = &video_ioctl_ops,
- .tvnorms = V4L2_STD_ALL,
- .current_norm = V4L2_STD_NTSC,
-};
-
-int go7007_v4l2_init(struct go7007 *go)
-{
- int rv;
-
- go->video_dev = video_device_alloc();
- if (go->video_dev == NULL)
- return -ENOMEM;
- *go->video_dev = go7007_template;
- go->video_dev->parent = go->dev;
- rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1);
- if (rv < 0) {
- video_device_release(go->video_dev);
- go->video_dev = NULL;
- return rv;
- }
- rv = v4l2_device_register(go->dev, &go->v4l2_dev);
- if (rv < 0) {
- video_device_release(go->video_dev);
- go->video_dev = NULL;
- return rv;
- }
- video_set_drvdata(go->video_dev, go);
- ++go->ref_count;
- printk(KERN_INFO "%s: registered device %s [v4l2]\n",
- go->video_dev->name, video_device_node_name(go->video_dev));
-
- return 0;
-}
-
-void go7007_v4l2_remove(struct go7007 *go)
-{
- unsigned long flags;
-
- mutex_lock(&go->hw_lock);
- if (go->streaming) {
- go->streaming = 0;
- go7007_stream_stop(go);
- spin_lock_irqsave(&go->spinlock, flags);
- abort_queued(go);
- spin_unlock_irqrestore(&go->spinlock, flags);
- }
- mutex_unlock(&go->hw_lock);
- if (go->video_dev)
- video_unregister_device(go->video_dev);
- v4l2_device_unregister(&go->v4l2_dev);
-}
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and the associated README documentation file (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/* DEPRECATED -- use V4L2_PIX_FMT_MPEG and then call GO7007IOC_S_MPEG_PARAMS
- * to select between MPEG1, MPEG2, and MPEG4 */
-#define V4L2_PIX_FMT_MPEG4 v4l2_fourcc('M', 'P', 'G', '4') /* MPEG4 */
-
-/* These will be replaced with a better interface
- * soon, so don't get too attached to them */
-#define GO7007IOC_S_BITRATE _IOW('V', BASE_VIDIOC_PRIVATE + 0, int)
-#define GO7007IOC_G_BITRATE _IOR('V', BASE_VIDIOC_PRIVATE + 1, int)
-
-enum go7007_aspect_ratio {
- GO7007_ASPECT_RATIO_1_1 = 0,
- GO7007_ASPECT_RATIO_4_3_NTSC = 1,
- GO7007_ASPECT_RATIO_4_3_PAL = 2,
- GO7007_ASPECT_RATIO_16_9_NTSC = 3,
- GO7007_ASPECT_RATIO_16_9_PAL = 4,
-};
-
-/* Used to set generic compression parameters */
-struct go7007_comp_params {
- __u32 gop_size;
- __u32 max_b_frames;
- enum go7007_aspect_ratio aspect_ratio;
- __u32 flags;
- __u32 reserved[8];
-};
-
-#define GO7007_COMP_CLOSED_GOP 0x00000001
-#define GO7007_COMP_OMIT_SEQ_HEADER 0x00000002
-
-enum go7007_mpeg_video_standard {
- GO7007_MPEG_VIDEO_MPEG1 = 0,
- GO7007_MPEG_VIDEO_MPEG2 = 1,
- GO7007_MPEG_VIDEO_MPEG4 = 2,
-};
-
-/* Used to set parameters for V4L2_PIX_FMT_MPEG format */
-struct go7007_mpeg_params {
- enum go7007_mpeg_video_standard mpeg_video_standard;
- __u32 flags;
- __u32 pali;
- __u32 reserved[8];
-};
-
-#define GO7007_MPEG_FORCE_DVD_MODE 0x00000001
-#define GO7007_MPEG_OMIT_GOP_HEADER 0x00000002
-#define GO7007_MPEG_REPEAT_SEQHEADER 0x00000004
-
-#define GO7007_MPEG_PROFILE(format, pali) (((format)<<24)|(pali))
-
-#define GO7007_MPEG2_PROFILE_MAIN_MAIN GO7007_MPEG_PROFILE(2, 0x48)
-
-#define GO7007_MPEG4_PROFILE_S_L0 GO7007_MPEG_PROFILE(4, 0x08)
-#define GO7007_MPEG4_PROFILE_S_L1 GO7007_MPEG_PROFILE(4, 0x01)
-#define GO7007_MPEG4_PROFILE_S_L2 GO7007_MPEG_PROFILE(4, 0x02)
-#define GO7007_MPEG4_PROFILE_S_L3 GO7007_MPEG_PROFILE(4, 0x03)
-#define GO7007_MPEG4_PROFILE_ARTS_L1 GO7007_MPEG_PROFILE(4, 0x91)
-#define GO7007_MPEG4_PROFILE_ARTS_L2 GO7007_MPEG_PROFILE(4, 0x92)
-#define GO7007_MPEG4_PROFILE_ARTS_L3 GO7007_MPEG_PROFILE(4, 0x93)
-#define GO7007_MPEG4_PROFILE_ARTS_L4 GO7007_MPEG_PROFILE(4, 0x94)
-#define GO7007_MPEG4_PROFILE_AS_L0 GO7007_MPEG_PROFILE(4, 0xf0)
-#define GO7007_MPEG4_PROFILE_AS_L1 GO7007_MPEG_PROFILE(4, 0xf1)
-#define GO7007_MPEG4_PROFILE_AS_L2 GO7007_MPEG_PROFILE(4, 0xf2)
-#define GO7007_MPEG4_PROFILE_AS_L3 GO7007_MPEG_PROFILE(4, 0xf3)
-#define GO7007_MPEG4_PROFILE_AS_L4 GO7007_MPEG_PROFILE(4, 0xf4)
-#define GO7007_MPEG4_PROFILE_AS_L5 GO7007_MPEG_PROFILE(4, 0xf5)
-
-struct go7007_md_params {
- __u16 region;
- __u16 trigger;
- __u16 pixel_threshold;
- __u16 motion_threshold;
- __u32 reserved[8];
-};
-
-struct go7007_md_region {
- __u16 region;
- __u16 flags;
- struct v4l2_clip *clips;
- __u32 reserved[8];
-};
-
-#define GO7007IOC_S_MPEG_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 2, \
- struct go7007_mpeg_params)
-#define GO7007IOC_G_MPEG_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 3, \
- struct go7007_mpeg_params)
-#define GO7007IOC_S_COMP_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 4, \
- struct go7007_comp_params)
-#define GO7007IOC_G_COMP_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 5, \
- struct go7007_comp_params)
-#define GO7007IOC_S_MD_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 6, \
- struct go7007_md_params)
-#define GO7007IOC_G_MD_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 7, \
- struct go7007_md_params)
-#define GO7007IOC_S_MD_REGION _IOW('V', BASE_VIDIOC_PRIVATE + 8, \
- struct go7007_md_region)
+++ /dev/null
-This is a driver for the WIS GO7007SB multi-format video encoder.
-
-Pete Eberlein <pete@sensoray.com>
-
-The driver was originally released under the GPL and is currently hosted at:
-http://nikosapi.org/wiki/index.php/WIS_Go7007_Linux_driver
-The go7007 firmware can be acquired from the package on the site above.
-
-I've modified the driver to support the following Video4Linux2 MPEG
-controls, with acceptable values:
-
-V4L2_CID_MPEG_STREAM_TYPE V4L2_MPEG_STREAM_TYPE_MPEG2_DVD
- V4L2_MPEG_STREAM_TYPE_MPEG_ELEM
-V4L2_CID_MPEG_VIDEO_ENCODING V4L2_MPEG_VIDEO_ENCODING_MPEG_1
- V4L2_MPEG_VIDEO_ENCODING_MPEG_2
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4
-V4L2_CID_MPEG_VIDEO_ASPECT V4L2_MPEG_VIDEO_ASPECT_1x1
- V4L2_MPEG_VIDEO_ASPECT_4x3
- V4L2_MPEG_VIDEO_ASPECT_16x9
-V4L2_CID_MPEG_VIDEO_GOP_SIZE integer
-V4L2_CID_MPEG_VIDEO_BITRATE 64000 .. 10000000
-
-These should be used instead of the non-standard GO7007 ioctls described
-below.
-
-
-The README files from the orignal package appear below:
-
----------------------------------------------------------------------------
- WIS GO7007SB Public Linux Driver
----------------------------------------------------------------------------
-
-
-*** Please see the file RELEASE-NOTES for important last-minute updates ***
-
-
- 0. OVERVIEW AND LICENSING/DISCLAIMER
-
-
-This driver kit contains Linux drivers for the WIS GO7007SB multi-format
-video encoder. Only kernel version 2.6.x is supported. The video stream
-is available through the Video4Linux2 API and the audio stream is available
-through the ALSA API (or the OSS emulation layer of the ALSA system).
-
-The files in kernel/ and hotplug/ are licensed under the GNU General Public
-License Version 2 from the Free Software Foundation. A copy of the license
-is included in the file COPYING.
-
-The example applications in apps/ and C header files in include/ are
-licensed under a permissive license included in the source files which
-allows copying, modification and redistribution for any purpose without
-attribution.
-
-The firmware files included in the firmware/ directory may be freely
-redistributed only in conjunction with this document; but modification,
-tampering and reverse engineering are prohibited.
-
-MICRONAS USA, INC., MAKES NO WARRANTIES TO ANY PERSON OR ENTITY WITH
-RESPECT TO THE SOFTWARE OR ANY DERIVATIVES THEREOF OR ANY SERVICES OR
-LICENSES AND DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATION
-WARRANTIES OF MERCHANTABILITY, SUPPORT, AND FITNESS FOR A PARTICULAR
-PURPOSE AND NON-INFRINGEMENT.
-
-
- 1. SYSTEM REQUIREMENTS
-
-
-This driver requires Linux kernel 2.6. Kernel 2.4 is not supported. Using
-kernel 2.6.10 or later is recommended, as earlier kernels are known to have
-unstable USB 2.0 support.
-
-A fully built kernel source tree must be available. Typically this will be
-linked from "/lib/modules/<KERNEL VERSION>/build" for convenience. If this
-link does not exist, an extra parameter will need to be passed to the
-`make` command.
-
-All vendor-built kernels should already be configured properly. However,
-for custom-built kernels, the following options need to be enabled in the
-kernel as built-in or modules:
-
- CONFIG_HOTPLUG - Support for hot-pluggable devices
- CONFIG_MODULES - Enable loadable module support
- CONFIG_KMOD - Automatic kernel module loading
- CONFIG_FW_LOADER - Hotplug firmware loading support
- CONFIG_I2C - I2C support
- CONFIG_VIDEO_DEV - Video For Linux
- CONFIG_SOUND - Sound card support
- CONFIG_SND - Advanced Linux Sound Architecture
- CONFIG_USB - Support for Host-side USB
- CONFIG_USB_DEVICEFS - USB device filesystem
- CONFIG_USB_EHCI_HCD - EHCI HCD (USB 2.0) support
-
-Additionally, to use the example application, the following options need to
-be enabled in the ALSA section:
-
- CONFIG_SND_MIXER_OSS - OSS Mixer API
- CONFIG_SND_PCM_OSS - OSS PCM (digital audio) API
-
-The hotplug scripts, along with the fxload utility, must also be installed.
-These scripts can be obtained from <http://linux-hotplug.sourceforge.net/>.
-Hotplugging is used for loading firmware into the Cypruss EZ-USB chip using
-fxload and for loading firmware into the driver using the firmware agent.
-
-
- 2. COMPILING AND INSTALLING THE DRIVER
-
-
-Most users should be able to compile the driver by simply running:
-
- $ make
-
-in the top-level directory of the driver kit. First the kernel modules
-will be built, followed by the example applications.
-
-If the build system is unable to locate the kernel source tree for the
-currently-running kernel, or if the module should be built for a kernel
-other than the currently-running kernel, an additional parameter will need
-to be passed to make to specify the appropriate kernel source directory:
-
- $ make KERNELSRC=/usr/src/linux-2.6.10-custom3
-
-Once the compile completes, the driver and firmware files should be
-installed by running:
-
- $ make install
-
-The kernel modules will be placed in "/lib/modules/<KERNEL VERSION>/extra"
-and the firmware files will be placed in the appropriate hotplug firmware
-directory, usually /lib/firmware. In addition, USB maps and scripts will
-be placed in /etc/hotplug/usb to enable fxload to initialize the EZ-USB
-control chip when the device is connected.
-
-
- 3. PAL/SECAM TUNER CONFIGURATION (TV402U-EU only)
-
-
-The PAL model of the Plextor ConvertX TV402U may require additional
-configuration to correctly select the appropriate TV frequency band and
-audio subchannel.
-
-Users with a device other than the Plextor ConvertX TV402U-EU should skip
-this section.
-
-The wide variety of PAL TV systems used in Europe requires that additional
-information about the local TV standards be passed to the driver in order
-to properly tune TV channels. The two necessary parameters are (a) the PAL
-TV band, and (b) the audio subchannel format in use.
-
-In many cases, the appropriate TV band selection is passed to the driver
-from applications. However, in some cases, the application only specifies
-that the driver should use PAL but not the specific information about the
-appropriate TV band. To work around this issue, the correct TV band may be
-specified in the "force_band" parameter to the wis-sony-tuner module:
-
- TV band force_band
- ------- ----------
- PAL B/G B
- PAL I I
- PAL D/K D
- SECAM L L
-
-If the "force_band" parameter is specified, the driver will ignore any TV
-band specified by applications and will always use the band provided in the
-module parameter.
-
-The other parameter that can be specified is the audio subchannel format.
-There are several stereo audio carrier systems in use, including NICAM and
-three varieties of A2. To receive audio broadcast on one of these stereo
-carriers, the "force_mpx_mode" parameter must be specified to the
-wis-sony-tuner module.
-
- TV band Audio subcarrier force_mpx_mode
- ------- ---------------- --------------
- PAL B/G Mono (default) 1
- PAL B/G A2 2
- PAL B/G NICAM 3
- PAL I Mono (default) 4
- PAL I NICAM 5
- PAL D/K Mono (default) 6
- PAL D/K A2 (1) 7
- PAL D/K A2 (2) 8
- PAL D/K A2 (3) 9
- PAL D/K NICAM 10
- SECAM L Mono (default) 11
- SECAM L NICAM 12
-
-If the "force_mpx_mode" parameter is not specified, the correct mono-only
-mode will be chosen based on the TV band. However, the tuner will not
-receive stereo audio or bilingual broadcasts correctly.
-
-To pass the "force_band" or "force_mpx_mode" parameters to the
-wis-sony-tuner module, the following line must be added to the modprobe
-configuration file, which varies from one Linux distribution to another.
-
- options wis-sony-tuner force_band=B force_mpx_mode=2
-
-The above example would force the tuner to the PAL B/G TV band and receive
-stereo audio broadcasts on the A2 carrier.
-
-To verify that the configuration has been placed in the correct location,
-execute:
-
- $ modprobe -c | grep wis-sony-tuner
-
-If the configuration line appears, then modprobe will pass the parameters
-correctly the next time the wis-sony-tuner module is loaded into the
-kernel.
-
-
- 4. TESTING THE DRIVER
-
-
-Because few Linux applications are able to correctly capture from
-Video4Linux2 devices with only compressed formats supported, the new driver
-should be tested with the "gorecord" application in the apps/ directory.
-
-First connect a video source to the device, such as a DVD player or VCR.
-This will be captured to a file for testing the driver. If an input source
-is unavailable, a test file can still be captured, but the video will be
-black and the audio will be silent.
-
-This application will auto-detect the V4L2 and ALSA/OSS device names of the
-hardware and will record video and audio to an AVI file for a specified
-number of seconds. For example:
-
- $ apps/gorecord -duration 60 capture.avi
-
-If this application does not successfully record an AVI file, the error
-messages produced by gorecord and recorded in the system log (usually in
-/var/log/messages) should provide information to help resolve the problem.
-
-Supplying no parameters to gorecord will cause it to probe the available
-devices and exit. Use the -help flag for usage information.
-
-
- 5. USING THE DRIVER
-
-
-The V4L2 device implemented by the driver provides a standard compressed
-format API, within the following criteria:
-
- * Applications that only support the original Video4Linux1 API will not
- be able to communicate with this driver at all.
-
- * No raw video modes are supported, so applications like xawtv that
- expect only uncompressed video will not function.
-
- * Supported compression formats are: Motion-JPEG, MPEG1, MPEG2 and MPEG4.
-
- * MPEG video formats are delivered as Video Elementary Streams only.
- Program Stream (PS), Transport Stream (TS) and Packetized Elementary
- Stream (PES) formats are not supported.
-
- * Video parameters such as format and input port may not be changed while
- the encoder is active.
-
- * The audio capture device only functions when the video encoder is
- actively capturing video. Attempts to read from the audio device when
- the encoder is inactive will result in an I/O error.
-
- * The native format of the audio device is 48Khz 2-channel 16-bit
- little-endian PCM, delivered through the ALSA system. No audio
- compression is implemented in the hardware. ALSA may convert to other
- uncompressed formats on the fly.
-
-The include/ directory contains a C header file describing non-standard
-features of the GO7007SB encoder, which are described below:
-
-
- GO7007IOC_S_COMP_PARAMS, GO7007IOC_G_COMP_PARAMS
-
- These ioctls are used to negotiate general compression parameters.
-
- To query the current parameters, call the GO7007IOC_G_COMP_PARAMS ioctl
- with a pointer to a struct go7007_comp_params. If the driver is not
- set to MPEG format, the EINVAL error code will be returned.
-
- To change the current parameters, initialize all fields of a struct
- go7007_comp_params and call the GO7007_IOC_S_COMP_PARAMS ioctl with a
- pointer to this structure. The driver will return the current
- parameters with any necessary changes to conform to the limitations of
- the hardware or current compression mode. Any or all fields can be set
- to zero to request a reasonable default value. If the driver is not
- set to MPEG format, the EINVAL error code will be returned. When I/O
- is in progress, the EBUSY error code will be returned.
-
- Fields in struct go7007_comp_params:
-
- __u32 The maximum number of frames in each
- gop_size Group Of Pictures; i.e. the maximum
- number of frames minus one between
- each key frame.
-
- __u32 The maximum number of sequential
- max_b_frames bidirectionally-predicted frames.
- (B-frames are not yet supported.)
-
- enum go7007_aspect_ratio The aspect ratio to be encoded in the
- aspect_ratio meta-data of the compressed format.
-
- Choices are:
- GO7007_ASPECT_RATIO_1_1
- GO7007_ASPECT_RATIO_4_3_NTSC
- GO7007_ASPECT_RATIO_4_3_PAL
- GO7007_ASPECT_RATIO_16_9_NTSC
- GO7007_ASPECT_RATIO_16_9_PAL
-
- __u32 Bit-wise OR of control flags (below)
- flags
-
- Flags in struct go7007_comp_params:
-
- GO7007_COMP_CLOSED_GOP Only produce self-contained GOPs, used
- to produce streams appropriate for
- random seeking.
-
- GO7007_COMP_OMIT_SEQ_HEADER Omit the stream sequence header.
-
-
- GO7007IOC_S_MPEG_PARAMS, GO7007IOC_G_MPEG_PARAMS
-
- These ioctls are used to negotiate MPEG-specific stream parameters when
- the pixelformat has been set to V4L2_PIX_FMT_MPEG.
-
- To query the current parameters, call the GO7007IOC_G_MPEG_PARAMS ioctl
- with a pointer to a struct go7007_mpeg_params. If the driver is not
- set to MPEG format, the EINVAL error code will be returned.
-
- To change the current parameters, initialize all fields of a struct
- go7007_mpeg_params and call the GO7007_IOC_S_MPEG_PARAMS ioctl with a
- pointer to this structure. The driver will return the current
- parameters with any necessary changes to conform to the limitations of
- the hardware or selected MPEG mode. Any or all fields can be set to
- zero to request a reasonable default value. If the driver is not set
- to MPEG format, the EINVAL error code will be returned. When I/O is in
- progress, the EBUSY error code will be returned.
-
- Fields in struct go7007_mpeg_params:
-
- enum go7007_mpeg_video_standard
- mpeg_video_standard The MPEG video standard in which to
- compress the video.
-
- Choices are:
- GO7007_MPEG_VIDEO_MPEG1
- GO7007_MPEG_VIDEO_MPEG2
- GO7007_MPEG_VIDEO_MPEG4
-
- __u32 Bit-wise OR of control flags (below)
- flags
-
- __u32 The profile and level indication to be
- pali stored in the sequence header. This
- is only used as an indicator to the
- decoder, and does not affect the MPEG
- features used in the video stream.
- Not valid for MPEG1.
-
- Choices for MPEG2 are:
- GO7007_MPEG2_PROFILE_MAIN_MAIN
-
- Choices for MPEG4 are:
- GO7007_MPEG4_PROFILE_S_L0
- GO7007_MPEG4_PROFILE_S_L1
- GO7007_MPEG4_PROFILE_S_L2
- GO7007_MPEG4_PROFILE_S_L3
- GO7007_MPEG4_PROFILE_ARTS_L1
- GO7007_MPEG4_PROFILE_ARTS_L2
- GO7007_MPEG4_PROFILE_ARTS_L3
- GO7007_MPEG4_PROFILE_ARTS_L4
- GO7007_MPEG4_PROFILE_AS_L0
- GO7007_MPEG4_PROFILE_AS_L1
- GO7007_MPEG4_PROFILE_AS_L2
- GO7007_MPEG4_PROFILE_AS_L3
- GO7007_MPEG4_PROFILE_AS_L4
- GO7007_MPEG4_PROFILE_AS_L5
-
- Flags in struct go7007_mpeg_params:
-
- GO7007_MPEG_FORCE_DVD_MODE Force all compression parameters and
- bitrate control settings to comply
- with DVD MPEG2 stream requirements.
- This overrides most compression and
- bitrate settings!
-
- GO7007_MPEG_OMIT_GOP_HEADER Omit the GOP header.
-
- GO7007_MPEG_REPEAT_SEQHEADER Repeat the MPEG sequence header at
- the start of each GOP.
-
-
- GO7007IOC_S_BITRATE, GO7007IOC_G_BITRATE
-
- These ioctls are used to set and query the target bitrate value for the
- compressed video stream. The bitrate may be selected by storing the
- target bits per second in an int and calling GO7007IOC_S_BITRATE with a
- pointer to the int. The bitrate may be queried by calling
- GO7007IOC_G_BITRATE with a pointer to an int where the current bitrate
- will be stored.
-
- Note that this is the primary means of controlling the video quality
- for all compression modes, including V4L2_PIX_FMT_MJPEG. The
- VIDIOC_S_JPEGCOMP ioctl is not supported.
-
-
-----------------------------------------------------------------------------
- Installing the WIS PCI Voyager Driver
----------------------------------------------------------------------------
-
-The WIS PCI Voyager driver requires several patches to the Linux 2.6.11.x
-kernel source tree before compiling the driver. These patches update the
-in-kernel SAA7134 driver to the newest development version and patch bugs
-in the TDA8290/TDA8275 tuner driver.
-
-The following patches must be downloaded from Gerd Knorr's website and
-applied in the order listed:
-
- http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner
- http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner2
- http://dl.bytesex.org/patches/2.6.11-2/v4l2-api-mpeg
- http://dl.bytesex.org/patches/2.6.11-2/saa7134-update
-
-The following patches are included with this SDK and can be applied in any
-order:
-
- patches/2.6.11/saa7134-voyager.diff
- patches/2.6.11/tda8275-newaddr.diff
- patches/2.6.11/tda8290-ntsc.diff
-
-Check to make sure the CONFIG_VIDEO_SAA7134 option is enabled in the kernel
-configuration, and build and install the kernel.
-
-After rebooting into the new kernel, the GO7007 driver can be compiled and
-installed:
-
- $ make SAA7134_BUILD=y
- $ make install
- $ modprobe saa7134-go7007
-
-There will be two V4L video devices associated with the PCI Voyager. The
-first device (most likely /dev/video0) provides access to the raw video
-capture mode of the SAA7133 device and is used to configure the source
-video parameters and tune the TV tuner. This device can be used with xawtv
-or other V4L(2) video software as a standard uncompressed device.
-
-The second device (most likely /dev/video1) provides access to the
-compression functions of the GO7007. It can be tested using the gorecord
-application in the apps/ directory of this SDK:
-
- $ apps/gorecord -vdevice /dev/video1 -noaudio test.avi
-
-Currently the frame resolution is fixed at 720x480 (NTSC) or 720x576 (PAL),
-and the video standard must be specified to both the raw and the compressed
-video devices (xawtv and gorecord, for example).
-
-
---------------------------------------------------------------------------
-RELEASE NOTES FOR WIS GO7007SB LINUX DRIVER
----------------------------------------------------------------------------
-
-Last updated: 5 November 2005
-
- - Release 0.9.7 includes new support for using udev to run fxload. The
- install script should automatically detect whether the old hotplug
- scripts or the new udev rules should be used. To force the use of
- hotplug, run "make install USE_UDEV=n". To force the use of udev, run
- "make install USE_UDEV=y".
-
- - Motion detection is supported but undocumented. Try the `modet` app
- for a demonstration of how to use the facility.
-
- - Using USB2.0 devices such as the TV402U with USB1.1 HCDs or hubs can
- cause buffer overruns and frame drops, even at low framerates, due to
- inconsistency in the bitrate control mechanism.
-
- - On devices with an SAA7115, including the Plextor ConvertX, video height
- values of 96, 128, 160, 192, 256, 320, and 384 do not work in NTSC mode.
- All valid heights up to 512 work correctly in PAL mode.
-
- - The WIS Star Trek and PCI Voyager boards have no support yet for audio
- or the TV tuner.
+++ /dev/null
-/*
- * Copyright (C) 2008 Sensoray Company Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-subdev.h>
-#include "go7007-priv.h"
-
-MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver");
-MODULE_LICENSE("GPL v2");
-
-#define TLV320_ADDRESS 0x34
-#define VPX322_ADDR_ANALOGCONTROL1 0x02
-#define VPX322_ADDR_BRIGHTNESS0 0x0127
-#define VPX322_ADDR_BRIGHTNESS1 0x0131
-#define VPX322_ADDR_CONTRAST0 0x0128
-#define VPX322_ADDR_CONTRAST1 0x0132
-#define VPX322_ADDR_HUE 0x00dc
-#define VPX322_ADDR_SAT 0x0030
-
-struct go7007_usb_board {
- unsigned int flags;
- struct go7007_board_info main_info;
-};
-
-struct go7007_usb {
- struct go7007_usb_board *board;
- struct mutex i2c_lock;
- struct usb_device *usbdev;
- struct urb *video_urbs[8];
- struct urb *audio_urbs[8];
- struct urb *intr_urb;
-};
-
-static unsigned char aud_regs[] = {
- 0x1e, 0x00,
- 0x00, 0x17,
- 0x02, 0x17,
- 0x04, 0xf9,
- 0x06, 0xf9,
- 0x08, 0x02,
- 0x0a, 0x00,
- 0x0c, 0x00,
- 0x0a, 0x00,
- 0x0c, 0x00,
- 0x0e, 0x02,
- 0x10, 0x00,
- 0x12, 0x01,
- 0x00, 0x00,
-};
-
-
-static unsigned char vid_regs[] = {
- 0xF2, 0x0f,
- 0xAA, 0x00,
- 0xF8, 0xff,
- 0x00, 0x00,
-};
-
-static u16 vid_regs_fp[] = {
- 0x028, 0x067,
- 0x120, 0x016,
- 0x121, 0xcF2,
- 0x122, 0x0F2,
- 0x123, 0x00c,
- 0x124, 0x2d0,
- 0x125, 0x2e0,
- 0x126, 0x004,
- 0x128, 0x1E0,
- 0x12A, 0x016,
- 0x12B, 0x0F2,
- 0x12C, 0x0F2,
- 0x12D, 0x00c,
- 0x12E, 0x2d0,
- 0x12F, 0x2e0,
- 0x130, 0x004,
- 0x132, 0x1E0,
- 0x140, 0x060,
- 0x153, 0x00C,
- 0x154, 0x200,
- 0x150, 0x801,
- 0x000, 0x000
-};
-
-/* PAL specific values */
-static u16 vid_regs_fp_pal[] =
-{
- 0x120, 0x017,
- 0x121, 0xd22,
- 0x122, 0x122,
- 0x12A, 0x017,
- 0x12B, 0x122,
- 0x12C, 0x122,
- 0x140, 0x060,
- 0x000, 0x000,
-};
-
-struct s2250 {
- struct v4l2_subdev sd;
- v4l2_std_id std;
- int input;
- int brightness;
- int contrast;
- int saturation;
- int hue;
- int reg12b_val;
- int audio_input;
- struct i2c_client *audio;
-};
-
-static inline struct s2250 *to_state(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct s2250, sd);
-}
-
-/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
-static int go7007_usb_vendor_request(struct go7007 *go, u16 request,
- u16 value, u16 index, void *transfer_buffer, int length, int in)
-{
- struct go7007_usb *usb = go->hpi_context;
- int timeout = 5000;
-
- if (in) {
- return usb_control_msg(usb->usbdev,
- usb_rcvctrlpipe(usb->usbdev, 0), request,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- value, index, transfer_buffer, length, timeout);
- } else {
- return usb_control_msg(usb->usbdev,
- usb_sndctrlpipe(usb->usbdev, 0), request,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, transfer_buffer, length, timeout);
- }
-}
-/* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
-
-static int write_reg(struct i2c_client *client, u8 reg, u8 value)
-{
- struct go7007 *go = i2c_get_adapdata(client->adapter);
- struct go7007_usb *usb;
- int rc;
- int dev_addr = client->addr << 1; /* firmware wants 8-bit address */
- u8 *buf;
-
- if (go == NULL)
- return -ENODEV;
-
- if (go->status == STATUS_SHUTDOWN)
- return -EBUSY;
-
- buf = kzalloc(16, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- usb = go->hpi_context;
- if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
- printk(KERN_INFO "i2c lock failed\n");
- kfree(buf);
- return -EINTR;
- }
- rc = go7007_usb_vendor_request(go, 0x55, dev_addr,
- (reg<<8 | value),
- buf,
- 16, 1);
-
- mutex_unlock(&usb->i2c_lock);
- kfree(buf);
- return rc;
-}
-
-static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val)
-{
- struct go7007 *go = i2c_get_adapdata(client->adapter);
- struct go7007_usb *usb;
- u8 *buf;
- struct s2250 *dec = i2c_get_clientdata(client);
-
- if (go == NULL)
- return -ENODEV;
-
- if (go->status == STATUS_SHUTDOWN)
- return -EBUSY;
-
- buf = kzalloc(16, GFP_KERNEL);
-
- if (buf == NULL)
- return -ENOMEM;
-
-
-
- memset(buf, 0xcd, 6);
-
- usb = go->hpi_context;
- if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
- printk(KERN_INFO "i2c lock failed\n");
- kfree(buf);
- return -EINTR;
- }
- if (go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1) < 0) {
- kfree(buf);
- return -EFAULT;
- }
-
- mutex_unlock(&usb->i2c_lock);
- if (buf[0] == 0) {
- unsigned int subaddr, val_read;
-
- subaddr = (buf[4] << 8) + buf[5];
- val_read = (buf[2] << 8) + buf[3];
- kfree(buf);
- if (val_read != val) {
- printk(KERN_INFO "invalid fp write %x %x\n",
- val_read, val);
- return -EFAULT;
- }
- if (subaddr != addr) {
- printk(KERN_INFO "invalid fp write addr %x %x\n",
- subaddr, addr);
- return -EFAULT;
- }
- } else {
- kfree(buf);
- return -EFAULT;
- }
-
- /* save last 12b value */
- if (addr == 0x12b)
- dec->reg12b_val = val;
-
- return 0;
-}
-
-static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val)
-{
- struct go7007 *go = i2c_get_adapdata(client->adapter);
- struct go7007_usb *usb;
- u8 *buf;
-
- if (go == NULL)
- return -ENODEV;
-
- if (go->status == STATUS_SHUTDOWN)
- return -EBUSY;
-
- buf = kzalloc(16, GFP_KERNEL);
-
- if (buf == NULL)
- return -ENOMEM;
-
-
-
- memset(buf, 0xcd, 6);
- usb = go->hpi_context;
- if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
- printk(KERN_INFO "i2c lock failed\n");
- kfree(buf);
- return -EINTR;
- }
- if (go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1) < 0) {
- kfree(buf);
- return -EFAULT;
- }
- mutex_unlock(&usb->i2c_lock);
-
- *val = (buf[0] << 8) | buf[1];
- kfree(buf);
-
- return 0;
-}
-
-
-static int write_regs(struct i2c_client *client, u8 *regs)
-{
- int i;
-
- for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) {
- if (write_reg(client, regs[i], regs[i+1]) < 0) {
- printk(KERN_INFO "s2250: failed\n");
- return -1;
- }
- }
- return 0;
-}
-
-static int write_regs_fp(struct i2c_client *client, u16 *regs)
-{
- int i;
-
- for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) {
- if (write_reg_fp(client, regs[i], regs[i+1]) < 0) {
- printk(KERN_INFO "s2250: failed fp\n");
- return -1;
- }
- }
- return 0;
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
- u32 config)
-{
- struct s2250 *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int vidsys;
-
- vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00;
- if (input == 0) {
- /* composite */
- write_reg_fp(client, 0x20, 0x020 | vidsys);
- write_reg_fp(client, 0x21, 0x662);
- write_reg_fp(client, 0x140, 0x060);
- } else if (input == 1) {
- /* S-Video */
- write_reg_fp(client, 0x20, 0x040 | vidsys);
- write_reg_fp(client, 0x21, 0x666);
- write_reg_fp(client, 0x140, 0x060);
- } else {
- return -EINVAL;
- }
- state->input = input;
- return 0;
-}
-
-static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
-{
- struct s2250 *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- u16 vidsource;
-
- vidsource = (state->input == 1) ? 0x040 : 0x020;
- switch (norm) {
- case V4L2_STD_NTSC:
- write_regs_fp(client, vid_regs_fp);
- write_reg_fp(client, 0x20, vidsource | 1);
- break;
- case V4L2_STD_PAL:
- write_regs_fp(client, vid_regs_fp);
- write_regs_fp(client, vid_regs_fp_pal);
- write_reg_fp(client, 0x20, vidsource);
- break;
- default:
- return -EINVAL;
- }
- state->std = norm;
- return 0;
-}
-
-static int s2250_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query)
-{
- switch (query->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(query, -50, 50, 1, 0);
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int s2250_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct s2250 *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int value1;
- u16 oldvalue;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value > 100)
- state->brightness = 100;
- else if (ctrl->value < 0)
- state->brightness = 0;
- else
- state->brightness = ctrl->value;
- value1 = (state->brightness - 50) * 255 / 100;
- read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue);
- write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0,
- value1 | (oldvalue & ~0xff));
- read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue);
- write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1,
- value1 | (oldvalue & ~0xff));
- write_reg_fp(client, 0x140, 0x60);
- break;
- case V4L2_CID_CONTRAST:
- if (ctrl->value > 100)
- state->contrast = 100;
- else if (ctrl->value < 0)
- state->contrast = 0;
- else
- state->contrast = ctrl->value;
- value1 = state->contrast * 0x40 / 100;
- if (value1 > 0x3f)
- value1 = 0x3f; /* max */
- read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue);
- write_reg_fp(client, VPX322_ADDR_CONTRAST0,
- value1 | (oldvalue & ~0x3f));
- read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue);
- write_reg_fp(client, VPX322_ADDR_CONTRAST1,
- value1 | (oldvalue & ~0x3f));
- write_reg_fp(client, 0x140, 0x60);
- break;
- case V4L2_CID_SATURATION:
- if (ctrl->value > 100)
- state->saturation = 100;
- else if (ctrl->value < 0)
- state->saturation = 0;
- else
- state->saturation = ctrl->value;
- value1 = state->saturation * 4140 / 100;
- if (value1 > 4094)
- value1 = 4094;
- write_reg_fp(client, VPX322_ADDR_SAT, value1);
- break;
- case V4L2_CID_HUE:
- if (ctrl->value > 50)
- state->hue = 50;
- else if (ctrl->value < -50)
- state->hue = -50;
- else
- state->hue = ctrl->value;
- /* clamp the hue range */
- value1 = state->hue * 280 / 50;
- write_reg_fp(client, VPX322_ADDR_HUE, value1);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int s2250_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct s2250 *state = to_state(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = state->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = state->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = state->saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = state->hue;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int s2250_s_mbus_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *fmt)
-{
- struct s2250 *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (fmt->height < 640) {
- write_reg_fp(client, 0x12b, state->reg12b_val | 0x400);
- write_reg_fp(client, 0x140, 0x060);
- } else {
- write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400);
- write_reg_fp(client, 0x140, 0x060);
- }
- return 0;
-}
-
-static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output,
- u32 config)
-{
- struct s2250 *state = to_state(sd);
-
- switch (input) {
- case 0:
- write_reg(state->audio, 0x08, 0x02); /* Line In */
- break;
- case 1:
- write_reg(state->audio, 0x08, 0x04); /* Mic */
- break;
- case 2:
- write_reg(state->audio, 0x08, 0x05); /* Mic Boost */
- break;
- default:
- return -EINVAL;
- }
- state->audio_input = input;
- return 0;
-}
-
-
-static int s2250_log_status(struct v4l2_subdev *sd)
-{
- struct s2250 *state = to_state(sd);
-
- v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" :
- state->std == V4L2_STD_PAL ? "PAL" :
- state->std == V4L2_STD_SECAM ? "SECAM" :
- "unknown");
- v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" :
- state->input == 1 ? "S-video" :
- "error");
- v4l2_info(sd, "Brightness: %d\n", state->brightness);
- v4l2_info(sd, "Contrast: %d\n", state->contrast);
- v4l2_info(sd, "Saturation: %d\n", state->saturation);
- v4l2_info(sd, "Hue: %d\n", state->hue); return 0;
- v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" :
- state->audio_input == 1 ? "Mic" :
- state->audio_input == 2 ? "Mic Boost" :
- "error");
- return 0;
-}
-
-/* --------------------------------------------------------------------------*/
-
-static const struct v4l2_subdev_core_ops s2250_core_ops = {
- .log_status = s2250_log_status,
- .g_ctrl = s2250_g_ctrl,
- .s_ctrl = s2250_s_ctrl,
- .queryctrl = s2250_queryctrl,
- .s_std = s2250_s_std,
-};
-
-static const struct v4l2_subdev_audio_ops s2250_audio_ops = {
- .s_routing = s2250_s_audio_routing,
-};
-
-static const struct v4l2_subdev_video_ops s2250_video_ops = {
- .s_routing = s2250_s_video_routing,
- .s_mbus_fmt = s2250_s_mbus_fmt,
-};
-
-static const struct v4l2_subdev_ops s2250_ops = {
- .core = &s2250_core_ops,
- .audio = &s2250_audio_ops,
- .video = &s2250_video_ops,
-};
-
-/* --------------------------------------------------------------------------*/
-
-static int s2250_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_client *audio;
- struct i2c_adapter *adapter = client->adapter;
- struct s2250 *state;
- struct v4l2_subdev *sd;
- u8 *data;
- struct go7007 *go = i2c_get_adapdata(adapter);
- struct go7007_usb *usb = go->hpi_context;
-
- audio = i2c_new_dummy(adapter, TLV320_ADDRESS >> 1);
- if (audio == NULL)
- return -ENOMEM;
-
- state = kmalloc(sizeof(struct s2250), GFP_KERNEL);
- if (state == NULL) {
- i2c_unregister_device(audio);
- return -ENOMEM;
- }
-
- sd = &state->sd;
- v4l2_i2c_subdev_init(sd, client, &s2250_ops);
-
- v4l2_info(sd, "initializing %s at address 0x%x on %s\n",
- "Sensoray 2250/2251", client->addr, client->adapter->name);
-
- state->std = V4L2_STD_NTSC;
- state->brightness = 50;
- state->contrast = 50;
- state->saturation = 50;
- state->hue = 0;
- state->audio = audio;
-
- /* initialize the audio */
- if (write_regs(audio, aud_regs) < 0) {
- printk(KERN_ERR
- "s2250: error initializing audio\n");
- i2c_unregister_device(audio);
- kfree(state);
- return 0;
- }
-
- if (write_regs(client, vid_regs) < 0) {
- printk(KERN_ERR
- "s2250: error initializing decoder\n");
- i2c_unregister_device(audio);
- kfree(state);
- return 0;
- }
- if (write_regs_fp(client, vid_regs_fp) < 0) {
- printk(KERN_ERR
- "s2250: error initializing decoder\n");
- i2c_unregister_device(audio);
- kfree(state);
- return 0;
- }
- /* set default channel */
- /* composite */
- write_reg_fp(client, 0x20, 0x020 | 1);
- write_reg_fp(client, 0x21, 0x662);
- write_reg_fp(client, 0x140, 0x060);
-
- /* set default audio input */
- state->audio_input = 0;
- write_reg(client, 0x08, 0x02); /* Line In */
-
- if (mutex_lock_interruptible(&usb->i2c_lock) == 0) {
- data = kzalloc(16, GFP_KERNEL);
- if (data != NULL) {
- int rc;
- rc = go7007_usb_vendor_request(go, 0x41, 0, 0,
- data, 16, 1);
- if (rc > 0) {
- u8 mask;
- data[0] = 0;
- mask = 1<<5;
- data[0] &= ~mask;
- data[1] |= mask;
- go7007_usb_vendor_request(go, 0x40, 0,
- (data[1]<<8)
- + data[1],
- data, 16, 0);
- }
- kfree(data);
- }
- mutex_unlock(&usb->i2c_lock);
- }
-
- v4l2_info(sd, "initialized successfully\n");
- return 0;
-}
-
-static int s2250_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
- v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
- return 0;
-}
-
-static const struct i2c_device_id s2250_id[] = {
- { "s2250", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, s2250_id);
-
-static struct i2c_driver s2250_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "s2250",
- },
- .probe = s2250_probe,
- .remove = s2250_remove,
- .id_table = s2250_id,
-};
-
-static __init int init_s2250(void)
-{
- return i2c_add_driver(&s2250_driver);
-}
-
-static __exit void exit_s2250(void)
-{
- i2c_del_driver(&s2250_driver);
-}
-
-module_init(init_s2250);
-module_exit(exit_s2250);
+++ /dev/null
-/*
- * Copyright (C) 2008 Sensoray Company Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <dvb-usb.h>
-
-#define S2250_LOADER_FIRMWARE "s2250_loader.fw"
-#define S2250_FIRMWARE "s2250.fw"
-
-typedef struct device_extension_s {
- struct kref kref;
- int minor;
- struct usb_device *usbdev;
-} device_extension_t, *pdevice_extension_t;
-
-#define USB_s2250loader_MAJOR 240
-#define USB_s2250loader_MINOR_BASE 0
-#define MAX_DEVICES 256
-
-static pdevice_extension_t s2250_dev_table[MAX_DEVICES];
-static DEFINE_MUTEX(s2250_dev_table_mutex);
-
-#define to_s2250loader_dev_common(d) container_of(d, device_extension_t, kref)
-static void s2250loader_delete(struct kref *kref)
-{
- pdevice_extension_t s = to_s2250loader_dev_common(kref);
- s2250_dev_table[s->minor] = NULL;
- kfree(s);
-}
-
-static int s2250loader_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct usb_device *usbdev;
- int minor, ret;
- pdevice_extension_t s = NULL;
- const struct firmware *fw;
-
- usbdev = usb_get_dev(interface_to_usbdev(interface));
- if (!usbdev) {
- printk(KERN_ERR "Enter s2250loader_probe failed\n");
- return -1;
- }
- printk(KERN_INFO "Enter s2250loader_probe 2.6 kernel\n");
- printk(KERN_INFO "vendor id 0x%x, device id 0x%x devnum:%d\n",
- usbdev->descriptor.idVendor, usbdev->descriptor.idProduct,
- usbdev->devnum);
-
- if (usbdev->descriptor.bNumConfigurations != 1) {
- printk(KERN_ERR "can't handle multiple config\n");
- return -1;
- }
- mutex_lock(&s2250_dev_table_mutex);
-
- for (minor = 0; minor < MAX_DEVICES; minor++) {
- if (s2250_dev_table[minor] == NULL)
- break;
- }
-
- if (minor < 0 || minor >= MAX_DEVICES) {
- printk(KERN_ERR "Invalid minor: %d\n", minor);
- goto failed;
- }
-
- /* Allocate dev data structure */
- s = kmalloc(sizeof(device_extension_t), GFP_KERNEL);
- if (s == NULL) {
- printk(KERN_ERR "Out of memory\n");
- goto failed;
- }
- s2250_dev_table[minor] = s;
-
- printk(KERN_INFO "s2250loader_probe: Device %d on Bus %d Minor %d\n",
- usbdev->devnum, usbdev->bus->busnum, minor);
-
- memset(s, 0, sizeof(device_extension_t));
- s->usbdev = usbdev;
- printk(KERN_INFO "loading 2250 loader\n");
-
- kref_init(&(s->kref));
-
- mutex_unlock(&s2250_dev_table_mutex);
-
- if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) {
- printk(KERN_ERR
- "s2250: unable to load firmware from file \"%s\"\n",
- S2250_LOADER_FIRMWARE);
- goto failed2;
- }
- ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2);
- release_firmware(fw);
- if (0 != ret) {
- printk(KERN_ERR "loader download failed\n");
- goto failed2;
- }
-
- if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) {
- printk(KERN_ERR
- "s2250: unable to load firmware from file \"%s\"\n",
- S2250_FIRMWARE);
- goto failed2;
- }
- ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2);
- release_firmware(fw);
- if (0 != ret) {
- printk(KERN_ERR "firmware_s2250 download failed\n");
- goto failed2;
- }
-
- usb_set_intfdata(interface, s);
- return 0;
-
-failed:
- mutex_unlock(&s2250_dev_table_mutex);
-failed2:
- if (s)
- kref_put(&(s->kref), s2250loader_delete);
-
- printk(KERN_ERR "probe failed\n");
- return -1;
-}
-
-static void s2250loader_disconnect(struct usb_interface *interface)
-{
- pdevice_extension_t s;
- printk(KERN_INFO "s2250: disconnect\n");
- s = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
- kref_put(&(s->kref), s2250loader_delete);
-}
-
-static const struct usb_device_id s2250loader_ids[] = {
- {USB_DEVICE(0x1943, 0xa250)},
- {} /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, s2250loader_ids);
-
-static struct usb_driver s2250loader_driver = {
- .name = "s2250-loader",
- .probe = s2250loader_probe,
- .disconnect = s2250loader_disconnect,
- .id_table = s2250loader_ids,
-};
-
-static int __init s2250loader_init(void)
-{
- int r;
- unsigned i = 0;
-
- for (i = 0; i < MAX_DEVICES; i++)
- s2250_dev_table[i] = NULL;
-
- r = usb_register(&s2250loader_driver);
- if (r) {
- printk(KERN_ERR "usb_register failed. Error number %d\n", r);
- return -1;
- }
-
- printk(KERN_INFO "s2250loader_init: driver registered\n");
- return 0;
-}
-module_init(s2250loader_init);
-
-static void __exit s2250loader_cleanup(void)
-{
- printk(KERN_INFO "s2250loader_cleanup\n");
- usb_deregister(&s2250loader_driver);
-}
-module_exit(s2250loader_cleanup);
-
-MODULE_AUTHOR("");
-MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251");
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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 _S2250_LOADER_H_
-#define _S2250_LOADER_H_
-
-extern int s2250loader_init(void);
-extern void s2250loader_cleanup(void);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/wait.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/mm.h>
-#include <linux/usb.h>
-#include <linux/i2c.h>
-#include <asm/byteorder.h>
-#include <media/v4l2-common.h>
-
-#include "saa7134-reg.h"
-#include "saa7134.h"
-#include "go7007-priv.h"
-
-#define GO7007_HPI_DEBUG
-
-enum hpi_address {
- HPI_ADDR_VIDEO_BUFFER = 0xe4,
- HPI_ADDR_INIT_BUFFER = 0xea,
- HPI_ADDR_INTR_RET_VALUE = 0xee,
- HPI_ADDR_INTR_RET_DATA = 0xec,
- HPI_ADDR_INTR_STATUS = 0xf4,
- HPI_ADDR_INTR_WR_PARAM = 0xf6,
- HPI_ADDR_INTR_WR_INDEX = 0xf8,
-};
-
-enum gpio_command {
- GPIO_COMMAND_RESET = 0x00, /* 000b */
- GPIO_COMMAND_REQ1 = 0x04, /* 001b */
- GPIO_COMMAND_WRITE = 0x20, /* 010b */
- GPIO_COMMAND_REQ2 = 0x24, /* 011b */
- GPIO_COMMAND_READ = 0x80, /* 100b */
- GPIO_COMMAND_VIDEO = 0x84, /* 101b */
- GPIO_COMMAND_IDLE = 0xA0, /* 110b */
- GPIO_COMMAND_ADDR = 0xA4, /* 111b */
-};
-
-struct saa7134_go7007 {
- struct saa7134_dev *dev;
- u8 *top;
- u8 *bottom;
- dma_addr_t top_dma;
- dma_addr_t bottom_dma;
-};
-
-static struct go7007_board_info board_voyager = {
- .firmware = "go7007tv.bin",
- .flags = 0,
- .sensor_flags = GO7007_SENSOR_656 |
- GO7007_SENSOR_VALID_ENABLE |
- GO7007_SENSOR_TV |
- GO7007_SENSOR_VBI,
- .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
- GO7007_AUDIO_WORD_16,
- .audio_rate = 48000,
- .audio_bclk_div = 8,
- .audio_main_div = 2,
- .hpi_buffer_cap = 7,
- .num_inputs = 1,
- .inputs = {
- {
- .name = "SAA7134",
- },
- },
-};
-MODULE_FIRMWARE("go7007tv.bin");
-
-/********************* Driver for GPIO HPI interface *********************/
-
-static int gpio_write(struct saa7134_dev *dev, u8 addr, u16 data)
-{
- saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
-
- /* Write HPI address */
- saa_writeb(SAA7134_GPIO_GPSTATUS0, addr);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
-
- /* Write low byte */
- saa_writeb(SAA7134_GPIO_GPSTATUS0, data & 0xff);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
-
- /* Write high byte */
- saa_writeb(SAA7134_GPIO_GPSTATUS0, data >> 8);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
-
- return 0;
-}
-
-static int gpio_read(struct saa7134_dev *dev, u8 addr, u16 *data)
-{
- saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
-
- /* Write HPI address */
- saa_writeb(SAA7134_GPIO_GPSTATUS0, addr);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
-
- saa_writeb(SAA7134_GPIO_GPMODE0, 0x00);
-
- /* Read low byte */
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ);
- saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- *data = saa_readb(SAA7134_GPIO_GPSTATUS0);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
-
- /* Read high byte */
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ);
- saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- *data |= saa_readb(SAA7134_GPIO_GPSTATUS0) << 8;
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
-
- return 0;
-}
-
-static int saa7134_go7007_interface_reset(struct go7007 *go)
-{
- struct saa7134_go7007 *saa = go->hpi_context;
- struct saa7134_dev *dev = saa->dev;
- u32 status;
- u16 intr_val, intr_data;
- int count = 20;
-
- saa_clearb(SAA7134_TS_PARALLEL, 0x80); /* Disable TS interface */
- saa_writeb(SAA7134_GPIO_GPMODE2, 0xa4);
- saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
-
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_RESET);
- msleep(1);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2);
- msleep(10);
-
- saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
-
- status = saa_readb(SAA7134_GPIO_GPSTATUS2);
- /*printk(KERN_DEBUG "status is %s\n", status & 0x40 ? "OK" : "not OK"); */
-
- /* enter command mode...(?) */
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2);
-
- do {
- saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- status = saa_readb(SAA7134_GPIO_GPSTATUS2);
- /*printk(KERN_INFO "gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */
- } while (--count > 0);
-
- /* Wait for an interrupt to indicate successful hardware reset */
- if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
- (intr_val & ~0x1) != 0x55aa) {
- printk(KERN_ERR
- "saa7134-go7007: unable to reset the GO7007\n");
- return -1;
- }
- return 0;
-}
-
-static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data)
-{
- struct saa7134_go7007 *saa = go->hpi_context;
- struct saa7134_dev *dev = saa->dev;
- int i;
- u16 status_reg;
-
-#ifdef GO7007_HPI_DEBUG
- printk(KERN_DEBUG
- "saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data);
-#endif
-
- for (i = 0; i < 100; ++i) {
- gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg);
- if (!(status_reg & 0x0010))
- break;
- msleep(10);
- }
- if (i == 100) {
- printk(KERN_ERR
- "saa7134-go7007: device is hung, status reg = 0x%04x\n",
- status_reg);
- return -1;
- }
- gpio_write(dev, HPI_ADDR_INTR_WR_PARAM, data);
- gpio_write(dev, HPI_ADDR_INTR_WR_INDEX, addr);
-
- return 0;
-}
-
-static int saa7134_go7007_read_interrupt(struct go7007 *go)
-{
- struct saa7134_go7007 *saa = go->hpi_context;
- struct saa7134_dev *dev = saa->dev;
-
- /* XXX we need to wait if there is no interrupt available */
- go->interrupt_available = 1;
- gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value);
- gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data);
-#ifdef GO7007_HPI_DEBUG
- printk(KERN_DEBUG "saa7134-go7007: ReadInterrupt: %04x %04x\n",
- go->interrupt_value, go->interrupt_data);
-#endif
- return 0;
-}
-
-static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev,
- unsigned long status)
-{
- struct go7007 *go = video_get_drvdata(dev->empress_dev);
- struct saa7134_go7007 *saa = go->hpi_context;
-
- if (!go->streaming)
- return;
- if (0 != (status & 0x000f0000))
- printk(KERN_DEBUG "saa7134-go7007: irq: lost %ld\n",
- (status >> 16) & 0x0f);
- if (status & 0x100000) {
- dma_sync_single_for_cpu(&dev->pci->dev,
- saa->bottom_dma, PAGE_SIZE, DMA_FROM_DEVICE);
- go7007_parse_video_stream(go, saa->bottom, PAGE_SIZE);
- saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma));
- } else {
- dma_sync_single_for_cpu(&dev->pci->dev,
- saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE);
- go7007_parse_video_stream(go, saa->top, PAGE_SIZE);
- saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma));
- }
-}
-
-static int saa7134_go7007_stream_start(struct go7007 *go)
-{
- struct saa7134_go7007 *saa = go->hpi_context;
- struct saa7134_dev *dev = saa->dev;
-
- saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top),
- 0, PAGE_SIZE, DMA_FROM_DEVICE);
- if (!saa->top_dma)
- return -ENOMEM;
- saa->bottom_dma = dma_map_page(&dev->pci->dev,
- virt_to_page(saa->bottom),
- 0, PAGE_SIZE, DMA_FROM_DEVICE);
- if (!saa->bottom_dma) {
- dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE,
- DMA_FROM_DEVICE);
- return -ENOMEM;
- }
-
- saa_writel(SAA7134_VIDEO_PORT_CTRL0 >> 2, 0xA300B000);
- saa_writel(SAA7134_VIDEO_PORT_CTRL4 >> 2, 0x40000200);
-
- /* Set HPI interface for video */
- saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
- saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_VIDEO_BUFFER);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
- saa_writeb(SAA7134_GPIO_GPMODE0, 0x00);
-
- /* Enable TS interface */
- saa_writeb(SAA7134_TS_PARALLEL, 0xe6);
-
- /* Reset TS interface */
- saa_setb(SAA7134_TS_SERIAL1, 0x01);
- saa_clearb(SAA7134_TS_SERIAL1, 0x01);
-
- /* Set up transfer block size */
- saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1);
- saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1);
- saa_writeb(SAA7134_TS_DMA1, 0);
- saa_writeb(SAA7134_TS_DMA2, 0);
-
- /* Enable video streaming mode */
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO);
-
- saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma));
- saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma));
- saa_writel(SAA7134_RS_PITCH(5), 128);
- saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_MAX);
-
- /* Enable TS FIFO */
- saa_setl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5);
-
- /* Enable DMA IRQ */
- saa_setl(SAA7134_IRQ1,
- SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0);
-
- return 0;
-}
-
-static int saa7134_go7007_stream_stop(struct go7007 *go)
-{
- struct saa7134_go7007 *saa = go->hpi_context;
- struct saa7134_dev *dev;
-
- if (!saa)
- return -EINVAL;
- dev = saa->dev;
- if (!dev)
- return -EINVAL;
-
- /* Shut down TS FIFO */
- saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5);
-
- /* Disable DMA IRQ */
- saa_clearl(SAA7134_IRQ1,
- SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0);
-
- /* Disable TS interface */
- saa_clearb(SAA7134_TS_PARALLEL, 0x80);
-
- dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE,
- DMA_FROM_DEVICE);
- dma_unmap_page(&dev->pci->dev, saa->bottom_dma, PAGE_SIZE,
- DMA_FROM_DEVICE);
-
- return 0;
-}
-
-static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len)
-{
- struct saa7134_go7007 *saa = go->hpi_context;
- struct saa7134_dev *dev = saa->dev;
- u16 status_reg;
- int i;
-
-#ifdef GO7007_HPI_DEBUG
- printk(KERN_DEBUG "saa7134-go7007: DownloadBuffer "
- "sending %d bytes\n", len);
-#endif
-
- while (len > 0) {
- i = len > 64 ? 64 : len;
- saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
- saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_INIT_BUFFER);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
- while (i-- > 0) {
- saa_writeb(SAA7134_GPIO_GPSTATUS0, *data);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
- saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
- ++data;
- --len;
- }
- for (i = 0; i < 100; ++i) {
- gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg);
- if (!(status_reg & 0x0002))
- break;
- }
- if (i == 100) {
- printk(KERN_ERR "saa7134-go7007: device is hung, "
- "status reg = 0x%04x\n", status_reg);
- return -1;
- }
- }
- return 0;
-}
-
-static int saa7134_go7007_send_command(struct go7007 *go, unsigned int cmd,
- void *arg)
-{
- struct saa7134_go7007 *saa = go->hpi_context;
- struct saa7134_dev *dev = saa->dev;
-
- switch (cmd) {
- case VIDIOC_S_STD:
- {
- v4l2_std_id *std = arg;
- return saa7134_s_std_internal(dev, NULL, std);
- }
- case VIDIOC_G_STD:
- {
- v4l2_std_id *std = arg;
- *std = dev->tvnorm->id;
- return 0;
- }
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *ctrl = arg;
- if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER)
- return saa7134_queryctrl(NULL, NULL, ctrl);
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER)
- return saa7134_g_ctrl_internal(dev, NULL, ctrl);
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER)
- return saa7134_s_ctrl_internal(dev, NULL, ctrl);
- }
- }
- return -EINVAL;
-
-}
-
-static struct go7007_hpi_ops saa7134_go7007_hpi_ops = {
- .interface_reset = saa7134_go7007_interface_reset,
- .write_interrupt = saa7134_go7007_write_interrupt,
- .read_interrupt = saa7134_go7007_read_interrupt,
- .stream_start = saa7134_go7007_stream_start,
- .stream_stop = saa7134_go7007_stream_stop,
- .send_firmware = saa7134_go7007_send_firmware,
- .send_command = saa7134_go7007_send_command,
-};
-
-/********************* Add/remove functions *********************/
-
-static int saa7134_go7007_init(struct saa7134_dev *dev)
-{
- struct go7007 *go;
- struct saa7134_go7007 *saa;
-
- printk(KERN_DEBUG "saa7134-go7007: probing new SAA713X board\n");
-
- saa = kzalloc(sizeof(struct saa7134_go7007), GFP_KERNEL);
- if (saa == NULL)
- return -ENOMEM;
-
- /* Allocate a couple pages for receiving the compressed stream */
- saa->top = (u8 *)get_zeroed_page(GFP_KERNEL);
- if (!saa->top)
- goto allocfail;
- saa->bottom = (u8 *)get_zeroed_page(GFP_KERNEL);
- if (!saa->bottom)
- goto allocfail;
-
- go = go7007_alloc(&board_voyager, &dev->pci->dev);
- if (go == NULL)
- goto allocfail;
- go->board_id = GO7007_BOARDID_PCI_VOYAGER;
- strncpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name));
- go->hpi_ops = &saa7134_go7007_hpi_ops;
- go->hpi_context = saa;
- saa->dev = dev;
-
- /* Boot the GO7007 */
- if (go7007_boot_encoder(go, go->board_info->flags &
- GO7007_BOARD_USE_ONBOARD_I2C) < 0)
- goto initfail;
-
- /* Do any final GO7007 initialization, then register the
- * V4L2 and ALSA interfaces */
- if (go7007_register_encoder(go) < 0)
- goto initfail;
- dev->empress_dev = go->video_dev;
- video_set_drvdata(dev->empress_dev, go);
-
- go->status = STATUS_ONLINE;
- return 0;
-
-initfail:
- go->status = STATUS_SHUTDOWN;
- return 0;
-
-allocfail:
- if (saa->top)
- free_page((unsigned long)saa->top);
- if (saa->bottom)
- free_page((unsigned long)saa->bottom);
- kfree(saa);
- return -ENOMEM;
-}
-
-static int saa7134_go7007_fini(struct saa7134_dev *dev)
-{
- struct go7007 *go;
- struct saa7134_go7007 *saa;
-
- if (NULL == dev->empress_dev)
- return 0;
-
- go = video_get_drvdata(dev->empress_dev);
- saa = go->hpi_context;
- go->status = STATUS_SHUTDOWN;
- free_page((unsigned long)saa->top);
- free_page((unsigned long)saa->bottom);
- kfree(saa);
- go7007_remove(go);
- dev->empress_dev = NULL;
-
- return 0;
-}
-
-static struct saa7134_mpeg_ops saa7134_go7007_ops = {
- .type = SAA7134_MPEG_GO7007,
- .init = saa7134_go7007_init,
- .fini = saa7134_go7007_fini,
- .irq_ts_done = saa7134_go7007_irq_ts_done,
-};
-
-static int __init saa7134_go7007_mod_init(void)
-{
- return saa7134_ts_register(&saa7134_go7007_ops);
-}
-
-static void __exit saa7134_go7007_mod_cleanup(void)
-{
- saa7134_ts_unregister(&saa7134_go7007_ops);
-}
-
-module_init(saa7134_go7007_mod_init);
-module_exit(saa7134_go7007_mod_cleanup);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/moduleparam.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/vmalloc.h>
-#include <linux/time.h>
-#include <linux/mm.h>
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-#include <linux/slab.h>
-#include <asm/system.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/initval.h>
-
-#include "go7007-priv.h"
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
-
-module_param_array(index, int, NULL, 0444);
-module_param_array(id, charp, NULL, 0444);
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for the go7007 audio driver");
-MODULE_PARM_DESC(id, "ID string for the go7007 audio driver");
-MODULE_PARM_DESC(enable, "Enable for the go7007 audio driver");
-
-struct go7007_snd {
- struct snd_card *card;
- struct snd_pcm *pcm;
- struct snd_pcm_substream *substream;
- spinlock_t lock;
- int w_idx;
- int hw_ptr;
- int avail;
- int capturing;
-};
-
-static struct snd_pcm_hardware go7007_snd_capture_hw = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = 4096,
- .period_bytes_max = (128*1024),
- .periods_min = 1,
- .periods_max = 32,
-};
-
-static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length)
-{
- struct go7007_snd *gosnd = go->snd_context;
- struct snd_pcm_runtime *runtime = gosnd->substream->runtime;
- int frames = bytes_to_frames(runtime, length);
-
- spin_lock(&gosnd->lock);
- gosnd->hw_ptr += frames;
- if (gosnd->hw_ptr >= runtime->buffer_size)
- gosnd->hw_ptr -= runtime->buffer_size;
- gosnd->avail += frames;
- spin_unlock(&gosnd->lock);
- if (gosnd->w_idx + length > runtime->dma_bytes) {
- int cpy = runtime->dma_bytes - gosnd->w_idx;
-
- memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy);
- length -= cpy;
- buf += cpy;
- gosnd->w_idx = 0;
- }
- memcpy(runtime->dma_area + gosnd->w_idx, buf, length);
- gosnd->w_idx += length;
- spin_lock(&gosnd->lock);
- if (gosnd->avail < runtime->period_size) {
- spin_unlock(&gosnd->lock);
- return;
- }
- gosnd->avail -= runtime->period_size;
- spin_unlock(&gosnd->lock);
- if (gosnd->capturing)
- snd_pcm_period_elapsed(gosnd->substream);
-}
-
-static int go7007_snd_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct go7007 *go = snd_pcm_substream_chip(substream);
- unsigned int bytes;
-
- bytes = params_buffer_bytes(hw_params);
- if (substream->runtime->dma_bytes > 0)
- vfree(substream->runtime->dma_area);
- substream->runtime->dma_bytes = 0;
- substream->runtime->dma_area = vmalloc(bytes);
- if (substream->runtime->dma_area == NULL)
- return -ENOMEM;
- substream->runtime->dma_bytes = bytes;
- go->audio_deliver = parse_audio_stream_data;
- return 0;
-}
-
-static int go7007_snd_hw_free(struct snd_pcm_substream *substream)
-{
- struct go7007 *go = snd_pcm_substream_chip(substream);
-
- go->audio_deliver = NULL;
- if (substream->runtime->dma_bytes > 0)
- vfree(substream->runtime->dma_area);
- substream->runtime->dma_bytes = 0;
- return 0;
-}
-
-static int go7007_snd_capture_open(struct snd_pcm_substream *substream)
-{
- struct go7007 *go = snd_pcm_substream_chip(substream);
- struct go7007_snd *gosnd = go->snd_context;
- unsigned long flags;
- int r;
-
- spin_lock_irqsave(&gosnd->lock, flags);
- if (gosnd->substream == NULL) {
- gosnd->substream = substream;
- substream->runtime->hw = go7007_snd_capture_hw;
- r = 0;
- } else
- r = -EBUSY;
- spin_unlock_irqrestore(&gosnd->lock, flags);
- return r;
-}
-
-static int go7007_snd_capture_close(struct snd_pcm_substream *substream)
-{
- struct go7007 *go = snd_pcm_substream_chip(substream);
- struct go7007_snd *gosnd = go->snd_context;
-
- gosnd->substream = NULL;
- return 0;
-}
-
-static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream)
-{
- return 0;
-}
-
-static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct go7007 *go = snd_pcm_substream_chip(substream);
- struct go7007_snd *gosnd = go->snd_context;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- /* Just set a flag to indicate we should signal ALSA when
- * sound comes in */
- gosnd->capturing = 1;
- return 0;
- case SNDRV_PCM_TRIGGER_STOP:
- gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
- gosnd->capturing = 0;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct go7007 *go = snd_pcm_substream_chip(substream);
- struct go7007_snd *gosnd = go->snd_context;
-
- return gosnd->hw_ptr;
-}
-
-static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream,
- unsigned long offset)
-{
- return vmalloc_to_page(substream->runtime->dma_area + offset);
-}
-
-static struct snd_pcm_ops go7007_snd_capture_ops = {
- .open = go7007_snd_capture_open,
- .close = go7007_snd_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = go7007_snd_hw_params,
- .hw_free = go7007_snd_hw_free,
- .prepare = go7007_snd_pcm_prepare,
- .trigger = go7007_snd_pcm_trigger,
- .pointer = go7007_snd_pcm_pointer,
- .page = go7007_snd_pcm_page,
-};
-
-static int go7007_snd_free(struct snd_device *device)
-{
- struct go7007 *go = device->device_data;
-
- kfree(go->snd_context);
- go->snd_context = NULL;
- if (--go->ref_count == 0)
- kfree(go);
- return 0;
-}
-
-static struct snd_device_ops go7007_snd_device_ops = {
- .dev_free = go7007_snd_free,
-};
-
-int go7007_snd_init(struct go7007 *go)
-{
- static int dev;
- struct go7007_snd *gosnd;
- int ret = 0;
-
- if (dev >= SNDRV_CARDS)
- return -ENODEV;
- if (!enable[dev]) {
- dev++;
- return -ENOENT;
- }
- gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL);
- if (gosnd == NULL)
- return -ENOMEM;
- spin_lock_init(&gosnd->lock);
- gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
- gosnd->capturing = 0;
- ret = snd_card_create(index[dev], id[dev], THIS_MODULE, 0,
- &gosnd->card);
- if (ret < 0) {
- kfree(gosnd);
- return ret;
- }
- ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go,
- &go7007_snd_device_ops);
- if (ret < 0) {
- kfree(gosnd);
- return ret;
- }
- snd_card_set_dev(gosnd->card, go->dev);
- ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm);
- if (ret < 0) {
- snd_card_free(gosnd->card);
- kfree(gosnd);
- return ret;
- }
- strncpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver));
- strncpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver));
- strncpy(gosnd->card->longname, gosnd->card->shortname,
- sizeof(gosnd->card->longname));
-
- gosnd->pcm->private_data = go;
- snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE,
- &go7007_snd_capture_ops);
-
- ret = snd_card_register(gosnd->card);
- if (ret < 0) {
- snd_card_free(gosnd->card);
- kfree(gosnd);
- return ret;
- }
-
- gosnd->substream = NULL;
- go->snd_context = gosnd;
- ++dev;
- ++go->ref_count;
-
- return 0;
-}
-EXPORT_SYMBOL(go7007_snd_init);
-
-int go7007_snd_remove(struct go7007 *go)
-{
- struct go7007_snd *gosnd = go->snd_context;
-
- snd_card_disconnect(gosnd->card);
- snd_card_free_when_closed(gosnd->card);
- return 0;
-}
-EXPORT_SYMBOL(go7007_snd_remove);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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.
- */
-
-/* Temporary I2C IDs -- these need to be replaced with real registered IDs */
-#define I2C_DRIVERID_WIS_SAA7115 0xf0f0
-#define I2C_DRIVERID_WIS_UDA1342 0xf0f1
-#define I2C_DRIVERID_WIS_SONY_TUNER 0xf0f2
-#define I2C_DRIVERID_WIS_TW9903 0xf0f3
-#define I2C_DRIVERID_WIS_SAA7113 0xf0f4
-#define I2C_DRIVERID_WIS_OV7640 0xf0f5
-#define I2C_DRIVERID_WIS_TW2804 0xf0f6
-#define I2C_DRIVERID_S2250 0xf0f7
-
-/* Flag to indicate that the client needs to be accessed with SCCB semantics */
-/* We re-use the I2C_M_TEN value so the flag passes through the masks in the
- * core I2C code. Major kludge, but the I2C layer ain't exactly flexible. */
-#define I2C_CLIENT_SCCB 0x10
-
-/* Definitions for new video decoder commands */
-
-struct video_decoder_resolution {
- unsigned int width;
- unsigned int height;
-};
-
-#define DECODER_SET_RESOLUTION _IOW('d', 200, struct video_decoder_resolution)
-#define DECODER_SET_CHANNEL _IOW('d', 201, int)
-
-/* Sony tuner types */
-
-#define TUNER_SONY_BTF_PG472Z 200
-#define TUNER_SONY_BTF_PK467Z 201
-#define TUNER_SONY_BTF_PB463Z 202
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-
-#include "wis-i2c.h"
-
-struct wis_ov7640 {
- int brightness;
- int contrast;
- int saturation;
- int hue;
-};
-
-static u8 initial_registers[] =
-{
- 0x12, 0x80,
- 0x12, 0x54,
- 0x14, 0x24,
- 0x15, 0x01,
- 0x28, 0x20,
- 0x75, 0x82,
- 0xFF, 0xFF, /* Terminator (reg 0xFF is unused) */
-};
-
-static int write_regs(struct i2c_client *client, u8 *regs)
-{
- int i;
-
- for (i = 0; regs[i] != 0xFF; i += 2)
- if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
- return -1;
- return 0;
-}
-
-static int wis_ov7640_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_adapter *adapter = client->adapter;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -ENODEV;
-
- client->flags = I2C_CLIENT_SCCB;
-
- printk(KERN_DEBUG
- "wis-ov7640: initializing OV7640 at address %d on %s\n",
- client->addr, adapter->name);
-
- if (write_regs(client, initial_registers) < 0) {
- printk(KERN_ERR "wis-ov7640: error initializing OV7640\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int wis_ov7640_remove(struct i2c_client *client)
-{
- return 0;
-}
-
-static const struct i2c_device_id wis_ov7640_id[] = {
- { "wis_ov7640", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wis_ov7640_id);
-
-static struct i2c_driver wis_ov7640_driver = {
- .driver = {
- .name = "WIS OV7640 I2C driver",
- },
- .probe = wis_ov7640_probe,
- .remove = wis_ov7640_remove,
- .id_table = wis_ov7640_id,
-};
-
-static int __init wis_ov7640_init(void)
-{
- return i2c_add_driver(&wis_ov7640_driver);
-}
-
-static void __exit wis_ov7640_cleanup(void)
-{
- i2c_del_driver(&wis_ov7640_driver);
-}
-
-module_init(wis_ov7640_init);
-module_exit(wis_ov7640_cleanup);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <linux/ioctl.h>
-#include <linux/slab.h>
-
-#include "wis-i2c.h"
-
-struct wis_saa7113 {
- int norm;
- int brightness;
- int contrast;
- int saturation;
- int hue;
-};
-
-static u8 initial_registers[] =
-{
- 0x01, 0x08,
- 0x02, 0xc0,
- 0x03, 0x33,
- 0x04, 0x00,
- 0x05, 0x00,
- 0x06, 0xe9,
- 0x07, 0x0d,
- 0x08, 0xd8,
- 0x09, 0x40,
- 0x0a, 0x80,
- 0x0b, 0x47,
- 0x0c, 0x40,
- 0x0d, 0x00,
- 0x0e, 0x01,
- 0x0f, 0x2a,
- 0x10, 0x40,
- 0x11, 0x0c,
- 0x12, 0xfe,
- 0x13, 0x00,
- 0x14, 0x00,
- 0x15, 0x04,
- 0x16, 0x00,
- 0x17, 0x00,
- 0x18, 0x00,
- 0x19, 0x00,
- 0x1a, 0x00,
- 0x1b, 0x00,
- 0x1c, 0x00,
- 0x1d, 0x00,
- 0x1e, 0x00,
- 0x1f, 0xc8,
- 0x40, 0x00,
- 0x41, 0xff,
- 0x42, 0xff,
- 0x43, 0xff,
- 0x44, 0xff,
- 0x45, 0xff,
- 0x46, 0xff,
- 0x47, 0xff,
- 0x48, 0xff,
- 0x49, 0xff,
- 0x4a, 0xff,
- 0x4b, 0xff,
- 0x4c, 0xff,
- 0x4d, 0xff,
- 0x4e, 0xff,
- 0x4f, 0xff,
- 0x50, 0xff,
- 0x51, 0xff,
- 0x52, 0xff,
- 0x53, 0xff,
- 0x54, 0xff,
- 0x55, 0xff,
- 0x56, 0xff,
- 0x57, 0xff,
- 0x58, 0x00,
- 0x59, 0x54,
- 0x5a, 0x07,
- 0x5b, 0x83,
- 0x5c, 0x00,
- 0x5d, 0x00,
- 0x5e, 0x00,
- 0x5f, 0x00,
- 0x60, 0x00,
- 0x61, 0x00,
- 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
-};
-
-static int write_reg(struct i2c_client *client, u8 reg, u8 value)
-{
- return i2c_smbus_write_byte_data(client, reg, value);
-}
-
-static int write_regs(struct i2c_client *client, u8 *regs)
-{
- int i;
-
- for (i = 0; regs[i] != 0x00; i += 2)
- if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
- return -1;
- return 0;
-}
-
-static int wis_saa7113_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
-{
- struct wis_saa7113 *dec = i2c_get_clientdata(client);
-
- switch (cmd) {
- case VIDIOC_S_INPUT:
- {
- int *input = arg;
-
- i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input);
- i2c_smbus_write_byte_data(client, 0x09,
- *input < 6 ? 0x40 : 0x80);
- break;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *input = arg;
- dec->norm = *input;
- if (dec->norm & V4L2_STD_NTSC) {
- write_reg(client, 0x0e, 0x01);
- write_reg(client, 0x10, 0x40);
- } else if (dec->norm & V4L2_STD_PAL) {
- write_reg(client, 0x0e, 0x01);
- write_reg(client, 0x10, 0x48);
- } else if (dec->norm * V4L2_STD_SECAM) {
- write_reg(client, 0x0e, 0x50);
- write_reg(client, 0x10, 0x48);
- }
- break;
- }
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 71;
- ctrl->flags = 0;
- break;
- case V4L2_CID_SATURATION:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 64;
- ctrl->flags = 0;
- break;
- case V4L2_CID_HUE:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
- ctrl->minimum = -128;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 0;
- ctrl->flags = 0;
- break;
- }
- break;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value > 255)
- dec->brightness = 255;
- else if (ctrl->value < 0)
- dec->brightness = 0;
- else
- dec->brightness = ctrl->value;
- write_reg(client, 0x0a, dec->brightness);
- break;
- case V4L2_CID_CONTRAST:
- if (ctrl->value > 127)
- dec->contrast = 127;
- else if (ctrl->value < 0)
- dec->contrast = 0;
- else
- dec->contrast = ctrl->value;
- write_reg(client, 0x0b, dec->contrast);
- break;
- case V4L2_CID_SATURATION:
- if (ctrl->value > 127)
- dec->saturation = 127;
- else if (ctrl->value < 0)
- dec->saturation = 0;
- else
- dec->saturation = ctrl->value;
- write_reg(client, 0x0c, dec->saturation);
- break;
- case V4L2_CID_HUE:
- if (ctrl->value > 127)
- dec->hue = 127;
- else if (ctrl->value < -128)
- dec->hue = -128;
- else
- dec->hue = ctrl->value;
- write_reg(client, 0x0d, dec->hue);
- break;
- }
- break;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = dec->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = dec->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = dec->saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = dec->hue;
- break;
- }
- break;
- }
- default:
- break;
- }
- return 0;
-}
-
-static int wis_saa7113_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_adapter *adapter = client->adapter;
- struct wis_saa7113 *dec;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -ENODEV;
-
- dec = kmalloc(sizeof(struct wis_saa7113), GFP_KERNEL);
- if (dec == NULL)
- return -ENOMEM;
-
- dec->norm = V4L2_STD_NTSC;
- dec->brightness = 128;
- dec->contrast = 71;
- dec->saturation = 64;
- dec->hue = 0;
- i2c_set_clientdata(client, dec);
-
- printk(KERN_DEBUG
- "wis-saa7113: initializing SAA7113 at address %d on %s\n",
- client->addr, adapter->name);
-
- if (write_regs(client, initial_registers) < 0) {
- printk(KERN_ERR
- "wis-saa7113: error initializing SAA7113\n");
- kfree(dec);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int wis_saa7113_remove(struct i2c_client *client)
-{
- struct wis_saa7113 *dec = i2c_get_clientdata(client);
-
- kfree(dec);
- return 0;
-}
-
-static const struct i2c_device_id wis_saa7113_id[] = {
- { "wis_saa7113", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wis_saa7113_id);
-
-static struct i2c_driver wis_saa7113_driver = {
- .driver = {
- .name = "WIS SAA7113 I2C driver",
- },
- .probe = wis_saa7113_probe,
- .remove = wis_saa7113_remove,
- .command = wis_saa7113_command,
- .id_table = wis_saa7113_id,
-};
-
-static int __init wis_saa7113_init(void)
-{
- return i2c_add_driver(&wis_saa7113_driver);
-}
-
-static void __exit wis_saa7113_cleanup(void)
-{
- i2c_del_driver(&wis_saa7113_driver);
-}
-
-module_init(wis_saa7113_init);
-module_exit(wis_saa7113_cleanup);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <linux/ioctl.h>
-#include <linux/slab.h>
-
-#include "wis-i2c.h"
-
-struct wis_saa7115 {
- int norm;
- int brightness;
- int contrast;
- int saturation;
- int hue;
-};
-
-static u8 initial_registers[] =
-{
- 0x01, 0x08,
- 0x02, 0xc0,
- 0x03, 0x20,
- 0x04, 0x80,
- 0x05, 0x80,
- 0x06, 0xeb,
- 0x07, 0xe0,
- 0x08, 0xf0, /* always toggle FID */
- 0x09, 0x40,
- 0x0a, 0x80,
- 0x0b, 0x40,
- 0x0c, 0x40,
- 0x0d, 0x00,
- 0x0e, 0x03,
- 0x0f, 0x2a,
- 0x10, 0x0e,
- 0x11, 0x00,
- 0x12, 0x8d,
- 0x13, 0x00,
- 0x14, 0x00,
- 0x15, 0x11,
- 0x16, 0x01,
- 0x17, 0xda,
- 0x18, 0x40,
- 0x19, 0x80,
- 0x1a, 0x00,
- 0x1b, 0x42,
- 0x1c, 0xa9,
- 0x30, 0x66,
- 0x31, 0x90,
- 0x32, 0x01,
- 0x34, 0x00,
- 0x35, 0x00,
- 0x36, 0x20,
- 0x38, 0x03,
- 0x39, 0x20,
- 0x3a, 0x88,
- 0x40, 0x00,
- 0x41, 0xff,
- 0x42, 0xff,
- 0x43, 0xff,
- 0x44, 0xff,
- 0x45, 0xff,
- 0x46, 0xff,
- 0x47, 0xff,
- 0x48, 0xff,
- 0x49, 0xff,
- 0x4a, 0xff,
- 0x4b, 0xff,
- 0x4c, 0xff,
- 0x4d, 0xff,
- 0x4e, 0xff,
- 0x4f, 0xff,
- 0x50, 0xff,
- 0x51, 0xff,
- 0x52, 0xff,
- 0x53, 0xff,
- 0x54, 0xf4 /*0xff*/,
- 0x55, 0xff,
- 0x56, 0xff,
- 0x57, 0xff,
- 0x58, 0x40,
- 0x59, 0x47,
- 0x5a, 0x06 /*0x03*/,
- 0x5b, 0x83,
- 0x5d, 0x06,
- 0x5e, 0x00,
- 0x80, 0x30, /* window defined scaler operation, task A and B enabled */
- 0x81, 0x03, /* use scaler datapath generated V */
- 0x83, 0x00,
- 0x84, 0x00,
- 0x85, 0x00,
- 0x86, 0x45,
- 0x87, 0x31,
- 0x88, 0xc0,
- 0x90, 0x02, /* task A process top field */
- 0x91, 0x08,
- 0x92, 0x09,
- 0x93, 0x80,
- 0x94, 0x06,
- 0x95, 0x00,
- 0x96, 0xc0,
- 0x97, 0x02,
- 0x98, 0x12,
- 0x99, 0x00,
- 0x9a, 0xf2,
- 0x9b, 0x00,
- 0x9c, 0xd0,
- 0x9d, 0x02,
- 0x9e, 0xf2,
- 0x9f, 0x00,
- 0xa0, 0x01,
- 0xa1, 0x01,
- 0xa2, 0x01,
- 0xa4, 0x80,
- 0xa5, 0x40,
- 0xa6, 0x40,
- 0xa8, 0x00,
- 0xa9, 0x04,
- 0xaa, 0x00,
- 0xac, 0x00,
- 0xad, 0x02,
- 0xae, 0x00,
- 0xb0, 0x00,
- 0xb1, 0x04,
- 0xb2, 0x00,
- 0xb3, 0x04,
- 0xb4, 0x00,
- 0xb8, 0x00,
- 0xbc, 0x00,
- 0xc0, 0x03, /* task B process bottom field */
- 0xc1, 0x08,
- 0xc2, 0x09,
- 0xc3, 0x80,
- 0xc4, 0x06,
- 0xc5, 0x00,
- 0xc6, 0xc0,
- 0xc7, 0x02,
- 0xc8, 0x12,
- 0xc9, 0x00,
- 0xca, 0xf2,
- 0xcb, 0x00,
- 0xcc, 0xd0,
- 0xcd, 0x02,
- 0xce, 0xf2,
- 0xcf, 0x00,
- 0xd0, 0x01,
- 0xd1, 0x01,
- 0xd2, 0x01,
- 0xd4, 0x80,
- 0xd5, 0x40,
- 0xd6, 0x40,
- 0xd8, 0x00,
- 0xd9, 0x04,
- 0xda, 0x00,
- 0xdc, 0x00,
- 0xdd, 0x02,
- 0xde, 0x00,
- 0xe0, 0x00,
- 0xe1, 0x04,
- 0xe2, 0x00,
- 0xe3, 0x04,
- 0xe4, 0x00,
- 0xe8, 0x00,
- 0x88, 0xf0, /* End of original static list */
- 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
-};
-
-static int write_reg(struct i2c_client *client, u8 reg, u8 value)
-{
- return i2c_smbus_write_byte_data(client, reg, value);
-}
-
-static int write_regs(struct i2c_client *client, u8 *regs)
-{
- int i;
-
- for (i = 0; regs[i] != 0x00; i += 2)
- if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
- return -1;
- return 0;
-}
-
-static int wis_saa7115_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
-{
- struct wis_saa7115 *dec = i2c_get_clientdata(client);
-
- switch (cmd) {
- case VIDIOC_S_INPUT:
- {
- int *input = arg;
-
- i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input);
- i2c_smbus_write_byte_data(client, 0x09,
- *input < 6 ? 0x40 : 0xC0);
- break;
- }
- case DECODER_SET_RESOLUTION:
- {
- struct video_decoder_resolution *res = arg;
- /* Course-grained scaler */
- int h_integer_scaler = res->width < 704 ? 704 / res->width : 1;
- /* Fine-grained scaler to take care of remainder */
- int h_scaling_increment = (704 / h_integer_scaler) *
- 1024 / res->width;
- /* Fine-grained scaler only */
- int v_scaling_increment = (dec->norm & V4L2_STD_NTSC ?
- 240 : 288) * 1024 / res->height;
- u8 regs[] = {
- 0x88, 0xc0,
- 0x9c, res->width & 0xff,
- 0x9d, res->width >> 8,
- 0x9e, res->height & 0xff,
- 0x9f, res->height >> 8,
- 0xa0, h_integer_scaler,
- 0xa1, 1,
- 0xa2, 1,
- 0xa8, h_scaling_increment & 0xff,
- 0xa9, h_scaling_increment >> 8,
- 0xac, (h_scaling_increment / 2) & 0xff,
- 0xad, (h_scaling_increment / 2) >> 8,
- 0xb0, v_scaling_increment & 0xff,
- 0xb1, v_scaling_increment >> 8,
- 0xb2, v_scaling_increment & 0xff,
- 0xb3, v_scaling_increment >> 8,
- 0xcc, res->width & 0xff,
- 0xcd, res->width >> 8,
- 0xce, res->height & 0xff,
- 0xcf, res->height >> 8,
- 0xd0, h_integer_scaler,
- 0xd1, 1,
- 0xd2, 1,
- 0xd8, h_scaling_increment & 0xff,
- 0xd9, h_scaling_increment >> 8,
- 0xdc, (h_scaling_increment / 2) & 0xff,
- 0xdd, (h_scaling_increment / 2) >> 8,
- 0xe0, v_scaling_increment & 0xff,
- 0xe1, v_scaling_increment >> 8,
- 0xe2, v_scaling_increment & 0xff,
- 0xe3, v_scaling_increment >> 8,
- 0x88, 0xf0,
- 0, 0,
- };
- write_regs(client, regs);
- break;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *input = arg;
- u8 regs[] = {
- 0x88, 0xc0,
- 0x98, *input & V4L2_STD_NTSC ? 0x12 : 0x16,
- 0x9a, *input & V4L2_STD_NTSC ? 0xf2 : 0x20,
- 0x9b, *input & V4L2_STD_NTSC ? 0x00 : 0x01,
- 0xc8, *input & V4L2_STD_NTSC ? 0x12 : 0x16,
- 0xca, *input & V4L2_STD_NTSC ? 0xf2 : 0x20,
- 0xcb, *input & V4L2_STD_NTSC ? 0x00 : 0x01,
- 0x88, 0xf0,
- 0x30, *input & V4L2_STD_NTSC ? 0x66 : 0x00,
- 0x31, *input & V4L2_STD_NTSC ? 0x90 : 0xe0,
- 0, 0,
- };
- write_regs(client, regs);
- dec->norm = *input;
- break;
- }
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 64;
- ctrl->flags = 0;
- break;
- case V4L2_CID_SATURATION:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 64;
- ctrl->flags = 0;
- break;
- case V4L2_CID_HUE:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
- ctrl->minimum = -128;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 0;
- ctrl->flags = 0;
- break;
- }
- break;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value > 255)
- dec->brightness = 255;
- else if (ctrl->value < 0)
- dec->brightness = 0;
- else
- dec->brightness = ctrl->value;
- write_reg(client, 0x0a, dec->brightness);
- break;
- case V4L2_CID_CONTRAST:
- if (ctrl->value > 127)
- dec->contrast = 127;
- else if (ctrl->value < 0)
- dec->contrast = 0;
- else
- dec->contrast = ctrl->value;
- write_reg(client, 0x0b, dec->contrast);
- break;
- case V4L2_CID_SATURATION:
- if (ctrl->value > 127)
- dec->saturation = 127;
- else if (ctrl->value < 0)
- dec->saturation = 0;
- else
- dec->saturation = ctrl->value;
- write_reg(client, 0x0c, dec->saturation);
- break;
- case V4L2_CID_HUE:
- if (ctrl->value > 127)
- dec->hue = 127;
- else if (ctrl->value < -128)
- dec->hue = -128;
- else
- dec->hue = ctrl->value;
- write_reg(client, 0x0d, dec->hue);
- break;
- }
- break;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = dec->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = dec->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = dec->saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = dec->hue;
- break;
- }
- break;
- }
- default:
- break;
- }
- return 0;
-}
-
-static int wis_saa7115_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_adapter *adapter = client->adapter;
- struct wis_saa7115 *dec;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -ENODEV;
-
- dec = kmalloc(sizeof(struct wis_saa7115), GFP_KERNEL);
- if (dec == NULL)
- return -ENOMEM;
-
- dec->norm = V4L2_STD_NTSC;
- dec->brightness = 128;
- dec->contrast = 64;
- dec->saturation = 64;
- dec->hue = 0;
- i2c_set_clientdata(client, dec);
-
- printk(KERN_DEBUG
- "wis-saa7115: initializing SAA7115 at address %d on %s\n",
- client->addr, adapter->name);
-
- if (write_regs(client, initial_registers) < 0) {
- printk(KERN_ERR
- "wis-saa7115: error initializing SAA7115\n");
- kfree(dec);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int wis_saa7115_remove(struct i2c_client *client)
-{
- struct wis_saa7115 *dec = i2c_get_clientdata(client);
-
- kfree(dec);
- return 0;
-}
-
-static const struct i2c_device_id wis_saa7115_id[] = {
- { "wis_saa7115", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wis_saa7115_id);
-
-static struct i2c_driver wis_saa7115_driver = {
- .driver = {
- .name = "WIS SAA7115 I2C driver",
- },
- .probe = wis_saa7115_probe,
- .remove = wis_saa7115_remove,
- .command = wis_saa7115_command,
- .id_table = wis_saa7115_id,
-};
-
-static int __init wis_saa7115_init(void)
-{
- return i2c_add_driver(&wis_saa7115_driver);
-}
-
-static void __exit wis_saa7115_cleanup(void)
-{
- i2c_del_driver(&wis_saa7115_driver);
-}
-
-module_init(wis_saa7115_init);
-module_exit(wis_saa7115_cleanup);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-#include <media/tuner.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-
-#include "wis-i2c.h"
-
-/* #define MPX_DEBUG */
-
-/* AS(IF/MPX) pin: LOW HIGH/OPEN
- * IF/MPX address: 0x42/0x40 0x43/0x44
- */
-#define IF_I2C_ADDR 0x43
-#define MPX_I2C_ADDR 0x44
-
-static v4l2_std_id force_band;
-static char force_band_str[] = "-";
-module_param_string(force_band, force_band_str, sizeof(force_band_str), 0644);
-static int force_mpx_mode = -1;
-module_param(force_mpx_mode, int, 0644);
-
-/* Store tuner info in the same format as tuner.c, so maybe we can put the
- * Sony tuner support in there. */
-struct sony_tunertype {
- char *name;
- unsigned char Vendor; /* unused here */
- unsigned char Type; /* unused here */
-
- unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */
- unsigned short thresh2; /* band switch VHF_HI <=> UHF */
- unsigned char VHF_L;
- unsigned char VHF_H;
- unsigned char UHF;
- unsigned char config;
- unsigned short IFPCoff;
-};
-
-/* This array is indexed by (tuner_type - 200) */
-static struct sony_tunertype sony_tuners[] = {
- { "Sony PAL+SECAM (BTF-PG472Z)", 0, 0,
- 16*144.25, 16*427.25, 0x01, 0x02, 0x04, 0xc6, 623},
- { "Sony NTSC_JP (BTF-PK467Z)", 0, 0,
- 16*220.25, 16*467.25, 0x01, 0x02, 0x04, 0xc6, 940},
- { "Sony NTSC (BTF-PB463Z)", 0, 0,
- 16*130.25, 16*364.25, 0x01, 0x02, 0x04, 0xc6, 732},
-};
-
-struct wis_sony_tuner {
- int type;
- v4l2_std_id std;
- unsigned int freq;
- int mpxmode;
- u32 audmode;
-};
-
-/* Basically the same as default_set_tv_freq() in tuner.c */
-static int set_freq(struct i2c_client *client, int freq)
-{
- struct wis_sony_tuner *t = i2c_get_clientdata(client);
- char *band_name;
- int n;
- int band_select;
- struct sony_tunertype *tun;
- u8 buffer[4];
-
- tun = &sony_tuners[t->type - 200];
- if (freq < tun->thresh1) {
- band_name = "VHF_L";
- band_select = tun->VHF_L;
- } else if (freq < tun->thresh2) {
- band_name = "VHF_H";
- band_select = tun->VHF_H;
- } else {
- band_name = "UHF";
- band_select = tun->UHF;
- }
- printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n",
- freq / 16, (freq % 16) * 625, band_name);
- n = freq + tun->IFPCoff;
-
- buffer[0] = n >> 8;
- buffer[1] = n & 0xff;
- buffer[2] = tun->config;
- buffer[3] = band_select;
- i2c_master_send(client, buffer, 4);
-
- return 0;
-}
-
-static int mpx_write(struct i2c_client *client, int dev, int addr, int val)
-{
- u8 buffer[5];
- struct i2c_msg msg;
-
- buffer[0] = dev;
- buffer[1] = addr >> 8;
- buffer[2] = addr & 0xff;
- buffer[3] = val >> 8;
- buffer[4] = val & 0xff;
- msg.addr = MPX_I2C_ADDR;
- msg.flags = 0;
- msg.len = 5;
- msg.buf = buffer;
- i2c_transfer(client->adapter, &msg, 1);
- return 0;
-}
-
-/*
- * MPX register values for the BTF-PG472Z:
- *
- * FM_ NICAM_ SCART_
- * MODUS SOURCE ACB PRESCAL PRESCAL PRESCAL SYSTEM VOLUME
- * 10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000
- * ---------------------------------------------------------------
- * Auto 1003 0020 0100 2603 5000 XXXX 0001 7500
- *
- * B/G
- * Mono 1003 0020 0100 2603 5000 XXXX 0003 7500
- * A2 1003 0020 0100 2601 5000 XXXX 0003 7500
- * NICAM 1003 0120 0100 2603 5000 XXXX 0008 7500
- *
- * I
- * Mono 1003 0020 0100 2603 7900 XXXX 000A 7500
- * NICAM 1003 0120 0100 2603 7900 XXXX 000A 7500
- *
- * D/K
- * Mono 1003 0020 0100 2603 5000 XXXX 0004 7500
- * A2-1 1003 0020 0100 2601 5000 XXXX 0004 7500
- * A2-2 1003 0020 0100 2601 5000 XXXX 0005 7500
- * A2-3 1003 0020 0100 2601 5000 XXXX 0007 7500
- * NICAM 1003 0120 0100 2603 5000 XXXX 000B 7500
- *
- * L/L'
- * Mono 0003 0200 0100 7C03 5000 2200 0009 7500
- * NICAM 0003 0120 0100 7C03 5000 XXXX 0009 7500
- *
- * M
- * Mono 1003 0200 0100 2B03 5000 2B00 0002 7500
- *
- * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX.
- *
- * Bilingual selection in A2/NICAM:
- *
- * High byte of SOURCE Left chan Right chan
- * 0x01 MAIN SUB
- * 0x03 MAIN MAIN
- * 0x04 SUB SUB
- *
- * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or
- * 0x00 (all other bands). Force mono in A2 with FMONO_A2:
- *
- * FMONO_A2
- * 10/0022
- * --------
- * Forced mono ON 07F0
- * Forced mono OFF 0190
- */
-
-static struct {
- enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode;
- u16 modus;
- u16 source;
- u16 acb;
- u16 fm_prescale;
- u16 nicam_prescale;
- u16 scart_prescale;
- u16 system;
- u16 volume;
-} mpx_audio_modes[] = {
- /* Auto */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
- 0x5000, 0x0000, 0x0001, 0x7500 },
- /* B/G Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
- 0x5000, 0x0000, 0x0003, 0x7500 },
- /* B/G A2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
- 0x5000, 0x0000, 0x0003, 0x7500 },
- /* B/G NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
- 0x5000, 0x0000, 0x0008, 0x7500 },
- /* I Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
- 0x7900, 0x0000, 0x000A, 0x7500 },
- /* I NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
- 0x7900, 0x0000, 0x000A, 0x7500 },
- /* D/K Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
- 0x5000, 0x0000, 0x0004, 0x7500 },
- /* D/K A2-1 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
- 0x5000, 0x0000, 0x0004, 0x7500 },
- /* D/K A2-2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
- 0x5000, 0x0000, 0x0005, 0x7500 },
- /* D/K A2-3 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
- 0x5000, 0x0000, 0x0007, 0x7500 },
- /* D/K NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
- 0x5000, 0x0000, 0x000B, 0x7500 },
- /* L/L' Mono */ { AUD_MONO, 0x0003, 0x0200, 0x0100, 0x7C03,
- 0x5000, 0x2200, 0x0009, 0x7500 },
- /* L/L' NICAM */{ AUD_NICAM_L, 0x0003, 0x0120, 0x0100, 0x7C03,
- 0x5000, 0x0000, 0x0009, 0x7500 },
-};
-
-#define MPX_NUM_MODES ARRAY_SIZE(mpx_audio_modes)
-
-static int mpx_setup(struct i2c_client *client)
-{
- struct wis_sony_tuner *t = i2c_get_clientdata(client);
- u16 source = 0;
- u8 buffer[3];
- struct i2c_msg msg;
-
- /* reset MPX */
- buffer[0] = 0x00;
- buffer[1] = 0x80;
- buffer[2] = 0x00;
- msg.addr = MPX_I2C_ADDR;
- msg.flags = 0;
- msg.len = 3;
- msg.buf = buffer;
- i2c_transfer(client->adapter, &msg, 1);
- buffer[1] = 0x00;
- i2c_transfer(client->adapter, &msg, 1);
-
- if (mpx_audio_modes[t->mpxmode].audio_mode != AUD_MONO) {
- switch (t->audmode) {
- case V4L2_TUNER_MODE_MONO:
- switch (mpx_audio_modes[t->mpxmode].audio_mode) {
- case AUD_A2:
- source = mpx_audio_modes[t->mpxmode].source;
- break;
- case AUD_NICAM:
- source = 0x0000;
- break;
- case AUD_NICAM_L:
- source = 0x0200;
- break;
- default:
- break;
- }
- break;
- case V4L2_TUNER_MODE_STEREO:
- source = mpx_audio_modes[t->mpxmode].source;
- break;
- case V4L2_TUNER_MODE_LANG1:
- source = 0x0300;
- break;
- case V4L2_TUNER_MODE_LANG2:
- source = 0x0400;
- break;
- }
- source |= mpx_audio_modes[t->mpxmode].source & 0x00ff;
- } else
- source = mpx_audio_modes[t->mpxmode].source;
-
- mpx_write(client, 0x10, 0x0030, mpx_audio_modes[t->mpxmode].modus);
- mpx_write(client, 0x12, 0x0008, source);
- mpx_write(client, 0x12, 0x0013, mpx_audio_modes[t->mpxmode].acb);
- mpx_write(client, 0x12, 0x000e,
- mpx_audio_modes[t->mpxmode].fm_prescale);
- mpx_write(client, 0x12, 0x0010,
- mpx_audio_modes[t->mpxmode].nicam_prescale);
- mpx_write(client, 0x12, 0x000d,
- mpx_audio_modes[t->mpxmode].scart_prescale);
- mpx_write(client, 0x10, 0x0020, mpx_audio_modes[t->mpxmode].system);
- mpx_write(client, 0x12, 0x0000, mpx_audio_modes[t->mpxmode].volume);
- if (mpx_audio_modes[t->mpxmode].audio_mode == AUD_A2)
- mpx_write(client, 0x10, 0x0022,
- t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190);
-
-#ifdef MPX_DEBUG
- {
- u8 buf1[3], buf2[2];
- struct i2c_msg msgs[2];
-
- printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x "
- "%04x %04x %04x %04x %04x %04x\n",
- mpx_audio_modes[t->mpxmode].modus,
- source,
- mpx_audio_modes[t->mpxmode].acb,
- mpx_audio_modes[t->mpxmode].fm_prescale,
- mpx_audio_modes[t->mpxmode].nicam_prescale,
- mpx_audio_modes[t->mpxmode].scart_prescale,
- mpx_audio_modes[t->mpxmode].system,
- mpx_audio_modes[t->mpxmode].volume);
- buf1[0] = 0x11;
- buf1[1] = 0x00;
- buf1[2] = 0x7e;
- msgs[0].addr = MPX_I2C_ADDR;
- msgs[0].flags = 0;
- msgs[0].len = 3;
- msgs[0].buf = buf1;
- msgs[1].addr = MPX_I2C_ADDR;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = 2;
- msgs[1].buf = buf2;
- i2c_transfer(client->adapter, msgs, 2);
- printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n",
- buf2[0], buf2[1]);
- buf1[0] = 0x11;
- buf1[1] = 0x02;
- buf1[2] = 0x00;
- i2c_transfer(client->adapter, msgs, 2);
- printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n",
- buf2[0], buf2[1]);
- }
-#endif
- return 0;
-}
-
-/*
- * IF configuration values for the BTF-PG472Z:
- *
- * B/G: 0x94 0x70 0x49
- * I: 0x14 0x70 0x4a
- * D/K: 0x14 0x70 0x4b
- * L: 0x04 0x70 0x4b
- * L': 0x44 0x70 0x53
- * M: 0x50 0x30 0x4c
- */
-
-static int set_if(struct i2c_client *client)
-{
- struct wis_sony_tuner *t = i2c_get_clientdata(client);
- u8 buffer[4];
- struct i2c_msg msg;
- int default_mpx_mode = 0;
-
- /* configure IF */
- buffer[0] = 0;
- if (t->std & V4L2_STD_PAL_BG) {
- buffer[1] = 0x94;
- buffer[2] = 0x70;
- buffer[3] = 0x49;
- default_mpx_mode = 1;
- } else if (t->std & V4L2_STD_PAL_I) {
- buffer[1] = 0x14;
- buffer[2] = 0x70;
- buffer[3] = 0x4a;
- default_mpx_mode = 4;
- } else if (t->std & V4L2_STD_PAL_DK) {
- buffer[1] = 0x14;
- buffer[2] = 0x70;
- buffer[3] = 0x4b;
- default_mpx_mode = 6;
- } else if (t->std & V4L2_STD_SECAM_L) {
- buffer[1] = 0x04;
- buffer[2] = 0x70;
- buffer[3] = 0x4b;
- default_mpx_mode = 11;
- }
- msg.addr = IF_I2C_ADDR;
- msg.flags = 0;
- msg.len = 4;
- msg.buf = buffer;
- i2c_transfer(client->adapter, &msg, 1);
-
- /* Select MPX mode if not forced by the user */
- if (force_mpx_mode >= 0 && force_mpx_mode < MPX_NUM_MODES)
- t->mpxmode = force_mpx_mode;
- else
- t->mpxmode = default_mpx_mode;
- printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n",
- t->mpxmode);
- mpx_setup(client);
-
- return 0;
-}
-
-static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
-{
- struct wis_sony_tuner *t = i2c_get_clientdata(client);
-
- switch (cmd) {
-#if 0
-#ifdef TUNER_SET_TYPE_ADDR
- case TUNER_SET_TYPE_ADDR:
- {
- struct tuner_setup *tun_setup = arg;
- int *type = &tun_setup->type;
-#else
- case TUNER_SET_TYPE:
- {
- int *type = arg;
-#endif
-
- if (t->type >= 0) {
- if (t->type != *type)
- printk(KERN_ERR "wis-sony-tuner: type already "
- "set to %d, ignoring request for %d\n",
- t->type, *type);
- break;
- }
- t->type = *type;
- switch (t->type) {
- case TUNER_SONY_BTF_PG472Z:
- switch (force_band_str[0]) {
- case 'b':
- case 'B':
- case 'g':
- case 'G':
- printk(KERN_INFO "wis-sony-tuner: forcing "
- "tuner to PAL-B/G bands\n");
- force_band = V4L2_STD_PAL_BG;
- break;
- case 'i':
- case 'I':
- printk(KERN_INFO "wis-sony-tuner: forcing "
- "tuner to PAL-I band\n");
- force_band = V4L2_STD_PAL_I;
- break;
- case 'd':
- case 'D':
- case 'k':
- case 'K':
- printk(KERN_INFO "wis-sony-tuner: forcing "
- "tuner to PAL-D/K bands\n");
- force_band = V4L2_STD_PAL_I;
- break;
- case 'l':
- case 'L':
- printk(KERN_INFO "wis-sony-tuner: forcing "
- "tuner to SECAM-L band\n");
- force_band = V4L2_STD_SECAM_L;
- break;
- default:
- force_band = 0;
- break;
- }
- if (force_band)
- t->std = force_band;
- else
- t->std = V4L2_STD_PAL_BG;
- set_if(client);
- break;
- case TUNER_SONY_BTF_PK467Z:
- t->std = V4L2_STD_NTSC_M_JP;
- break;
- case TUNER_SONY_BTF_PB463Z:
- t->std = V4L2_STD_NTSC_M;
- break;
- default:
- printk(KERN_ERR "wis-sony-tuner: tuner type %d is not "
- "supported by this module\n", *type);
- break;
- }
- if (type >= 0)
- printk(KERN_INFO
- "wis-sony-tuner: type set to %d (%s)\n",
- t->type, sony_tuners[t->type - 200].name);
- break;
- }
-#endif
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
-
- f->frequency = t->freq;
- break;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
-
- t->freq = f->frequency;
- set_freq(client, t->freq);
- break;
- }
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *std = arg;
-
- switch (t->type) {
- case TUNER_SONY_BTF_PG472Z:
- switch (std->index) {
- case 0:
- v4l2_video_std_construct(std,
- V4L2_STD_PAL_BG, "PAL-B/G");
- break;
- case 1:
- v4l2_video_std_construct(std,
- V4L2_STD_PAL_I, "PAL-I");
- break;
- case 2:
- v4l2_video_std_construct(std,
- V4L2_STD_PAL_DK, "PAL-D/K");
- break;
- case 3:
- v4l2_video_std_construct(std,
- V4L2_STD_SECAM_L, "SECAM-L");
- break;
- default:
- std->id = 0; /* hack to indicate EINVAL */
- break;
- }
- break;
- case TUNER_SONY_BTF_PK467Z:
- if (std->index != 0) {
- std->id = 0; /* hack to indicate EINVAL */
- break;
- }
- v4l2_video_std_construct(std,
- V4L2_STD_NTSC_M_JP, "NTSC-J");
- break;
- case TUNER_SONY_BTF_PB463Z:
- if (std->index != 0) {
- std->id = 0; /* hack to indicate EINVAL */
- break;
- }
- v4l2_video_std_construct(std, V4L2_STD_NTSC_M, "NTSC");
- break;
- }
- break;
- }
- case VIDIOC_G_STD:
- {
- v4l2_std_id *std = arg;
-
- *std = t->std;
- break;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *std = arg;
- v4l2_std_id old = t->std;
-
- switch (t->type) {
- case TUNER_SONY_BTF_PG472Z:
- if (force_band && (*std & force_band) != *std &&
- *std != V4L2_STD_PAL &&
- *std != V4L2_STD_SECAM) {
- printk(KERN_DEBUG "wis-sony-tuner: ignoring "
- "requested TV standard in "
- "favor of force_band value\n");
- t->std = force_band;
- } else if (*std & V4L2_STD_PAL_BG) { /* default */
- t->std = V4L2_STD_PAL_BG;
- } else if (*std & V4L2_STD_PAL_I) {
- t->std = V4L2_STD_PAL_I;
- } else if (*std & V4L2_STD_PAL_DK) {
- t->std = V4L2_STD_PAL_DK;
- } else if (*std & V4L2_STD_SECAM_L) {
- t->std = V4L2_STD_SECAM_L;
- } else {
- printk(KERN_ERR "wis-sony-tuner: TV standard "
- "not supported\n");
- *std = 0; /* hack to indicate EINVAL */
- break;
- }
- if (old != t->std)
- set_if(client);
- break;
- case TUNER_SONY_BTF_PK467Z:
- if (!(*std & V4L2_STD_NTSC_M_JP)) {
- printk(KERN_ERR "wis-sony-tuner: TV standard "
- "not supported\n");
- *std = 0; /* hack to indicate EINVAL */
- }
- break;
- case TUNER_SONY_BTF_PB463Z:
- if (!(*std & V4L2_STD_NTSC_M)) {
- printk(KERN_ERR "wis-sony-tuner: TV standard "
- "not supported\n");
- *std = 0; /* hack to indicate EINVAL */
- }
- break;
- }
- break;
- }
- case VIDIOC_QUERYSTD:
- {
- v4l2_std_id *std = arg;
-
- switch (t->type) {
- case TUNER_SONY_BTF_PG472Z:
- if (force_band)
- *std = force_band;
- else
- *std = V4L2_STD_PAL_BG | V4L2_STD_PAL_I |
- V4L2_STD_PAL_DK | V4L2_STD_SECAM_L;
- break;
- case TUNER_SONY_BTF_PK467Z:
- *std = V4L2_STD_NTSC_M_JP;
- break;
- case TUNER_SONY_BTF_PB463Z:
- *std = V4L2_STD_NTSC_M;
- break;
- }
- break;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *tun = arg;
-
- memset(tun, 0, sizeof(*tun));
- strcpy(tun->name, "Television");
- tun->type = V4L2_TUNER_ANALOG_TV;
- tun->rangelow = 0UL; /* does anything use these? */
- tun->rangehigh = 0xffffffffUL;
- switch (t->type) {
- case TUNER_SONY_BTF_PG472Z:
- tun->capability = V4L2_TUNER_CAP_NORM |
- V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
- V4L2_TUNER_CAP_LANG2;
- tun->rxsubchans = V4L2_TUNER_SUB_MONO |
- V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 |
- V4L2_TUNER_SUB_LANG2;
- break;
- case TUNER_SONY_BTF_PK467Z:
- case TUNER_SONY_BTF_PB463Z:
- tun->capability = V4L2_TUNER_CAP_STEREO;
- tun->rxsubchans = V4L2_TUNER_SUB_MONO |
- V4L2_TUNER_SUB_STEREO;
- break;
- }
- tun->audmode = t->audmode;
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *tun = arg;
-
- switch (t->type) {
- case TUNER_SONY_BTF_PG472Z:
- if (tun->audmode != t->audmode) {
- t->audmode = tun->audmode;
- mpx_setup(client);
- }
- break;
- case TUNER_SONY_BTF_PK467Z:
- case TUNER_SONY_BTF_PB463Z:
- break;
- }
- return 0;
- }
- default:
- break;
- }
- return 0;
-}
-
-static int wis_sony_tuner_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_adapter *adapter = client->adapter;
- struct wis_sony_tuner *t;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
- return -ENODEV;
-
- t = kmalloc(sizeof(struct wis_sony_tuner), GFP_KERNEL);
- if (t == NULL)
- return -ENOMEM;
-
- t->type = -1;
- t->freq = 0;
- t->mpxmode = 0;
- t->audmode = V4L2_TUNER_MODE_STEREO;
- i2c_set_clientdata(client, t);
-
- printk(KERN_DEBUG
- "wis-sony-tuner: initializing tuner at address %d on %s\n",
- client->addr, adapter->name);
-
- return 0;
-}
-
-static int wis_sony_tuner_remove(struct i2c_client *client)
-{
- struct wis_sony_tuner *t = i2c_get_clientdata(client);
-
- kfree(t);
- return 0;
-}
-
-static const struct i2c_device_id wis_sony_tuner_id[] = {
- { "wis_sony_tuner", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wis_sony_tuner_id);
-
-static struct i2c_driver wis_sony_tuner_driver = {
- .driver = {
- .name = "WIS Sony TV Tuner I2C driver",
- },
- .probe = wis_sony_tuner_probe,
- .remove = wis_sony_tuner_remove,
- .command = tuner_command,
- .id_table = wis_sony_tuner_id,
-};
-
-static int __init wis_sony_tuner_init(void)
-{
- return i2c_add_driver(&wis_sony_tuner_driver);
-}
-
-static void __exit wis_sony_tuner_cleanup(void)
-{
- i2c_del_driver(&wis_sony_tuner_driver);
-}
-
-module_init(wis_sony_tuner_init);
-module_exit(wis_sony_tuner_cleanup);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <linux/ioctl.h>
-#include <linux/slab.h>
-
-#include "wis-i2c.h"
-
-struct wis_tw2804 {
- int channel;
- int norm;
- int brightness;
- int contrast;
- int saturation;
- int hue;
-};
-
-static u8 global_registers[] = {
- 0x39, 0x00,
- 0x3a, 0xff,
- 0x3b, 0x84,
- 0x3c, 0x80,
- 0x3d, 0x80,
- 0x3e, 0x82,
- 0x3f, 0x82,
- 0xff, 0xff, /* Terminator (reg 0xff does not exist) */
-};
-
-static u8 channel_registers[] = {
- 0x01, 0xc4,
- 0x02, 0xa5,
- 0x03, 0x20,
- 0x04, 0xd0,
- 0x05, 0x20,
- 0x06, 0xd0,
- 0x07, 0x88,
- 0x08, 0x20,
- 0x09, 0x07,
- 0x0a, 0xf0,
- 0x0b, 0x07,
- 0x0c, 0xf0,
- 0x0d, 0x40,
- 0x0e, 0xd2,
- 0x0f, 0x80,
- 0x10, 0x80,
- 0x11, 0x80,
- 0x12, 0x80,
- 0x13, 0x1f,
- 0x14, 0x00,
- 0x15, 0x00,
- 0x16, 0x00,
- 0x17, 0x00,
- 0x18, 0xff,
- 0x19, 0xff,
- 0x1a, 0xff,
- 0x1b, 0xff,
- 0x1c, 0xff,
- 0x1d, 0xff,
- 0x1e, 0xff,
- 0x1f, 0xff,
- 0x20, 0x07,
- 0x21, 0x07,
- 0x22, 0x00,
- 0x23, 0x91,
- 0x24, 0x51,
- 0x25, 0x03,
- 0x26, 0x00,
- 0x27, 0x00,
- 0x28, 0x00,
- 0x29, 0x00,
- 0x2a, 0x00,
- 0x2b, 0x00,
- 0x2c, 0x00,
- 0x2d, 0x00,
- 0x2e, 0x00,
- 0x2f, 0x00,
- 0x30, 0x00,
- 0x31, 0x00,
- 0x32, 0x00,
- 0x33, 0x00,
- 0x34, 0x00,
- 0x35, 0x00,
- 0x36, 0x00,
- 0x37, 0x00,
- 0xff, 0xff, /* Terminator (reg 0xff does not exist) */
-};
-
-static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel)
-{
- return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
-}
-
-static int write_regs(struct i2c_client *client, u8 *regs, int channel)
-{
- int i;
-
- for (i = 0; regs[i] != 0xff; i += 2)
- if (i2c_smbus_write_byte_data(client,
- regs[i] | (channel << 6), regs[i + 1]) < 0)
- return -1;
- return 0;
-}
-
-static int wis_tw2804_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
-{
- struct wis_tw2804 *dec = i2c_get_clientdata(client);
-
- if (cmd == DECODER_SET_CHANNEL) {
- int *input = arg;
-
- if (*input < 0 || *input > 3) {
- printk(KERN_ERR "wis-tw2804: channel %d is not "
- "between 0 and 3!\n", *input);
- return 0;
- }
- dec->channel = *input;
- printk(KERN_DEBUG "wis-tw2804: initializing TW2804 "
- "channel %d\n", dec->channel);
- if (dec->channel == 0 &&
- write_regs(client, global_registers, 0) < 0) {
- printk(KERN_ERR "wis-tw2804: error initializing "
- "TW2804 global registers\n");
- return 0;
- }
- if (write_regs(client, channel_registers, dec->channel) < 0) {
- printk(KERN_ERR "wis-tw2804: error initializing "
- "TW2804 channel %d\n", dec->channel);
- return 0;
- }
- return 0;
- }
-
- if (dec->channel < 0) {
- printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until "
- "channel number is set\n", cmd);
- return 0;
- }
-
- switch (cmd) {
- case VIDIOC_S_STD:
- {
- v4l2_std_id *input = arg;
- u8 regs[] = {
- 0x01, *input & V4L2_STD_NTSC ? 0xc4 : 0x84,
- 0x09, *input & V4L2_STD_NTSC ? 0x07 : 0x04,
- 0x0a, *input & V4L2_STD_NTSC ? 0xf0 : 0x20,
- 0x0b, *input & V4L2_STD_NTSC ? 0x07 : 0x04,
- 0x0c, *input & V4L2_STD_NTSC ? 0xf0 : 0x20,
- 0x0d, *input & V4L2_STD_NTSC ? 0x40 : 0x4a,
- 0x16, *input & V4L2_STD_NTSC ? 0x00 : 0x40,
- 0x17, *input & V4L2_STD_NTSC ? 0x00 : 0x40,
- 0x20, *input & V4L2_STD_NTSC ? 0x07 : 0x0f,
- 0x21, *input & V4L2_STD_NTSC ? 0x07 : 0x0f,
- 0xff, 0xff,
- };
- write_regs(client, regs, dec->channel);
- dec->norm = *input;
- break;
- }
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
- case V4L2_CID_SATURATION:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
- case V4L2_CID_HUE:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
- }
- break;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value > 255)
- dec->brightness = 255;
- else if (ctrl->value < 0)
- dec->brightness = 0;
- else
- dec->brightness = ctrl->value;
- write_reg(client, 0x12, dec->brightness, dec->channel);
- break;
- case V4L2_CID_CONTRAST:
- if (ctrl->value > 255)
- dec->contrast = 255;
- else if (ctrl->value < 0)
- dec->contrast = 0;
- else
- dec->contrast = ctrl->value;
- write_reg(client, 0x11, dec->contrast, dec->channel);
- break;
- case V4L2_CID_SATURATION:
- if (ctrl->value > 255)
- dec->saturation = 255;
- else if (ctrl->value < 0)
- dec->saturation = 0;
- else
- dec->saturation = ctrl->value;
- write_reg(client, 0x10, dec->saturation, dec->channel);
- break;
- case V4L2_CID_HUE:
- if (ctrl->value > 255)
- dec->hue = 255;
- else if (ctrl->value < 0)
- dec->hue = 0;
- else
- dec->hue = ctrl->value;
- write_reg(client, 0x0f, dec->hue, dec->channel);
- break;
- }
- break;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = dec->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = dec->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = dec->saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = dec->hue;
- break;
- }
- break;
- }
- default:
- break;
- }
- return 0;
-}
-
-static int wis_tw2804_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_adapter *adapter = client->adapter;
- struct wis_tw2804 *dec;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -ENODEV;
-
- dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL);
- if (dec == NULL)
- return -ENOMEM;
-
- dec->channel = -1;
- dec->norm = V4L2_STD_NTSC;
- dec->brightness = 128;
- dec->contrast = 128;
- dec->saturation = 128;
- dec->hue = 128;
- i2c_set_clientdata(client, dec);
-
- printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n",
- client->addr, adapter->name);
-
- return 0;
-}
-
-static int wis_tw2804_remove(struct i2c_client *client)
-{
- struct wis_tw2804 *dec = i2c_get_clientdata(client);
-
- kfree(dec);
- return 0;
-}
-
-static const struct i2c_device_id wis_tw2804_id[] = {
- { "wis_tw2804", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wis_tw2804_id);
-
-static struct i2c_driver wis_tw2804_driver = {
- .driver = {
- .name = "WIS TW2804 I2C driver",
- },
- .probe = wis_tw2804_probe,
- .remove = wis_tw2804_remove,
- .command = wis_tw2804_command,
- .id_table = wis_tw2804_id,
-};
-
-static int __init wis_tw2804_init(void)
-{
- return i2c_add_driver(&wis_tw2804_driver);
-}
-
-static void __exit wis_tw2804_cleanup(void)
-{
- i2c_del_driver(&wis_tw2804_driver);
-}
-
-module_init(wis_tw2804_init);
-module_exit(wis_tw2804_cleanup);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <linux/ioctl.h>
-#include <linux/slab.h>
-
-#include "wis-i2c.h"
-
-struct wis_tw9903 {
- int norm;
- int brightness;
- int contrast;
- int hue;
-};
-
-static u8 initial_registers[] =
-{
- 0x02, 0x44, /* input 1, composite */
- 0x03, 0x92, /* correct digital format */
- 0x04, 0x00,
- 0x05, 0x80, /* or 0x00 for PAL */
- 0x06, 0x40, /* second internal current reference */
- 0x07, 0x02, /* window */
- 0x08, 0x14, /* window */
- 0x09, 0xf0, /* window */
- 0x0a, 0x81, /* window */
- 0x0b, 0xd0, /* window */
- 0x0c, 0x8c,
- 0x0d, 0x00, /* scaling */
- 0x0e, 0x11, /* scaling */
- 0x0f, 0x00, /* scaling */
- 0x10, 0x00, /* brightness */
- 0x11, 0x60, /* contrast */
- 0x12, 0x01, /* sharpness */
- 0x13, 0x7f, /* U gain */
- 0x14, 0x5a, /* V gain */
- 0x15, 0x00, /* hue */
- 0x16, 0xc3, /* sharpness */
- 0x18, 0x00,
- 0x19, 0x58, /* vbi */
- 0x1a, 0x80,
- 0x1c, 0x0f, /* video norm */
- 0x1d, 0x7f, /* video norm */
- 0x20, 0xa0, /* clamping gain (working 0x50) */
- 0x21, 0x22,
- 0x22, 0xf0,
- 0x23, 0xfe,
- 0x24, 0x3c,
- 0x25, 0x38,
- 0x26, 0x44,
- 0x27, 0x20,
- 0x28, 0x00,
- 0x29, 0x15,
- 0x2a, 0xa0,
- 0x2b, 0x44,
- 0x2c, 0x37,
- 0x2d, 0x00,
- 0x2e, 0xa5, /* burst PLL control (working: a9) */
- 0x2f, 0xe0, /* 0xea is blue test frame -- 0xe0 for normal */
- 0x31, 0x00,
- 0x33, 0x22,
- 0x34, 0x11,
- 0x35, 0x35,
- 0x3b, 0x05,
- 0x06, 0xc0, /* reset device */
- 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
-};
-
-static int write_reg(struct i2c_client *client, u8 reg, u8 value)
-{
- return i2c_smbus_write_byte_data(client, reg, value);
-}
-
-static int write_regs(struct i2c_client *client, u8 *regs)
-{
- int i;
-
- for (i = 0; regs[i] != 0x00; i += 2)
- if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
- return -1;
- return 0;
-}
-
-static int wis_tw9903_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
-{
- struct wis_tw9903 *dec = i2c_get_clientdata(client);
-
- switch (cmd) {
- case VIDIOC_S_INPUT:
- {
- int *input = arg;
-
- i2c_smbus_write_byte_data(client, 0x02, 0x40 | (*input << 1));
- break;
- }
-#if 0
- /* The scaler on this thing seems to be horribly broken */
- case DECODER_SET_RESOLUTION:
- {
- struct video_decoder_resolution *res = arg;
- /*int hscale = 256 * 720 / res->width;*/
- int hscale = 256 * 720 / (res->width - (res->width > 704 ? 0 : 8));
- int vscale = 256 * (dec->norm & V4L2_STD_NTSC ? 240 : 288)
- / res->height;
- u8 regs[] = {
- 0x0d, vscale & 0xff,
- 0x0f, hscale & 0xff,
- 0x0e, ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8),
- 0x06, 0xc0, /* reset device */
- 0, 0,
- };
- printk(KERN_DEBUG "vscale is %04x, hscale is %04x\n",
- vscale, hscale);
- /*write_regs(client, regs);*/
- break;
- }
-#endif
- case VIDIOC_S_STD:
- {
- v4l2_std_id *input = arg;
- u8 regs[] = {
- 0x05, *input & V4L2_STD_NTSC ? 0x80 : 0x00,
- 0x07, *input & V4L2_STD_NTSC ? 0x02 : 0x12,
- 0x08, *input & V4L2_STD_NTSC ? 0x14 : 0x18,
- 0x09, *input & V4L2_STD_NTSC ? 0xf0 : 0x20,
- 0, 0,
- };
- write_regs(client, regs);
- dec->norm = *input;
- break;
- }
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
- ctrl->minimum = -128;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 0x00;
- ctrl->flags = 0;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 0x60;
- ctrl->flags = 0;
- break;
-#if 0
- /* I don't understand how the Chroma Gain registers work... */
- case V4L2_CID_SATURATION:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 64;
- ctrl->flags = 0;
- break;
-#endif
- case V4L2_CID_HUE:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
- ctrl->minimum = -128;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 0;
- ctrl->flags = 0;
- break;
- }
- break;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value > 127)
- dec->brightness = 127;
- else if (ctrl->value < -128)
- dec->brightness = -128;
- else
- dec->brightness = ctrl->value;
- write_reg(client, 0x10, dec->brightness);
- break;
- case V4L2_CID_CONTRAST:
- if (ctrl->value > 255)
- dec->contrast = 255;
- else if (ctrl->value < 0)
- dec->contrast = 0;
- else
- dec->contrast = ctrl->value;
- write_reg(client, 0x11, dec->contrast);
- break;
-#if 0
- case V4L2_CID_SATURATION:
- if (ctrl->value > 127)
- dec->saturation = 127;
- else if (ctrl->value < 0)
- dec->saturation = 0;
- else
- dec->saturation = ctrl->value;
- /*write_reg(client, 0x0c, dec->saturation);*/
- break;
-#endif
- case V4L2_CID_HUE:
- if (ctrl->value > 127)
- dec->hue = 127;
- else if (ctrl->value < -128)
- dec->hue = -128;
- else
- dec->hue = ctrl->value;
- write_reg(client, 0x15, dec->hue);
- break;
- }
- break;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = dec->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = dec->contrast;
- break;
-#if 0
- case V4L2_CID_SATURATION:
- ctrl->value = dec->saturation;
- break;
-#endif
- case V4L2_CID_HUE:
- ctrl->value = dec->hue;
- break;
- }
- break;
- }
- default:
- break;
- }
- return 0;
-}
-
-static int wis_tw9903_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_adapter *adapter = client->adapter;
- struct wis_tw9903 *dec;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -ENODEV;
-
- dec = kmalloc(sizeof(struct wis_tw9903), GFP_KERNEL);
- if (dec == NULL)
- return -ENOMEM;
-
- dec->norm = V4L2_STD_NTSC;
- dec->brightness = 0;
- dec->contrast = 0x60;
- dec->hue = 0;
- i2c_set_clientdata(client, dec);
-
- printk(KERN_DEBUG
- "wis-tw9903: initializing TW9903 at address %d on %s\n",
- client->addr, adapter->name);
-
- if (write_regs(client, initial_registers) < 0) {
- printk(KERN_ERR "wis-tw9903: error initializing TW9903\n");
- kfree(dec);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int wis_tw9903_remove(struct i2c_client *client)
-{
- struct wis_tw9903 *dec = i2c_get_clientdata(client);
-
- kfree(dec);
- return 0;
-}
-
-static const struct i2c_device_id wis_tw9903_id[] = {
- { "wis_tw9903", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wis_tw9903_id);
-
-static struct i2c_driver wis_tw9903_driver = {
- .driver = {
- .name = "WIS TW9903 I2C driver",
- },
- .probe = wis_tw9903_probe,
- .remove = wis_tw9903_remove,
- .command = wis_tw9903_command,
- .id_table = wis_tw9903_id,
-};
-
-static int __init wis_tw9903_init(void)
-{
- return i2c_add_driver(&wis_tw9903_driver);
-}
-
-static void __exit wis_tw9903_cleanup(void)
-{
- i2c_del_driver(&wis_tw9903_driver);
-}
-
-module_init(wis_tw9903_init);
-module_exit(wis_tw9903_cleanup);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Micronas USA Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <media/tvaudio.h>
-#include <media/v4l2-common.h>
-
-#include "wis-i2c.h"
-
-static int write_reg(struct i2c_client *client, int reg, int value)
-{
- /* UDA1342 wants MSB first, but SMBus sends LSB first */
- i2c_smbus_write_word_data(client, reg, swab16(value));
- return 0;
-}
-
-static int wis_uda1342_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
-{
- switch (cmd) {
- case VIDIOC_S_AUDIO:
- {
- int *inp = arg;
-
- switch (*inp) {
- case TVAUDIO_INPUT_TUNER:
- write_reg(client, 0x00, 0x1441); /* select input 2 */
- break;
- case TVAUDIO_INPUT_EXTERN:
- write_reg(client, 0x00, 0x1241); /* select input 1 */
- break;
- default:
- printk(KERN_ERR "wis-uda1342: input %d not supported\n",
- *inp);
- break;
- }
- break;
- }
- default:
- break;
- }
- return 0;
-}
-
-static int wis_uda1342_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_adapter *adapter = client->adapter;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
- return -ENODEV;
-
- printk(KERN_DEBUG
- "wis-uda1342: initializing UDA1342 at address %d on %s\n",
- client->addr, adapter->name);
-
- write_reg(client, 0x00, 0x8000); /* reset registers */
- write_reg(client, 0x00, 0x1241); /* select input 1 */
-
- return 0;
-}
-
-static int wis_uda1342_remove(struct i2c_client *client)
-{
- return 0;
-}
-
-static const struct i2c_device_id wis_uda1342_id[] = {
- { "wis_uda1342", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wis_uda1342_id);
-
-static struct i2c_driver wis_uda1342_driver = {
- .driver = {
- .name = "WIS UDA1342 I2C driver",
- },
- .probe = wis_uda1342_probe,
- .remove = wis_uda1342_remove,
- .command = wis_uda1342_command,
- .id_table = wis_uda1342_id,
-};
-
-static int __init wis_uda1342_init(void)
-{
- return i2c_add_driver(&wis_uda1342_driver);
-}
-
-static void __exit wis_uda1342_cleanup(void)
-{
- i2c_del_driver(&wis_uda1342_driver);
-}
-
-module_init(wis_uda1342_init);
-module_exit(wis_uda1342_cleanup);
-
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-#
-# LIRC driver(s) configuration
-#
-menuconfig LIRC_STAGING
- bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
- depends on LIRC
- help
- Say Y here, and all supported Linux Infrared Remote Control IR and
- RF receiver and transmitter drivers will be displayed. When paired
- with a remote control and the lirc daemon, the receiver drivers
- allow control of your Linux system via remote control.
-
-if LIRC_STAGING
-
-config LIRC_BT829
- tristate "BT829 based hardware"
- depends on LIRC && PCI
- help
- Driver for the IR interface on BT829-based hardware
-
-config LIRC_IGORPLUGUSB
- tristate "Igor Cesko's USB IR Receiver"
- depends on LIRC && USB
- help
- Driver for Igor Cesko's USB IR Receiver
-
-config LIRC_IMON
- tristate "Legacy SoundGraph iMON Receiver and Display"
- depends on LIRC && USB
- help
- Driver for the original SoundGraph iMON IR Receiver and Display
-
- Current generation iMON devices use the input layer imon driver.
-
-config LIRC_PARALLEL
- tristate "Homebrew Parallel Port Receiver"
- depends on LIRC && PARPORT
- help
- Driver for Homebrew Parallel Port Receivers
-
-config LIRC_SASEM
- tristate "Sasem USB IR Remote"
- depends on LIRC && USB
- help
- Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
-
-config LIRC_SERIAL
- tristate "Homebrew Serial Port Receiver"
- depends on LIRC
- help
- Driver for Homebrew Serial Port Receivers
-
-config LIRC_SERIAL_TRANSMITTER
- bool "Serial Port Transmitter"
- default y
- depends on LIRC_SERIAL
- help
- Serial Port Transmitter support
-
-config LIRC_SIR
- tristate "Built-in SIR IrDA port"
- depends on LIRC
- help
- Driver for the SIR IrDA port
-
-config LIRC_TTUSBIR
- tristate "Technotrend USB IR Receiver"
- depends on LIRC && USB
- help
- Driver for the Technotrend USB IR Receiver
-
-config LIRC_ZILOG
- tristate "Zilog/Hauppauge IR Transmitter"
- depends on LIRC && I2C
- help
- Driver for the Zilog/Hauppauge IR Transmitter, found on
- PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
-endif
+++ /dev/null
-# Makefile for the lirc drivers.
-#
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
-obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
-obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
-obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
-obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
-obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
-obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
-obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o
-obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o
+++ /dev/null
-- All drivers should either be ported to ir-core, or dropped entirely
- (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an
- example of a previously completed port).
-
-Please send patches to:
-Jarod Wilson <jarod@wilsonet.com>
-Greg Kroah-Hartman <greg@kroah.com>
-
+++ /dev/null
-1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for
-the chips supported by lirc_zilog. Before moving lirc_zilog out of staging:
-
-a. ir-kbd-i2c needs a module parameter added to allow the user to tell
- ir-kbd-i2c to ignore Z8 IR units.
-
-b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c
- does.
-
-
-2. lirc_zilog module ref-counting need examination. It has not been
-verified that cdev and lirc_dev will take the proper module references on
-lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node
-is open.
-
-(The good news is ref-counting of lirc_zilog internal structures appears to be
-complete. Testing has shown the cx18 module can be unloaded out from under
-irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse
-effects. The cx18 module could then be reloaded and irw properly began
-receiving button presses again and ir_send worked without error.)
-
-
-3. Bridge drivers, if able, should provide a chip reset() callback
-to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines
-to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog
-to bring the chip back to normal when it hangs, in the same places the
-original lirc_pvr150 driver code does. This is not strictly needed, so it
-is not required to move lirc_zilog out of staging.
-
-Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed
-and installed on Hauppauge products. When working on either module, developers
-must consider at least the following bridge drivers which mention an IR Rx unit
-at address 0x71 (indicative of a Z8):
-
- ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134
-
+++ /dev/null
-/*
- * Remote control driver for the TV-card based on bt829
- *
- * by Leonid Froenchenko <lfroen@galileo.co.il>
- *
- * 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/threads.h>
-#include <linux/sched.h>
-#include <linux/ioport.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-
-#include <media/lirc_dev.h>
-
-static int poll_main(void);
-static int atir_init_start(void);
-
-static void write_index(unsigned char index, unsigned int value);
-static unsigned int read_index(unsigned char index);
-
-static void do_i2c_start(void);
-static void do_i2c_stop(void);
-
-static void seems_wr_byte(unsigned char al);
-static unsigned char seems_rd_byte(void);
-
-static unsigned int read_index(unsigned char al);
-static void write_index(unsigned char ah, unsigned int edx);
-
-static void cycle_delay(int cycle);
-
-static void do_set_bits(unsigned char bl);
-static unsigned char do_get_bits(void);
-
-#define DATA_PCI_OFF 0x7FFC00
-#define WAIT_CYCLE 20
-
-#define DRIVER_NAME "lirc_bt829"
-
-static int debug;
-#define dprintk(fmt, args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG DRIVER_NAME ": "fmt, ## args); \
- } while (0)
-
-static int atir_minor;
-static unsigned long pci_addr_phys;
-static unsigned char *pci_addr_lin;
-
-static struct lirc_driver atir_driver;
-
-static struct pci_dev *do_pci_probe(void)
-{
- struct pci_dev *my_dev;
- my_dev = pci_get_device(PCI_VENDOR_ID_ATI,
- PCI_DEVICE_ID_ATI_264VT, NULL);
- if (my_dev) {
- printk(KERN_ERR DRIVER_NAME ": Using device: %s\n",
- pci_name(my_dev));
- pci_addr_phys = 0;
- if (my_dev->resource[0].flags & IORESOURCE_MEM) {
- pci_addr_phys = my_dev->resource[0].start;
- printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X\n",
- (unsigned int)pci_addr_phys);
- }
- if (pci_addr_phys == 0) {
- printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n");
- return NULL;
- }
- } else {
- printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n");
- return NULL;
- }
- return my_dev;
-}
-
-static int atir_add_to_buf(void *data, struct lirc_buffer *buf)
-{
- unsigned char key;
- int status;
- status = poll_main();
- key = (status >> 8) & 0xFF;
- if (status & 0xFF) {
- dprintk("reading key %02X\n", key);
- lirc_buffer_write(buf, &key);
- return 0;
- }
- return -ENODATA;
-}
-
-static int atir_set_use_inc(void *data)
-{
- dprintk("driver is opened\n");
- return 0;
-}
-
-static void atir_set_use_dec(void *data)
-{
- dprintk("driver is closed\n");
-}
-
-int init_module(void)
-{
- struct pci_dev *pdev;
-
- pdev = do_pci_probe();
- if (pdev == NULL)
- return -ENODEV;
-
- if (!atir_init_start())
- return -ENODEV;
-
- strcpy(atir_driver.name, "ATIR");
- atir_driver.minor = -1;
- atir_driver.code_length = 8;
- atir_driver.sample_rate = 10;
- atir_driver.data = 0;
- atir_driver.add_to_buf = atir_add_to_buf;
- atir_driver.set_use_inc = atir_set_use_inc;
- atir_driver.set_use_dec = atir_set_use_dec;
- atir_driver.dev = &pdev->dev;
- atir_driver.owner = THIS_MODULE;
-
- atir_minor = lirc_register_driver(&atir_driver);
- if (atir_minor < 0) {
- printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n");
- return atir_minor;
- }
- dprintk("driver is registered on minor %d\n", atir_minor);
-
- return 0;
-}
-
-
-void cleanup_module(void)
-{
- lirc_unregister_driver(atir_minor);
-}
-
-
-static int atir_init_start(void)
-{
- pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400);
- if (pci_addr_lin == 0) {
- printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n");
- return 0;
- }
- return 1;
-}
-
-static void cycle_delay(int cycle)
-{
- udelay(WAIT_CYCLE*cycle);
-}
-
-
-static int poll_main()
-{
- unsigned char status_high, status_low;
-
- do_i2c_start();
-
- seems_wr_byte(0xAA);
- seems_wr_byte(0x01);
-
- do_i2c_start();
-
- seems_wr_byte(0xAB);
-
- status_low = seems_rd_byte();
- status_high = seems_rd_byte();
-
- do_i2c_stop();
-
- return (status_high << 8) | status_low;
-}
-
-static void do_i2c_start(void)
-{
- do_set_bits(3);
- cycle_delay(4);
-
- do_set_bits(1);
- cycle_delay(7);
-
- do_set_bits(0);
- cycle_delay(2);
-}
-
-static void do_i2c_stop(void)
-{
- unsigned char bits;
- bits = do_get_bits() & 0xFD;
- do_set_bits(bits);
- cycle_delay(1);
-
- bits |= 1;
- do_set_bits(bits);
- cycle_delay(2);
-
- bits |= 2;
- do_set_bits(bits);
- bits = 3;
- do_set_bits(bits);
- cycle_delay(2);
-}
-
-static void seems_wr_byte(unsigned char value)
-{
- int i;
- unsigned char reg;
-
- reg = do_get_bits();
- for (i = 0; i < 8; i++) {
- if (value & 0x80)
- reg |= 0x02;
- else
- reg &= 0xFD;
-
- do_set_bits(reg);
- cycle_delay(1);
-
- reg |= 1;
- do_set_bits(reg);
- cycle_delay(1);
-
- reg &= 0xFE;
- do_set_bits(reg);
- cycle_delay(1);
- value <<= 1;
- }
- cycle_delay(2);
-
- reg |= 2;
- do_set_bits(reg);
-
- reg |= 1;
- do_set_bits(reg);
-
- cycle_delay(1);
- do_get_bits();
-
- reg &= 0xFE;
- do_set_bits(reg);
- cycle_delay(3);
-}
-
-static unsigned char seems_rd_byte(void)
-{
- int i;
- int rd_byte;
- unsigned char bits_2, bits_1;
-
- bits_1 = do_get_bits() | 2;
- do_set_bits(bits_1);
-
- rd_byte = 0;
- for (i = 0; i < 8; i++) {
- bits_1 &= 0xFE;
- do_set_bits(bits_1);
- cycle_delay(2);
-
- bits_1 |= 1;
- do_set_bits(bits_1);
- cycle_delay(1);
-
- bits_2 = do_get_bits();
- if (bits_2 & 2)
- rd_byte |= 1;
-
- rd_byte <<= 1;
- }
-
- bits_1 = 0;
- if (bits_2 == 0)
- bits_1 |= 2;
-
- do_set_bits(bits_1);
- cycle_delay(2);
-
- bits_1 |= 1;
- do_set_bits(bits_1);
- cycle_delay(3);
-
- bits_1 &= 0xFE;
- do_set_bits(bits_1);
- cycle_delay(2);
-
- rd_byte >>= 1;
- rd_byte &= 0xFF;
- return rd_byte;
-}
-
-static void do_set_bits(unsigned char new_bits)
-{
- int reg_val;
- reg_val = read_index(0x34);
- if (new_bits & 2) {
- reg_val &= 0xFFFFFFDF;
- reg_val |= 1;
- } else {
- reg_val &= 0xFFFFFFFE;
- reg_val |= 0x20;
- }
- reg_val |= 0x10;
- write_index(0x34, reg_val);
-
- reg_val = read_index(0x31);
- if (new_bits & 1)
- reg_val |= 0x1000000;
- else
- reg_val &= 0xFEFFFFFF;
-
- reg_val |= 0x8000000;
- write_index(0x31, reg_val);
-}
-
-static unsigned char do_get_bits(void)
-{
- unsigned char bits;
- int reg_val;
-
- reg_val = read_index(0x34);
- reg_val |= 0x10;
- reg_val &= 0xFFFFFFDF;
- write_index(0x34, reg_val);
-
- reg_val = read_index(0x34);
- bits = 0;
- if (reg_val & 8)
- bits |= 2;
- else
- bits &= 0xFD;
-
- reg_val = read_index(0x31);
- if (reg_val & 0x1000000)
- bits |= 1;
- else
- bits &= 0xFE;
-
- return bits;
-}
-
-static unsigned int read_index(unsigned char index)
-{
- unsigned char *addr;
- unsigned int value;
- /* addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */
- addr = pci_addr_lin + ((index & 0xFF) << 2);
- value = readl(addr);
- return value;
-}
-
-static void write_index(unsigned char index, unsigned int reg_val)
-{
- unsigned char *addr;
- addr = pci_addr_lin + ((index & 0xFF) << 2);
- writel(reg_val, addr);
-}
-
-MODULE_AUTHOR("Froenchenko Leonid");
-MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
-MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
+++ /dev/null
-/*
- * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
- *
- * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
- *
- * 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 <media/lirc.h>
-#include <media/lirc_dev.h>
-
-/* hardware address */
-#define ENE_STATUS 0 /* hardware status - unused */
-#define ENE_ADDR_HI 1 /* hi byte of register address */
-#define ENE_ADDR_LO 2 /* low byte of register address */
-#define ENE_IO 3 /* read/write window */
-#define ENE_MAX_IO 4
-
-/* 8 bytes of samples, divided in 2 halfs*/
-#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */
-#define ENE_SAMPLE_SPC_MASK (1 << 7) /* sample is space */
-#define ENE_SAMPLE_VALUE_MASK 0x7F
-#define ENE_SAMPLE_OVERFLOW 0x7F
-#define ENE_SAMPLES_SIZE 4
-
-/* fan input sample buffer */
-#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */
- /* each sample of normal buffer */
-
-#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */
- /* if set, says that sample is pulse */
-#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */
-
-/* first firmware register */
-#define ENE_FW1 0xF8F8
-#define ENE_FW1_ENABLE (1 << 0) /* enable fw processing */
-#define ENE_FW1_TXIRQ (1 << 1) /* TX interrupt pending */
-#define ENE_FW1_WAKE (1 << 6) /* enable wake from S3 */
-#define ENE_FW1_IRQ (1 << 7) /* enable interrupt */
-
-/* second firmware register */
-#define ENE_FW2 0xF8F9
-#define ENE_FW2_BUF_HIGH (1 << 0) /* which half of the buffer to read */
-#define ENE_FW2_IRQ_CLR (1 << 2) /* clear this on IRQ */
-#define ENE_FW2_GP40_AS_LEARN (1 << 4) /* normal input is used as */
- /* learning input */
-#define ENE_FW2_FAN_AS_NRML_IN (1 << 6) /* fan is used as normal input */
-#define ENE_FW2_LEARNING (1 << 7) /* hardware supports learning and TX */
-
-/* fan as input settings - only if learning capable */
-#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */
-#define ENE_FAN_AS_IN1_EN 0xCD
-#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */
-#define ENE_FAN_AS_IN2_EN 0x03
-#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */
-
-/* IRQ registers block (for revision B) */
-#define ENEB_IRQ 0xFD09 /* IRQ number */
-#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */
-#define ENEB_IRQ_STATUS 0xFD80 /* irq status */
-#define ENEB_IRQ_STATUS_IR (1 << 5) /* IR irq */
-
-/* IRQ registers block (for revision C,D) */
-#define ENEC_IRQ 0xFE9B /* new irq settings register */
-#define ENEC_IRQ_MASK 0x0F /* irq number mask */
-#define ENEC_IRQ_UNK_EN (1 << 4) /* always enabled */
-#define ENEC_IRQ_STATUS (1 << 5) /* irq status and ACK */
-
-/* CIR block settings */
-#define ENE_CIR_CONF1 0xFEC0
-#define ENE_CIR_CONF1_ADC_ON 0x7 /* receiver on gpio40 enabled */
-#define ENE_CIR_CONF1_LEARN1 (1 << 3) /* enabled on learning mode */
-#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */
-#define ENE_CIR_CONF1_TX_CARR (1 << 7) /* send TX carrier or not */
-
-#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */
-#define ENE_CIR_CONF2_LEARN2 (1 << 4) /* set on enable learning */
-#define ENE_CIR_CONF2_GPIO40DIS (1 << 5) /* disable normal input via gpio40 */
-
-#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */
-#define ENE_CIR_SAMPLE_OVERFLOW (1 << 7) /* interrupt on overflows if set */
-
-
-/* transmitter - not implemented yet */
-/* KB3926C and higher */
-/* transmission is very similar to receiving, a byte is written to */
-/* ENE_TX_INPUT, in same manner as it is read from sample buffer */
-/* sample period is fixed*/
-
-
-/* transmitter ports */
-#define ENE_TX_PORT1 0xFC01 /* this enables one or both */
-#define ENE_TX_PORT1_EN (1 << 5) /* TX ports */
-#define ENE_TX_PORT2 0xFC08
-#define ENE_TX_PORT2_EN (1 << 1)
-
-#define ENE_TX_INPUT 0xFEC9 /* next byte to transmit */
-#define ENE_TX_SPC_MASK (1 << 7) /* Transmitted sample is space */
-#define ENE_TX_UNK1 0xFECB /* set to 0x63 */
-#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period */
-
-
-#define ENE_TX_CARRIER 0xFECE /* TX carrier * 2 (khz) */
-#define ENE_TX_CARRIER_UNKBIT 0x80 /* This bit set on transmit */
-#define ENE_TX_CARRIER_LOW 0xFECF /* TX carrier / 2 */
-
-/* Hardware versions */
-#define ENE_HW_VERSION 0xFF00 /* hardware revision */
-#define ENE_HW_UNK 0xFF1D
-#define ENE_HW_UNK_CLR (1 << 2)
-#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */
-#define ENE_HW_VER_MINOR 0xFF1F
-#define ENE_HW_VER_OLD 0xFD00
-
-#define same_sign(a, b) ((((a) > 0) && (b) > 0) || ((a) < 0 && (b) < 0))
-
-#define ENE_DRIVER_NAME "enecir"
-#define ENE_MAXGAP 250000 /* this is amount of time we wait
- before turning the sampler, chosen
- arbitry */
-
-#define space(len) (-(len)) /* add a space */
-
-/* software defines */
-#define ENE_IRQ_RX 1
-#define ENE_IRQ_TX 2
-
-#define ENE_HW_B 1 /* 3926B */
-#define ENE_HW_C 2 /* 3926C */
-#define ENE_HW_D 3 /* 3926D */
-
-#define ene_printk(level, text, ...) \
- printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
-
-struct ene_device {
- struct pnp_dev *pnp_dev;
- struct lirc_driver *lirc_driver;
-
- /* hw settings */
- unsigned long hw_io;
- int irq;
-
- int hw_revision; /* hardware revision */
- int hw_learning_and_tx_capable; /* learning capable */
- int hw_gpio40_learning; /* gpio40 is learning */
- int hw_fan_as_normal_input; /* fan input is used as regular input */
-
- /* device data */
- int idle;
- int fan_input_inuse;
-
- int sample;
- int in_use;
-
- struct timeval gap_start;
-};
+++ /dev/null
-/*
- * lirc_igorplugusb - USB remote support for LIRC
- *
- * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware.
- * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm
- *
- * The device can only record bursts of up to 36 pulses/spaces.
- * Works fine with RC5. Longer commands lead to device buffer overrun.
- * (Maybe a better firmware or a microcontroller with more ram can help?)
- *
- * Version 0.1 [beta status]
- *
- * Copyright (C) 2004 Jan M. Hochstein
- * <hochstein@algo.informatik.tu-darmstadt.de>
- *
- * This driver was derived from:
- * Paul Miller <pmiller9@users.sourceforge.net>
- * "lirc_atiusb" module
- * Vladimir Dergachev <volodya@minspring.com>'s 2002
- * "USB ATI Remote support" (input device)
- * Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
- * "USB StreamZap remote driver" (LIRC)
- * Artur Lipowski <alipowski@kki.net.pl>'s 2002
- * "lirc_dev" and "lirc_gpio" LIRC modules
- */
-
-/*
- * 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/module.h>
-#include <linux/kernel.h>
-#include <linux/kmod.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/usb.h>
-#include <linux/time.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-
-/* module identification */
-#define DRIVER_VERSION "0.2"
-#define DRIVER_AUTHOR \
- "Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
-#define DRIVER_DESC "Igorplug USB remote driver for LIRC"
-#define DRIVER_NAME "lirc_igorplugusb"
-
-/* debugging support */
-#ifdef CONFIG_USB_DEBUG
-static int debug = 1;
-#else
-static int debug;
-#endif
-
-#define dprintk(fmt, args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG fmt, ## args); \
- } while (0)
-
-/* One mode2 pulse/space has 4 bytes. */
-#define CODE_LENGTH sizeof(int)
-
-/* Igor's firmware cannot record bursts longer than 36. */
-#define DEVICE_BUFLEN 36
-
-/*
- * Header at the beginning of the device's buffer:
- * unsigned char data_length
- * unsigned char data_start (!=0 means ring-buffer overrun)
- * unsigned char counter (incremented by each burst)
- */
-#define DEVICE_HEADERLEN 3
-
-/* This is for the gap */
-#define ADDITIONAL_LIRC_BYTES 2
-
-/* times to poll per second */
-#define SAMPLE_RATE 100
-static int sample_rate = SAMPLE_RATE;
-
-
-/**** Igor's USB Request Codes */
-
-#define SET_INFRABUFFER_EMPTY 1
-/**
- * Params: none
- * Answer: empty
- */
-
-#define GET_INFRACODE 2
-/**
- * Params:
- * wValue: offset to begin reading infra buffer
- *
- * Answer: infra data
- */
-
-#define SET_DATAPORT_DIRECTION 3
-/**
- * Params:
- * wValue: (byte) 1 bit for each data port pin (0=in, 1=out)
- *
- * Answer: empty
- */
-
-#define GET_DATAPORT_DIRECTION 4
-/**
- * Params: none
- *
- * Answer: (byte) 1 bit for each data port pin (0=in, 1=out)
- */
-
-#define SET_OUT_DATAPORT 5
-/**
- * Params:
- * wValue: byte to write to output data port
- *
- * Answer: empty
- */
-
-#define GET_OUT_DATAPORT 6
-/**
- * Params: none
- *
- * Answer: least significant 3 bits read from output data port
- */
-
-#define GET_IN_DATAPORT 7
-/**
- * Params: none
- *
- * Answer: least significant 3 bits read from input data port
- */
-
-#define READ_EEPROM 8
-/**
- * Params:
- * wValue: offset to begin reading EEPROM
- *
- * Answer: EEPROM bytes
- */
-
-#define WRITE_EEPROM 9
-/**
- * Params:
- * wValue: offset to EEPROM byte
- * wIndex: byte to write
- *
- * Answer: empty
- */
-
-#define SEND_RS232 10
-/**
- * Params:
- * wValue: byte to send
- *
- * Answer: empty
- */
-
-#define RECV_RS232 11
-/**
- * Params: none
- *
- * Answer: byte received
- */
-
-#define SET_RS232_BAUD 12
-/**
- * Params:
- * wValue: byte to write to UART bit rate register (UBRR)
- *
- * Answer: empty
- */
-
-#define GET_RS232_BAUD 13
-/**
- * Params: none
- *
- * Answer: byte read from UART bit rate register (UBRR)
- */
-
-
-/* data structure for each usb remote */
-struct igorplug {
-
- /* usb */
- struct usb_device *usbdev;
- int devnum;
-
- unsigned char *buf_in;
- unsigned int len_in;
- int in_space;
- struct timeval last_time;
-
- dma_addr_t dma_in;
-
- /* lirc */
- struct lirc_driver *d;
-
- /* handle sending (init strings) */
- int send_flags;
-};
-
-static int unregister_from_lirc(struct igorplug *ir)
-{
- struct lirc_driver *d;
- int devnum;
-
- if (!ir) {
- printk(KERN_ERR "%s: called with NULL device struct!\n",
- __func__);
- return -EINVAL;
- }
-
- devnum = ir->devnum;
- d = ir->d;
-
- if (!d) {
- printk(KERN_ERR "%s: called with NULL lirc driver struct!\n",
- __func__);
- return -EINVAL;
- }
-
- dprintk(DRIVER_NAME "[%d]: calling lirc_unregister_driver\n", devnum);
- lirc_unregister_driver(d->minor);
-
- kfree(d);
- ir->d = NULL;
- kfree(ir);
-
- return devnum;
-}
-
-static int set_use_inc(void *data)
-{
- struct igorplug *ir = data;
-
- if (!ir) {
- printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
- return -EIO;
- }
-
- dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
-
- if (!ir->usbdev)
- return -ENODEV;
-
- return 0;
-}
-
-static void set_use_dec(void *data)
-{
- struct igorplug *ir = data;
-
- if (!ir) {
- printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
- return;
- }
-
- dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
-}
-
-static void send_fragment(struct igorplug *ir, struct lirc_buffer *buf,
- int i, int max)
-{
- int code;
-
- /* MODE2: pulse/space (PULSE_BIT) in 1us units */
- while (i < max) {
- /* 1 Igor-tick = 85.333333 us */
- code = (unsigned int)ir->buf_in[i] * 85 +
- (unsigned int)ir->buf_in[i] / 3;
- ir->last_time.tv_usec += code;
- if (ir->in_space)
- code |= PULSE_BIT;
- lirc_buffer_write(buf, (unsigned char *)&code);
- /* 1 chunk = CODE_LENGTH bytes */
- ir->in_space ^= 1;
- ++i;
- }
-}
-
-/**
- * Called in user context.
- * return 0 if data was added to the buffer and
- * -ENODATA if none was available. This should add some number of bits
- * evenly divisible by code_length to the buffer
- */
-static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf)
-{
- int ret;
- struct igorplug *ir = (struct igorplug *)data;
-
- if (!ir || !ir->usbdev) /* Has the device been removed? */
- return -ENODEV;
-
- memset(ir->buf_in, 0, ir->len_in);
-
- ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
- GET_INFRACODE, USB_TYPE_VENDOR | USB_DIR_IN,
- 0/* offset */, /*unused*/0,
- ir->buf_in, ir->len_in,
- /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
- if (ret > 0) {
- int code, timediff;
- struct timeval now;
-
- /* ACK packet has 1 byte --> ignore */
- if (ret < DEVICE_HEADERLEN)
- return -ENODATA;
-
- dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n",
- ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
-
- do_gettimeofday(&now);
- timediff = now.tv_sec - ir->last_time.tv_sec;
- if (timediff + 1 > PULSE_MASK / 1000000)
- timediff = PULSE_MASK;
- else {
- timediff *= 1000000;
- timediff += now.tv_usec - ir->last_time.tv_usec;
- }
- ir->last_time.tv_sec = now.tv_sec;
- ir->last_time.tv_usec = now.tv_usec;
-
- /* create leading gap */
- code = timediff;
- lirc_buffer_write(buf, (unsigned char *)&code);
- ir->in_space = 1; /* next comes a pulse */
-
- if (ir->buf_in[2] == 0)
- send_fragment(ir, buf, DEVICE_HEADERLEN, ret);
- else {
- printk(KERN_WARNING DRIVER_NAME
- "[%d]: Device buffer overrun.\n", ir->devnum);
- /* HHHNNNNNNNNNNNOOOOOOOO H = header
- <---[2]---> N = newer
- <---------ret--------> O = older */
- ir->buf_in[2] %= ret - DEVICE_HEADERLEN; /* sanitize */
- /* keep even-ness to not desync pulse/pause */
- send_fragment(ir, buf, DEVICE_HEADERLEN +
- ir->buf_in[2] - (ir->buf_in[2] & 1), ret);
- send_fragment(ir, buf, DEVICE_HEADERLEN,
- DEVICE_HEADERLEN + ir->buf_in[2]);
- }
-
- ret = usb_control_msg(
- ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
- SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
- /*unused*/0, /*unused*/0,
- /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
- /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
- if (ret < 0)
- printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: "
- "error %d\n", ir->devnum, ret);
- return 0;
- } else if (ret < 0)
- printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n",
- ir->devnum, ret);
-
- return -ENODATA;
-}
-
-
-
-static int igorplugusb_remote_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *dev = NULL;
- struct usb_host_interface *idesc = NULL;
- struct usb_endpoint_descriptor *ep;
- struct igorplug *ir = NULL;
- struct lirc_driver *driver = NULL;
- int devnum, pipe, maxp;
- int minor = 0;
- char buf[63], name[128] = "";
- int mem_failure = 0;
- int ret;
-
- dprintk(DRIVER_NAME ": usb probe called.\n");
-
- dev = interface_to_usbdev(intf);
-
- idesc = intf->cur_altsetting;
-
- if (idesc->desc.bNumEndpoints != 1)
- return -ENODEV;
-
- ep = &idesc->endpoint->desc;
- if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- != USB_DIR_IN)
- || (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- != USB_ENDPOINT_XFER_CONTROL)
- return -ENODEV;
-
- pipe = usb_rcvctrlpipe(dev, ep->bEndpointAddress);
- devnum = dev->devnum;
- maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
-
- dprintk(DRIVER_NAME "[%d]: bytes_in_key=%zu maxp=%d\n",
- devnum, CODE_LENGTH, maxp);
-
- mem_failure = 0;
- ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL);
- if (!ir) {
- mem_failure = 1;
- goto mem_failure_switch;
- }
- driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
- if (!driver) {
- mem_failure = 2;
- goto mem_failure_switch;
- }
-
- ir->buf_in = usb_alloc_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN,
- GFP_ATOMIC, &ir->dma_in);
- if (!ir->buf_in) {
- mem_failure = 3;
- goto mem_failure_switch;
- }
-
- strcpy(driver->name, DRIVER_NAME " ");
- driver->minor = -1;
- driver->code_length = CODE_LENGTH * 8; /* in bits */
- driver->features = LIRC_CAN_REC_MODE2;
- driver->data = ir;
- driver->chunk_size = CODE_LENGTH;
- driver->buffer_size = DEVICE_BUFLEN + ADDITIONAL_LIRC_BYTES;
- driver->set_use_inc = &set_use_inc;
- driver->set_use_dec = &set_use_dec;
- driver->sample_rate = sample_rate; /* per second */
- driver->add_to_buf = &igorplugusb_remote_poll;
- driver->dev = &intf->dev;
- driver->owner = THIS_MODULE;
-
- minor = lirc_register_driver(driver);
- if (minor < 0)
- mem_failure = 9;
-
-mem_failure_switch:
-
- switch (mem_failure) {
- case 9:
- usb_free_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN,
- ir->buf_in, ir->dma_in);
- case 3:
- kfree(driver);
- case 2:
- kfree(ir);
- case 1:
- printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
- devnum, mem_failure);
- return -ENOMEM;
- }
-
- driver->minor = minor;
- ir->d = driver;
- ir->devnum = devnum;
- ir->usbdev = dev;
- ir->len_in = DEVICE_BUFLEN + DEVICE_HEADERLEN;
- ir->in_space = 1; /* First mode2 event is a space. */
- do_gettimeofday(&ir->last_time);
-
- if (dev->descriptor.iManufacturer
- && usb_string(dev, dev->descriptor.iManufacturer,
- buf, sizeof(buf)) > 0)
- strlcpy(name, buf, sizeof(name));
- if (dev->descriptor.iProduct
- && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0)
- snprintf(name + strlen(name), sizeof(name) - strlen(name),
- " %s", buf);
- printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
- dev->bus->busnum, devnum);
-
- /* clear device buffer */
- ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
- SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
- /*unused*/0, /*unused*/0,
- /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
- /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
- if (ret < 0)
- printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n",
- devnum, ret);
-
- usb_set_intfdata(intf, ir);
- return 0;
-}
-
-
-static void igorplugusb_remote_disconnect(struct usb_interface *intf)
-{
- struct usb_device *usbdev = interface_to_usbdev(intf);
- struct igorplug *ir = usb_get_intfdata(intf);
- struct device *dev = &intf->dev;
- int devnum;
-
- usb_set_intfdata(intf, NULL);
-
- if (!ir || !ir->d)
- return;
-
- ir->usbdev = NULL;
-
- usb_free_coherent(usbdev, ir->len_in, ir->buf_in, ir->dma_in);
-
- devnum = unregister_from_lirc(ir);
-
- dev_info(dev, DRIVER_NAME "[%d]: %s done\n", devnum, __func__);
-}
-
-static struct usb_device_id igorplugusb_remote_id_table[] = {
- /* Igor Plug USB (Atmel's Manufact. ID) */
- { USB_DEVICE(0x03eb, 0x0002) },
- /* Fit PC2 Infrared Adapter */
- { USB_DEVICE(0x03eb, 0x21fe) },
-
- /* Terminating entry */
- { }
-};
-
-static struct usb_driver igorplugusb_remote_driver = {
- .name = DRIVER_NAME,
- .probe = igorplugusb_remote_probe,
- .disconnect = igorplugusb_remote_disconnect,
- .id_table = igorplugusb_remote_id_table
-};
-
-static int __init igorplugusb_remote_init(void)
-{
- int ret = 0;
-
- dprintk(DRIVER_NAME ": loaded, debug mode enabled\n");
-
- ret = usb_register(&igorplugusb_remote_driver);
- if (ret)
- printk(KERN_ERR DRIVER_NAME ": usb register failed!\n");
-
- return ret;
-}
-
-static void __exit igorplugusb_remote_exit(void)
-{
- usb_deregister(&igorplugusb_remote_driver);
-}
-
-module_init(igorplugusb_remote_init);
-module_exit(igorplugusb_remote_exit);
-
-#include <linux/vermagic.h>
-MODULE_INFO(vermagic, VERMAGIC_STRING);
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(usb, igorplugusb_remote_id_table);
-
-module_param(sample_rate, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
+++ /dev/null
-/*
- * lirc_imon.c: LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD
- * including the iMON PAD model
- *
- * Copyright(C) 2004 Venky Raju(dev@venky.ws)
- * Copyright(C) 2009 Jarod Wilson <jarod@wilsonet.com>
- *
- * lirc_imon 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/usb.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-
-#define MOD_AUTHOR "Venky Raju <dev@venky.ws>"
-#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display"
-#define MOD_NAME "lirc_imon"
-#define MOD_VERSION "0.8"
-
-#define DISPLAY_MINOR_BASE 144
-#define DEVICE_NAME "lcd%d"
-
-#define BUF_CHUNK_SIZE 4
-#define BUF_SIZE 128
-
-#define BIT_DURATION 250 /* each bit received is 250us */
-
-/*** P R O T O T Y P E S ***/
-
-/* USB Callback prototypes */
-static int imon_probe(struct usb_interface *interface,
- const struct usb_device_id *id);
-static void imon_disconnect(struct usb_interface *interface);
-static void usb_rx_callback(struct urb *urb);
-static void usb_tx_callback(struct urb *urb);
-
-/* suspend/resume support */
-static int imon_resume(struct usb_interface *intf);
-static int imon_suspend(struct usb_interface *intf, pm_message_t message);
-
-/* Display file_operations function prototypes */
-static int display_open(struct inode *inode, struct file *file);
-static int display_close(struct inode *inode, struct file *file);
-
-/* VFD write operation */
-static ssize_t vfd_write(struct file *file, const char *buf,
- size_t n_bytes, loff_t *pos);
-
-/* LIRC driver function prototypes */
-static int ir_open(void *data);
-static void ir_close(void *data);
-
-/* Driver init/exit prototypes */
-static int __init imon_init(void);
-static void __exit imon_exit(void);
-
-/*** G L O B A L S ***/
-#define IMON_DATA_BUF_SZ 35
-
-struct imon_context {
- struct usb_device *usbdev;
- /* Newer devices have two interfaces */
- int display; /* not all controllers do */
- int display_isopen; /* display port has been opened */
- int ir_isopen; /* IR port open */
- int dev_present; /* USB device presence */
- struct mutex ctx_lock; /* to lock this object */
- wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
-
- int vfd_proto_6p; /* some VFD require a 6th packet */
-
- struct lirc_driver *driver;
- struct usb_endpoint_descriptor *rx_endpoint;
- struct usb_endpoint_descriptor *tx_endpoint;
- struct urb *rx_urb;
- struct urb *tx_urb;
- unsigned char usb_rx_buf[8];
- unsigned char usb_tx_buf[8];
-
- struct rx_data {
- int count; /* length of 0 or 1 sequence */
- int prev_bit; /* logic level of sequence */
- int initial_space; /* initial space flag */
- } rx;
-
- struct tx_t {
- unsigned char data_buf[IMON_DATA_BUF_SZ]; /* user data buffer */
- struct completion finished; /* wait for write to finish */
- atomic_t busy; /* write in progress */
- int status; /* status of tx completion */
- } tx;
-};
-
-static const struct file_operations display_fops = {
- .owner = THIS_MODULE,
- .open = &display_open,
- .write = &vfd_write,
- .release = &display_close,
- .llseek = noop_llseek,
-};
-
-/*
- * USB Device ID for iMON USB Control Boards
- *
- * The Windows drivers contain 6 different inf files, more or less one for
- * each new device until the 0x0034-0x0046 devices, which all use the same
- * driver. Some of the devices in the 34-46 range haven't been definitively
- * identified yet. Early devices have either a TriGem Computer, Inc. or a
- * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later
- * devices use the SoundGraph vendor ID (0x15c2).
- */
-static struct usb_device_id imon_usb_id_table[] = {
- /* TriGem iMON (IR only) -- TG_iMON.inf */
- { USB_DEVICE(0x0aa8, 0x8001) },
-
- /* SoundGraph iMON (IR only) -- sg_imon.inf */
- { USB_DEVICE(0x04e8, 0xff30) },
-
- /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */
- { USB_DEVICE(0x0aa8, 0xffda) },
-
- /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */
- { USB_DEVICE(0x15c2, 0xffda) },
-
- {}
-};
-
-/* Some iMON VFD models requires a 6th packet for VFD writes */
-static struct usb_device_id vfd_proto_6p_list[] = {
- { USB_DEVICE(0x15c2, 0xffda) },
- {}
-};
-
-/* Some iMON devices have no lcd/vfd, don't set one up */
-static struct usb_device_id ir_only_list[] = {
- { USB_DEVICE(0x0aa8, 0x8001) },
- { USB_DEVICE(0x04e8, 0xff30) },
- {}
-};
-
-/* USB Device data */
-static struct usb_driver imon_driver = {
- .name = MOD_NAME,
- .probe = imon_probe,
- .disconnect = imon_disconnect,
- .suspend = imon_suspend,
- .resume = imon_resume,
- .id_table = imon_usb_id_table,
-};
-
-static struct usb_class_driver imon_class = {
- .name = DEVICE_NAME,
- .fops = &display_fops,
- .minor_base = DISPLAY_MINOR_BASE,
-};
-
-/* to prevent races between open() and disconnect(), probing, etc */
-static DEFINE_MUTEX(driver_lock);
-
-static int debug;
-
-/*** M O D U L E C O D E ***/
-
-MODULE_AUTHOR(MOD_AUTHOR);
-MODULE_DESCRIPTION(MOD_DESC);
-MODULE_VERSION(MOD_VERSION);
-MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
-module_param(debug, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
-
-static void free_imon_context(struct imon_context *context)
-{
- struct device *dev = context->driver->dev;
- usb_free_urb(context->tx_urb);
- usb_free_urb(context->rx_urb);
- lirc_buffer_free(context->driver->rbuf);
- kfree(context->driver->rbuf);
- kfree(context->driver);
- kfree(context);
-
- dev_dbg(dev, "%s: iMON context freed\n", __func__);
-}
-
-static void deregister_from_lirc(struct imon_context *context)
-{
- int retval;
- int minor = context->driver->minor;
-
- retval = lirc_unregister_driver(minor);
- if (retval)
- err("%s: unable to deregister from lirc(%d)",
- __func__, retval);
- else
- printk(KERN_INFO MOD_NAME ": Deregistered iMON driver "
- "(minor:%d)\n", minor);
-
-}
-
-/**
- * Called when the Display device (e.g. /dev/lcd0)
- * is opened by the application.
- */
-static int display_open(struct inode *inode, struct file *file)
-{
- struct usb_interface *interface;
- struct imon_context *context = NULL;
- int subminor;
- int retval = 0;
-
- /* prevent races with disconnect */
- mutex_lock(&driver_lock);
-
- subminor = iminor(inode);
- interface = usb_find_interface(&imon_driver, subminor);
- if (!interface) {
- err("%s: could not find interface for minor %d",
- __func__, subminor);
- retval = -ENODEV;
- goto exit;
- }
- context = usb_get_intfdata(interface);
-
- if (!context) {
- err("%s: no context found for minor %d",
- __func__, subminor);
- retval = -ENODEV;
- goto exit;
- }
-
- mutex_lock(&context->ctx_lock);
-
- if (!context->display) {
- err("%s: display not supported by device", __func__);
- retval = -ENODEV;
- } else if (context->display_isopen) {
- err("%s: display port is already open", __func__);
- retval = -EBUSY;
- } else {
- context->display_isopen = 1;
- file->private_data = context;
- dev_info(context->driver->dev, "display port opened\n");
- }
-
- mutex_unlock(&context->ctx_lock);
-
-exit:
- mutex_unlock(&driver_lock);
- return retval;
-}
-
-/**
- * Called when the display device (e.g. /dev/lcd0)
- * is closed by the application.
- */
-static int display_close(struct inode *inode, struct file *file)
-{
- struct imon_context *context = NULL;
- int retval = 0;
-
- context = file->private_data;
-
- if (!context) {
- err("%s: no context for device", __func__);
- return -ENODEV;
- }
-
- mutex_lock(&context->ctx_lock);
-
- if (!context->display) {
- err("%s: display not supported by device", __func__);
- retval = -ENODEV;
- } else if (!context->display_isopen) {
- err("%s: display is not open", __func__);
- retval = -EIO;
- } else {
- context->display_isopen = 0;
- dev_info(context->driver->dev, "display port closed\n");
- if (!context->dev_present && !context->ir_isopen) {
- /*
- * Device disconnected before close and IR port is not
- * open. If IR port is open, context will be deleted by
- * ir_close.
- */
- mutex_unlock(&context->ctx_lock);
- free_imon_context(context);
- return retval;
- }
- }
-
- mutex_unlock(&context->ctx_lock);
- return retval;
-}
-
-/**
- * Sends a packet to the device -- this function must be called
- * with context->ctx_lock held.
- */
-static int send_packet(struct imon_context *context)
-{
- unsigned int pipe;
- int interval = 0;
- int retval = 0;
-
- /* Check if we need to use control or interrupt urb */
- pipe = usb_sndintpipe(context->usbdev,
- context->tx_endpoint->bEndpointAddress);
- interval = context->tx_endpoint->bInterval;
-
- usb_fill_int_urb(context->tx_urb, context->usbdev, pipe,
- context->usb_tx_buf,
- sizeof(context->usb_tx_buf),
- usb_tx_callback, context, interval);
-
- context->tx_urb->actual_length = 0;
-
- init_completion(&context->tx.finished);
- atomic_set(&(context->tx.busy), 1);
-
- retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
- if (retval) {
- atomic_set(&(context->tx.busy), 0);
- err("%s: error submitting urb(%d)", __func__, retval);
- } else {
- /* Wait for transmission to complete (or abort) */
- mutex_unlock(&context->ctx_lock);
- retval = wait_for_completion_interruptible(
- &context->tx.finished);
- if (retval)
- err("%s: task interrupted", __func__);
- mutex_lock(&context->ctx_lock);
-
- retval = context->tx.status;
- if (retval)
- err("%s: packet tx failed (%d)", __func__, retval);
- }
-
- return retval;
-}
-
-/**
- * Writes data to the VFD. The iMON VFD is 2x16 characters
- * and requires data in 5 consecutive USB interrupt packets,
- * each packet but the last carrying 7 bytes.
- *
- * I don't know if the VFD board supports features such as
- * scrolling, clearing rows, blanking, etc. so at
- * the caller must provide a full screen of data. If fewer
- * than 32 bytes are provided spaces will be appended to
- * generate a full screen.
- */
-static ssize_t vfd_write(struct file *file, const char *buf,
- size_t n_bytes, loff_t *pos)
-{
- int i;
- int offset;
- int seq;
- int retval = 0;
- struct imon_context *context;
- const unsigned char vfd_packet6[] = {
- 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
- int *data_buf = NULL;
-
- context = file->private_data;
- if (!context) {
- err("%s: no context for device", __func__);
- return -ENODEV;
- }
-
- mutex_lock(&context->ctx_lock);
-
- if (!context->dev_present) {
- err("%s: no iMON device present", __func__);
- retval = -ENODEV;
- goto exit;
- }
-
- if (n_bytes <= 0 || n_bytes > IMON_DATA_BUF_SZ - 3) {
- err("%s: invalid payload size", __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- data_buf = memdup_user(buf, n_bytes);
- if (IS_ERR(data_buf)) {
- retval = PTR_ERR(data_buf);
- goto exit;
- }
-
- memcpy(context->tx.data_buf, data_buf, n_bytes);
-
- /* Pad with spaces */
- for (i = n_bytes; i < IMON_DATA_BUF_SZ - 3; ++i)
- context->tx.data_buf[i] = ' ';
-
- for (i = IMON_DATA_BUF_SZ - 3; i < IMON_DATA_BUF_SZ; ++i)
- context->tx.data_buf[i] = 0xFF;
-
- offset = 0;
- seq = 0;
-
- do {
- memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7);
- context->usb_tx_buf[7] = (unsigned char) seq;
-
- retval = send_packet(context);
- if (retval) {
- err("%s: send packet failed for packet #%d",
- __func__, seq/2);
- goto exit;
- } else {
- seq += 2;
- offset += 7;
- }
-
- } while (offset < IMON_DATA_BUF_SZ);
-
- if (context->vfd_proto_6p) {
- /* Send packet #6 */
- memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6));
- context->usb_tx_buf[7] = (unsigned char) seq;
- retval = send_packet(context);
- if (retval)
- err("%s: send packet failed for packet #%d",
- __func__, seq/2);
- }
-
-exit:
- mutex_unlock(&context->ctx_lock);
- kfree(data_buf);
-
- return (!retval) ? n_bytes : retval;
-}
-
-/**
- * Callback function for USB core API: transmit data
- */
-static void usb_tx_callback(struct urb *urb)
-{
- struct imon_context *context;
-
- if (!urb)
- return;
- context = (struct imon_context *)urb->context;
- if (!context)
- return;
-
- context->tx.status = urb->status;
-
- /* notify waiters that write has finished */
- atomic_set(&context->tx.busy, 0);
- complete(&context->tx.finished);
-
- return;
-}
-
-/**
- * Called by lirc_dev when the application opens /dev/lirc
- */
-static int ir_open(void *data)
-{
- int retval = 0;
- struct imon_context *context;
-
- /* prevent races with disconnect */
- mutex_lock(&driver_lock);
-
- context = (struct imon_context *)data;
-
- /* initial IR protocol decode variables */
- context->rx.count = 0;
- context->rx.initial_space = 1;
- context->rx.prev_bit = 0;
-
- context->ir_isopen = 1;
- dev_info(context->driver->dev, "IR port opened\n");
-
- mutex_unlock(&driver_lock);
- return retval;
-}
-
-/**
- * Called by lirc_dev when the application closes /dev/lirc
- */
-static void ir_close(void *data)
-{
- struct imon_context *context;
-
- context = (struct imon_context *)data;
- if (!context) {
- err("%s: no context for device", __func__);
- return;
- }
-
- mutex_lock(&context->ctx_lock);
-
- context->ir_isopen = 0;
- dev_info(context->driver->dev, "IR port closed\n");
-
- if (!context->dev_present) {
- /*
- * Device disconnected while IR port was still open. Driver
- * was not deregistered at disconnect time, so do it now.
- */
- deregister_from_lirc(context);
-
- if (!context->display_isopen) {
- mutex_unlock(&context->ctx_lock);
- free_imon_context(context);
- return;
- }
- /*
- * If display port is open, context will be deleted by
- * display_close
- */
- }
-
- mutex_unlock(&context->ctx_lock);
- return;
-}
-
-/**
- * Convert bit count to time duration (in us) and submit
- * the value to lirc_dev.
- */
-static void submit_data(struct imon_context *context)
-{
- unsigned char buf[4];
- int value = context->rx.count;
- int i;
-
- dev_dbg(context->driver->dev, "submitting data to LIRC\n");
-
- value *= BIT_DURATION;
- value &= PULSE_MASK;
- if (context->rx.prev_bit)
- value |= PULSE_BIT;
-
- for (i = 0; i < 4; ++i)
- buf[i] = value>>(i*8);
-
- lirc_buffer_write(context->driver->rbuf, buf);
- wake_up(&context->driver->rbuf->wait_poll);
- return;
-}
-
-static inline int tv2int(const struct timeval *a, const struct timeval *b)
-{
- int usecs = 0;
- int sec = 0;
-
- if (b->tv_usec > a->tv_usec) {
- usecs = 1000000;
- sec--;
- }
-
- usecs += a->tv_usec - b->tv_usec;
-
- sec += a->tv_sec - b->tv_sec;
- sec *= 1000;
- usecs /= 1000;
- sec += usecs;
-
- if (sec < 0)
- sec = 1000;
-
- return sec;
-}
-
-/**
- * Process the incoming packet
- */
-static void imon_incoming_packet(struct imon_context *context,
- struct urb *urb, int intf)
-{
- int len = urb->actual_length;
- unsigned char *buf = urb->transfer_buffer;
- struct device *dev = context->driver->dev;
- int octet, bit;
- unsigned char mask;
- int i;
-
- /*
- * just bail out if no listening IR client
- */
- if (!context->ir_isopen)
- return;
-
- if (len != 8) {
- dev_warn(dev, "imon %s: invalid incoming packet "
- "size (len = %d, intf%d)\n", __func__, len, intf);
- return;
- }
-
- if (debug) {
- printk(KERN_INFO "raw packet: ");
- for (i = 0; i < len; ++i)
- printk("%02x ", buf[i]);
- printk("\n");
- }
-
- /*
- * Translate received data to pulse and space lengths.
- * Received data is active low, i.e. pulses are 0 and
- * spaces are 1.
- *
- * My original algorithm was essentially similar to
- * Changwoo Ryu's with the exception that he switched
- * the incoming bits to active high and also fed an
- * initial space to LIRC at the start of a new sequence
- * if the previous bit was a pulse.
- *
- * I've decided to adopt his algorithm.
- */
-
- if (buf[7] == 1 && context->rx.initial_space) {
- /* LIRC requires a leading space */
- context->rx.prev_bit = 0;
- context->rx.count = 4;
- submit_data(context);
- context->rx.count = 0;
- }
-
- for (octet = 0; octet < 5; ++octet) {
- mask = 0x80;
- for (bit = 0; bit < 8; ++bit) {
- int curr_bit = !(buf[octet] & mask);
- if (curr_bit != context->rx.prev_bit) {
- if (context->rx.count) {
- submit_data(context);
- context->rx.count = 0;
- }
- context->rx.prev_bit = curr_bit;
- }
- ++context->rx.count;
- mask >>= 1;
- }
- }
-
- if (buf[7] == 10) {
- if (context->rx.count) {
- submit_data(context);
- context->rx.count = 0;
- }
- context->rx.initial_space = context->rx.prev_bit;
- }
-}
-
-/**
- * Callback function for USB core API: receive data
- */
-static void usb_rx_callback(struct urb *urb)
-{
- struct imon_context *context;
- int intfnum = 0;
-
- if (!urb)
- return;
-
- context = (struct imon_context *)urb->context;
- if (!context)
- return;
-
- switch (urb->status) {
- case -ENOENT: /* usbcore unlink successful! */
- return;
-
- case 0:
- imon_incoming_packet(context, urb, intfnum);
- break;
-
- default:
- dev_warn(context->driver->dev, "imon %s: status(%d): ignored\n",
- __func__, urb->status);
- break;
- }
-
- usb_submit_urb(context->rx_urb, GFP_ATOMIC);
-
- return;
-}
-
-/**
- * Callback function for USB core API: Probe
- */
-static int imon_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct usb_device *usbdev = NULL;
- struct usb_host_interface *iface_desc = NULL;
- struct usb_endpoint_descriptor *rx_endpoint = NULL;
- struct usb_endpoint_descriptor *tx_endpoint = NULL;
- struct urb *rx_urb = NULL;
- struct urb *tx_urb = NULL;
- struct lirc_driver *driver = NULL;
- struct lirc_buffer *rbuf = NULL;
- struct device *dev = &interface->dev;
- int ifnum;
- int lirc_minor = 0;
- int num_endpts;
- int retval = 0;
- int display_ep_found = 0;
- int ir_ep_found = 0;
- int alloc_status = 0;
- int vfd_proto_6p = 0;
- struct imon_context *context = NULL;
- int i;
- u16 vendor, product;
-
- /* prevent races probing devices w/multiple interfaces */
- mutex_lock(&driver_lock);
-
- context = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
- if (!context) {
- err("%s: kzalloc failed for context", __func__);
- alloc_status = 1;
- goto alloc_status_switch;
- }
-
- /*
- * Try to auto-detect the type of display if the user hasn't set
- * it by hand via the display_type modparam. Default is VFD.
- */
- if (usb_match_id(interface, ir_only_list))
- context->display = 0;
- else
- context->display = 1;
-
- usbdev = usb_get_dev(interface_to_usbdev(interface));
- iface_desc = interface->cur_altsetting;
- num_endpts = iface_desc->desc.bNumEndpoints;
- ifnum = iface_desc->desc.bInterfaceNumber;
- vendor = le16_to_cpu(usbdev->descriptor.idVendor);
- product = le16_to_cpu(usbdev->descriptor.idProduct);
-
- dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n",
- __func__, vendor, product, ifnum);
-
- /*
- * Scan the endpoint list and set:
- * first input endpoint = IR endpoint
- * first output endpoint = display endpoint
- */
- for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) {
- struct usb_endpoint_descriptor *ep;
- int ep_dir;
- int ep_type;
- ep = &iface_desc->endpoint[i].desc;
- ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
- ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
-
- if (!ir_ep_found &&
- ep_dir == USB_DIR_IN &&
- ep_type == USB_ENDPOINT_XFER_INT) {
-
- rx_endpoint = ep;
- ir_ep_found = 1;
- dev_dbg(dev, "%s: found IR endpoint\n", __func__);
-
- } else if (!display_ep_found && ep_dir == USB_DIR_OUT &&
- ep_type == USB_ENDPOINT_XFER_INT) {
- tx_endpoint = ep;
- display_ep_found = 1;
- dev_dbg(dev, "%s: found display endpoint\n", __func__);
- }
- }
-
- /*
- * Some iMON receivers have no display. Unfortunately, it seems
- * that SoundGraph recycles device IDs between devices both with
- * and without... :\
- */
- if (context->display == 0) {
- display_ep_found = 0;
- dev_dbg(dev, "%s: device has no display\n", __func__);
- }
-
- /* Input endpoint is mandatory */
- if (!ir_ep_found) {
- err("%s: no valid input (IR) endpoint found.", __func__);
- retval = -ENODEV;
- alloc_status = 2;
- goto alloc_status_switch;
- }
-
- /* Determine if display requires 6 packets */
- if (display_ep_found) {
- if (usb_match_id(interface, vfd_proto_6p_list))
- vfd_proto_6p = 1;
-
- dev_dbg(dev, "%s: vfd_proto_6p: %d\n",
- __func__, vfd_proto_6p);
- }
-
- driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
- if (!driver) {
- err("%s: kzalloc failed for lirc_driver", __func__);
- alloc_status = 2;
- goto alloc_status_switch;
- }
- rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
- if (!rbuf) {
- err("%s: kmalloc failed for lirc_buffer", __func__);
- alloc_status = 3;
- goto alloc_status_switch;
- }
- if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
- err("%s: lirc_buffer_init failed", __func__);
- alloc_status = 4;
- goto alloc_status_switch;
- }
- rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!rx_urb) {
- err("%s: usb_alloc_urb failed for IR urb", __func__);
- alloc_status = 5;
- goto alloc_status_switch;
- }
- tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!tx_urb) {
- err("%s: usb_alloc_urb failed for display urb",
- __func__);
- alloc_status = 6;
- goto alloc_status_switch;
- }
-
- mutex_init(&context->ctx_lock);
- context->vfd_proto_6p = vfd_proto_6p;
-
- strcpy(driver->name, MOD_NAME);
- driver->minor = -1;
- driver->code_length = BUF_CHUNK_SIZE * 8;
- driver->sample_rate = 0;
- driver->features = LIRC_CAN_REC_MODE2;
- driver->data = context;
- driver->rbuf = rbuf;
- driver->set_use_inc = ir_open;
- driver->set_use_dec = ir_close;
- driver->dev = &interface->dev;
- driver->owner = THIS_MODULE;
-
- mutex_lock(&context->ctx_lock);
-
- context->driver = driver;
- /* start out in keyboard mode */
-
- lirc_minor = lirc_register_driver(driver);
- if (lirc_minor < 0) {
- err("%s: lirc_register_driver failed", __func__);
- alloc_status = 7;
- goto unlock;
- } else
- dev_info(dev, "Registered iMON driver "
- "(lirc minor: %d)\n", lirc_minor);
-
- /* Needed while unregistering! */
- driver->minor = lirc_minor;
-
- context->usbdev = usbdev;
- context->dev_present = 1;
- context->rx_endpoint = rx_endpoint;
- context->rx_urb = rx_urb;
-
- /*
- * tx is used to send characters to lcd/vfd, associate RF
- * remotes, set IR protocol, and maybe more...
- */
- context->tx_endpoint = tx_endpoint;
- context->tx_urb = tx_urb;
-
- if (display_ep_found)
- context->display = 1;
-
- usb_fill_int_urb(context->rx_urb, context->usbdev,
- usb_rcvintpipe(context->usbdev,
- context->rx_endpoint->bEndpointAddress),
- context->usb_rx_buf, sizeof(context->usb_rx_buf),
- usb_rx_callback, context,
- context->rx_endpoint->bInterval);
-
- retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
-
- if (retval) {
- err("%s: usb_submit_urb failed for intf0 (%d)",
- __func__, retval);
- mutex_unlock(&context->ctx_lock);
- goto exit;
- }
-
- usb_set_intfdata(interface, context);
-
- if (context->display && ifnum == 0) {
- dev_dbg(dev, "%s: Registering iMON display with sysfs\n",
- __func__);
-
- if (usb_register_dev(interface, &imon_class)) {
- /* Not a fatal error, so ignore */
- dev_info(dev, "%s: could not get a minor number for "
- "display\n", __func__);
- }
- }
-
- dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
- "usb<%d:%d> initialized\n", vendor, product, ifnum,
- usbdev->bus->busnum, usbdev->devnum);
-
-unlock:
- mutex_unlock(&context->ctx_lock);
-alloc_status_switch:
-
- switch (alloc_status) {
- case 7:
- usb_free_urb(tx_urb);
- case 6:
- usb_free_urb(rx_urb);
- case 5:
- if (rbuf)
- lirc_buffer_free(rbuf);
- case 4:
- kfree(rbuf);
- case 3:
- kfree(driver);
- case 2:
- kfree(context);
- context = NULL;
- case 1:
- if (retval != -ENODEV)
- retval = -ENOMEM;
- break;
- case 0:
- retval = 0;
- }
-
-exit:
- mutex_unlock(&driver_lock);
-
- return retval;
-}
-
-/**
- * Callback function for USB core API: disconnect
- */
-static void imon_disconnect(struct usb_interface *interface)
-{
- struct imon_context *context;
- int ifnum;
-
- /* prevent races with ir_open()/display_open() */
- mutex_lock(&driver_lock);
-
- context = usb_get_intfdata(interface);
- ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
-
- mutex_lock(&context->ctx_lock);
-
- usb_set_intfdata(interface, NULL);
-
- /* Abort ongoing write */
- if (atomic_read(&context->tx.busy)) {
- usb_kill_urb(context->tx_urb);
- complete_all(&context->tx.finished);
- }
-
- context->dev_present = 0;
- usb_kill_urb(context->rx_urb);
- if (context->display)
- usb_deregister_dev(interface, &imon_class);
-
- if (!context->ir_isopen && !context->dev_present) {
- deregister_from_lirc(context);
- mutex_unlock(&context->ctx_lock);
- if (!context->display_isopen)
- free_imon_context(context);
- } else
- mutex_unlock(&context->ctx_lock);
-
- mutex_unlock(&driver_lock);
-
- printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n",
- __func__, ifnum);
-}
-
-static int imon_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct imon_context *context = usb_get_intfdata(intf);
-
- usb_kill_urb(context->rx_urb);
-
- return 0;
-}
-
-static int imon_resume(struct usb_interface *intf)
-{
- int rc = 0;
- struct imon_context *context = usb_get_intfdata(intf);
-
- usb_fill_int_urb(context->rx_urb, context->usbdev,
- usb_rcvintpipe(context->usbdev,
- context->rx_endpoint->bEndpointAddress),
- context->usb_rx_buf, sizeof(context->usb_rx_buf),
- usb_rx_callback, context,
- context->rx_endpoint->bInterval);
-
- rc = usb_submit_urb(context->rx_urb, GFP_ATOMIC);
-
- return rc;
-}
-
-static int __init imon_init(void)
-{
- int rc;
-
- printk(KERN_INFO MOD_NAME ": " MOD_DESC ", v" MOD_VERSION "\n");
-
- rc = usb_register(&imon_driver);
- if (rc) {
- err("%s: usb register failed(%d)", __func__, rc);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void __exit imon_exit(void)
-{
- usb_deregister(&imon_driver);
- printk(KERN_INFO MOD_NAME ": module removed. Goodbye!\n");
-}
-
-module_init(imon_init);
-module_exit(imon_exit);
+++ /dev/null
-/*
- * lirc_parallel.c
- *
- * lirc_parallel - device driver for infra-red signal receiving and
- * transmitting unit built by the author
- *
- * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
- *
- * 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
- *
- */
-
-/*** Includes ***/
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/time.h>
-#include <linux/mm.h>
-#include <linux/delay.h>
-
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/uaccess.h>
-#include <asm/div64.h>
-
-#include <linux/poll.h>
-#include <linux/parport.h>
-#include <linux/platform_device.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-#include "lirc_parallel.h"
-
-#define LIRC_DRIVER_NAME "lirc_parallel"
-
-#ifndef LIRC_IRQ
-#define LIRC_IRQ 7
-#endif
-#ifndef LIRC_PORT
-#define LIRC_PORT 0x378
-#endif
-#ifndef LIRC_TIMER
-#define LIRC_TIMER 65536
-#endif
-
-/*** Global Variables ***/
-
-static int debug;
-static int check_pselecd;
-
-unsigned int irq = LIRC_IRQ;
-unsigned int io = LIRC_PORT;
-#ifdef LIRC_TIMER
-unsigned int timer;
-unsigned int default_timer = LIRC_TIMER;
-#endif
-
-#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
-
-static int rbuf[RBUF_SIZE];
-
-DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
-
-unsigned int rptr;
-unsigned int wptr;
-unsigned int lost_irqs;
-int is_open;
-
-struct parport *pport;
-struct pardevice *ppdevice;
-int is_claimed;
-
-unsigned int tx_mask = 1;
-
-/*** Internal Functions ***/
-
-static unsigned int in(int offset)
-{
- switch (offset) {
- case LIRC_LP_BASE:
- return parport_read_data(pport);
- case LIRC_LP_STATUS:
- return parport_read_status(pport);
- case LIRC_LP_CONTROL:
- return parport_read_control(pport);
- }
- return 0; /* make compiler happy */
-}
-
-static void out(int offset, int value)
-{
- switch (offset) {
- case LIRC_LP_BASE:
- parport_write_data(pport, value);
- break;
- case LIRC_LP_CONTROL:
- parport_write_control(pport, value);
- break;
- case LIRC_LP_STATUS:
- printk(KERN_INFO "%s: attempt to write to status register\n",
- LIRC_DRIVER_NAME);
- break;
- }
-}
-
-static unsigned int lirc_get_timer(void)
-{
- return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
-}
-
-static unsigned int lirc_get_signal(void)
-{
- return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
-}
-
-static void lirc_on(void)
-{
- out(LIRC_PORT_DATA, tx_mask);
-}
-
-static void lirc_off(void)
-{
- out(LIRC_PORT_DATA, 0);
-}
-
-static unsigned int init_lirc_timer(void)
-{
- struct timeval tv, now;
- unsigned int level, newlevel, timeelapsed, newtimer;
- int count = 0;
-
- do_gettimeofday(&tv);
- tv.tv_sec++; /* wait max. 1 sec. */
- level = lirc_get_timer();
- do {
- newlevel = lirc_get_timer();
- if (level == 0 && newlevel != 0)
- count++;
- level = newlevel;
- do_gettimeofday(&now);
- } while (count < 1000 && (now.tv_sec < tv.tv_sec
- || (now.tv_sec == tv.tv_sec
- && now.tv_usec < tv.tv_usec)));
-
- timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000
- + (now.tv_usec - tv.tv_usec));
- if (count >= 1000 && timeelapsed > 0) {
- if (default_timer == 0) {
- /* autodetect timer */
- newtimer = (1000000*count)/timeelapsed;
- printk(KERN_INFO "%s: %u Hz timer detected\n",
- LIRC_DRIVER_NAME, newtimer);
- return newtimer;
- } else {
- newtimer = (1000000*count)/timeelapsed;
- if (abs(newtimer - default_timer) > default_timer/10) {
- /* bad timer */
- printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
- LIRC_DRIVER_NAME, newtimer);
- printk(KERN_NOTICE "%s: using default timer: "
- "%u Hz\n",
- LIRC_DRIVER_NAME, default_timer);
- return default_timer;
- } else {
- printk(KERN_INFO "%s: %u Hz timer detected\n",
- LIRC_DRIVER_NAME, newtimer);
- return newtimer; /* use detected value */
- }
- }
- } else {
- printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME);
- return 0;
- }
-}
-
-static int lirc_claim(void)
-{
- if (parport_claim(ppdevice) != 0) {
- printk(KERN_WARNING "%s: could not claim port\n",
- LIRC_DRIVER_NAME);
- printk(KERN_WARNING "%s: waiting for port becoming available"
- "\n", LIRC_DRIVER_NAME);
- if (parport_claim_or_block(ppdevice) < 0) {
- printk(KERN_NOTICE "%s: could not claim port, giving"
- " up\n", LIRC_DRIVER_NAME);
- return 0;
- }
- }
- out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
- is_claimed = 1;
- return 1;
-}
-
-/*** interrupt handler ***/
-
-static void rbuf_write(int signal)
-{
- unsigned int nwptr;
-
- nwptr = (wptr + 1) & (RBUF_SIZE - 1);
- if (nwptr == rptr) {
- /* no new signals will be accepted */
- lost_irqs++;
- printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME);
- return;
- }
- rbuf[wptr] = signal;
- wptr = nwptr;
-}
-
-static void irq_handler(void *blah)
-{
- struct timeval tv;
- static struct timeval lasttv;
- static int init;
- long signal;
- int data;
- unsigned int level, newlevel;
- unsigned int timeout;
-
- if (!is_open)
- return;
-
- if (!is_claimed)
- return;
-
-#if 0
- /* disable interrupt */
- disable_irq(irq);
- out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
-#endif
- if (check_pselecd && (in(1) & LP_PSELECD))
- return;
-
-#ifdef LIRC_TIMER
- if (init) {
- do_gettimeofday(&tv);
-
- signal = tv.tv_sec - lasttv.tv_sec;
- if (signal > 15)
- /* really long time */
- data = PULSE_MASK;
- else
- data = (int) (signal*1000000 +
- tv.tv_usec - lasttv.tv_usec +
- LIRC_SFH506_DELAY);
-
- rbuf_write(data); /* space */
- } else {
- if (timer == 0) {
- /*
- * wake up; we'll lose this signal, but it will be
- * garbage if the device is turned on anyway
- */
- timer = init_lirc_timer();
- /* enable_irq(irq); */
- return;
- }
- init = 1;
- }
-
- timeout = timer/10; /* timeout after 1/10 sec. */
- signal = 1;
- level = lirc_get_timer();
- do {
- newlevel = lirc_get_timer();
- if (level == 0 && newlevel != 0)
- signal++;
- level = newlevel;
-
- /* giving up */
- if (signal > timeout
- || (check_pselecd && (in(1) & LP_PSELECD))) {
- signal = 0;
- printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME);
- break;
- }
- } while (lirc_get_signal());
-
- if (signal != 0) {
- /* adjust value to usecs */
- __u64 helper;
-
- helper = ((__u64) signal)*1000000;
- do_div(helper, timer);
- signal = (long) helper;
-
- if (signal > LIRC_SFH506_DELAY)
- data = signal - LIRC_SFH506_DELAY;
- else
- data = 1;
- rbuf_write(PULSE_BIT|data); /* pulse */
- }
- do_gettimeofday(&lasttv);
-#else
- /* add your code here */
-#endif
-
- wake_up_interruptible(&lirc_wait);
-
- /* enable interrupt */
- /*
- enable_irq(irq);
- out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
- */
-}
-
-/*** file operations ***/
-
-static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig)
-{
- return -ESPIPE;
-}
-
-static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos)
-{
- int result = 0;
- int count = 0;
- DECLARE_WAITQUEUE(wait, current);
-
- if (n % sizeof(int))
- return -EINVAL;
-
- add_wait_queue(&lirc_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- while (count < n) {
- if (rptr != wptr) {
- if (copy_to_user(buf+count, (char *) &rbuf[rptr],
- sizeof(int))) {
- result = -EFAULT;
- break;
- }
- rptr = (rptr + 1) & (RBUF_SIZE - 1);
- count += sizeof(int);
- } else {
- if (filep->f_flags & O_NONBLOCK) {
- result = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- result = -ERESTARTSYS;
- break;
- }
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- }
- remove_wait_queue(&lirc_wait, &wait);
- set_current_state(TASK_RUNNING);
- return count ? count : result;
-}
-
-static ssize_t lirc_write(struct file *filep, const char *buf, size_t n,
- loff_t *ppos)
-{
- int count;
- unsigned int i;
- unsigned int level, newlevel;
- unsigned long flags;
- int counttimer;
- int *wbuf;
- ssize_t ret;
-
- if (!is_claimed)
- return -EBUSY;
-
- count = n / sizeof(int);
-
- if (n % sizeof(int) || count % 2 == 0)
- return -EINVAL;
-
- wbuf = memdup_user(buf, n);
- if (IS_ERR(wbuf))
- return PTR_ERR(wbuf);
-
-#ifdef LIRC_TIMER
- if (timer == 0) {
- /* try again if device is ready */
- timer = init_lirc_timer();
- if (timer == 0) {
- ret = -EIO;
- goto out;
- }
- }
-
- /* adjust values from usecs */
- for (i = 0; i < count; i++) {
- __u64 helper;
-
- helper = ((__u64) wbuf[i])*timer;
- do_div(helper, 1000000);
- wbuf[i] = (int) helper;
- }
-
- local_irq_save(flags);
- i = 0;
- while (i < count) {
- level = lirc_get_timer();
- counttimer = 0;
- lirc_on();
- do {
- newlevel = lirc_get_timer();
- if (level == 0 && newlevel != 0)
- counttimer++;
- level = newlevel;
- if (check_pselecd && (in(1) & LP_PSELECD)) {
- lirc_off();
- local_irq_restore(flags);
- ret = -EIO;
- goto out;
- }
- } while (counttimer < wbuf[i]);
- i++;
-
- lirc_off();
- if (i == count)
- break;
- counttimer = 0;
- do {
- newlevel = lirc_get_timer();
- if (level == 0 && newlevel != 0)
- counttimer++;
- level = newlevel;
- if (check_pselecd && (in(1) & LP_PSELECD)) {
- local_irq_restore(flags);
- ret = -EIO;
- goto out;
- }
- } while (counttimer < wbuf[i]);
- i++;
- }
- local_irq_restore(flags);
-#else
- /* place code that handles write without external timer here */
-#endif
- ret = n;
-out:
- kfree(wbuf);
-
- return ret;
-}
-
-static unsigned int lirc_poll(struct file *file, poll_table *wait)
-{
- poll_wait(file, &lirc_wait, wait);
- if (rptr != wptr)
- return POLLIN | POLLRDNORM;
- return 0;
-}
-
-static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
-{
- int result;
- __u32 features = LIRC_CAN_SET_TRANSMITTER_MASK |
- LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
- __u32 mode;
- __u32 value;
-
- switch (cmd) {
- case LIRC_GET_FEATURES:
- result = put_user(features, (__u32 *) arg);
- if (result)
- return result;
- break;
- case LIRC_GET_SEND_MODE:
- result = put_user(LIRC_MODE_PULSE, (__u32 *) arg);
- if (result)
- return result;
- break;
- case LIRC_GET_REC_MODE:
- result = put_user(LIRC_MODE_MODE2, (__u32 *) arg);
- if (result)
- return result;
- break;
- case LIRC_SET_SEND_MODE:
- result = get_user(mode, (__u32 *) arg);
- if (result)
- return result;
- if (mode != LIRC_MODE_PULSE)
- return -EINVAL;
- break;
- case LIRC_SET_REC_MODE:
- result = get_user(mode, (__u32 *) arg);
- if (result)
- return result;
- if (mode != LIRC_MODE_MODE2)
- return -ENOSYS;
- break;
- case LIRC_SET_TRANSMITTER_MASK:
- result = get_user(value, (__u32 *) arg);
- if (result)
- return result;
- if ((value & LIRC_PARALLEL_TRANSMITTER_MASK) != value)
- return LIRC_PARALLEL_MAX_TRANSMITTERS;
- tx_mask = value;
- break;
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static int lirc_open(struct inode *node, struct file *filep)
-{
- if (is_open || !lirc_claim())
- return -EBUSY;
-
- parport_enable_irq(pport);
-
- /* init read ptr */
- rptr = 0;
- wptr = 0;
- lost_irqs = 0;
-
- is_open = 1;
- return 0;
-}
-
-static int lirc_close(struct inode *node, struct file *filep)
-{
- if (is_claimed) {
- is_claimed = 0;
- parport_release(ppdevice);
- }
- is_open = 0;
- return 0;
-}
-
-static const struct file_operations lirc_fops = {
- .owner = THIS_MODULE,
- .llseek = lirc_lseek,
- .read = lirc_read,
- .write = lirc_write,
- .poll = lirc_poll,
- .unlocked_ioctl = lirc_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = lirc_ioctl,
-#endif
- .open = lirc_open,
- .release = lirc_close
-};
-
-static int set_use_inc(void *data)
-{
- return 0;
-}
-
-static void set_use_dec(void *data)
-{
-}
-
-static struct lirc_driver driver = {
- .name = LIRC_DRIVER_NAME,
- .minor = -1,
- .code_length = 1,
- .sample_rate = 0,
- .data = NULL,
- .add_to_buf = NULL,
- .set_use_inc = set_use_inc,
- .set_use_dec = set_use_dec,
- .fops = &lirc_fops,
- .dev = NULL,
- .owner = THIS_MODULE,
-};
-
-static struct platform_device *lirc_parallel_dev;
-
-static int __devinit lirc_parallel_probe(struct platform_device *dev)
-{
- return 0;
-}
-
-static int __devexit lirc_parallel_remove(struct platform_device *dev)
-{
- return 0;
-}
-
-static int lirc_parallel_suspend(struct platform_device *dev,
- pm_message_t state)
-{
- return 0;
-}
-
-static int lirc_parallel_resume(struct platform_device *dev)
-{
- return 0;
-}
-
-static struct platform_driver lirc_parallel_driver = {
- .probe = lirc_parallel_probe,
- .remove = __devexit_p(lirc_parallel_remove),
- .suspend = lirc_parallel_suspend,
- .resume = lirc_parallel_resume,
- .driver = {
- .name = LIRC_DRIVER_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-static int pf(void *handle)
-{
- parport_disable_irq(pport);
- is_claimed = 0;
- return 0;
-}
-
-static void kf(void *handle)
-{
- if (!is_open)
- return;
- if (!lirc_claim())
- return;
- parport_enable_irq(pport);
- lirc_off();
- /* this is a bit annoying when you actually print...*/
- /*
- printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
- */
-}
-
-/*** module initialization and cleanup ***/
-
-static int __init lirc_parallel_init(void)
-{
- int result;
-
- result = platform_driver_register(&lirc_parallel_driver);
- if (result) {
- printk(KERN_NOTICE "platform_driver_register"
- " returned %d\n", result);
- return result;
- }
-
- lirc_parallel_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
- if (!lirc_parallel_dev) {
- result = -ENOMEM;
- goto exit_driver_unregister;
- }
-
- result = platform_device_add(lirc_parallel_dev);
- if (result)
- goto exit_device_put;
-
- pport = parport_find_base(io);
- if (pport == NULL) {
- printk(KERN_NOTICE "%s: no port at %x found\n",
- LIRC_DRIVER_NAME, io);
- result = -ENXIO;
- goto exit_device_put;
- }
- ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME,
- pf, kf, irq_handler, 0, NULL);
- parport_put_port(pport);
- if (ppdevice == NULL) {
- printk(KERN_NOTICE "%s: parport_register_device() failed\n",
- LIRC_DRIVER_NAME);
- result = -ENXIO;
- goto exit_device_put;
- }
- if (parport_claim(ppdevice) != 0)
- goto skip_init;
- is_claimed = 1;
- out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
-
-#ifdef LIRC_TIMER
- if (debug)
- out(LIRC_PORT_DATA, tx_mask);
-
- timer = init_lirc_timer();
-
-#if 0 /* continue even if device is offline */
- if (timer == 0) {
- is_claimed = 0;
- parport_release(pport);
- parport_unregister_device(ppdevice);
- result = -EIO;
- goto exit_device_put;
- }
-
-#endif
- if (debug)
- out(LIRC_PORT_DATA, 0);
-#endif
-
- is_claimed = 0;
- parport_release(ppdevice);
- skip_init:
- driver.dev = &lirc_parallel_dev->dev;
- driver.minor = lirc_register_driver(&driver);
- if (driver.minor < 0) {
- printk(KERN_NOTICE "%s: register_chrdev() failed\n",
- LIRC_DRIVER_NAME);
- parport_unregister_device(ppdevice);
- result = -EIO;
- goto exit_device_put;
- }
- printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n",
- LIRC_DRIVER_NAME, io, irq);
- return 0;
-
-exit_device_put:
- platform_device_put(lirc_parallel_dev);
-exit_driver_unregister:
- platform_driver_unregister(&lirc_parallel_driver);
- return result;
-}
-
-static void __exit lirc_parallel_exit(void)
-{
- parport_unregister_device(ppdevice);
- lirc_unregister_driver(driver.minor);
-
- platform_device_unregister(lirc_parallel_dev);
- platform_driver_unregister(&lirc_parallel_driver);
-}
-
-module_init(lirc_parallel_init);
-module_exit(lirc_parallel_exit);
-
-MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
-MODULE_AUTHOR("Christoph Bartelmus");
-MODULE_LICENSE("GPL");
-
-module_param(io, int, S_IRUGO);
-MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
-
-module_param(irq, int, S_IRUGO);
-MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
-
-module_param(tx_mask, int, S_IRUGO);
-MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging messages");
-
-module_param(check_pselecd, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Check for printer (default: 0)");
+++ /dev/null
-/* lirc_parallel.h */
-
-#ifndef _LIRC_PARALLEL_H
-#define _LIRC_PARALLEL_H
-
-#include <linux/lp.h>
-
-#define LIRC_PORT_LEN 3
-
-#define LIRC_LP_BASE 0
-#define LIRC_LP_STATUS 1
-#define LIRC_LP_CONTROL 2
-
-#define LIRC_PORT_DATA LIRC_LP_BASE /* base */
-#define LIRC_PORT_TIMER LIRC_LP_STATUS /* status port */
-#define LIRC_PORT_TIMER_BIT LP_PBUSY /* busy signal */
-#define LIRC_PORT_SIGNAL LIRC_LP_STATUS /* status port */
-#define LIRC_PORT_SIGNAL_BIT LP_PACK /* ack signal */
-#define LIRC_PORT_IRQ LIRC_LP_CONTROL /* control port */
-
-#define LIRC_SFH506_DELAY 0 /* delay t_phl in usecs */
-
-#define LIRC_PARALLEL_MAX_TRANSMITTERS 8
-#define LIRC_PARALLEL_TRANSMITTER_MASK ((1<<LIRC_PARALLEL_MAX_TRANSMITTERS) - 1)
-
-#endif
+++ /dev/null
-/*
- * lirc_sasem.c - USB remote support for LIRC
- * Version 0.5
- *
- * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
- * Tim Davies <tim@opensystems.net.au>
- *
- * This driver was derived from:
- * Venky Raju <dev@venky.ws>
- * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
- * Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
- * "lirc_atiusb - USB remote support for LIRC"
- * Culver Consulting Services <henry@culcon.com>'s 2003
- * "Sasem OnAir VFD/IR USB driver"
- *
- *
- * NOTE - The LCDproc iMon driver should work with this module. More info at
- * http://www.frogstorm.info/sasem
- */
-
-/*
- * 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/errno.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/usb.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-
-#define MOD_AUTHOR "Oliver Stabel <oliver.stabel@gmx.de>, " \
- "Tim Davies <tim@opensystems.net.au>"
-#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1"
-#define MOD_NAME "lirc_sasem"
-#define MOD_VERSION "0.5"
-
-#define VFD_MINOR_BASE 144 /* Same as LCD */
-#define DEVICE_NAME "lcd%d"
-
-#define BUF_CHUNK_SIZE 8
-#define BUF_SIZE 128
-
-#define IOCTL_LCD_CONTRAST 1
-
-/*** P R O T O T Y P E S ***/
-
-/* USB Callback prototypes */
-static int sasem_probe(struct usb_interface *interface,
- const struct usb_device_id *id);
-static void sasem_disconnect(struct usb_interface *interface);
-static void usb_rx_callback(struct urb *urb);
-static void usb_tx_callback(struct urb *urb);
-
-/* VFD file_operations function prototypes */
-static int vfd_open(struct inode *inode, struct file *file);
-static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg);
-static int vfd_close(struct inode *inode, struct file *file);
-static ssize_t vfd_write(struct file *file, const char *buf,
- size_t n_bytes, loff_t *pos);
-
-/* LIRC driver function prototypes */
-static int ir_open(void *data);
-static void ir_close(void *data);
-
-/* Driver init/exit prototypes */
-static int __init sasem_init(void);
-static void __exit sasem_exit(void);
-
-/*** G L O B A L S ***/
-#define SASEM_DATA_BUF_SZ 32
-
-struct sasem_context {
-
- struct usb_device *dev;
- int vfd_isopen; /* VFD port has been opened */
- unsigned int vfd_contrast; /* VFD contrast */
- int ir_isopen; /* IR port has been opened */
- int dev_present; /* USB device presence */
- struct mutex ctx_lock; /* to lock this object */
- wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
-
- struct lirc_driver *driver;
- struct usb_endpoint_descriptor *rx_endpoint;
- struct usb_endpoint_descriptor *tx_endpoint;
- struct urb *rx_urb;
- struct urb *tx_urb;
- unsigned char usb_rx_buf[8];
- unsigned char usb_tx_buf[8];
-
- struct tx_t {
- unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data buffer */
- struct completion finished; /* wait for write to finish */
- atomic_t busy; /* write in progress */
- int status; /* status of tx completion */
- } tx;
-
- /* for dealing with repeat codes (wish there was a toggle bit!) */
- struct timeval presstime;
- char lastcode[8];
- int codesaved;
-};
-
-/* VFD file operations */
-static const struct file_operations vfd_fops = {
- .owner = THIS_MODULE,
- .open = &vfd_open,
- .write = &vfd_write,
- .unlocked_ioctl = &vfd_ioctl,
- .release = &vfd_close,
- .llseek = noop_llseek,
-};
-
-/* USB Device ID for Sasem USB Control Board */
-static struct usb_device_id sasem_usb_id_table[] = {
- /* Sasem USB Control Board */
- { USB_DEVICE(0x11ba, 0x0101) },
- /* Terminating entry */
- {}
-};
-
-/* USB Device data */
-static struct usb_driver sasem_driver = {
- .name = MOD_NAME,
- .probe = sasem_probe,
- .disconnect = sasem_disconnect,
- .id_table = sasem_usb_id_table,
-};
-
-static struct usb_class_driver sasem_class = {
- .name = DEVICE_NAME,
- .fops = &vfd_fops,
- .minor_base = VFD_MINOR_BASE,
-};
-
-/* to prevent races between open() and disconnect() */
-static DEFINE_MUTEX(disconnect_lock);
-
-static int debug;
-
-
-/*** M O D U L E C O D E ***/
-
-MODULE_AUTHOR(MOD_AUTHOR);
-MODULE_DESCRIPTION(MOD_DESC);
-MODULE_LICENSE("GPL");
-module_param(debug, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
-
-static void delete_context(struct sasem_context *context)
-{
- usb_free_urb(context->tx_urb); /* VFD */
- usb_free_urb(context->rx_urb); /* IR */
- lirc_buffer_free(context->driver->rbuf);
- kfree(context->driver->rbuf);
- kfree(context->driver);
- kfree(context);
-
- if (debug)
- printk(KERN_INFO "%s: context deleted\n", __func__);
-}
-
-static void deregister_from_lirc(struct sasem_context *context)
-{
- int retval;
- int minor = context->driver->minor;
-
- retval = lirc_unregister_driver(minor);
- if (retval)
- err("%s: unable to deregister from lirc (%d)",
- __func__, retval);
- else
- printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n",
- minor);
-
-}
-
-/**
- * Called when the VFD device (e.g. /dev/usb/lcd)
- * is opened by the application.
- */
-static int vfd_open(struct inode *inode, struct file *file)
-{
- struct usb_interface *interface;
- struct sasem_context *context = NULL;
- int subminor;
- int retval = 0;
-
- /* prevent races with disconnect */
- mutex_lock(&disconnect_lock);
-
- subminor = iminor(inode);
- interface = usb_find_interface(&sasem_driver, subminor);
- if (!interface) {
- err("%s: could not find interface for minor %d",
- __func__, subminor);
- retval = -ENODEV;
- goto exit;
- }
- context = usb_get_intfdata(interface);
-
- if (!context) {
- err("%s: no context found for minor %d",
- __func__, subminor);
- retval = -ENODEV;
- goto exit;
- }
-
- mutex_lock(&context->ctx_lock);
-
- if (context->vfd_isopen) {
- err("%s: VFD port is already open", __func__);
- retval = -EBUSY;
- } else {
- context->vfd_isopen = 1;
- file->private_data = context;
- printk(KERN_INFO "VFD port opened\n");
- }
-
- mutex_unlock(&context->ctx_lock);
-
-exit:
- mutex_unlock(&disconnect_lock);
- return retval;
-}
-
-/**
- * Called when the VFD device (e.g. /dev/usb/lcd)
- * is closed by the application.
- */
-static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg)
-{
- struct sasem_context *context = NULL;
-
- context = (struct sasem_context *) file->private_data;
-
- if (!context) {
- err("%s: no context for device", __func__);
- return -ENODEV;
- }
-
- mutex_lock(&context->ctx_lock);
-
- switch (cmd) {
- case IOCTL_LCD_CONTRAST:
- if (arg > 1000)
- arg = 1000;
- context->vfd_contrast = (unsigned int)arg;
- break;
- default:
- printk(KERN_INFO "Unknown IOCTL command\n");
- mutex_unlock(&context->ctx_lock);
- return -ENOIOCTLCMD; /* not supported */
- }
-
- mutex_unlock(&context->ctx_lock);
- return 0;
-}
-
-/**
- * Called when the VFD device (e.g. /dev/usb/lcd)
- * is closed by the application.
- */
-static int vfd_close(struct inode *inode, struct file *file)
-{
- struct sasem_context *context = NULL;
- int retval = 0;
-
- context = (struct sasem_context *) file->private_data;
-
- if (!context) {
- err("%s: no context for device", __func__);
- return -ENODEV;
- }
-
- mutex_lock(&context->ctx_lock);
-
- if (!context->vfd_isopen) {
- err("%s: VFD is not open", __func__);
- retval = -EIO;
- } else {
- context->vfd_isopen = 0;
- printk(KERN_INFO "VFD port closed\n");
- if (!context->dev_present && !context->ir_isopen) {
-
- /* Device disconnected before close and IR port is
- * not open. If IR port is open, context will be
- * deleted by ir_close. */
- mutex_unlock(&context->ctx_lock);
- delete_context(context);
- return retval;
- }
- }
-
- mutex_unlock(&context->ctx_lock);
- return retval;
-}
-
-/**
- * Sends a packet to the VFD.
- */
-static int send_packet(struct sasem_context *context)
-{
- unsigned int pipe;
- int interval = 0;
- int retval = 0;
-
- pipe = usb_sndintpipe(context->dev,
- context->tx_endpoint->bEndpointAddress);
- interval = context->tx_endpoint->bInterval;
-
- usb_fill_int_urb(context->tx_urb, context->dev, pipe,
- context->usb_tx_buf, sizeof(context->usb_tx_buf),
- usb_tx_callback, context, interval);
-
- context->tx_urb->actual_length = 0;
-
- init_completion(&context->tx.finished);
- atomic_set(&(context->tx.busy), 1);
-
- retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
- if (retval) {
- atomic_set(&(context->tx.busy), 0);
- err("%s: error submitting urb (%d)", __func__, retval);
- } else {
- /* Wait for transmission to complete (or abort) */
- mutex_unlock(&context->ctx_lock);
- wait_for_completion(&context->tx.finished);
- mutex_lock(&context->ctx_lock);
-
- retval = context->tx.status;
- if (retval)
- err("%s: packet tx failed (%d)", __func__, retval);
- }
-
- return retval;
-}
-
-/**
- * Writes data to the VFD. The Sasem VFD is 2x16 characters
- * and requires data in 9 consecutive USB interrupt packets,
- * each packet carrying 8 bytes.
- */
-static ssize_t vfd_write(struct file *file, const char *buf,
- size_t n_bytes, loff_t *pos)
-{
- int i;
- int retval = 0;
- struct sasem_context *context;
- int *data_buf = NULL;
-
- context = (struct sasem_context *) file->private_data;
- if (!context) {
- err("%s: no context for device", __func__);
- return -ENODEV;
- }
-
- mutex_lock(&context->ctx_lock);
-
- if (!context->dev_present) {
- err("%s: no Sasem device present", __func__);
- retval = -ENODEV;
- goto exit;
- }
-
- if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) {
- err("%s: invalid payload size", __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- data_buf = memdup_user(buf, n_bytes);
- if (IS_ERR(data_buf)) {
- retval = PTR_ERR(data_buf);
- goto exit;
- }
-
- memcpy(context->tx.data_buf, data_buf, n_bytes);
-
- /* Pad with spaces */
- for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i)
- context->tx.data_buf[i] = ' ';
-
- /* Nine 8 byte packets to be sent */
- /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
- * will clear the VFD */
- for (i = 0; i < 9; i++) {
- switch (i) {
- case 0:
- memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
- context->usb_tx_buf[1] = (context->vfd_contrast) ?
- (0x2B - (context->vfd_contrast - 1) / 250)
- : 0x2B;
- break;
- case 1:
- memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
- break;
- case 2:
- memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
- break;
- case 3:
- memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
- break;
- case 4:
- memcpy(context->usb_tx_buf,
- context->tx.data_buf + 8, 8);
- break;
- case 5:
- memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
- break;
- case 6:
- memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
- break;
- case 7:
- memcpy(context->usb_tx_buf,
- context->tx.data_buf + 16, 8);
- break;
- case 8:
- memcpy(context->usb_tx_buf,
- context->tx.data_buf + 24, 8);
- break;
- }
- retval = send_packet(context);
- if (retval) {
-
- err("%s: send packet failed for packet #%d",
- __func__, i);
- goto exit;
- }
- }
-exit:
-
- mutex_unlock(&context->ctx_lock);
- kfree(data_buf);
-
- return (!retval) ? n_bytes : retval;
-}
-
-/**
- * Callback function for USB core API: transmit data
- */
-static void usb_tx_callback(struct urb *urb)
-{
- struct sasem_context *context;
-
- if (!urb)
- return;
- context = (struct sasem_context *) urb->context;
- if (!context)
- return;
-
- context->tx.status = urb->status;
-
- /* notify waiters that write has finished */
- atomic_set(&context->tx.busy, 0);
- complete(&context->tx.finished);
-
- return;
-}
-
-/**
- * Called by lirc_dev when the application opens /dev/lirc
- */
-static int ir_open(void *data)
-{
- int retval = 0;
- struct sasem_context *context;
-
- /* prevent races with disconnect */
- mutex_lock(&disconnect_lock);
-
- context = (struct sasem_context *) data;
-
- mutex_lock(&context->ctx_lock);
-
- if (context->ir_isopen) {
- err("%s: IR port is already open", __func__);
- retval = -EBUSY;
- goto exit;
- }
-
- usb_fill_int_urb(context->rx_urb, context->dev,
- usb_rcvintpipe(context->dev,
- context->rx_endpoint->bEndpointAddress),
- context->usb_rx_buf, sizeof(context->usb_rx_buf),
- usb_rx_callback, context, context->rx_endpoint->bInterval);
-
- retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
-
- if (retval)
- err("%s: usb_submit_urb failed for ir_open (%d)",
- __func__, retval);
- else {
- context->ir_isopen = 1;
- printk(KERN_INFO "IR port opened\n");
- }
-
-exit:
- mutex_unlock(&context->ctx_lock);
-
- mutex_unlock(&disconnect_lock);
- return retval;
-}
-
-/**
- * Called by lirc_dev when the application closes /dev/lirc
- */
-static void ir_close(void *data)
-{
- struct sasem_context *context;
-
- context = (struct sasem_context *)data;
- if (!context) {
- err("%s: no context for device", __func__);
- return;
- }
-
- mutex_lock(&context->ctx_lock);
-
- usb_kill_urb(context->rx_urb);
- context->ir_isopen = 0;
- printk(KERN_INFO "IR port closed\n");
-
- if (!context->dev_present) {
-
- /*
- * Device disconnected while IR port was
- * still open. Driver was not deregistered
- * at disconnect time, so do it now.
- */
- deregister_from_lirc(context);
-
- if (!context->vfd_isopen) {
-
- mutex_unlock(&context->ctx_lock);
- delete_context(context);
- return;
- }
- /* If VFD port is open, context will be deleted by vfd_close */
- }
-
- mutex_unlock(&context->ctx_lock);
- return;
-}
-
-/**
- * Process the incoming packet
- */
-static void incoming_packet(struct sasem_context *context,
- struct urb *urb)
-{
- int len = urb->actual_length;
- unsigned char *buf = urb->transfer_buffer;
- long ms;
- struct timeval tv;
- int i;
-
- if (len != 8) {
- printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n",
- __func__, len);
- return;
- }
-
- if (debug) {
- printk(KERN_INFO "Incoming data: ");
- for (i = 0; i < 8; ++i)
- printk(KERN_CONT "%02x ", buf[i]);
- printk(KERN_CONT "\n");
- }
-
- /*
- * Lirc could deal with the repeat code, but we really need to block it
- * if it arrives too late. Otherwise we could repeat the wrong code.
- */
-
- /* get the time since the last button press */
- do_gettimeofday(&tv);
- ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
- (tv.tv_usec - context->presstime.tv_usec) / 1000;
-
- if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
- /*
- * the repeat code is being sent, so we copy
- * the old code to LIRC
- */
-
- /*
- * NOTE: Only if the last code was less than 250ms ago
- * - no one should be able to push another (undetected) button
- * in that time and then get a false repeat of the previous
- * press but it is long enough for a genuine repeat
- */
- if ((ms < 250) && (context->codesaved != 0)) {
- memcpy(buf, &context->lastcode, 8);
- context->presstime.tv_sec = tv.tv_sec;
- context->presstime.tv_usec = tv.tv_usec;
- }
- } else {
- /* save the current valid code for repeats */
- memcpy(&context->lastcode, buf, 8);
- /*
- * set flag to signal a valid code was save;
- * just for safety reasons
- */
- context->codesaved = 1;
- context->presstime.tv_sec = tv.tv_sec;
- context->presstime.tv_usec = tv.tv_usec;
- }
-
- lirc_buffer_write(context->driver->rbuf, buf);
- wake_up(&context->driver->rbuf->wait_poll);
-}
-
-/**
- * Callback function for USB core API: receive data
- */
-static void usb_rx_callback(struct urb *urb)
-{
- struct sasem_context *context;
-
- if (!urb)
- return;
- context = (struct sasem_context *) urb->context;
- if (!context)
- return;
-
- switch (urb->status) {
-
- case -ENOENT: /* usbcore unlink successful! */
- return;
-
- case 0:
- if (context->ir_isopen)
- incoming_packet(context, urb);
- break;
-
- default:
- printk(KERN_WARNING "%s: status (%d): ignored",
- __func__, urb->status);
- break;
- }
-
- usb_submit_urb(context->rx_urb, GFP_ATOMIC);
- return;
-}
-
-
-
-/**
- * Callback function for USB core API: Probe
- */
-static int sasem_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct usb_device *dev = NULL;
- struct usb_host_interface *iface_desc = NULL;
- struct usb_endpoint_descriptor *rx_endpoint = NULL;
- struct usb_endpoint_descriptor *tx_endpoint = NULL;
- struct urb *rx_urb = NULL;
- struct urb *tx_urb = NULL;
- struct lirc_driver *driver = NULL;
- struct lirc_buffer *rbuf = NULL;
- int lirc_minor = 0;
- int num_endpoints;
- int retval = 0;
- int vfd_ep_found;
- int ir_ep_found;
- int alloc_status;
- struct sasem_context *context = NULL;
- int i;
-
- printk(KERN_INFO "%s: found Sasem device\n", __func__);
-
-
- dev = usb_get_dev(interface_to_usbdev(interface));
- iface_desc = interface->cur_altsetting;
- num_endpoints = iface_desc->desc.bNumEndpoints;
-
- /*
- * Scan the endpoint list and set:
- * first input endpoint = IR endpoint
- * first output endpoint = VFD endpoint
- */
-
- ir_ep_found = 0;
- vfd_ep_found = 0;
-
- for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
-
- struct usb_endpoint_descriptor *ep;
- int ep_dir;
- int ep_type;
- ep = &iface_desc->endpoint [i].desc;
- ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
- ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
-
- if (!ir_ep_found &&
- ep_dir == USB_DIR_IN &&
- ep_type == USB_ENDPOINT_XFER_INT) {
-
- rx_endpoint = ep;
- ir_ep_found = 1;
- if (debug)
- printk(KERN_INFO "%s: found IR endpoint\n",
- __func__);
-
- } else if (!vfd_ep_found &&
- ep_dir == USB_DIR_OUT &&
- ep_type == USB_ENDPOINT_XFER_INT) {
-
- tx_endpoint = ep;
- vfd_ep_found = 1;
- if (debug)
- printk(KERN_INFO "%s: found VFD endpoint\n",
- __func__);
- }
- }
-
- /* Input endpoint is mandatory */
- if (!ir_ep_found) {
-
- err("%s: no valid input (IR) endpoint found.", __func__);
- retval = -ENODEV;
- goto exit;
- }
-
- if (!vfd_ep_found)
- printk(KERN_INFO "%s: no valid output (VFD) endpoint found.\n",
- __func__);
-
-
- /* Allocate memory */
- alloc_status = 0;
-
- context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL);
- if (!context) {
- err("%s: kzalloc failed for context", __func__);
- alloc_status = 1;
- goto alloc_status_switch;
- }
- driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
- if (!driver) {
- err("%s: kzalloc failed for lirc_driver", __func__);
- alloc_status = 2;
- goto alloc_status_switch;
- }
- rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
- if (!rbuf) {
- err("%s: kmalloc failed for lirc_buffer", __func__);
- alloc_status = 3;
- goto alloc_status_switch;
- }
- if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
- err("%s: lirc_buffer_init failed", __func__);
- alloc_status = 4;
- goto alloc_status_switch;
- }
- rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!rx_urb) {
- err("%s: usb_alloc_urb failed for IR urb", __func__);
- alloc_status = 5;
- goto alloc_status_switch;
- }
- if (vfd_ep_found) {
- tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!tx_urb) {
- err("%s: usb_alloc_urb failed for VFD urb",
- __func__);
- alloc_status = 6;
- goto alloc_status_switch;
- }
- }
-
- mutex_init(&context->ctx_lock);
-
- strcpy(driver->name, MOD_NAME);
- driver->minor = -1;
- driver->code_length = 64;
- driver->sample_rate = 0;
- driver->features = LIRC_CAN_REC_LIRCCODE;
- driver->data = context;
- driver->rbuf = rbuf;
- driver->set_use_inc = ir_open;
- driver->set_use_dec = ir_close;
- driver->dev = &interface->dev;
- driver->owner = THIS_MODULE;
-
- mutex_lock(&context->ctx_lock);
-
- lirc_minor = lirc_register_driver(driver);
- if (lirc_minor < 0) {
- err("%s: lirc_register_driver failed", __func__);
- alloc_status = 7;
- retval = lirc_minor;
- goto unlock;
- } else
- printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n",
- __func__, lirc_minor);
-
- /* Needed while unregistering! */
- driver->minor = lirc_minor;
-
- context->dev = dev;
- context->dev_present = 1;
- context->rx_endpoint = rx_endpoint;
- context->rx_urb = rx_urb;
- if (vfd_ep_found) {
- context->tx_endpoint = tx_endpoint;
- context->tx_urb = tx_urb;
- context->vfd_contrast = 1000; /* range 0 - 1000 */
- }
- context->driver = driver;
-
- usb_set_intfdata(interface, context);
-
- if (vfd_ep_found) {
-
- if (debug)
- printk(KERN_INFO "Registering VFD with sysfs\n");
- if (usb_register_dev(interface, &sasem_class))
- /* Not a fatal error, so ignore */
- printk(KERN_INFO "%s: could not get a minor number "
- "for VFD\n", __func__);
- }
-
- printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n",
- __func__, dev->bus->busnum, dev->devnum);
-unlock:
- mutex_unlock(&context->ctx_lock);
-
-alloc_status_switch:
- switch (alloc_status) {
-
- case 7:
- if (vfd_ep_found)
- usb_free_urb(tx_urb);
- case 6:
- usb_free_urb(rx_urb);
- case 5:
- lirc_buffer_free(rbuf);
- case 4:
- kfree(rbuf);
- case 3:
- kfree(driver);
- case 2:
- kfree(context);
- context = NULL;
- case 1:
- if (retval == 0)
- retval = -ENOMEM;
- }
-
-exit:
- return retval;
-}
-
-/**
- * Callback function for USB core API: disonnect
- */
-static void sasem_disconnect(struct usb_interface *interface)
-{
- struct sasem_context *context;
-
- /* prevent races with ir_open()/vfd_open() */
- mutex_lock(&disconnect_lock);
-
- context = usb_get_intfdata(interface);
- mutex_lock(&context->ctx_lock);
-
- printk(KERN_INFO "%s: Sasem device disconnected\n", __func__);
-
- usb_set_intfdata(interface, NULL);
- context->dev_present = 0;
-
- /* Stop reception */
- usb_kill_urb(context->rx_urb);
-
- /* Abort ongoing write */
- if (atomic_read(&context->tx.busy)) {
-
- usb_kill_urb(context->tx_urb);
- wait_for_completion(&context->tx.finished);
- }
-
- /* De-register from lirc_dev if IR port is not open */
- if (!context->ir_isopen)
- deregister_from_lirc(context);
-
- usb_deregister_dev(interface, &sasem_class);
-
- mutex_unlock(&context->ctx_lock);
-
- if (!context->ir_isopen && !context->vfd_isopen)
- delete_context(context);
-
- mutex_unlock(&disconnect_lock);
-}
-
-static int __init sasem_init(void)
-{
- int rc;
-
- printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n");
- printk(KERN_INFO MOD_AUTHOR "\n");
-
- rc = usb_register(&sasem_driver);
- if (rc < 0) {
- err("%s: usb register failed (%d)", __func__, rc);
- return -ENODEV;
- }
- return 0;
-}
-
-static void __exit sasem_exit(void)
-{
- usb_deregister(&sasem_driver);
- printk(KERN_INFO "module removed. Goodbye!\n");
-}
-
-
-module_init(sasem_init);
-module_exit(sasem_exit);
+++ /dev/null
-/*
- * lirc_serial.c
- *
- * lirc_serial - Device driver that records pulse- and pause-lengths
- * (space-lengths) between DDCD event on a serial port.
- *
- * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
- * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
- * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
- * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
- * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
- * 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
- *
- */
-
-/*
- * Steve's changes to improve transmission fidelity:
- * - for systems with the rdtsc instruction and the clock counter, a
- * send_pule that times the pulses directly using the counter.
- * This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is
- * not needed. Measurement shows very stable waveform, even where
- * PCI activity slows the access to the UART, which trips up other
- * versions.
- * - For other system, non-integer-microsecond pulse/space lengths,
- * done using fixed point binary. So, much more accurate carrier
- * frequency.
- * - fine tuned transmitter latency, taking advantage of fractional
- * microseconds in previous change
- * - Fixed bug in the way transmitter latency was accounted for by
- * tuning the pulse lengths down - the send_pulse routine ignored
- * this overhead as it timed the overall pulse length - so the
- * pulse frequency was right but overall pulse length was too
- * long. Fixed by accounting for latency on each pulse/space
- * iteration.
- *
- * Steve Davies <steve@daviesfam.org> July 2001
- */
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/serial_reg.h>
-#include <linux/time.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <linux/mm.h>
-#include <linux/delay.h>
-#include <linux/poll.h>
-#include <linux/platform_device.h>
-
-#include <asm/system.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/fcntl.h>
-#include <linux/spinlock.h>
-
-#ifdef CONFIG_LIRC_SERIAL_NSLU2
-#include <asm/hardware.h>
-#endif
-/* From Intel IXP42X Developer's Manual (#252480-005): */
-/* ftp://download.intel.com/design/network/manuals/25248005.pdf */
-#define UART_IE_IXP42X_UUE 0x40 /* IXP42X UART Unit enable */
-#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-#define LIRC_DRIVER_NAME "lirc_serial"
-
-struct lirc_serial {
- int signal_pin;
- int signal_pin_change;
- u8 on;
- u8 off;
- long (*send_pulse)(unsigned long length);
- void (*send_space)(long length);
- int features;
- spinlock_t lock;
-};
-
-#define LIRC_HOMEBREW 0
-#define LIRC_IRDEO 1
-#define LIRC_IRDEO_REMOTE 2
-#define LIRC_ANIMAX 3
-#define LIRC_IGOR 4
-#define LIRC_NSLU2 5
-
-/*** module parameters ***/
-static int type;
-static int io;
-static int irq;
-static int iommap;
-static int ioshift;
-static int softcarrier = 1;
-static int share_irq;
-static int debug;
-static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */
-static int txsense; /* 0 = active high, 1 = active low */
-
-#define dprintk(fmt, args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
- fmt, ## args); \
- } while (0)
-
-/* forward declarations */
-static long send_pulse_irdeo(unsigned long length);
-static long send_pulse_homebrew(unsigned long length);
-static void send_space_irdeo(long length);
-static void send_space_homebrew(long length);
-
-static struct lirc_serial hardware[] = {
- [LIRC_HOMEBREW] = {
- .signal_pin = UART_MSR_DCD,
- .signal_pin_change = UART_MSR_DDCD,
- .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
- .off = (UART_MCR_RTS | UART_MCR_OUT2),
- .send_pulse = send_pulse_homebrew,
- .send_space = send_space_homebrew,
-#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
- .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
- LIRC_CAN_SET_SEND_CARRIER |
- LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
-#else
- .features = LIRC_CAN_REC_MODE2
-#endif
- },
-
- [LIRC_IRDEO] = {
- .signal_pin = UART_MSR_DSR,
- .signal_pin_change = UART_MSR_DDSR,
- .on = UART_MCR_OUT2,
- .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
- .send_pulse = send_pulse_irdeo,
- .send_space = send_space_irdeo,
- .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
- LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
- },
-
- [LIRC_IRDEO_REMOTE] = {
- .signal_pin = UART_MSR_DSR,
- .signal_pin_change = UART_MSR_DDSR,
- .on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
- .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
- .send_pulse = send_pulse_irdeo,
- .send_space = send_space_irdeo,
- .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
- LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
- },
-
- [LIRC_ANIMAX] = {
- .signal_pin = UART_MSR_DCD,
- .signal_pin_change = UART_MSR_DDCD,
- .on = 0,
- .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
- .send_pulse = NULL,
- .send_space = NULL,
- .features = LIRC_CAN_REC_MODE2
- },
-
- [LIRC_IGOR] = {
- .signal_pin = UART_MSR_DSR,
- .signal_pin_change = UART_MSR_DDSR,
- .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
- .off = (UART_MCR_RTS | UART_MCR_OUT2),
- .send_pulse = send_pulse_homebrew,
- .send_space = send_space_homebrew,
-#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
- .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
- LIRC_CAN_SET_SEND_CARRIER |
- LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
-#else
- .features = LIRC_CAN_REC_MODE2
-#endif
- },
-
-#ifdef CONFIG_LIRC_SERIAL_NSLU2
- /*
- * Modified Linksys Network Storage Link USB 2.0 (NSLU2):
- * We receive on CTS of the 2nd serial port (R142,LHS), we
- * transmit with a IR diode between GPIO[1] (green status LED),
- * and ground (Matthias Goebl <matthias.goebl@goebl.net>).
- * See also http://www.nslu2-linux.org for this device
- */
- [LIRC_NSLU2] = {
- .signal_pin = UART_MSR_CTS,
- .signal_pin_change = UART_MSR_DCTS,
- .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
- .off = (UART_MCR_RTS | UART_MCR_OUT2),
- .send_pulse = send_pulse_homebrew,
- .send_space = send_space_homebrew,
-#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
- .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
- LIRC_CAN_SET_SEND_CARRIER |
- LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
-#else
- .features = LIRC_CAN_REC_MODE2
-#endif
- },
-#endif
-
-};
-
-#define RS_ISR_PASS_LIMIT 256
-
-/*
- * A long pulse code from a remote might take up to 300 bytes. The
- * daemon should read the bytes as soon as they are generated, so take
- * the number of keys you think you can push before the daemon runs
- * and multiply by 300. The driver will warn you if you overrun this
- * buffer. If you have a slow computer or non-busmastering IDE disks,
- * maybe you will need to increase this.
- */
-
-/* This MUST be a power of two! It has to be larger than 1 as well. */
-
-#define RBUF_LEN 256
-
-static struct timeval lasttv = {0, 0};
-
-static struct lirc_buffer rbuf;
-
-static unsigned int freq = 38000;
-static unsigned int duty_cycle = 50;
-
-/* Initialized in init_timing_params() */
-static unsigned long period;
-static unsigned long pulse_width;
-static unsigned long space_width;
-
-#if defined(__i386__)
-/*
- * From:
- * Linux I/O port programming mini-HOWTO
- * Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
- * v, 28 December 1997
- *
- * [...]
- * Actually, a port I/O instruction on most ports in the 0-0x3ff range
- * takes almost exactly 1 microsecond, so if you're, for example, using
- * the parallel port directly, just do additional inb()s from that port
- * to delay.
- * [...]
- */
-/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
- * comment above plus trimming to match actual measured frequency.
- * This will be sensitive to cpu speed, though hopefully most of the 1.5us
- * is spent in the uart access. Still - for reference test machine was a
- * 1.13GHz Athlon system - Steve
- */
-
-/*
- * changed from 400 to 450 as this works better on slower machines;
- * faster machines will use the rdtsc code anyway
- */
-#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
-
-#else
-
-/* does anybody have information on other platforms ? */
-/* 256 = 1<<8 */
-#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
-
-#endif /* __i386__ */
-/*
- * FIXME: should we be using hrtimers instead of this
- * LIRC_SERIAL_TRANSMITTER_LATENCY nonsense?
- */
-
-/* fetch serial input packet (1 byte) from register offset */
-static u8 sinp(int offset)
-{
- if (iommap != 0)
- /* the register is memory-mapped */
- offset <<= ioshift;
-
- return inb(io + offset);
-}
-
-/* write serial output packet (1 byte) of value to register offset */
-static void soutp(int offset, u8 value)
-{
- if (iommap != 0)
- /* the register is memory-mapped */
- offset <<= ioshift;
-
- outb(value, io + offset);
-}
-
-static void on(void)
-{
-#ifdef CONFIG_LIRC_SERIAL_NSLU2
- /*
- * On NSLU2, we put the transmit diode between the output of the green
- * status LED and ground
- */
- if (type == LIRC_NSLU2) {
- gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_LOW);
- return;
- }
-#endif
- if (txsense)
- soutp(UART_MCR, hardware[type].off);
- else
- soutp(UART_MCR, hardware[type].on);
-}
-
-static void off(void)
-{
-#ifdef CONFIG_LIRC_SERIAL_NSLU2
- if (type == LIRC_NSLU2) {
- gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_HIGH);
- return;
- }
-#endif
- if (txsense)
- soutp(UART_MCR, hardware[type].on);
- else
- soutp(UART_MCR, hardware[type].off);
-}
-
-#ifndef MAX_UDELAY_MS
-#define MAX_UDELAY_US 5000
-#else
-#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
-#endif
-
-static void safe_udelay(unsigned long usecs)
-{
- while (usecs > MAX_UDELAY_US) {
- udelay(MAX_UDELAY_US);
- usecs -= MAX_UDELAY_US;
- }
- udelay(usecs);
-}
-
-#ifdef USE_RDTSC
-/*
- * This is an overflow/precision juggle, complicated in that we can't
- * do long long divide in the kernel
- */
-
-/*
- * When we use the rdtsc instruction to measure clocks, we keep the
- * pulse and space widths as clock cycles. As this is CPU speed
- * dependent, the widths must be calculated in init_port and ioctl
- * time
- */
-
-/* So send_pulse can quickly convert microseconds to clocks */
-static unsigned long conv_us_to_clocks;
-
-static int init_timing_params(unsigned int new_duty_cycle,
- unsigned int new_freq)
-{
- __u64 loops_per_sec, work;
-
- duty_cycle = new_duty_cycle;
- freq = new_freq;
-
- loops_per_sec = __this_cpu_read(cpu.info.loops_per_jiffy);
- loops_per_sec *= HZ;
-
- /* How many clocks in a microsecond?, avoiding long long divide */
- work = loops_per_sec;
- work *= 4295; /* 4295 = 2^32 / 1e6 */
- conv_us_to_clocks = (work >> 32);
-
- /*
- * Carrier period in clocks, approach good up to 32GHz clock,
- * gets carrier frequency within 8Hz
- */
- period = loops_per_sec >> 3;
- period /= (freq >> 3);
-
- /* Derive pulse and space from the period */
- pulse_width = period * duty_cycle / 100;
- space_width = period - pulse_width;
- dprintk("in init_timing_params, freq=%d, duty_cycle=%d, "
- "clk/jiffy=%ld, pulse=%ld, space=%ld, "
- "conv_us_to_clocks=%ld\n",
- freq, duty_cycle, __this_cpu_read(cpu_info.loops_per_jiffy),
- pulse_width, space_width, conv_us_to_clocks);
- return 0;
-}
-#else /* ! USE_RDTSC */
-static int init_timing_params(unsigned int new_duty_cycle,
- unsigned int new_freq)
-{
-/*
- * period, pulse/space width are kept with 8 binary places -
- * IE multiplied by 256.
- */
- if (256 * 1000000L / new_freq * new_duty_cycle / 100 <=
- LIRC_SERIAL_TRANSMITTER_LATENCY)
- return -EINVAL;
- if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <=
- LIRC_SERIAL_TRANSMITTER_LATENCY)
- return -EINVAL;
- duty_cycle = new_duty_cycle;
- freq = new_freq;
- period = 256 * 1000000L / freq;
- pulse_width = period * duty_cycle / 100;
- space_width = period - pulse_width;
- dprintk("in init_timing_params, freq=%d pulse=%ld, "
- "space=%ld\n", freq, pulse_width, space_width);
- return 0;
-}
-#endif /* USE_RDTSC */
-
-
-/* return value: space length delta */
-
-static long send_pulse_irdeo(unsigned long length)
-{
- long rawbits, ret;
- int i;
- unsigned char output;
- unsigned char chunk, shifted;
-
- /* how many bits have to be sent ? */
- rawbits = length * 1152 / 10000;
- if (duty_cycle > 50)
- chunk = 3;
- else
- chunk = 1;
- for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) {
- shifted = chunk << (i * 3);
- shifted >>= 1;
- output &= (~shifted);
- i++;
- if (i == 3) {
- soutp(UART_TX, output);
- while (!(sinp(UART_LSR) & UART_LSR_THRE))
- ;
- output = 0x7f;
- i = 0;
- }
- }
- if (i != 0) {
- soutp(UART_TX, output);
- while (!(sinp(UART_LSR) & UART_LSR_TEMT))
- ;
- }
-
- if (i == 0)
- ret = (-rawbits) * 10000 / 1152;
- else
- ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152;
-
- return ret;
-}
-
-#ifdef USE_RDTSC
-/* Version that uses Pentium rdtsc instruction to measure clocks */
-
-/*
- * This version does sub-microsecond timing using rdtsc instruction,
- * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
- * Implicitly i586 architecture... - Steve
- */
-
-static long send_pulse_homebrew_softcarrier(unsigned long length)
-{
- int flag;
- unsigned long target, start, now;
-
- /* Get going quick as we can */
- rdtscl(start);
- on();
- /* Convert length from microseconds to clocks */
- length *= conv_us_to_clocks;
- /* And loop till time is up - flipping at right intervals */
- now = start;
- target = pulse_width;
- flag = 1;
- /*
- * FIXME: This looks like a hard busy wait, without even an occasional,
- * polite, cpu_relax() call. There's got to be a better way?
- *
- * The i2c code has the result of a lot of bit-banging work, I wonder if
- * there's something there which could be helpful here.
- */
- while ((now - start) < length) {
- /* Delay till flip time */
- do {
- rdtscl(now);
- } while ((now - start) < target);
-
- /* flip */
- if (flag) {
- rdtscl(now);
- off();
- target += space_width;
- } else {
- rdtscl(now); on();
- target += pulse_width;
- }
- flag = !flag;
- }
- rdtscl(now);
- return ((now - start) - length) / conv_us_to_clocks;
-}
-#else /* ! USE_RDTSC */
-/* Version using udelay() */
-
-/*
- * here we use fixed point arithmetic, with 8
- * fractional bits. that gets us within 0.1% or so of the right average
- * frequency, albeit with some jitter in pulse length - Steve
- */
-
-/* To match 8 fractional bits used for pulse/space length */
-
-static long send_pulse_homebrew_softcarrier(unsigned long length)
-{
- int flag;
- unsigned long actual, target, d;
- length <<= 8;
-
- actual = 0; target = 0; flag = 0;
- while (actual < length) {
- if (flag) {
- off();
- target += space_width;
- } else {
- on();
- target += pulse_width;
- }
- d = (target - actual -
- LIRC_SERIAL_TRANSMITTER_LATENCY + 128) >> 8;
- /*
- * Note - we've checked in ioctl that the pulse/space
- * widths are big enough so that d is > 0
- */
- udelay(d);
- actual += (d << 8) + LIRC_SERIAL_TRANSMITTER_LATENCY;
- flag = !flag;
- }
- return (actual-length) >> 8;
-}
-#endif /* USE_RDTSC */
-
-static long send_pulse_homebrew(unsigned long length)
-{
- if (length <= 0)
- return 0;
-
- if (softcarrier)
- return send_pulse_homebrew_softcarrier(length);
- else {
- on();
- safe_udelay(length);
- return 0;
- }
-}
-
-static void send_space_irdeo(long length)
-{
- if (length <= 0)
- return;
-
- safe_udelay(length);
-}
-
-static void send_space_homebrew(long length)
-{
- off();
- if (length <= 0)
- return;
- safe_udelay(length);
-}
-
-static void rbwrite(int l)
-{
- if (lirc_buffer_full(&rbuf)) {
- /* no new signals will be accepted */
- dprintk("Buffer overrun\n");
- return;
- }
- lirc_buffer_write(&rbuf, (void *)&l);
-}
-
-static void frbwrite(int l)
-{
- /* simple noise filter */
- static int pulse, space;
- static unsigned int ptr;
-
- if (ptr > 0 && (l & PULSE_BIT)) {
- pulse += l & PULSE_MASK;
- if (pulse > 250) {
- rbwrite(space);
- rbwrite(pulse | PULSE_BIT);
- ptr = 0;
- pulse = 0;
- }
- return;
- }
- if (!(l & PULSE_BIT)) {
- if (ptr == 0) {
- if (l > 20000) {
- space = l;
- ptr++;
- return;
- }
- } else {
- if (l > 20000) {
- space += pulse;
- if (space > PULSE_MASK)
- space = PULSE_MASK;
- space += l;
- if (space > PULSE_MASK)
- space = PULSE_MASK;
- pulse = 0;
- return;
- }
- rbwrite(space);
- rbwrite(pulse | PULSE_BIT);
- ptr = 0;
- pulse = 0;
- }
- }
- rbwrite(l);
-}
-
-static irqreturn_t irq_handler(int i, void *blah)
-{
- struct timeval tv;
- int counter, dcd;
- u8 status;
- long deltv;
- int data;
- static int last_dcd = -1;
-
- if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
- /* not our interrupt */
- return IRQ_NONE;
- }
-
- counter = 0;
- do {
- counter++;
- status = sinp(UART_MSR);
- if (counter > RS_ISR_PASS_LIMIT) {
- printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: "
- "We're caught!\n");
- break;
- }
- if ((status & hardware[type].signal_pin_change)
- && sense != -1) {
- /* get current time */
- do_gettimeofday(&tv);
-
- /* New mode, written by Trent Piepho
- <xyzzy@u.washington.edu>. */
-
- /*
- * The old format was not very portable.
- * We now use an int to pass pulses
- * and spaces to user space.
- *
- * If PULSE_BIT is set a pulse has been
- * received, otherwise a space has been
- * received. The driver needs to know if your
- * receiver is active high or active low, or
- * the space/pulse sense could be
- * inverted. The bits denoted by PULSE_MASK are
- * the length in microseconds. Lengths greater
- * than or equal to 16 seconds are clamped to
- * PULSE_MASK. All other bits are unused.
- * This is a much simpler interface for user
- * programs, as well as eliminating "out of
- * phase" errors with space/pulse
- * autodetection.
- */
-
- /* calc time since last interrupt in microseconds */
- dcd = (status & hardware[type].signal_pin) ? 1 : 0;
-
- if (dcd == last_dcd) {
- printk(KERN_WARNING LIRC_DRIVER_NAME
- ": ignoring spike: %d %d %lx %lx %lx %lx\n",
- dcd, sense,
- tv.tv_sec, lasttv.tv_sec,
- tv.tv_usec, lasttv.tv_usec);
- continue;
- }
-
- deltv = tv.tv_sec-lasttv.tv_sec;
- if (tv.tv_sec < lasttv.tv_sec ||
- (tv.tv_sec == lasttv.tv_sec &&
- tv.tv_usec < lasttv.tv_usec)) {
- printk(KERN_WARNING LIRC_DRIVER_NAME
- ": AIEEEE: your clock just jumped "
- "backwards\n");
- printk(KERN_WARNING LIRC_DRIVER_NAME
- ": %d %d %lx %lx %lx %lx\n",
- dcd, sense,
- tv.tv_sec, lasttv.tv_sec,
- tv.tv_usec, lasttv.tv_usec);
- data = PULSE_MASK;
- } else if (deltv > 15) {
- data = PULSE_MASK; /* really long time */
- if (!(dcd^sense)) {
- /* sanity check */
- printk(KERN_WARNING LIRC_DRIVER_NAME
- ": AIEEEE: "
- "%d %d %lx %lx %lx %lx\n",
- dcd, sense,
- tv.tv_sec, lasttv.tv_sec,
- tv.tv_usec, lasttv.tv_usec);
- /*
- * detecting pulse while this
- * MUST be a space!
- */
- sense = sense ? 0 : 1;
- }
- } else
- data = (int) (deltv*1000000 +
- tv.tv_usec -
- lasttv.tv_usec);
- frbwrite(dcd^sense ? data : (data|PULSE_BIT));
- lasttv = tv;
- last_dcd = dcd;
- wake_up_interruptible(&rbuf.wait_poll);
- }
- } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
- return IRQ_HANDLED;
-}
-
-
-static int hardware_init_port(void)
-{
- u8 scratch, scratch2, scratch3;
-
- /*
- * This is a simple port existence test, borrowed from the autoconfig
- * function in drivers/serial/8250.c
- */
- scratch = sinp(UART_IER);
- soutp(UART_IER, 0);
-#ifdef __i386__
- outb(0xff, 0x080);
-#endif
- scratch2 = sinp(UART_IER) & 0x0f;
- soutp(UART_IER, 0x0f);
-#ifdef __i386__
- outb(0x00, 0x080);
-#endif
- scratch3 = sinp(UART_IER) & 0x0f;
- soutp(UART_IER, scratch);
- if (scratch2 != 0 || scratch3 != 0x0f) {
- /* we fail, there's nothing here */
- printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test "
- "failed, cannot continue\n");
- return -EINVAL;
- }
-
-
-
- /* Set DLAB 0. */
- soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
-
- /* First of all, disable all interrupts */
- soutp(UART_IER, sinp(UART_IER) &
- (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
-
- /* Clear registers. */
- sinp(UART_LSR);
- sinp(UART_RX);
- sinp(UART_IIR);
- sinp(UART_MSR);
-
-#ifdef CONFIG_LIRC_SERIAL_NSLU2
- if (type == LIRC_NSLU2) {
- /* Setup NSLU2 UART */
-
- /* Enable UART */
- soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE);
- /* Disable Receiver data Time out interrupt */
- soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE);
- /* set out2 = interrupt unmask; off() doesn't set MCR
- on NSLU2 */
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
- }
-#endif
-
- /* Set line for power source */
- off();
-
- /* Clear registers again to be sure. */
- sinp(UART_LSR);
- sinp(UART_RX);
- sinp(UART_IIR);
- sinp(UART_MSR);
-
- switch (type) {
- case LIRC_IRDEO:
- case LIRC_IRDEO_REMOTE:
- /* setup port to 7N1 @ 115200 Baud */
- /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
-
- /* Set DLAB 1. */
- soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
- /* Set divisor to 1 => 115200 Baud */
- soutp(UART_DLM, 0);
- soutp(UART_DLL, 1);
- /* Set DLAB 0 + 7N1 */
- soutp(UART_LCR, UART_LCR_WLEN7);
- /* THR interrupt already disabled at this point */
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int init_port(void)
-{
- int i, nlow, nhigh, result;
-
- result = request_irq(irq, irq_handler,
- (share_irq ? IRQF_SHARED : 0),
- LIRC_DRIVER_NAME, (void *)&hardware);
-
- switch (result) {
- case -EBUSY:
- printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
- return -EBUSY;
- case -EINVAL:
- printk(KERN_ERR LIRC_DRIVER_NAME
- ": Bad irq number or handler\n");
- return -EINVAL;
- default:
- break;
- };
-
- /* Reserve io region. */
- /*
- * Future MMAP-Developers: Attention!
- * For memory mapped I/O you *might* need to use ioremap() first,
- * for the NSLU2 it's done in boot code.
- */
- if (((iommap != 0)
- && (request_mem_region(iommap, 8 << ioshift,
- LIRC_DRIVER_NAME) == NULL))
- || ((iommap == 0)
- && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) {
- printk(KERN_ERR LIRC_DRIVER_NAME
- ": port %04x already in use\n", io);
- printk(KERN_WARNING LIRC_DRIVER_NAME
- ": use 'setserial /dev/ttySX uart none'\n");
- printk(KERN_WARNING LIRC_DRIVER_NAME
- ": or compile the serial port driver as module and\n");
- printk(KERN_WARNING LIRC_DRIVER_NAME
- ": make sure this module is loaded first\n");
- return -EBUSY;
- }
-
- if (hardware_init_port() < 0)
- return -EINVAL;
-
- /* Initialize pulse/space widths */
- init_timing_params(duty_cycle, freq);
-
- /* If pin is high, then this must be an active low receiver. */
- if (sense == -1) {
- /* wait 1/2 sec for the power supply */
- msleep(500);
-
- /*
- * probe 9 times every 0.04s, collect "votes" for
- * active high/low
- */
- nlow = 0;
- nhigh = 0;
- for (i = 0; i < 9; i++) {
- if (sinp(UART_MSR) & hardware[type].signal_pin)
- nlow++;
- else
- nhigh++;
- msleep(40);
- }
- sense = (nlow >= nhigh ? 1 : 0);
- printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active "
- "%s receiver\n", sense ? "low" : "high");
- } else
- printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active "
- "%s receiver\n", sense ? "low" : "high");
-
- dprintk("Interrupt %d, port %04x obtained\n", irq, io);
- return 0;
-}
-
-static int set_use_inc(void *data)
-{
- unsigned long flags;
-
- /* initialize timestamp */
- do_gettimeofday(&lasttv);
-
- spin_lock_irqsave(&hardware[type].lock, flags);
-
- /* Set DLAB 0. */
- soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
-
- soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
-
- spin_unlock_irqrestore(&hardware[type].lock, flags);
-
- return 0;
-}
-
-static void set_use_dec(void *data)
-{ unsigned long flags;
-
- spin_lock_irqsave(&hardware[type].lock, flags);
-
- /* Set DLAB 0. */
- soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
-
- /* First of all, disable all interrupts */
- soutp(UART_IER, sinp(UART_IER) &
- (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
- spin_unlock_irqrestore(&hardware[type].lock, flags);
-}
-
-static ssize_t lirc_write(struct file *file, const char *buf,
- size_t n, loff_t *ppos)
-{
- int i, count;
- unsigned long flags;
- long delta = 0;
- int *wbuf;
-
- if (!(hardware[type].features & LIRC_CAN_SEND_PULSE))
- return -EBADF;
-
- count = n / sizeof(int);
- if (n % sizeof(int) || count % 2 == 0)
- return -EINVAL;
- wbuf = memdup_user(buf, n);
- if (IS_ERR(wbuf))
- return PTR_ERR(wbuf);
- spin_lock_irqsave(&hardware[type].lock, flags);
- if (type == LIRC_IRDEO) {
- /* DTR, RTS down */
- on();
- }
- for (i = 0; i < count; i++) {
- if (i%2)
- hardware[type].send_space(wbuf[i] - delta);
- else
- delta = hardware[type].send_pulse(wbuf[i]);
- }
- off();
- spin_unlock_irqrestore(&hardware[type].lock, flags);
- kfree(wbuf);
- return n;
-}
-
-static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
-{
- int result;
- __u32 value;
-
- switch (cmd) {
- case LIRC_GET_SEND_MODE:
- if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
- return -ENOIOCTLCMD;
-
- result = put_user(LIRC_SEND2MODE
- (hardware[type].features&LIRC_CAN_SEND_MASK),
- (__u32 *) arg);
- if (result)
- return result;
- break;
-
- case LIRC_SET_SEND_MODE:
- if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
- return -ENOIOCTLCMD;
-
- result = get_user(value, (__u32 *) arg);
- if (result)
- return result;
- /* only LIRC_MODE_PULSE supported */
- if (value != LIRC_MODE_PULSE)
- return -ENOSYS;
- break;
-
- case LIRC_GET_LENGTH:
- return -ENOSYS;
- break;
-
- case LIRC_SET_SEND_DUTY_CYCLE:
- dprintk("SET_SEND_DUTY_CYCLE\n");
- if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
- return -ENOIOCTLCMD;
-
- result = get_user(value, (__u32 *) arg);
- if (result)
- return result;
- if (value <= 0 || value > 100)
- return -EINVAL;
- return init_timing_params(value, freq);
- break;
-
- case LIRC_SET_SEND_CARRIER:
- dprintk("SET_SEND_CARRIER\n");
- if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
- return -ENOIOCTLCMD;
-
- result = get_user(value, (__u32 *) arg);
- if (result)
- return result;
- if (value > 500000 || value < 20000)
- return -EINVAL;
- return init_timing_params(duty_cycle, value);
- break;
-
- default:
- return lirc_dev_fop_ioctl(filep, cmd, arg);
- }
- return 0;
-}
-
-static const struct file_operations lirc_fops = {
- .owner = THIS_MODULE,
- .write = lirc_write,
- .unlocked_ioctl = lirc_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = lirc_ioctl,
-#endif
- .read = lirc_dev_fop_read,
- .poll = lirc_dev_fop_poll,
- .open = lirc_dev_fop_open,
- .release = lirc_dev_fop_close,
- .llseek = no_llseek,
-};
-
-static struct lirc_driver driver = {
- .name = LIRC_DRIVER_NAME,
- .minor = -1,
- .code_length = 1,
- .sample_rate = 0,
- .data = NULL,
- .add_to_buf = NULL,
- .rbuf = &rbuf,
- .set_use_inc = set_use_inc,
- .set_use_dec = set_use_dec,
- .fops = &lirc_fops,
- .dev = NULL,
- .owner = THIS_MODULE,
-};
-
-static struct platform_device *lirc_serial_dev;
-
-static int __devinit lirc_serial_probe(struct platform_device *dev)
-{
- return 0;
-}
-
-static int __devexit lirc_serial_remove(struct platform_device *dev)
-{
- return 0;
-}
-
-static int lirc_serial_suspend(struct platform_device *dev,
- pm_message_t state)
-{
- /* Set DLAB 0. */
- soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
-
- /* Disable all interrupts */
- soutp(UART_IER, sinp(UART_IER) &
- (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
-
- /* Clear registers. */
- sinp(UART_LSR);
- sinp(UART_RX);
- sinp(UART_IIR);
- sinp(UART_MSR);
-
- return 0;
-}
-
-/* twisty maze... need a forward-declaration here... */
-static void lirc_serial_exit(void);
-
-static int lirc_serial_resume(struct platform_device *dev)
-{
- unsigned long flags;
-
- if (hardware_init_port() < 0) {
- lirc_serial_exit();
- return -EINVAL;
- }
-
- spin_lock_irqsave(&hardware[type].lock, flags);
- /* Enable Interrupt */
- do_gettimeofday(&lasttv);
- soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
- off();
-
- lirc_buffer_clear(&rbuf);
-
- spin_unlock_irqrestore(&hardware[type].lock, flags);
-
- return 0;
-}
-
-static struct platform_driver lirc_serial_driver = {
- .probe = lirc_serial_probe,
- .remove = __devexit_p(lirc_serial_remove),
- .suspend = lirc_serial_suspend,
- .resume = lirc_serial_resume,
- .driver = {
- .name = "lirc_serial",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init lirc_serial_init(void)
-{
- int result;
-
- /* Init read buffer. */
- result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
- if (result < 0)
- return -ENOMEM;
-
- result = platform_driver_register(&lirc_serial_driver);
- if (result) {
- printk("lirc register returned %d\n", result);
- goto exit_buffer_free;
- }
-
- lirc_serial_dev = platform_device_alloc("lirc_serial", 0);
- if (!lirc_serial_dev) {
- result = -ENOMEM;
- goto exit_driver_unregister;
- }
-
- result = platform_device_add(lirc_serial_dev);
- if (result)
- goto exit_device_put;
-
- return 0;
-
-exit_device_put:
- platform_device_put(lirc_serial_dev);
-exit_driver_unregister:
- platform_driver_unregister(&lirc_serial_driver);
-exit_buffer_free:
- lirc_buffer_free(&rbuf);
- return result;
-}
-
-static void lirc_serial_exit(void)
-{
- platform_device_unregister(lirc_serial_dev);
- platform_driver_unregister(&lirc_serial_driver);
- lirc_buffer_free(&rbuf);
-}
-
-static int __init lirc_serial_init_module(void)
-{
- int result;
-
- result = lirc_serial_init();
- if (result)
- return result;
-
- switch (type) {
- case LIRC_HOMEBREW:
- case LIRC_IRDEO:
- case LIRC_IRDEO_REMOTE:
- case LIRC_ANIMAX:
- case LIRC_IGOR:
- /* if nothing specified, use ttyS0/com1 and irq 4 */
- io = io ? io : 0x3f8;
- irq = irq ? irq : 4;
- break;
-#ifdef CONFIG_LIRC_SERIAL_NSLU2
- case LIRC_NSLU2:
- io = io ? io : IRQ_IXP4XX_UART2;
- irq = irq ? irq : (IXP4XX_UART2_BASE_VIRT + REG_OFFSET);
- iommap = iommap ? iommap : IXP4XX_UART2_BASE_PHYS;
- ioshift = ioshift ? ioshift : 2;
- break;
-#endif
- default:
- result = -EINVAL;
- goto exit_serial_exit;
- }
- if (!softcarrier) {
- switch (type) {
- case LIRC_HOMEBREW:
- case LIRC_IGOR:
-#ifdef CONFIG_LIRC_SERIAL_NSLU2
- case LIRC_NSLU2:
-#endif
- hardware[type].features &=
- ~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
- LIRC_CAN_SET_SEND_CARRIER);
- break;
- }
- }
-
- result = init_port();
- if (result < 0)
- goto exit_serial_exit;
- driver.features = hardware[type].features;
- driver.dev = &lirc_serial_dev->dev;
- driver.minor = lirc_register_driver(&driver);
- if (driver.minor < 0) {
- printk(KERN_ERR LIRC_DRIVER_NAME
- ": register_chrdev failed!\n");
- result = -EIO;
- goto exit_release;
- }
- return 0;
-exit_release:
- release_region(io, 8);
-exit_serial_exit:
- lirc_serial_exit();
- return result;
-}
-
-static void __exit lirc_serial_exit_module(void)
-{
- lirc_serial_exit();
-
- free_irq(irq, (void *)&hardware);
-
- if (iommap != 0)
- release_mem_region(iommap, 8 << ioshift);
- else
- release_region(io, 8);
- lirc_unregister_driver(driver.minor);
- dprintk("cleaned up module\n");
-}
-
-
-module_init(lirc_serial_init_module);
-module_exit(lirc_serial_exit_module);
-
-MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
-MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, "
- "Christoph Bartelmus, Andrei Tanas");
-MODULE_LICENSE("GPL");
-
-module_param(type, int, S_IRUGO);
-MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
- " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug,"
- " 5 = NSLU2 RX:CTS2/TX:GreenLED)");
-
-module_param(io, int, S_IRUGO);
-MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
-
-/* some architectures (e.g. intel xscale) have memory mapped registers */
-module_param(iommap, bool, S_IRUGO);
-MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O"
- " (0 = no memory mapped io)");
-
-/*
- * some architectures (e.g. intel xscale) align the 8bit serial registers
- * on 32bit word boundaries.
- * See linux-kernel/serial/8250.c serial_in()/out()
- */
-module_param(ioshift, int, S_IRUGO);
-MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
-
-module_param(irq, int, S_IRUGO);
-MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
-
-module_param(share_irq, bool, S_IRUGO);
-MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
-
-module_param(sense, bool, S_IRUGO);
-MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
- " (0 = active high, 1 = active low )");
-
-#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
-module_param(txsense, bool, S_IRUGO);
-MODULE_PARM_DESC(txsense, "Sense of transmitter circuit"
- " (0 = active high, 1 = active low )");
-#endif
-
-module_param(softcarrier, bool, S_IRUGO);
-MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging messages");
+++ /dev/null
-/*
- * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
- *
- * lirc_sir - Device driver for use with SIR (serial infra red)
- * mode of IrDA on many notebooks.
- *
- * 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
- *
- *
- * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> :
- * added timeout and relaxed pulse detection, removed gap bug
- *
- * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> :
- * added support for Tekram Irmate 210 (sending does not work yet,
- * kind of disappointing that nobody was able to implement that
- * before),
- * major clean-up
- *
- * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
- * added support for StrongARM SA1100 embedded microprocessor
- * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King
- */
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fs.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/serial_reg.h>
-#include <linux/time.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <linux/mm.h>
-#include <linux/delay.h>
-#include <linux/poll.h>
-#include <asm/system.h>
-#include <linux/io.h>
-#include <asm/irq.h>
-#include <linux/fcntl.h>
-#ifdef LIRC_ON_SA1100
-#include <asm/hardware.h>
-#ifdef CONFIG_SA1100_COLLIE
-#include <asm/arch/tc35143.h>
-#include <asm/ucb1200.h>
-#endif
-#endif
-
-#include <linux/timer.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-/* SECTION: Definitions */
-
-/*** Tekram dongle ***/
-#ifdef LIRC_SIR_TEKRAM
-/* stolen from kernel source */
-/* definitions for Tekram dongle */
-#define TEKRAM_115200 0x00
-#define TEKRAM_57600 0x01
-#define TEKRAM_38400 0x02
-#define TEKRAM_19200 0x03
-#define TEKRAM_9600 0x04
-#define TEKRAM_2400 0x08
-
-#define TEKRAM_PW 0x10 /* Pulse select bit */
-
-/* 10bit * 1s/115200bit in milliseconds = 87ms*/
-#define TIME_CONST (10000000ul/115200ul)
-
-#endif
-
-#ifdef LIRC_SIR_ACTISYS_ACT200L
-static void init_act200(void);
-#elif defined(LIRC_SIR_ACTISYS_ACT220L)
-static void init_act220(void);
-#endif
-
-/*** SA1100 ***/
-#ifdef LIRC_ON_SA1100
-struct sa1100_ser2_registers {
- /* HSSP control register */
- unsigned char hscr0;
- /* UART registers */
- unsigned char utcr0;
- unsigned char utcr1;
- unsigned char utcr2;
- unsigned char utcr3;
- unsigned char utcr4;
- unsigned char utdr;
- unsigned char utsr0;
- unsigned char utsr1;
-} sr;
-
-static int irq = IRQ_Ser2ICP;
-
-#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0
-
-/* pulse/space ratio of 50/50 */
-static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
-/* 1000000/freq-pulse_width */
-static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
-static unsigned int freq = 38000; /* modulation frequency */
-static unsigned int duty_cycle = 50; /* duty cycle of 50% */
-
-#endif
-
-#define RBUF_LEN 1024
-#define WBUF_LEN 1024
-
-#define LIRC_DRIVER_NAME "lirc_sir"
-
-#define PULSE '['
-
-#ifndef LIRC_SIR_TEKRAM
-/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/
-#define TIME_CONST (9000000ul/115200ul)
-#endif
-
-
-/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */
-#define SIR_TIMEOUT (HZ*5/100)
-
-#ifndef LIRC_ON_SA1100
-#ifndef LIRC_IRQ
-#define LIRC_IRQ 4
-#endif
-#ifndef LIRC_PORT
-/* for external dongles, default to com1 */
-#if defined(LIRC_SIR_ACTISYS_ACT200L) || \
- defined(LIRC_SIR_ACTISYS_ACT220L) || \
- defined(LIRC_SIR_TEKRAM)
-#define LIRC_PORT 0x3f8
-#else
-/* onboard sir ports are typically com3 */
-#define LIRC_PORT 0x3e8
-#endif
-#endif
-
-static int io = LIRC_PORT;
-static int irq = LIRC_IRQ;
-static int threshold = 3;
-#endif
-
-static DEFINE_SPINLOCK(timer_lock);
-static struct timer_list timerlist;
-/* time of last signal change detected */
-static struct timeval last_tv = {0, 0};
-/* time of last UART data ready interrupt */
-static struct timeval last_intr_tv = {0, 0};
-static int last_value;
-
-static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
-
-static DEFINE_SPINLOCK(hardware_lock);
-
-static int rx_buf[RBUF_LEN];
-static unsigned int rx_tail, rx_head;
-
-static int debug;
-#define dprintk(fmt, args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
- fmt, ## args); \
- } while (0)
-
-/* SECTION: Prototypes */
-
-/* Communication with user-space */
-static unsigned int lirc_poll(struct file *file, poll_table *wait);
-static ssize_t lirc_read(struct file *file, char *buf, size_t count,
- loff_t *ppos);
-static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
- loff_t *pos);
-static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
-static void add_read_queue(int flag, unsigned long val);
-static int init_chrdev(void);
-static void drop_chrdev(void);
-/* Hardware */
-static irqreturn_t sir_interrupt(int irq, void *dev_id);
-static void send_space(unsigned long len);
-static void send_pulse(unsigned long len);
-static int init_hardware(void);
-static void drop_hardware(void);
-/* Initialisation */
-static int init_port(void);
-static void drop_port(void);
-
-#ifdef LIRC_ON_SA1100
-static void on(void)
-{
- PPSR |= PPC_TXD2;
-}
-
-static void off(void)
-{
- PPSR &= ~PPC_TXD2;
-}
-#else
-static inline unsigned int sinp(int offset)
-{
- return inb(io + offset);
-}
-
-static inline void soutp(int offset, int value)
-{
- outb(value, io + offset);
-}
-#endif
-
-#ifndef MAX_UDELAY_MS
-#define MAX_UDELAY_US 5000
-#else
-#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
-#endif
-
-static void safe_udelay(unsigned long usecs)
-{
- while (usecs > MAX_UDELAY_US) {
- udelay(MAX_UDELAY_US);
- usecs -= MAX_UDELAY_US;
- }
- udelay(usecs);
-}
-
-/* SECTION: Communication with user-space */
-
-static unsigned int lirc_poll(struct file *file, poll_table *wait)
-{
- poll_wait(file, &lirc_read_queue, wait);
- if (rx_head != rx_tail)
- return POLLIN | POLLRDNORM;
- return 0;
-}
-
-static ssize_t lirc_read(struct file *file, char *buf, size_t count,
- loff_t *ppos)
-{
- int n = 0;
- int retval = 0;
- DECLARE_WAITQUEUE(wait, current);
-
- if (count % sizeof(int))
- return -EINVAL;
-
- add_wait_queue(&lirc_read_queue, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- while (n < count) {
- if (rx_head != rx_tail) {
- if (copy_to_user((void *) buf + n,
- (void *) (rx_buf + rx_head),
- sizeof(int))) {
- retval = -EFAULT;
- break;
- }
- rx_head = (rx_head + 1) & (RBUF_LEN - 1);
- n += sizeof(int);
- } else {
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- }
- remove_wait_queue(&lirc_read_queue, &wait);
- set_current_state(TASK_RUNNING);
- return n ? n : retval;
-}
-static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
- loff_t *pos)
-{
- unsigned long flags;
- int i, count;
- int *tx_buf;
-
- count = n / sizeof(int);
- if (n % sizeof(int) || count % 2 == 0)
- return -EINVAL;
- tx_buf = memdup_user(buf, n);
- if (IS_ERR(tx_buf))
- return PTR_ERR(tx_buf);
- i = 0;
-#ifdef LIRC_ON_SA1100
- /* disable receiver */
- Ser2UTCR3 = 0;
-#endif
- local_irq_save(flags);
- while (1) {
- if (i >= count)
- break;
- if (tx_buf[i])
- send_pulse(tx_buf[i]);
- i++;
- if (i >= count)
- break;
- if (tx_buf[i])
- send_space(tx_buf[i]);
- i++;
- }
- local_irq_restore(flags);
-#ifdef LIRC_ON_SA1100
- off();
- udelay(1000); /* wait 1ms for IR diode to recover */
- Ser2UTCR3 = 0;
- /* clear status register to prevent unwanted interrupts */
- Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
- /* enable receiver */
- Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
-#endif
- kfree(tx_buf);
- return count;
-}
-
-static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
-{
- int retval = 0;
- __u32 value = 0;
-#ifdef LIRC_ON_SA1100
-
- if (cmd == LIRC_GET_FEATURES)
- value = LIRC_CAN_SEND_PULSE |
- LIRC_CAN_SET_SEND_DUTY_CYCLE |
- LIRC_CAN_SET_SEND_CARRIER |
- LIRC_CAN_REC_MODE2;
- else if (cmd == LIRC_GET_SEND_MODE)
- value = LIRC_MODE_PULSE;
- else if (cmd == LIRC_GET_REC_MODE)
- value = LIRC_MODE_MODE2;
-#else
- if (cmd == LIRC_GET_FEATURES)
- value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
- else if (cmd == LIRC_GET_SEND_MODE)
- value = LIRC_MODE_PULSE;
- else if (cmd == LIRC_GET_REC_MODE)
- value = LIRC_MODE_MODE2;
-#endif
-
- switch (cmd) {
- case LIRC_GET_FEATURES:
- case LIRC_GET_SEND_MODE:
- case LIRC_GET_REC_MODE:
- retval = put_user(value, (__u32 *) arg);
- break;
-
- case LIRC_SET_SEND_MODE:
- case LIRC_SET_REC_MODE:
- retval = get_user(value, (__u32 *) arg);
- break;
-#ifdef LIRC_ON_SA1100
- case LIRC_SET_SEND_DUTY_CYCLE:
- retval = get_user(value, (__u32 *) arg);
- if (retval)
- return retval;
- if (value <= 0 || value > 100)
- return -EINVAL;
- /* (value/100)*(1000000/freq) */
- duty_cycle = value;
- pulse_width = (unsigned long) duty_cycle*10000/freq;
- space_width = (unsigned long) 1000000L/freq-pulse_width;
- if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
- pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
- if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
- space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
- break;
- case LIRC_SET_SEND_CARRIER:
- retval = get_user(value, (__u32 *) arg);
- if (retval)
- return retval;
- if (value > 500000 || value < 20000)
- return -EINVAL;
- freq = value;
- pulse_width = (unsigned long) duty_cycle*10000/freq;
- space_width = (unsigned long) 1000000L/freq-pulse_width;
- if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
- pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
- if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
- space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
- break;
-#endif
- default:
- retval = -ENOIOCTLCMD;
-
- }
-
- if (retval)
- return retval;
- if (cmd == LIRC_SET_REC_MODE) {
- if (value != LIRC_MODE_MODE2)
- retval = -ENOSYS;
- } else if (cmd == LIRC_SET_SEND_MODE) {
- if (value != LIRC_MODE_PULSE)
- retval = -ENOSYS;
- }
-
- return retval;
-}
-
-static void add_read_queue(int flag, unsigned long val)
-{
- unsigned int new_rx_tail;
- int newval;
-
- dprintk("add flag %d with val %lu\n", flag, val);
-
- newval = val & PULSE_MASK;
-
- /*
- * statistically, pulses are ~TIME_CONST/2 too long. we could
- * maybe make this more exact, but this is good enough
- */
- if (flag) {
- /* pulse */
- if (newval > TIME_CONST/2)
- newval -= TIME_CONST/2;
- else /* should not ever happen */
- newval = 1;
- newval |= PULSE_BIT;
- } else {
- newval += TIME_CONST/2;
- }
- new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
- if (new_rx_tail == rx_head) {
- dprintk("Buffer overrun.\n");
- return;
- }
- rx_buf[rx_tail] = newval;
- rx_tail = new_rx_tail;
- wake_up_interruptible(&lirc_read_queue);
-}
-
-static const struct file_operations lirc_fops = {
- .owner = THIS_MODULE,
- .read = lirc_read,
- .write = lirc_write,
- .poll = lirc_poll,
- .unlocked_ioctl = lirc_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = lirc_ioctl,
-#endif
- .open = lirc_dev_fop_open,
- .release = lirc_dev_fop_close,
- .llseek = no_llseek,
-};
-
-static int set_use_inc(void *data)
-{
- return 0;
-}
-
-static void set_use_dec(void *data)
-{
-}
-
-static struct lirc_driver driver = {
- .name = LIRC_DRIVER_NAME,
- .minor = -1,
- .code_length = 1,
- .sample_rate = 0,
- .data = NULL,
- .add_to_buf = NULL,
- .set_use_inc = set_use_inc,
- .set_use_dec = set_use_dec,
- .fops = &lirc_fops,
- .dev = NULL,
- .owner = THIS_MODULE,
-};
-
-
-static int init_chrdev(void)
-{
- driver.minor = lirc_register_driver(&driver);
- if (driver.minor < 0) {
- printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
- return -EIO;
- }
- return 0;
-}
-
-static void drop_chrdev(void)
-{
- lirc_unregister_driver(driver.minor);
-}
-
-/* SECTION: Hardware */
-static long delta(struct timeval *tv1, struct timeval *tv2)
-{
- unsigned long deltv;
-
- deltv = tv2->tv_sec - tv1->tv_sec;
- if (deltv > 15)
- deltv = 0xFFFFFF;
- else
- deltv = deltv*1000000 +
- tv2->tv_usec -
- tv1->tv_usec;
- return deltv;
-}
-
-static void sir_timeout(unsigned long data)
-{
- /*
- * if last received signal was a pulse, but receiving stopped
- * within the 9 bit frame, we need to finish this pulse and
- * simulate a signal change to from pulse to space. Otherwise
- * upper layers will receive two sequences next time.
- */
-
- unsigned long flags;
- unsigned long pulse_end;
-
- /* avoid interference with interrupt */
- spin_lock_irqsave(&timer_lock, flags);
- if (last_value) {
-#ifndef LIRC_ON_SA1100
- /* clear unread bits in UART and restart */
- outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
-#endif
- /* determine 'virtual' pulse end: */
- pulse_end = delta(&last_tv, &last_intr_tv);
- dprintk("timeout add %d for %lu usec\n", last_value, pulse_end);
- add_read_queue(last_value, pulse_end);
- last_value = 0;
- last_tv = last_intr_tv;
- }
- spin_unlock_irqrestore(&timer_lock, flags);
-}
-
-static irqreturn_t sir_interrupt(int irq, void *dev_id)
-{
- unsigned char data;
- struct timeval curr_tv;
- static unsigned long deltv;
-#ifdef LIRC_ON_SA1100
- int status;
- static int n;
-
- status = Ser2UTSR0;
- /*
- * Deal with any receive errors first. The bytes in error may be
- * the only bytes in the receive FIFO, so we do this first.
- */
- while (status & UTSR0_EIF) {
- int bstat;
-
- if (debug) {
- dprintk("EIF\n");
- bstat = Ser2UTSR1;
-
- if (bstat & UTSR1_FRE)
- dprintk("frame error\n");
- if (bstat & UTSR1_ROR)
- dprintk("receive fifo overrun\n");
- if (bstat & UTSR1_PRE)
- dprintk("parity error\n");
- }
-
- bstat = Ser2UTDR;
- n++;
- status = Ser2UTSR0;
- }
-
- if (status & (UTSR0_RFS | UTSR0_RID)) {
- do_gettimeofday(&curr_tv);
- deltv = delta(&last_tv, &curr_tv);
- do {
- data = Ser2UTDR;
- dprintk("%d data: %u\n", n, (unsigned int) data);
- n++;
- } while (status & UTSR0_RID && /* do not empty fifo in order to
- * get UTSR0_RID in any case */
- Ser2UTSR1 & UTSR1_RNE); /* data ready */
-
- if (status&UTSR0_RID) {
- add_read_queue(0 , deltv - n * TIME_CONST); /*space*/
- add_read_queue(1, n * TIME_CONST); /*pulse*/
- n = 0;
- last_tv = curr_tv;
- }
- }
-
- if (status & UTSR0_TFS)
- printk(KERN_ERR "transmit fifo not full, shouldn't happen\n");
-
- /* We must clear certain bits. */
- status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
- if (status)
- Ser2UTSR0 = status;
-#else
- unsigned long deltintrtv;
- unsigned long flags;
- int iir, lsr;
-
- while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
- switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */
- case UART_IIR_MSI:
- (void) inb(io + UART_MSR);
- break;
- case UART_IIR_RLSI:
- (void) inb(io + UART_LSR);
- break;
- case UART_IIR_THRI:
-#if 0
- if (lsr & UART_LSR_THRE) /* FIFO is empty */
- outb(data, io + UART_TX)
-#endif
- break;
- case UART_IIR_RDI:
- /* avoid interference with timer */
- spin_lock_irqsave(&timer_lock, flags);
- do {
- del_timer(&timerlist);
- data = inb(io + UART_RX);
- do_gettimeofday(&curr_tv);
- deltv = delta(&last_tv, &curr_tv);
- deltintrtv = delta(&last_intr_tv, &curr_tv);
- dprintk("t %lu, d %d\n", deltintrtv, (int)data);
- /*
- * if nothing came in last X cycles,
- * it was gap
- */
- if (deltintrtv > TIME_CONST * threshold) {
- if (last_value) {
- dprintk("GAP\n");
- /* simulate signal change */
- add_read_queue(last_value,
- deltv -
- deltintrtv);
- last_value = 0;
- last_tv.tv_sec =
- last_intr_tv.tv_sec;
- last_tv.tv_usec =
- last_intr_tv.tv_usec;
- deltv = deltintrtv;
- }
- }
- data = 1;
- if (data ^ last_value) {
- /*
- * deltintrtv > 2*TIME_CONST, remember?
- * the other case is timeout
- */
- add_read_queue(last_value,
- deltv-TIME_CONST);
- last_value = data;
- last_tv = curr_tv;
- if (last_tv.tv_usec >= TIME_CONST) {
- last_tv.tv_usec -= TIME_CONST;
- } else {
- last_tv.tv_sec--;
- last_tv.tv_usec += 1000000 -
- TIME_CONST;
- }
- }
- last_intr_tv = curr_tv;
- if (data) {
- /*
- * start timer for end of
- * sequence detection
- */
- timerlist.expires = jiffies +
- SIR_TIMEOUT;
- add_timer(&timerlist);
- }
-
- lsr = inb(io + UART_LSR);
- } while (lsr & UART_LSR_DR); /* data ready */
- spin_unlock_irqrestore(&timer_lock, flags);
- break;
- default:
- break;
- }
- }
-#endif
- return IRQ_RETVAL(IRQ_HANDLED);
-}
-
-#ifdef LIRC_ON_SA1100
-static void send_pulse(unsigned long length)
-{
- unsigned long k, delay;
- int flag;
-
- if (length == 0)
- return;
- /*
- * this won't give us the carrier frequency we really want
- * due to integer arithmetic, but we can accept this inaccuracy
- */
-
- for (k = flag = 0; k < length; k += delay, flag = !flag) {
- if (flag) {
- off();
- delay = space_width;
- } else {
- on();
- delay = pulse_width;
- }
- safe_udelay(delay);
- }
- off();
-}
-
-static void send_space(unsigned long length)
-{
- if (length == 0)
- return;
- off();
- safe_udelay(length);
-}
-#else
-static void send_space(unsigned long len)
-{
- safe_udelay(len);
-}
-
-static void send_pulse(unsigned long len)
-{
- long bytes_out = len / TIME_CONST;
-
- if (bytes_out == 0)
- bytes_out++;
-
- while (bytes_out--) {
- outb(PULSE, io + UART_TX);
- /* FIXME treba seriozne cakanie z char/serial.c */
- while (!(inb(io + UART_LSR) & UART_LSR_THRE))
- ;
- }
-}
-#endif
-
-#ifdef CONFIG_SA1100_COLLIE
-static int sa1100_irda_set_power_collie(int state)
-{
- if (state) {
- /*
- * 0 - off
- * 1 - short range, lowest power
- * 2 - medium range, medium power
- * 3 - maximum range, high power
- */
- ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
- TC35143_IODIR_OUTPUT);
- ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW);
- udelay(100);
- } else {
- /* OFF */
- ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
- TC35143_IODIR_OUTPUT);
- ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH);
- }
- return 0;
-}
-#endif
-
-static int init_hardware(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&hardware_lock, flags);
- /* reset UART */
-#ifdef LIRC_ON_SA1100
-#ifdef CONFIG_SA1100_BITSY
- if (machine_is_bitsy()) {
- printk(KERN_INFO "Power on IR module\n");
- set_bitsy_egpio(EGPIO_BITSY_IR_ON);
- }
-#endif
-#ifdef CONFIG_SA1100_COLLIE
- sa1100_irda_set_power_collie(3); /* power on */
-#endif
- sr.hscr0 = Ser2HSCR0;
-
- sr.utcr0 = Ser2UTCR0;
- sr.utcr1 = Ser2UTCR1;
- sr.utcr2 = Ser2UTCR2;
- sr.utcr3 = Ser2UTCR3;
- sr.utcr4 = Ser2UTCR4;
-
- sr.utdr = Ser2UTDR;
- sr.utsr0 = Ser2UTSR0;
- sr.utsr1 = Ser2UTSR1;
-
- /* configure GPIO */
- /* output */
- PPDR |= PPC_TXD2;
- PSDR |= PPC_TXD2;
- /* set output to 0 */
- off();
-
- /* Enable HP-SIR modulation, and ensure that the port is disabled. */
- Ser2UTCR3 = 0;
- Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP);
-
- /* clear status register to prevent unwanted interrupts */
- Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
-
- /* 7N1 */
- Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData;
- /* 115200 */
- Ser2UTCR1 = 0;
- Ser2UTCR2 = 1;
- /* use HPSIR, 1.6 usec pulses */
- Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us;
-
- /* enable receiver, receive fifo interrupt */
- Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
-
- /* clear status register to prevent unwanted interrupts */
- Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
-
-#elif defined(LIRC_SIR_TEKRAM)
- /* disable FIFO */
- soutp(UART_FCR,
- UART_FCR_CLEAR_RCVR|
- UART_FCR_CLEAR_XMIT|
- UART_FCR_TRIGGER_1);
-
- /* Set DLAB 0. */
- soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
-
- /* First of all, disable all interrupts */
- soutp(UART_IER, sinp(UART_IER) &
- (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
-
- /* Set DLAB 1. */
- soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
-
- /* Set divisor to 12 => 9600 Baud */
- soutp(UART_DLM, 0);
- soutp(UART_DLL, 12);
-
- /* Set DLAB 0. */
- soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
-
- /* power supply */
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
- safe_udelay(50*1000);
-
- /* -DTR low -> reset PIC */
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
- udelay(1*1000);
-
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
- udelay(100);
-
-
- /* -RTS low -> send control byte */
- soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
- udelay(7);
- soutp(UART_TX, TEKRAM_115200|TEKRAM_PW);
-
- /* one byte takes ~1042 usec to transmit at 9600,8N1 */
- udelay(1500);
-
- /* back to normal operation */
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
- udelay(50);
-
- udelay(1500);
-
- /* read previous control byte */
- printk(KERN_INFO LIRC_DRIVER_NAME
- ": 0x%02x\n", sinp(UART_RX));
-
- /* Set DLAB 1. */
- soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
-
- /* Set divisor to 1 => 115200 Baud */
- soutp(UART_DLM, 0);
- soutp(UART_DLL, 1);
-
- /* Set DLAB 0, 8 Bit */
- soutp(UART_LCR, UART_LCR_WLEN8);
- /* enable interrupts */
- soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
-#else
- outb(0, io + UART_MCR);
- outb(0, io + UART_IER);
- /* init UART */
- /* set DLAB, speed = 115200 */
- outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
- outb(1, io + UART_DLL); outb(0, io + UART_DLM);
- /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
- outb(UART_LCR_WLEN7, io + UART_LCR);
- /* FIFO operation */
- outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
- /* interrupts */
- /* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */
- outb(UART_IER_RDI, io + UART_IER);
- /* turn on UART */
- outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR);
-#ifdef LIRC_SIR_ACTISYS_ACT200L
- init_act200();
-#elif defined(LIRC_SIR_ACTISYS_ACT220L)
- init_act220();
-#endif
-#endif
- spin_unlock_irqrestore(&hardware_lock, flags);
- return 0;
-}
-
-static void drop_hardware(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&hardware_lock, flags);
-
-#ifdef LIRC_ON_SA1100
- Ser2UTCR3 = 0;
-
- Ser2UTCR0 = sr.utcr0;
- Ser2UTCR1 = sr.utcr1;
- Ser2UTCR2 = sr.utcr2;
- Ser2UTCR4 = sr.utcr4;
- Ser2UTCR3 = sr.utcr3;
-
- Ser2HSCR0 = sr.hscr0;
-#ifdef CONFIG_SA1100_BITSY
- if (machine_is_bitsy())
- clr_bitsy_egpio(EGPIO_BITSY_IR_ON);
-#endif
-#ifdef CONFIG_SA1100_COLLIE
- sa1100_irda_set_power_collie(0); /* power off */
-#endif
-#else
- /* turn off interrupts */
- outb(0, io + UART_IER);
-#endif
- spin_unlock_irqrestore(&hardware_lock, flags);
-}
-
-/* SECTION: Initialisation */
-
-static int init_port(void)
-{
- int retval;
-
- /* get I/O port access and IRQ line */
-#ifndef LIRC_ON_SA1100
- if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
- printk(KERN_ERR LIRC_DRIVER_NAME
- ": i/o port 0x%.4x already in use.\n", io);
- return -EBUSY;
- }
-#endif
- retval = request_irq(irq, sir_interrupt, 0,
- LIRC_DRIVER_NAME, NULL);
- if (retval < 0) {
-# ifndef LIRC_ON_SA1100
- release_region(io, 8);
-# endif
- printk(KERN_ERR LIRC_DRIVER_NAME
- ": IRQ %d already in use.\n",
- irq);
- return retval;
- }
-#ifndef LIRC_ON_SA1100
- printk(KERN_INFO LIRC_DRIVER_NAME
- ": I/O port 0x%.4x, IRQ %d.\n",
- io, irq);
-#endif
-
- init_timer(&timerlist);
- timerlist.function = sir_timeout;
- timerlist.data = 0xabadcafe;
-
- return 0;
-}
-
-static void drop_port(void)
-{
- free_irq(irq, NULL);
- del_timer_sync(&timerlist);
-#ifndef LIRC_ON_SA1100
- release_region(io, 8);
-#endif
-}
-
-#ifdef LIRC_SIR_ACTISYS_ACT200L
-/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */
-/* some code borrowed from Linux IRDA driver */
-
-/* Register 0: Control register #1 */
-#define ACT200L_REG0 0x00
-#define ACT200L_TXEN 0x01 /* Enable transmitter */
-#define ACT200L_RXEN 0x02 /* Enable receiver */
-#define ACT200L_ECHO 0x08 /* Echo control chars */
-
-/* Register 1: Control register #2 */
-#define ACT200L_REG1 0x10
-#define ACT200L_LODB 0x01 /* Load new baud rate count value */
-#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */
-
-/* Register 3: Transmit mode register #2 */
-#define ACT200L_REG3 0x30
-#define ACT200L_B0 0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */
-#define ACT200L_B1 0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */
-#define ACT200L_CHSY 0x04 /* StartBit Synced 0=bittime, 1=startbit */
-
-/* Register 4: Output Power register */
-#define ACT200L_REG4 0x40
-#define ACT200L_OP0 0x01 /* Enable LED1C output */
-#define ACT200L_OP1 0x02 /* Enable LED2C output */
-#define ACT200L_BLKR 0x04
-
-/* Register 5: Receive Mode register */
-#define ACT200L_REG5 0x50
-#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */
- /*.. other various IRDA bit modes, and TV remote modes..*/
-
-/* Register 6: Receive Sensitivity register #1 */
-#define ACT200L_REG6 0x60
-#define ACT200L_RS0 0x01 /* receive threshold bit 0 */
-#define ACT200L_RS1 0x02 /* receive threshold bit 1 */
-
-/* Register 7: Receive Sensitivity register #2 */
-#define ACT200L_REG7 0x70
-#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */
-
-/* Register 8,9: Baud Rate Divider register #1,#2 */
-#define ACT200L_REG8 0x80
-#define ACT200L_REG9 0x90
-
-#define ACT200L_2400 0x5f
-#define ACT200L_9600 0x17
-#define ACT200L_19200 0x0b
-#define ACT200L_38400 0x05
-#define ACT200L_57600 0x03
-#define ACT200L_115200 0x01
-
-/* Register 13: Control register #3 */
-#define ACT200L_REG13 0xd0
-#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */
-
-/* Register 15: Status register */
-#define ACT200L_REG15 0xf0
-
-/* Register 21: Control register #4 */
-#define ACT200L_REG21 0x50
-#define ACT200L_EXCK 0x02 /* Disable clock output driver */
-#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */
-
-static void init_act200(void)
-{
- int i;
- __u8 control[] = {
- ACT200L_REG15,
- ACT200L_REG13 | ACT200L_SHDW,
- ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
- ACT200L_REG13,
- ACT200L_REG7 | ACT200L_ENPOS,
- ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1,
- ACT200L_REG5 | ACT200L_RWIDL,
- ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR,
- ACT200L_REG3 | ACT200L_B0,
- ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN,
- ACT200L_REG8 | (ACT200L_115200 & 0x0f),
- ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f),
- ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE
- };
-
- /* Set DLAB 1. */
- soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
-
- /* Set divisor to 12 => 9600 Baud */
- soutp(UART_DLM, 0);
- soutp(UART_DLL, 12);
-
- /* Set DLAB 0. */
- soutp(UART_LCR, UART_LCR_WLEN8);
- /* Set divisor to 12 => 9600 Baud */
-
- /* power supply */
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
- for (i = 0; i < 50; i++)
- safe_udelay(1000);
-
- /* Reset the dongle : set RTS low for 25 ms */
- soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
- for (i = 0; i < 25; i++)
- udelay(1000);
-
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
- udelay(100);
-
- /* Clear DTR and set RTS to enter command mode */
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
- udelay(7);
-
- /* send out the control register settings for 115K 7N1 SIR operation */
- for (i = 0; i < sizeof(control); i++) {
- soutp(UART_TX, control[i]);
- /* one byte takes ~1042 usec to transmit at 9600,8N1 */
- udelay(1500);
- }
-
- /* back to normal operation */
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
- udelay(50);
-
- udelay(1500);
- soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
-
- /* Set DLAB 1. */
- soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
-
- /* Set divisor to 1 => 115200 Baud */
- soutp(UART_DLM, 0);
- soutp(UART_DLL, 1);
-
- /* Set DLAB 0. */
- soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
-
- /* Set DLAB 0, 7 Bit */
- soutp(UART_LCR, UART_LCR_WLEN7);
-
- /* enable interrupts */
- soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
-}
-#endif
-
-#ifdef LIRC_SIR_ACTISYS_ACT220L
-/*
- * Derived from linux IrDA driver (net/irda/actisys.c)
- * Drop me a mail for any kind of comment: maxx@spaceboyz.net
- */
-
-void init_act220(void)
-{
- int i;
-
- /* DLAB 1 */
- soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7);
-
- /* 9600 baud */
- soutp(UART_DLM, 0);
- soutp(UART_DLL, 12);
-
- /* DLAB 0 */
- soutp(UART_LCR, UART_LCR_WLEN7);
-
- /* reset the dongle, set DTR low for 10us */
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
- udelay(10);
-
- /* back to normal (still 9600) */
- soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2);
-
- /*
- * send RTS pulses until we reach 115200
- * i hope this is really the same for act220l/act220l+
- */
- for (i = 0; i < 3; i++) {
- udelay(10);
- /* set RTS low for 10 us */
- soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
- udelay(10);
- /* set RTS high for 10 us */
- soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
- }
-
- /* back to normal operation */
- udelay(1500); /* better safe than sorry ;) */
-
- /* Set DLAB 1. */
- soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
-
- /* Set divisor to 1 => 115200 Baud */
- soutp(UART_DLM, 0);
- soutp(UART_DLL, 1);
-
- /* Set DLAB 0, 7 Bit */
- /* The dongle doesn't seem to have any problems with operation at 7N1 */
- soutp(UART_LCR, UART_LCR_WLEN7);
-
- /* enable interrupts */
- soutp(UART_IER, UART_IER_RDI);
-}
-#endif
-
-static int init_lirc_sir(void)
-{
- int retval;
-
- init_waitqueue_head(&lirc_read_queue);
- retval = init_port();
- if (retval < 0)
- return retval;
- init_hardware();
- printk(KERN_INFO LIRC_DRIVER_NAME
- ": Installed.\n");
- return 0;
-}
-
-
-static int __init lirc_sir_init(void)
-{
- int retval;
-
- retval = init_chrdev();
- if (retval < 0)
- return retval;
- retval = init_lirc_sir();
- if (retval) {
- drop_chrdev();
- return retval;
- }
- return 0;
-}
-
-static void __exit lirc_sir_exit(void)
-{
- drop_hardware();
- drop_chrdev();
- drop_port();
- printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
-}
-
-module_init(lirc_sir_init);
-module_exit(lirc_sir_exit);
-
-#ifdef LIRC_SIR_TEKRAM
-MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210");
-MODULE_AUTHOR("Christoph Bartelmus");
-#elif defined(LIRC_ON_SA1100)
-MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor");
-MODULE_AUTHOR("Christoph Bartelmus");
-#elif defined(LIRC_SIR_ACTISYS_ACT200L)
-MODULE_DESCRIPTION("LIRC driver for Actisys Act200L");
-MODULE_AUTHOR("Karl Bongers");
-#elif defined(LIRC_SIR_ACTISYS_ACT220L)
-MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)");
-MODULE_AUTHOR("Jan Roemisch");
-#else
-MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
-MODULE_AUTHOR("Milan Pikula");
-#endif
-MODULE_LICENSE("GPL");
-
-#ifdef LIRC_ON_SA1100
-module_param(irq, int, S_IRUGO);
-MODULE_PARM_DESC(irq, "Interrupt (16)");
-#else
-module_param(io, int, S_IRUGO);
-MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
-
-module_param(irq, int, S_IRUGO);
-MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
-
-module_param(threshold, int, S_IRUGO);
-MODULE_PARM_DESC(threshold, "space detection threshold (3)");
-#endif
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging messages");
+++ /dev/null
-/*
- * lirc_ttusbir.c
- *
- * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver
- *
- * Copyright (C) 2007 Stefan Macher <st_maker-lirc@yahoo.de>
- *
- * This LIRC driver provides access to the TechnoTrend USB IR Receiver.
- * The receiver delivers the IR signal as raw sampled true/false data in
- * isochronous USB packets each of size 128 byte.
- * Currently the driver reduces the sampling rate by factor of 8 as this
- * is still more than enough to decode RC-5 - others should be analyzed.
- * But the driver does not rely on RC-5 it should be able to decode every
- * IR signal that is not too fast.
- */
-
-/*
- * 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/init.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC");
-MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)");
-MODULE_LICENSE("GPL");
-
-/* #define DEBUG */
-#ifdef DEBUG
-#define DPRINTK printk
-#else
-#define DPRINTK(_x_, a...)
-#endif
-
-/* function declarations */
-static int probe(struct usb_interface *intf, const struct usb_device_id *id);
-static void disconnect(struct usb_interface *intf);
-static void urb_complete(struct urb *urb);
-static int set_use_inc(void *data);
-static void set_use_dec(void *data);
-
-static int num_urbs = 2;
-module_param(num_urbs, int, S_IRUGO);
-MODULE_PARM_DESC(num_urbs,
- "Number of URBs in queue. Try to increase to 4 in case "
- "of problems (default: 2; minimum: 2)");
-
-/* table of devices that work with this driver */
-static struct usb_device_id device_id_table[] = {
- /* TechnoTrend USB IR Receiver */
- { USB_DEVICE(0x0B48, 0x2003) },
- /* Terminating entry */
- { }
-};
-MODULE_DEVICE_TABLE(usb, device_id_table);
-
-/* USB driver definition */
-static struct usb_driver usb_driver = {
- .name = "TTUSBIR",
- .id_table = &(device_id_table[0]),
- .probe = probe,
- .disconnect = disconnect,
-};
-
-/* USB device definition */
-struct ttusbir_device {
- struct usb_driver *usb_driver;
- struct usb_device *udev;
- struct usb_interface *interf;
- struct usb_class_driver class_driver;
- unsigned int ifnum; /* Interface number to use */
- unsigned int alt_setting; /* alternate setting to use */
- unsigned int endpoint; /* Endpoint to use */
- struct urb **urb; /* num_urb URB pointers*/
- char **buffer; /* 128 byte buffer for each URB */
- struct lirc_buffer rbuf; /* Buffer towards LIRC */
- struct lirc_driver driver;
- int minor;
- int last_pulse; /* remembers if last received byte was pulse or space */
- int last_num; /* remembers how many last bytes appeared */
- int opened;
-};
-
-/*** LIRC specific functions ***/
-static int set_use_inc(void *data)
-{
- int i, retval;
- struct ttusbir_device *ttusbir = data;
-
- DPRINTK("Sending first URBs\n");
- /* @TODO Do I need to check if I am already opened */
- ttusbir->opened = 1;
-
- for (i = 0; i < num_urbs; i++) {
- retval = usb_submit_urb(ttusbir->urb[i], GFP_KERNEL);
- if (retval) {
- err("%s: usb_submit_urb failed on urb %d",
- __func__, i);
- return retval;
- }
- }
- return 0;
-}
-
-static void set_use_dec(void *data)
-{
- struct ttusbir_device *ttusbir = data;
-
- DPRINTK("Device closed\n");
-
- ttusbir->opened = 0;
-}
-
-/*** USB specific functions ***/
-
-/*
- * This mapping table is used to do a very simple filtering of the
- * input signal.
- * For a value with at least 4 bits set it returns 0xFF otherwise
- * 0x00. For faster IR signals this can not be used. But for RC-5 we
- * still have about 14 samples per pulse/space, i.e. we sample with 14
- * times higher frequency than the signal frequency
- */
-const unsigned char map_table[] = {
- 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-};
-
-static void urb_complete(struct urb *urb)
-{
- struct ttusbir_device *ttusbir;
- unsigned char *buf;
- int i;
- int l;
-
- ttusbir = urb->context;
-
- if (!ttusbir->opened)
- return;
-
- buf = (unsigned char *)urb->transfer_buffer;
-
- for (i = 0; i < 128; i++) {
- /* Here we do the filtering and some kind of down sampling */
- buf[i] = ~map_table[buf[i]];
- if (ttusbir->last_pulse == buf[i]) {
- if (ttusbir->last_num < PULSE_MASK/63)
- ttusbir->last_num++;
- /*
- * else we are in a idle period and do not need to
- * increment any longer
- */
- } else {
- l = ttusbir->last_num * 62; /* about 62 = us/byte */
- if (ttusbir->last_pulse) /* pulse or space? */
- l |= PULSE_BIT;
- if (!lirc_buffer_full(&ttusbir->rbuf)) {
- lirc_buffer_write(&ttusbir->rbuf, (void *)&l);
- wake_up_interruptible(&ttusbir->rbuf.wait_poll);
- }
- ttusbir->last_num = 0;
- ttusbir->last_pulse = buf[i];
- }
- }
- usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */
-}
-
-/*
- * Called whenever the USB subsystem thinks we could be the right driver
- * to handle this device
- */
-static int probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
- int alt_set, endp;
- int found = 0;
- int i, j;
- int struct_size;
- struct usb_host_interface *host_interf;
- struct usb_interface_descriptor *interf_desc;
- struct usb_host_endpoint *host_endpoint;
- struct ttusbir_device *ttusbir;
-
- DPRINTK("Module ttusbir probe\n");
-
- /* To reduce memory fragmentation we use only one allocation */
- struct_size = sizeof(struct ttusbir_device) +
- (sizeof(struct urb *) * num_urbs) +
- (sizeof(char *) * num_urbs) +
- (num_urbs * 128);
- ttusbir = kzalloc(struct_size, GFP_KERNEL);
- if (!ttusbir)
- return -ENOMEM;
-
- ttusbir->urb = (struct urb **)((char *)ttusbir +
- sizeof(struct ttusbir_device));
- ttusbir->buffer = (char **)((char *)ttusbir->urb +
- (sizeof(struct urb *) * num_urbs));
- for (i = 0; i < num_urbs; i++)
- ttusbir->buffer[i] = (char *)ttusbir->buffer +
- (sizeof(char *)*num_urbs) + (i * 128);
-
- ttusbir->usb_driver = &usb_driver;
- ttusbir->alt_setting = -1;
- /* @TODO check if error can be returned */
- ttusbir->udev = usb_get_dev(interface_to_usbdev(intf));
- ttusbir->interf = intf;
- ttusbir->last_pulse = 0x00;
- ttusbir->last_num = 0;
-
- /*
- * Now look for interface setting we can handle
- * We are searching for the alt setting where end point
- * 0x82 has max packet size 16
- */
- for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) {
- host_interf = &intf->altsetting[alt_set];
- interf_desc = &host_interf->desc;
- for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) {
- host_endpoint = &host_interf->endpoint[endp];
- if ((host_endpoint->desc.bEndpointAddress == 0x82) &&
- (host_endpoint->desc.wMaxPacketSize == 0x10)) {
- ttusbir->alt_setting = alt_set;
- ttusbir->endpoint = endp;
- found = 1;
- break;
- }
- }
- }
- if (ttusbir->alt_setting != -1)
- DPRINTK("alt setting: %d\n", ttusbir->alt_setting);
- else {
- err("Could not find alternate setting\n");
- kfree(ttusbir);
- return -EINVAL;
- }
-
- /* OK lets setup this interface setting */
- usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting);
-
- /* Store device info in interface structure */
- usb_set_intfdata(intf, ttusbir);
-
- /* Register as a LIRC driver */
- if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) {
- err("Could not get memory for LIRC data buffer\n");
- usb_set_intfdata(intf, NULL);
- kfree(ttusbir);
- return -ENOMEM;
- }
- strcpy(ttusbir->driver.name, "TTUSBIR");
- ttusbir->driver.minor = -1;
- ttusbir->driver.code_length = 1;
- ttusbir->driver.sample_rate = 0;
- ttusbir->driver.data = ttusbir;
- ttusbir->driver.add_to_buf = NULL;
- ttusbir->driver.rbuf = &ttusbir->rbuf;
- ttusbir->driver.set_use_inc = set_use_inc;
- ttusbir->driver.set_use_dec = set_use_dec;
- ttusbir->driver.dev = &intf->dev;
- ttusbir->driver.owner = THIS_MODULE;
- ttusbir->driver.features = LIRC_CAN_REC_MODE2;
- ttusbir->minor = lirc_register_driver(&ttusbir->driver);
- if (ttusbir->minor < 0) {
- err("Error registering as LIRC driver\n");
- usb_set_intfdata(intf, NULL);
- lirc_buffer_free(&ttusbir->rbuf);
- kfree(ttusbir);
- return -EIO;
- }
-
- /* Allocate and setup the URB that we will use to talk to the device */
- for (i = 0; i < num_urbs; i++) {
- ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL);
- if (!ttusbir->urb[i]) {
- err("Could not allocate memory for the URB\n");
- for (j = i - 1; j >= 0; j--)
- kfree(ttusbir->urb[j]);
- lirc_buffer_free(&ttusbir->rbuf);
- lirc_unregister_driver(ttusbir->minor);
- kfree(ttusbir);
- usb_set_intfdata(intf, NULL);
- return -ENOMEM;
- }
- ttusbir->urb[i]->dev = ttusbir->udev;
- ttusbir->urb[i]->context = ttusbir;
- ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev,
- ttusbir->endpoint);
- ttusbir->urb[i]->interval = 1;
- ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP;
- ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0];
- ttusbir->urb[i]->complete = urb_complete;
- ttusbir->urb[i]->number_of_packets = 8;
- ttusbir->urb[i]->transfer_buffer_length = 128;
- for (j = 0; j < 8; j++) {
- ttusbir->urb[i]->iso_frame_desc[j].offset = j*16;
- ttusbir->urb[i]->iso_frame_desc[j].length = 16;
- }
- }
- return 0;
-}
-
-/**
- * Called when the driver is unloaded or the device is unplugged
- */
-static void disconnect(struct usb_interface *intf)
-{
- int i;
- struct ttusbir_device *ttusbir;
-
- DPRINTK("Module ttusbir disconnect\n");
-
- ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf);
- usb_set_intfdata(intf, NULL);
- lirc_unregister_driver(ttusbir->minor);
- DPRINTK("unregistered\n");
-
- for (i = 0; i < num_urbs; i++) {
- usb_kill_urb(ttusbir->urb[i]);
- usb_free_urb(ttusbir->urb[i]);
- }
- DPRINTK("URBs killed\n");
- lirc_buffer_free(&ttusbir->rbuf);
- kfree(ttusbir);
-}
-
-static int ttusbir_init_module(void)
-{
- int result;
-
- DPRINTK(KERN_DEBUG "Module ttusbir init\n");
-
- /* register this driver with the USB subsystem */
- result = usb_register(&usb_driver);
- if (result)
- err("usb_register failed. Error number %d", result);
- return result;
-}
-
-static void ttusbir_exit_module(void)
-{
- printk(KERN_DEBUG "Module ttusbir exit\n");
- usb_deregister(&usb_driver);
-}
-
-module_init(ttusbir_init_module);
-module_exit(ttusbir_exit_module);
+++ /dev/null
-/*
- * i2c IR lirc driver for devices with zilog IR processors
- *
- * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
- * modified for PixelView (BT878P+W/FM) by
- * Michal Kochanowicz <mkochano@pld.org.pl>
- * Christoph Bartelmus <lirc@bartelmus.de>
- * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
- * Ulrich Mueller <ulrich.mueller42@web.de>
- * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
- * Stefan Jahn <stefan@lkcc.org>
- * modified for inclusion into kernel sources by
- * Jerome Brock <jbrock@users.sourceforge.net>
- * modified for Leadtek Winfast PVR2000 by
- * Thomas Reitmayr (treitmayr@yahoo.com)
- * modified for Hauppauge PVR-150 IR TX device by
- * Mark Weaver <mark@npsl.co.uk>
- * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150
- * Jarod Wilson <jarod@redhat.com>
- *
- * parts are cut&pasted from the lirc_i2c.c driver
- *
- * Numerous changes updating lirc_zilog.c in kernel 2.6.38 and later are
- * Copyright (C) 2011 Andy Walls <awalls@md.metrocast.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/module.h>
-#include <linux/kmod.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/completion.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/firmware.h>
-#include <linux/vmalloc.h>
-
-#include <linux/mutex.h>
-#include <linux/kthread.h>
-
-#include <media/lirc_dev.h>
-#include <media/lirc.h>
-
-struct IR;
-
-struct IR_rx {
- struct kref ref;
- struct IR *ir;
-
- /* RX device */
- struct mutex client_lock;
- struct i2c_client *c;
-
- /* RX polling thread data */
- struct task_struct *task;
-
- /* RX read data */
- unsigned char b[3];
- bool hdpvr_data_fmt;
-};
-
-struct IR_tx {
- struct kref ref;
- struct IR *ir;
-
- /* TX device */
- struct mutex client_lock;
- struct i2c_client *c;
-
- /* TX additional actions needed */
- int need_boot;
- bool post_tx_ready_poll;
-};
-
-struct IR {
- struct kref ref;
- struct list_head list;
-
- /* FIXME spinlock access to l.features */
- struct lirc_driver l;
- struct lirc_buffer rbuf;
-
- struct mutex ir_lock;
- atomic_t open_count;
-
- struct i2c_adapter *adapter;
-
- spinlock_t rx_ref_lock; /* struct IR_rx kref get()/put() */
- struct IR_rx *rx;
-
- spinlock_t tx_ref_lock; /* struct IR_tx kref get()/put() */
- struct IR_tx *tx;
-};
-
-/* IR transceiver instance object list */
-/*
- * This lock is used for the following:
- * a. ir_devices_list access, insertions, deletions
- * b. struct IR kref get()s and put()s
- * c. serialization of ir_probe() for the two i2c_clients for a Z8
- */
-static DEFINE_MUTEX(ir_devices_lock);
-static LIST_HEAD(ir_devices_list);
-
-/* Block size for IR transmitter */
-#define TX_BLOCK_SIZE 99
-
-/* Hauppauge IR transmitter data */
-struct tx_data_struct {
- /* Boot block */
- unsigned char *boot_data;
-
- /* Start of binary data block */
- unsigned char *datap;
-
- /* End of binary data block */
- unsigned char *endp;
-
- /* Number of installed codesets */
- unsigned int num_code_sets;
-
- /* Pointers to codesets */
- unsigned char **code_sets;
-
- /* Global fixed data template */
- int fixed[TX_BLOCK_SIZE];
-};
-
-static struct tx_data_struct *tx_data;
-static struct mutex tx_data_lock;
-
-#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \
- ## args)
-#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
-#define zilog_info(s, args...) printk(KERN_INFO KBUILD_MODNAME ": " s, ## args)
-
-/* module parameters */
-static int debug; /* debug output */
-static int tx_only; /* only handle the IR Tx function */
-static int minor = -1; /* minor number */
-
-#define dprintk(fmt, args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG KBUILD_MODNAME ": " fmt, \
- ## args); \
- } while (0)
-
-
-/* struct IR reference counting */
-static struct IR *get_ir_device(struct IR *ir, bool ir_devices_lock_held)
-{
- if (ir_devices_lock_held) {
- kref_get(&ir->ref);
- } else {
- mutex_lock(&ir_devices_lock);
- kref_get(&ir->ref);
- mutex_unlock(&ir_devices_lock);
- }
- return ir;
-}
-
-static void release_ir_device(struct kref *ref)
-{
- struct IR *ir = container_of(ref, struct IR, ref);
-
- /*
- * Things should be in this state by now:
- * ir->rx set to NULL and deallocated - happens before ir->rx->ir put()
- * ir->rx->task kthread stopped - happens before ir->rx->ir put()
- * ir->tx set to NULL and deallocated - happens before ir->tx->ir put()
- * ir->open_count == 0 - happens on final close()
- * ir_lock, tx_ref_lock, rx_ref_lock, all released
- */
- if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
- lirc_unregister_driver(ir->l.minor);
- ir->l.minor = MAX_IRCTL_DEVICES;
- }
- if (ir->rbuf.fifo_initialized)
- lirc_buffer_free(&ir->rbuf);
- list_del(&ir->list);
- kfree(ir);
-}
-
-static int put_ir_device(struct IR *ir, bool ir_devices_lock_held)
-{
- int released;
-
- if (ir_devices_lock_held)
- return kref_put(&ir->ref, release_ir_device);
-
- mutex_lock(&ir_devices_lock);
- released = kref_put(&ir->ref, release_ir_device);
- mutex_unlock(&ir_devices_lock);
-
- return released;
-}
-
-/* struct IR_rx reference counting */
-static struct IR_rx *get_ir_rx(struct IR *ir)
-{
- struct IR_rx *rx;
-
- spin_lock(&ir->rx_ref_lock);
- rx = ir->rx;
- if (rx != NULL)
- kref_get(&rx->ref);
- spin_unlock(&ir->rx_ref_lock);
- return rx;
-}
-
-static void destroy_rx_kthread(struct IR_rx *rx, bool ir_devices_lock_held)
-{
- /* end up polling thread */
- if (!IS_ERR_OR_NULL(rx->task)) {
- kthread_stop(rx->task);
- rx->task = NULL;
- /* Put the ir ptr that ir_probe() gave to the rx poll thread */
- put_ir_device(rx->ir, ir_devices_lock_held);
- }
-}
-
-static void release_ir_rx(struct kref *ref)
-{
- struct IR_rx *rx = container_of(ref, struct IR_rx, ref);
- struct IR *ir = rx->ir;
-
- /*
- * This release function can't do all the work, as we want
- * to keep the rx_ref_lock a spinlock, and killing the poll thread
- * and releasing the ir reference can cause a sleep. That work is
- * performed by put_ir_rx()
- */
- ir->l.features &= ~LIRC_CAN_REC_LIRCCODE;
- /* Don't put_ir_device(rx->ir) here; lock can't be freed yet */
- ir->rx = NULL;
- /* Don't do the kfree(rx) here; we still need to kill the poll thread */
- return;
-}
-
-static int put_ir_rx(struct IR_rx *rx, bool ir_devices_lock_held)
-{
- int released;
- struct IR *ir = rx->ir;
-
- spin_lock(&ir->rx_ref_lock);
- released = kref_put(&rx->ref, release_ir_rx);
- spin_unlock(&ir->rx_ref_lock);
- /* Destroy the rx kthread while not holding the spinlock */
- if (released) {
- destroy_rx_kthread(rx, ir_devices_lock_held);
- kfree(rx);
- /* Make sure we're not still in a poll_table somewhere */
- wake_up_interruptible(&ir->rbuf.wait_poll);
- }
- /* Do a reference put() for the rx->ir reference, if we released rx */
- if (released)
- put_ir_device(ir, ir_devices_lock_held);
- return released;
-}
-
-/* struct IR_tx reference counting */
-static struct IR_tx *get_ir_tx(struct IR *ir)
-{
- struct IR_tx *tx;
-
- spin_lock(&ir->tx_ref_lock);
- tx = ir->tx;
- if (tx != NULL)
- kref_get(&tx->ref);
- spin_unlock(&ir->tx_ref_lock);
- return tx;
-}
-
-static void release_ir_tx(struct kref *ref)
-{
- struct IR_tx *tx = container_of(ref, struct IR_tx, ref);
- struct IR *ir = tx->ir;
-
- ir->l.features &= ~LIRC_CAN_SEND_PULSE;
- /* Don't put_ir_device(tx->ir) here, so our lock doesn't get freed */
- ir->tx = NULL;
- kfree(tx);
-}
-
-static int put_ir_tx(struct IR_tx *tx, bool ir_devices_lock_held)
-{
- int released;
- struct IR *ir = tx->ir;
-
- spin_lock(&ir->tx_ref_lock);
- released = kref_put(&tx->ref, release_ir_tx);
- spin_unlock(&ir->tx_ref_lock);
- /* Do a reference put() for the tx->ir reference, if we released tx */
- if (released)
- put_ir_device(ir, ir_devices_lock_held);
- return released;
-}
-
-static int add_to_buf(struct IR *ir)
-{
- __u16 code;
- unsigned char codes[2];
- unsigned char keybuf[6];
- int got_data = 0;
- int ret;
- int failures = 0;
- unsigned char sendbuf[1] = { 0 };
- struct lirc_buffer *rbuf = ir->l.rbuf;
- struct IR_rx *rx;
- struct IR_tx *tx;
-
- if (lirc_buffer_full(rbuf)) {
- dprintk("buffer overflow\n");
- return -EOVERFLOW;
- }
-
- rx = get_ir_rx(ir);
- if (rx == NULL)
- return -ENXIO;
-
- /* Ensure our rx->c i2c_client remains valid for the duration */
- mutex_lock(&rx->client_lock);
- if (rx->c == NULL) {
- mutex_unlock(&rx->client_lock);
- put_ir_rx(rx, false);
- return -ENXIO;
- }
-
- tx = get_ir_tx(ir);
-
- /*
- * service the device as long as it is returning
- * data and we have space
- */
- do {
- if (kthread_should_stop()) {
- ret = -ENODATA;
- break;
- }
-
- /*
- * Lock i2c bus for the duration. RX/TX chips interfere so
- * this is worth it
- */
- mutex_lock(&ir->ir_lock);
-
- if (kthread_should_stop()) {
- mutex_unlock(&ir->ir_lock);
- ret = -ENODATA;
- break;
- }
-
- /*
- * Send random "poll command" (?) Windows driver does this
- * and it is a good point to detect chip failure.
- */
- ret = i2c_master_send(rx->c, sendbuf, 1);
- if (ret != 1) {
- zilog_error("i2c_master_send failed with %d\n", ret);
- if (failures >= 3) {
- mutex_unlock(&ir->ir_lock);
- zilog_error("unable to read from the IR chip "
- "after 3 resets, giving up\n");
- break;
- }
-
- /* Looks like the chip crashed, reset it */
- zilog_error("polling the IR receiver chip failed, "
- "trying reset\n");
-
- set_current_state(TASK_UNINTERRUPTIBLE);
- if (kthread_should_stop()) {
- mutex_unlock(&ir->ir_lock);
- ret = -ENODATA;
- break;
- }
- schedule_timeout((100 * HZ + 999) / 1000);
- if (tx != NULL)
- tx->need_boot = 1;
-
- ++failures;
- mutex_unlock(&ir->ir_lock);
- ret = 0;
- continue;
- }
-
- if (kthread_should_stop()) {
- mutex_unlock(&ir->ir_lock);
- ret = -ENODATA;
- break;
- }
- ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf));
- mutex_unlock(&ir->ir_lock);
- if (ret != sizeof(keybuf)) {
- zilog_error("i2c_master_recv failed with %d -- "
- "keeping last read buffer\n", ret);
- } else {
- rx->b[0] = keybuf[3];
- rx->b[1] = keybuf[4];
- rx->b[2] = keybuf[5];
- dprintk("key (0x%02x/0x%02x)\n", rx->b[0], rx->b[1]);
- }
-
- /* key pressed ? */
- if (rx->hdpvr_data_fmt) {
- if (got_data && (keybuf[0] == 0x80)) {
- ret = 0;
- break;
- } else if (got_data && (keybuf[0] == 0x00)) {
- ret = -ENODATA;
- break;
- }
- } else if ((rx->b[0] & 0x80) == 0) {
- ret = got_data ? 0 : -ENODATA;
- break;
- }
-
- /* look what we have */
- code = (((__u16)rx->b[0] & 0x7f) << 6) | (rx->b[1] >> 2);
-
- codes[0] = (code >> 8) & 0xff;
- codes[1] = code & 0xff;
-
- /* return it */
- lirc_buffer_write(rbuf, codes);
- ++got_data;
- ret = 0;
- } while (!lirc_buffer_full(rbuf));
-
- mutex_unlock(&rx->client_lock);
- if (tx != NULL)
- put_ir_tx(tx, false);
- put_ir_rx(rx, false);
- return ret;
-}
-
-/*
- * Main function of the polling thread -- from lirc_dev.
- * We don't fit the LIRC model at all anymore. This is horrible, but
- * basically we have a single RX/TX device with a nasty failure mode
- * that needs to be accounted for across the pair. lirc lets us provide
- * fops, but prevents us from using the internal polling, etc. if we do
- * so. Hence the replication. Might be neater to extend the LIRC model
- * to account for this but I'd think it's a very special case of seriously
- * messed up hardware.
- */
-static int lirc_thread(void *arg)
-{
- struct IR *ir = arg;
- struct lirc_buffer *rbuf = ir->l.rbuf;
-
- dprintk("poll thread started\n");
-
- while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
-
- /* if device not opened, we can sleep half a second */
- if (atomic_read(&ir->open_count) == 0) {
- schedule_timeout(HZ/2);
- continue;
- }
-
- /*
- * This is ~113*2 + 24 + jitter (2*repeat gap + code length).
- * We use this interval as the chip resets every time you poll
- * it (bad!). This is therefore just sufficient to catch all
- * of the button presses. It makes the remote much more
- * responsive. You can see the difference by running irw and
- * holding down a button. With 100ms, the old polling
- * interval, you'll notice breaks in the repeat sequence
- * corresponding to lost keypresses.
- */
- schedule_timeout((260 * HZ) / 1000);
- if (kthread_should_stop())
- break;
- if (!add_to_buf(ir))
- wake_up_interruptible(&rbuf->wait_poll);
- }
-
- dprintk("poll thread ended\n");
- return 0;
-}
-
-static int set_use_inc(void *data)
-{
- return 0;
-}
-
-static void set_use_dec(void *data)
-{
- return;
-}
-
-/* safe read of a uint32 (always network byte order) */
-static int read_uint32(unsigned char **data,
- unsigned char *endp, unsigned int *val)
-{
- if (*data + 4 > endp)
- return 0;
- *val = ((*data)[0] << 24) | ((*data)[1] << 16) |
- ((*data)[2] << 8) | (*data)[3];
- *data += 4;
- return 1;
-}
-
-/* safe read of a uint8 */
-static int read_uint8(unsigned char **data,
- unsigned char *endp, unsigned char *val)
-{
- if (*data + 1 > endp)
- return 0;
- *val = *((*data)++);
- return 1;
-}
-
-/* safe skipping of N bytes */
-static int skip(unsigned char **data,
- unsigned char *endp, unsigned int distance)
-{
- if (*data + distance > endp)
- return 0;
- *data += distance;
- return 1;
-}
-
-/* decompress key data into the given buffer */
-static int get_key_data(unsigned char *buf,
- unsigned int codeset, unsigned int key)
-{
- unsigned char *data, *endp, *diffs, *key_block;
- unsigned char keys, ndiffs, id;
- unsigned int base, lim, pos, i;
-
- /* Binary search for the codeset */
- for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) {
- pos = base + (lim >> 1);
- data = tx_data->code_sets[pos];
-
- if (!read_uint32(&data, tx_data->endp, &i))
- goto corrupt;
-
- if (i == codeset)
- break;
- else if (codeset > i) {
- base = pos + 1;
- --lim;
- }
- }
- /* Not found? */
- if (!lim)
- return -EPROTO;
-
- /* Set end of data block */
- endp = pos < tx_data->num_code_sets - 1 ?
- tx_data->code_sets[pos + 1] : tx_data->endp;
-
- /* Read the block header */
- if (!read_uint8(&data, endp, &keys) ||
- !read_uint8(&data, endp, &ndiffs) ||
- ndiffs > TX_BLOCK_SIZE || keys == 0)
- goto corrupt;
-
- /* Save diffs & skip */
- diffs = data;
- if (!skip(&data, endp, ndiffs))
- goto corrupt;
-
- /* Read the id of the first key */
- if (!read_uint8(&data, endp, &id))
- goto corrupt;
-
- /* Unpack the first key's data */
- for (i = 0; i < TX_BLOCK_SIZE; ++i) {
- if (tx_data->fixed[i] == -1) {
- if (!read_uint8(&data, endp, &buf[i]))
- goto corrupt;
- } else {
- buf[i] = (unsigned char)tx_data->fixed[i];
- }
- }
-
- /* Early out key found/not found */
- if (key == id)
- return 0;
- if (keys == 1)
- return -EPROTO;
-
- /* Sanity check */
- key_block = data;
- if (!skip(&data, endp, (keys - 1) * (ndiffs + 1)))
- goto corrupt;
-
- /* Binary search for the key */
- for (base = 0, lim = keys - 1; lim; lim >>= 1) {
- /* Seek to block */
- unsigned char *key_data;
- pos = base + (lim >> 1);
- key_data = key_block + (ndiffs + 1) * pos;
-
- if (*key_data == key) {
- /* skip key id */
- ++key_data;
-
- /* found, so unpack the diffs */
- for (i = 0; i < ndiffs; ++i) {
- unsigned char val;
- if (!read_uint8(&key_data, endp, &val) ||
- diffs[i] >= TX_BLOCK_SIZE)
- goto corrupt;
- buf[diffs[i]] = val;
- }
-
- return 0;
- } else if (key > *key_data) {
- base = pos + 1;
- --lim;
- }
- }
- /* Key not found */
- return -EPROTO;
-
-corrupt:
- zilog_error("firmware is corrupt\n");
- return -EFAULT;
-}
-
-/* send a block of data to the IR TX device */
-static int send_data_block(struct IR_tx *tx, unsigned char *data_block)
-{
- int i, j, ret;
- unsigned char buf[5];
-
- for (i = 0; i < TX_BLOCK_SIZE;) {
- int tosend = TX_BLOCK_SIZE - i;
- if (tosend > 4)
- tosend = 4;
- buf[0] = (unsigned char)(i + 1);
- for (j = 0; j < tosend; ++j)
- buf[1 + j] = data_block[i + j];
- dprintk("%02x %02x %02x %02x %02x",
- buf[0], buf[1], buf[2], buf[3], buf[4]);
- ret = i2c_master_send(tx->c, buf, tosend + 1);
- if (ret != tosend + 1) {
- zilog_error("i2c_master_send failed with %d\n", ret);
- return ret < 0 ? ret : -EFAULT;
- }
- i += tosend;
- }
- return 0;
-}
-
-/* send boot data to the IR TX device */
-static int send_boot_data(struct IR_tx *tx)
-{
- int ret, i;
- unsigned char buf[4];
-
- /* send the boot block */
- ret = send_data_block(tx, tx_data->boot_data);
- if (ret != 0)
- return ret;
-
- /* Hit the go button to activate the new boot data */
- buf[0] = 0x00;
- buf[1] = 0x20;
- ret = i2c_master_send(tx->c, buf, 2);
- if (ret != 2) {
- zilog_error("i2c_master_send failed with %d\n", ret);
- return ret < 0 ? ret : -EFAULT;
- }
-
- /*
- * Wait for zilog to settle after hitting go post boot block upload.
- * Without this delay, the HD-PVR and HVR-1950 both return an -EIO
- * upon attempting to get firmware revision, and tx probe thus fails.
- */
- for (i = 0; i < 10; i++) {
- ret = i2c_master_send(tx->c, buf, 1);
- if (ret == 1)
- break;
- udelay(100);
- }
-
- if (ret != 1) {
- zilog_error("i2c_master_send failed with %d\n", ret);
- return ret < 0 ? ret : -EFAULT;
- }
-
- /* Here comes the firmware version... (hopefully) */
- ret = i2c_master_recv(tx->c, buf, 4);
- if (ret != 4) {
- zilog_error("i2c_master_recv failed with %d\n", ret);
- return 0;
- }
- if ((buf[0] != 0x80) && (buf[0] != 0xa0)) {
- zilog_error("unexpected IR TX init response: %02x\n", buf[0]);
- return 0;
- }
- zilog_notify("Zilog/Hauppauge IR blaster firmware version "
- "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]);
-
- return 0;
-}
-
-/* unload "firmware", lock held */
-static void fw_unload_locked(void)
-{
- if (tx_data) {
- if (tx_data->code_sets)
- vfree(tx_data->code_sets);
-
- if (tx_data->datap)
- vfree(tx_data->datap);
-
- vfree(tx_data);
- tx_data = NULL;
- dprintk("successfully unloaded IR blaster firmware\n");
- }
-}
-
-/* unload "firmware" for the IR TX device */
-static void fw_unload(void)
-{
- mutex_lock(&tx_data_lock);
- fw_unload_locked();
- mutex_unlock(&tx_data_lock);
-}
-
-/* load "firmware" for the IR TX device */
-static int fw_load(struct IR_tx *tx)
-{
- int ret;
- unsigned int i;
- unsigned char *data, version, num_global_fixed;
- const struct firmware *fw_entry;
-
- /* Already loaded? */
- mutex_lock(&tx_data_lock);
- if (tx_data) {
- ret = 0;
- goto out;
- }
-
- /* Request codeset data file */
- ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", tx->ir->l.dev);
- if (ret != 0) {
- zilog_error("firmware haup-ir-blaster.bin not available "
- "(%d)\n", ret);
- ret = ret < 0 ? ret : -EFAULT;
- goto out;
- }
- dprintk("firmware of size %zu loaded\n", fw_entry->size);
-
- /* Parse the file */
- tx_data = vmalloc(sizeof(*tx_data));
- if (tx_data == NULL) {
- zilog_error("out of memory\n");
- release_firmware(fw_entry);
- ret = -ENOMEM;
- goto out;
- }
- tx_data->code_sets = NULL;
-
- /* Copy the data so hotplug doesn't get confused and timeout */
- tx_data->datap = vmalloc(fw_entry->size);
- if (tx_data->datap == NULL) {
- zilog_error("out of memory\n");
- release_firmware(fw_entry);
- vfree(tx_data);
- ret = -ENOMEM;
- goto out;
- }
- memcpy(tx_data->datap, fw_entry->data, fw_entry->size);
- tx_data->endp = tx_data->datap + fw_entry->size;
- release_firmware(fw_entry); fw_entry = NULL;
-
- /* Check version */
- data = tx_data->datap;
- if (!read_uint8(&data, tx_data->endp, &version))
- goto corrupt;
- if (version != 1) {
- zilog_error("unsupported code set file version (%u, expected"
- "1) -- please upgrade to a newer driver",
- version);
- fw_unload_locked();
- ret = -EFAULT;
- goto out;
- }
-
- /* Save boot block for later */
- tx_data->boot_data = data;
- if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE))
- goto corrupt;
-
- if (!read_uint32(&data, tx_data->endp,
- &tx_data->num_code_sets))
- goto corrupt;
-
- dprintk("%u IR blaster codesets loaded\n", tx_data->num_code_sets);
-
- tx_data->code_sets = vmalloc(
- tx_data->num_code_sets * sizeof(char *));
- if (tx_data->code_sets == NULL) {
- fw_unload_locked();
- ret = -ENOMEM;
- goto out;
- }
-
- for (i = 0; i < TX_BLOCK_SIZE; ++i)
- tx_data->fixed[i] = -1;
-
- /* Read global fixed data template */
- if (!read_uint8(&data, tx_data->endp, &num_global_fixed) ||
- num_global_fixed > TX_BLOCK_SIZE)
- goto corrupt;
- for (i = 0; i < num_global_fixed; ++i) {
- unsigned char pos, val;
- if (!read_uint8(&data, tx_data->endp, &pos) ||
- !read_uint8(&data, tx_data->endp, &val) ||
- pos >= TX_BLOCK_SIZE)
- goto corrupt;
- tx_data->fixed[pos] = (int)val;
- }
-
- /* Filch out the position of each code set */
- for (i = 0; i < tx_data->num_code_sets; ++i) {
- unsigned int id;
- unsigned char keys;
- unsigned char ndiffs;
-
- /* Save the codeset position */
- tx_data->code_sets[i] = data;
-
- /* Read header */
- if (!read_uint32(&data, tx_data->endp, &id) ||
- !read_uint8(&data, tx_data->endp, &keys) ||
- !read_uint8(&data, tx_data->endp, &ndiffs) ||
- ndiffs > TX_BLOCK_SIZE || keys == 0)
- goto corrupt;
-
- /* skip diff positions */
- if (!skip(&data, tx_data->endp, ndiffs))
- goto corrupt;
-
- /*
- * After the diffs we have the first key id + data -
- * global fixed
- */
- if (!skip(&data, tx_data->endp,
- 1 + TX_BLOCK_SIZE - num_global_fixed))
- goto corrupt;
-
- /* Then we have keys-1 blocks of key id+diffs */
- if (!skip(&data, tx_data->endp,
- (ndiffs + 1) * (keys - 1)))
- goto corrupt;
- }
- ret = 0;
- goto out;
-
-corrupt:
- zilog_error("firmware is corrupt\n");
- fw_unload_locked();
- ret = -EFAULT;
-
-out:
- mutex_unlock(&tx_data_lock);
- return ret;
-}
-
-/* copied from lirc_dev */
-static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
-{
- struct IR *ir = filep->private_data;
- struct IR_rx *rx;
- struct lirc_buffer *rbuf = ir->l.rbuf;
- int ret = 0, written = 0, retries = 0;
- unsigned int m;
- DECLARE_WAITQUEUE(wait, current);
-
- dprintk("read called\n");
- if (n % rbuf->chunk_size) {
- dprintk("read result = -EINVAL\n");
- return -EINVAL;
- }
-
- rx = get_ir_rx(ir);
- if (rx == NULL)
- return -ENXIO;
-
- /*
- * we add ourselves to the task queue before buffer check
- * to avoid losing scan code (in case when queue is awaken somewhere
- * between while condition checking and scheduling)
- */
- add_wait_queue(&rbuf->wait_poll, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
- /*
- * while we didn't provide 'length' bytes, device is opened in blocking
- * mode and 'copy_to_user' is happy, wait for data.
- */
- while (written < n && ret == 0) {
- if (lirc_buffer_empty(rbuf)) {
- /*
- * According to the read(2) man page, 'written' can be
- * returned as less than 'n', instead of blocking
- * again, returning -EWOULDBLOCK, or returning
- * -ERESTARTSYS
- */
- if (written)
- break;
- if (filep->f_flags & O_NONBLOCK) {
- ret = -EWOULDBLOCK;
- break;
- }
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- } else {
- unsigned char buf[rbuf->chunk_size];
- m = lirc_buffer_read(rbuf, buf);
- if (m == rbuf->chunk_size) {
- ret = copy_to_user((void *)outbuf+written, buf,
- rbuf->chunk_size);
- written += rbuf->chunk_size;
- } else {
- retries++;
- }
- if (retries >= 5) {
- zilog_error("Buffer read failed!\n");
- ret = -EIO;
- }
- }
- }
-
- remove_wait_queue(&rbuf->wait_poll, &wait);
- put_ir_rx(rx, false);
- set_current_state(TASK_RUNNING);
-
- dprintk("read result = %d (%s)\n", ret, ret ? "Error" : "OK");
-
- return ret ? ret : written;
-}
-
-/* send a keypress to the IR TX device */
-static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key)
-{
- unsigned char data_block[TX_BLOCK_SIZE];
- unsigned char buf[2];
- int i, ret;
-
- /* Get data for the codeset/key */
- ret = get_key_data(data_block, code, key);
-
- if (ret == -EPROTO) {
- zilog_error("failed to get data for code %u, key %u -- check "
- "lircd.conf entries\n", code, key);
- return ret;
- } else if (ret != 0)
- return ret;
-
- /* Send the data block */
- ret = send_data_block(tx, data_block);
- if (ret != 0)
- return ret;
-
- /* Send data block length? */
- buf[0] = 0x00;
- buf[1] = 0x40;
- ret = i2c_master_send(tx->c, buf, 2);
- if (ret != 2) {
- zilog_error("i2c_master_send failed with %d\n", ret);
- return ret < 0 ? ret : -EFAULT;
- }
-
- /* Give the z8 a moment to process data block */
- for (i = 0; i < 10; i++) {
- ret = i2c_master_send(tx->c, buf, 1);
- if (ret == 1)
- break;
- udelay(100);
- }
-
- if (ret != 1) {
- zilog_error("i2c_master_send failed with %d\n", ret);
- return ret < 0 ? ret : -EFAULT;
- }
-
- /* Send finished download? */
- ret = i2c_master_recv(tx->c, buf, 1);
- if (ret != 1) {
- zilog_error("i2c_master_recv failed with %d\n", ret);
- return ret < 0 ? ret : -EFAULT;
- }
- if (buf[0] != 0xA0) {
- zilog_error("unexpected IR TX response #1: %02x\n",
- buf[0]);
- return -EFAULT;
- }
-
- /* Send prepare command? */
- buf[0] = 0x00;
- buf[1] = 0x80;
- ret = i2c_master_send(tx->c, buf, 2);
- if (ret != 2) {
- zilog_error("i2c_master_send failed with %d\n", ret);
- return ret < 0 ? ret : -EFAULT;
- }
-
- /*
- * The sleep bits aren't necessary on the HD PVR, and in fact, the
- * last i2c_master_recv always fails with a -5, so for now, we're
- * going to skip this whole mess and say we're done on the HD PVR
- */
- if (!tx->post_tx_ready_poll) {
- dprintk("sent code %u, key %u\n", code, key);
- return 0;
- }
-
- /*
- * This bit NAKs until the device is ready, so we retry it
- * sleeping a bit each time. This seems to be what the windows
- * driver does, approximately.
- * Try for up to 1s.
- */
- for (i = 0; i < 20; ++i) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((50 * HZ + 999) / 1000);
- ret = i2c_master_send(tx->c, buf, 1);
- if (ret == 1)
- break;
- dprintk("NAK expected: i2c_master_send "
- "failed with %d (try %d)\n", ret, i+1);
- }
- if (ret != 1) {
- zilog_error("IR TX chip never got ready: last i2c_master_send "
- "failed with %d\n", ret);
- return ret < 0 ? ret : -EFAULT;
- }
-
- /* Seems to be an 'ok' response */
- i = i2c_master_recv(tx->c, buf, 1);
- if (i != 1) {
- zilog_error("i2c_master_recv failed with %d\n", ret);
- return -EFAULT;
- }
- if (buf[0] != 0x80) {
- zilog_error("unexpected IR TX response #2: %02x\n", buf[0]);
- return -EFAULT;
- }
-
- /* Oh good, it worked */
- dprintk("sent code %u, key %u\n", code, key);
- return 0;
-}
-
-/*
- * Write a code to the device. We take in a 32-bit number (an int) and then
- * decode this to a codeset/key index. The key data is then decompressed and
- * sent to the device. We have a spin lock as per i2c documentation to prevent
- * multiple concurrent sends which would probably cause the device to explode.
- */
-static ssize_t write(struct file *filep, const char *buf, size_t n,
- loff_t *ppos)
-{
- struct IR *ir = filep->private_data;
- struct IR_tx *tx;
- size_t i;
- int failures = 0;
-
- /* Validate user parameters */
- if (n % sizeof(int))
- return -EINVAL;
-
- /* Get a struct IR_tx reference */
- tx = get_ir_tx(ir);
- if (tx == NULL)
- return -ENXIO;
-
- /* Ensure our tx->c i2c_client remains valid for the duration */
- mutex_lock(&tx->client_lock);
- if (tx->c == NULL) {
- mutex_unlock(&tx->client_lock);
- put_ir_tx(tx, false);
- return -ENXIO;
- }
-
- /* Lock i2c bus for the duration */
- mutex_lock(&ir->ir_lock);
-
- /* Send each keypress */
- for (i = 0; i < n;) {
- int ret = 0;
- int command;
-
- if (copy_from_user(&command, buf + i, sizeof(command))) {
- mutex_unlock(&ir->ir_lock);
- mutex_unlock(&tx->client_lock);
- put_ir_tx(tx, false);
- return -EFAULT;
- }
-
- /* Send boot data first if required */
- if (tx->need_boot == 1) {
- /* Make sure we have the 'firmware' loaded, first */
- ret = fw_load(tx);
- if (ret != 0) {
- mutex_unlock(&ir->ir_lock);
- mutex_unlock(&tx->client_lock);
- put_ir_tx(tx, false);
- if (ret != -ENOMEM)
- ret = -EIO;
- return ret;
- }
- /* Prep the chip for transmitting codes */
- ret = send_boot_data(tx);
- if (ret == 0)
- tx->need_boot = 0;
- }
-
- /* Send the code */
- if (ret == 0) {
- ret = send_code(tx, (unsigned)command >> 16,
- (unsigned)command & 0xFFFF);
- if (ret == -EPROTO) {
- mutex_unlock(&ir->ir_lock);
- mutex_unlock(&tx->client_lock);
- put_ir_tx(tx, false);
- return ret;
- }
- }
-
- /*
- * Hmm, a failure. If we've had a few then give up, otherwise
- * try a reset
- */
- if (ret != 0) {
- /* Looks like the chip crashed, reset it */
- zilog_error("sending to the IR transmitter chip "
- "failed, trying reset\n");
-
- if (failures >= 3) {
- zilog_error("unable to send to the IR chip "
- "after 3 resets, giving up\n");
- mutex_unlock(&ir->ir_lock);
- mutex_unlock(&tx->client_lock);
- put_ir_tx(tx, false);
- return ret;
- }
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((100 * HZ + 999) / 1000);
- tx->need_boot = 1;
- ++failures;
- } else
- i += sizeof(int);
- }
-
- /* Release i2c bus */
- mutex_unlock(&ir->ir_lock);
-
- mutex_unlock(&tx->client_lock);
-
- /* Give back our struct IR_tx reference */
- put_ir_tx(tx, false);
-
- /* All looks good */
- return n;
-}
-
-/* copied from lirc_dev */
-static unsigned int poll(struct file *filep, poll_table *wait)
-{
- struct IR *ir = filep->private_data;
- struct IR_rx *rx;
- struct lirc_buffer *rbuf = ir->l.rbuf;
- unsigned int ret;
-
- dprintk("poll called\n");
-
- rx = get_ir_rx(ir);
- if (rx == NULL) {
- /*
- * Revisit this, if our poll function ever reports writeable
- * status for Tx
- */
- dprintk("poll result = POLLERR\n");
- return POLLERR;
- }
-
- /*
- * Add our lirc_buffer's wait_queue to the poll_table. A wake up on
- * that buffer's wait queue indicates we may have a new poll status.
- */
- poll_wait(filep, &rbuf->wait_poll, wait);
-
- /* Indicate what ops could happen immediately without blocking */
- ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM);
-
- dprintk("poll result = %s\n", ret ? "POLLIN|POLLRDNORM" : "none");
- return ret;
-}
-
-static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
-{
- struct IR *ir = filep->private_data;
- int result;
- unsigned long mode, features;
-
- features = ir->l.features;
-
- switch (cmd) {
- case LIRC_GET_LENGTH:
- result = put_user((unsigned long)13,
- (unsigned long *)arg);
- break;
- case LIRC_GET_FEATURES:
- result = put_user(features, (unsigned long *) arg);
- break;
- case LIRC_GET_REC_MODE:
- if (!(features&LIRC_CAN_REC_MASK))
- return -ENOSYS;
-
- result = put_user(LIRC_REC2MODE
- (features&LIRC_CAN_REC_MASK),
- (unsigned long *)arg);
- break;
- case LIRC_SET_REC_MODE:
- if (!(features&LIRC_CAN_REC_MASK))
- return -ENOSYS;
-
- result = get_user(mode, (unsigned long *)arg);
- if (!result && !(LIRC_MODE2REC(mode) & features))
- result = -EINVAL;
- break;
- case LIRC_GET_SEND_MODE:
- if (!(features&LIRC_CAN_SEND_MASK))
- return -ENOSYS;
-
- result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
- break;
- case LIRC_SET_SEND_MODE:
- if (!(features&LIRC_CAN_SEND_MASK))
- return -ENOSYS;
-
- result = get_user(mode, (unsigned long *) arg);
- if (!result && mode != LIRC_MODE_PULSE)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
- return result;
-}
-
-static struct IR *get_ir_device_by_minor(unsigned int minor)
-{
- struct IR *ir;
- struct IR *ret = NULL;
-
- mutex_lock(&ir_devices_lock);
-
- if (!list_empty(&ir_devices_list)) {
- list_for_each_entry(ir, &ir_devices_list, list) {
- if (ir->l.minor == minor) {
- ret = get_ir_device(ir, true);
- break;
- }
- }
- }
-
- mutex_unlock(&ir_devices_lock);
- return ret;
-}
-
-/*
- * Open the IR device. Get hold of our IR structure and
- * stash it in private_data for the file
- */
-static int open(struct inode *node, struct file *filep)
-{
- struct IR *ir;
- unsigned int minor = MINOR(node->i_rdev);
-
- /* find our IR struct */
- ir = get_ir_device_by_minor(minor);
-
- if (ir == NULL)
- return -ENODEV;
-
- atomic_inc(&ir->open_count);
-
- /* stash our IR struct */
- filep->private_data = ir;
-
- nonseekable_open(node, filep);
- return 0;
-}
-
-/* Close the IR device */
-static int close(struct inode *node, struct file *filep)
-{
- /* find our IR struct */
- struct IR *ir = filep->private_data;
- if (ir == NULL) {
- zilog_error("close: no private_data attached to the file!\n");
- return -ENODEV;
- }
-
- atomic_dec(&ir->open_count);
-
- put_ir_device(ir, false);
- return 0;
-}
-
-static int ir_remove(struct i2c_client *client);
-static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
-
-#define ID_FLAG_TX 0x01
-#define ID_FLAG_HDPVR 0x02
-
-static const struct i2c_device_id ir_transceiver_id[] = {
- { "ir_tx_z8f0811_haup", ID_FLAG_TX },
- { "ir_rx_z8f0811_haup", 0 },
- { "ir_tx_z8f0811_hdpvr", ID_FLAG_HDPVR | ID_FLAG_TX },
- { "ir_rx_z8f0811_hdpvr", ID_FLAG_HDPVR },
- { }
-};
-
-static struct i2c_driver driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "Zilog/Hauppauge i2c IR",
- },
- .probe = ir_probe,
- .remove = ir_remove,
- .id_table = ir_transceiver_id,
-};
-
-static const struct file_operations lirc_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = read,
- .write = write,
- .poll = poll,
- .unlocked_ioctl = ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = ioctl,
-#endif
- .open = open,
- .release = close
-};
-
-static struct lirc_driver lirc_template = {
- .name = "lirc_zilog",
- .minor = -1,
- .code_length = 13,
- .buffer_size = BUFLEN / 2,
- .sample_rate = 0, /* tell lirc_dev to not start its own kthread */
- .chunk_size = 2,
- .set_use_inc = set_use_inc,
- .set_use_dec = set_use_dec,
- .fops = &lirc_fops,
- .owner = THIS_MODULE,
-};
-
-static int ir_remove(struct i2c_client *client)
-{
- if (strncmp("ir_tx_z8", client->name, 8) == 0) {
- struct IR_tx *tx = i2c_get_clientdata(client);
- if (tx != NULL) {
- mutex_lock(&tx->client_lock);
- tx->c = NULL;
- mutex_unlock(&tx->client_lock);
- put_ir_tx(tx, false);
- }
- } else if (strncmp("ir_rx_z8", client->name, 8) == 0) {
- struct IR_rx *rx = i2c_get_clientdata(client);
- if (rx != NULL) {
- mutex_lock(&rx->client_lock);
- rx->c = NULL;
- mutex_unlock(&rx->client_lock);
- put_ir_rx(rx, false);
- }
- }
- return 0;
-}
-
-
-/* ir_devices_lock must be held */
-static struct IR *get_ir_device_by_adapter(struct i2c_adapter *adapter)
-{
- struct IR *ir;
-
- if (list_empty(&ir_devices_list))
- return NULL;
-
- list_for_each_entry(ir, &ir_devices_list, list)
- if (ir->adapter == adapter) {
- get_ir_device(ir, true);
- return ir;
- }
-
- return NULL;
-}
-
-static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
-{
- struct IR *ir;
- struct IR_tx *tx;
- struct IR_rx *rx;
- struct i2c_adapter *adap = client->adapter;
- int ret;
- bool tx_probe = false;
-
- dprintk("%s: %s on i2c-%d (%s), client addr=0x%02x\n",
- __func__, id->name, adap->nr, adap->name, client->addr);
-
- /*
- * The IR receiver is at i2c address 0x71.
- * The IR transmitter is at i2c address 0x70.
- */
-
- if (id->driver_data & ID_FLAG_TX)
- tx_probe = true;
- else if (tx_only) /* module option */
- return -ENXIO;
-
- zilog_info("probing IR %s on %s (i2c-%d)\n",
- tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
-
- mutex_lock(&ir_devices_lock);
-
- /* Use a single struct IR instance for both the Rx and Tx functions */
- ir = get_ir_device_by_adapter(adap);
- if (ir == NULL) {
- ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
- if (ir == NULL) {
- ret = -ENOMEM;
- goto out_no_ir;
- }
- kref_init(&ir->ref);
-
- /* store for use in ir_probe() again, and open() later on */
- INIT_LIST_HEAD(&ir->list);
- list_add_tail(&ir->list, &ir_devices_list);
-
- ir->adapter = adap;
- mutex_init(&ir->ir_lock);
- atomic_set(&ir->open_count, 0);
- spin_lock_init(&ir->tx_ref_lock);
- spin_lock_init(&ir->rx_ref_lock);
-
- /* set lirc_dev stuff */
- memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
- /*
- * FIXME this is a pointer reference to us, but no refcount.
- *
- * This OK for now, since lirc_dev currently won't touch this
- * buffer as we provide our own lirc_fops.
- *
- * Currently our own lirc_fops rely on this ir->l.rbuf pointer
- */
- ir->l.rbuf = &ir->rbuf;
- ir->l.dev = &adap->dev;
- ret = lirc_buffer_init(ir->l.rbuf,
- ir->l.chunk_size, ir->l.buffer_size);
- if (ret)
- goto out_put_ir;
- }
-
- if (tx_probe) {
- /* Get the IR_rx instance for later, if already allocated */
- rx = get_ir_rx(ir);
-
- /* Set up a struct IR_tx instance */
- tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL);
- if (tx == NULL) {
- ret = -ENOMEM;
- goto out_put_xx;
- }
- kref_init(&tx->ref);
- ir->tx = tx;
-
- ir->l.features |= LIRC_CAN_SEND_PULSE;
- mutex_init(&tx->client_lock);
- tx->c = client;
- tx->need_boot = 1;
- tx->post_tx_ready_poll =
- (id->driver_data & ID_FLAG_HDPVR) ? false : true;
-
- /* An ir ref goes to the struct IR_tx instance */
- tx->ir = get_ir_device(ir, true);
-
- /* A tx ref goes to the i2c_client */
- i2c_set_clientdata(client, get_ir_tx(ir));
-
- /*
- * Load the 'firmware'. We do this before registering with
- * lirc_dev, so the first firmware load attempt does not happen
- * after a open() or write() call on the device.
- *
- * Failure here is not deemed catastrophic, so the receiver will
- * still be usable. Firmware load will be retried in write(),
- * if it is needed.
- */
- fw_load(tx);
-
- /* Proceed only if the Rx client is also ready or not needed */
- if (rx == NULL && !tx_only) {
- zilog_info("probe of IR Tx on %s (i2c-%d) done. Waiting"
- " on IR Rx.\n", adap->name, adap->nr);
- goto out_ok;
- }
- } else {
- /* Get the IR_tx instance for later, if already allocated */
- tx = get_ir_tx(ir);
-
- /* Set up a struct IR_rx instance */
- rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL);
- if (rx == NULL) {
- ret = -ENOMEM;
- goto out_put_xx;
- }
- kref_init(&rx->ref);
- ir->rx = rx;
-
- ir->l.features |= LIRC_CAN_REC_LIRCCODE;
- mutex_init(&rx->client_lock);
- rx->c = client;
- rx->hdpvr_data_fmt =
- (id->driver_data & ID_FLAG_HDPVR) ? true : false;
-
- /* An ir ref goes to the struct IR_rx instance */
- rx->ir = get_ir_device(ir, true);
-
- /* An rx ref goes to the i2c_client */
- i2c_set_clientdata(client, get_ir_rx(ir));
-
- /*
- * Start the polling thread.
- * It will only perform an empty loop around schedule_timeout()
- * until we register with lirc_dev and the first user open()
- */
- /* An ir ref goes to the new rx polling kthread */
- rx->task = kthread_run(lirc_thread, get_ir_device(ir, true),
- "zilog-rx-i2c-%d", adap->nr);
- if (IS_ERR(rx->task)) {
- ret = PTR_ERR(rx->task);
- zilog_error("%s: could not start IR Rx polling thread"
- "\n", __func__);
- /* Failed kthread, so put back the ir ref */
- put_ir_device(ir, true);
- /* Failure exit, so put back rx ref from i2c_client */
- i2c_set_clientdata(client, NULL);
- put_ir_rx(rx, true);
- ir->l.features &= ~LIRC_CAN_REC_LIRCCODE;
- goto out_put_xx;
- }
-
- /* Proceed only if the Tx client is also ready */
- if (tx == NULL) {
- zilog_info("probe of IR Rx on %s (i2c-%d) done. Waiting"
- " on IR Tx.\n", adap->name, adap->nr);
- goto out_ok;
- }
- }
-
- /* register with lirc */
- ir->l.minor = minor; /* module option: user requested minor number */
- ir->l.minor = lirc_register_driver(&ir->l);
- if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
- zilog_error("%s: \"minor\" must be between 0 and %d (%d)!\n",
- __func__, MAX_IRCTL_DEVICES-1, ir->l.minor);
- ret = -EBADRQC;
- goto out_put_xx;
- }
- zilog_info("IR unit on %s (i2c-%d) registered as lirc%d and ready\n",
- adap->name, adap->nr, ir->l.minor);
-
-out_ok:
- if (rx != NULL)
- put_ir_rx(rx, true);
- if (tx != NULL)
- put_ir_tx(tx, true);
- put_ir_device(ir, true);
- zilog_info("probe of IR %s on %s (i2c-%d) done\n",
- tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
- mutex_unlock(&ir_devices_lock);
- return 0;
-
-out_put_xx:
- if (rx != NULL)
- put_ir_rx(rx, true);
- if (tx != NULL)
- put_ir_tx(tx, true);
-out_put_ir:
- put_ir_device(ir, true);
-out_no_ir:
- zilog_error("%s: probing IR %s on %s (i2c-%d) failed with %d\n",
- __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr,
- ret);
- mutex_unlock(&ir_devices_lock);
- return ret;
-}
-
-static int __init zilog_init(void)
-{
- int ret;
-
- zilog_notify("Zilog/Hauppauge IR driver initializing\n");
-
- mutex_init(&tx_data_lock);
-
- request_module("firmware_class");
-
- ret = i2c_add_driver(&driver);
- if (ret)
- zilog_error("initialization failed\n");
- else
- zilog_notify("initialization complete\n");
-
- return ret;
-}
-
-static void __exit zilog_exit(void)
-{
- i2c_del_driver(&driver);
- /* if loaded */
- fw_unload();
- zilog_notify("Zilog/Hauppauge IR driver unloaded\n");
-}
-
-module_init(zilog_init);
-module_exit(zilog_exit);
-
-MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
-MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
- "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, "
- "Andy Walls");
-MODULE_LICENSE("GPL");
-/* for compat with old name, which isn't all that accurate anymore */
-MODULE_ALIAS("lirc_pvr150");
-
-module_param(minor, int, 0444);
-MODULE_PARM_DESC(minor, "Preferred minor device number");
-
-module_param(debug, bool, 0644);
-MODULE_PARM_DESC(debug, "Enable debugging messages");
-
-module_param(tx_only, bool, 0644);
-MODULE_PARM_DESC(tx_only, "Only handle the IR transmit function");
--- /dev/null
+menuconfig STAGING_MEDIA
+ bool "Media staging drivers"
+ default n
+ ---help---
+ This option allows you to select a number of media drivers that
+ don't have the "normal" Linux kernel quality level.
+ Most of them don't follow properly the V4L, DVB and/or RC API's,
+ so, they won't likely work fine with the existing applications.
+ That also means that, one fixed, their API's will change to match
+ the existing ones.
+
+ If you wish to work on these drivers, to help improve them, or
+ to report problems you have with them, please use the
+ linux-media@vger.kernel.org mailing list.
+
+ If in doubt, say N here.
+
+
+if STAGING_MEDIA
+
+# Please keep them in alphabetic order
+source "drivers/staging/media/as102/Kconfig"
+
+source "drivers/staging/media/cxd2099/Kconfig"
+
+source "drivers/staging/media/dt3155v4l/Kconfig"
+
+source "drivers/staging/media/easycap/Kconfig"
+
+source "drivers/staging/media/go7007/Kconfig"
+
+source "drivers/staging/media/solo6x10/Kconfig"
+
+# Keep LIRC at the end, as it has sub-menus
+source "drivers/staging/media/lirc/Kconfig"
+
+endif
--- /dev/null
+obj-$(CONFIG_DVB_AS102) += as102/
+obj-$(CONFIG_DVB_CXD2099) += cxd2099/
+obj-$(CONFIG_EASYCAP) += easycap/
+obj-$(CONFIG_LIRC_STAGING) += lirc/
+obj-$(CONFIG_SOLO6X10) += solo6x10/
+obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/
+obj-$(CONFIG_VIDEO_GO7007) += go7007/
--- /dev/null
+config DVB_CXD2099
+ tristate "CXD2099AR Common Interface driver"
+ depends on DVB_CORE && PCI && I2C
+ ---help---
+ Support for the CI module found on cards based on
+ - Micronas ngene PCIe bridge: cineS2 etc.
+ - Digital Devices PCIe bridge: Octopus series
+
+ For now, data is passed through '/dev/dvb/adapterX/sec0':
+ - Encrypted data must be written to 'sec0'.
+ - Decrypted data can be read from 'sec0'.
+ - Setup the CAM using device 'ca0'.
--- /dev/null
+obj-$(CONFIG_DVB_CXD2099) += cxd2099.o
+
+ccflags-y += -Idrivers/media/dvb/dvb-core/
+ccflags-y += -Idrivers/media/dvb/frontends/
+ccflags-y += -Idrivers/media/common/tuners/
--- /dev/null
+For now, data is passed through '/dev/dvb/adapterX/sec0':
+ - Encrypted data must be written to 'sec0'.
+ - Decrypted data can be read from 'sec0'.
+ - Setup the CAM using device 'ca0'.
+
+But this is wrong. There are some discussions about the proper way for
+doing it, as seen at:
+ http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html
+
+While there's no proper fix for it, the driver should be kept in staging.
+
+Patches should be submitted to: linux-media@vger.kernel.org.
--- /dev/null
+/*
+ * cxd2099.c: Driver for the CXD2099AR Common Interface Controller
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "cxd2099.h"
+
+#define MAX_BUFFER_SIZE 248
+
+struct cxd {
+ struct dvb_ca_en50221 en;
+
+ struct i2c_adapter *i2c;
+ struct cxd2099_cfg cfg;
+
+ u8 regs[0x23];
+ u8 lastaddress;
+ u8 clk_reg_f;
+ u8 clk_reg_b;
+ int mode;
+ int ready;
+ int dr;
+ int slot_stat;
+
+ u8 amem[1024];
+ int amem_read;
+
+ int cammode;
+ struct mutex lock;
+};
+
+static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr,
+ u8 reg, u8 data)
+{
+ u8 m[2] = {reg, data};
+ struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2};
+
+ if (i2c_transfer(adapter, &msg, 1) != 1) {
+ printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n",
+ reg, adr);
+ return -1;
+ }
+ return 0;
+}
+
+static int i2c_write(struct i2c_adapter *adapter, u8 adr,
+ u8 *data, u8 len)
+{
+ struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len};
+
+ if (i2c_transfer(adapter, &msg, 1) != 1) {
+ printk(KERN_ERR "Failed to write to I2C!\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr,
+ u8 reg, u8 *val)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = ®, .len = 1},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1} };
+
+ if (i2c_transfer(adapter, msgs, 2) != 2) {
+ printk(KERN_ERR "error in i2c_read_reg\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int i2c_read(struct i2c_adapter *adapter, u8 adr,
+ u8 reg, u8 *data, u8 n)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = ®, .len = 1},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = data, .len = n} };
+
+ if (i2c_transfer(adapter, msgs, 2) != 2) {
+ printk(KERN_ERR "error in i2c_read\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n)
+{
+ int status;
+
+ status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
+ if (!status) {
+ ci->lastaddress = adr;
+ status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, n);
+ }
+ return status;
+}
+
+static int read_reg(struct cxd *ci, u8 reg, u8 *val)
+{
+ return read_block(ci, reg, val, 1);
+}
+
+
+static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[3] = {2, address & 0xff, address >> 8};
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status)
+ status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n);
+ return status;
+}
+
+static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[3] = {2, address & 0xff, address >> 8};
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status) {
+ u8 buf[256] = {3};
+ memcpy(buf+1, data, n);
+ status = i2c_write(ci->i2c, ci->cfg.adr, buf, n+1);
+ }
+ return status;
+}
+
+static int read_io(struct cxd *ci, u16 address, u8 *val)
+{
+ int status;
+ u8 addr[3] = {2, address & 0xff, address >> 8};
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status)
+ status = i2c_read(ci->i2c, ci->cfg.adr, 3, val, 1);
+ return status;
+}
+
+static int write_io(struct cxd *ci, u16 address, u8 val)
+{
+ int status;
+ u8 addr[3] = {2, address & 0xff, address >> 8};
+ u8 buf[2] = {3, val};
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status)
+ status = i2c_write(ci->i2c, ci->cfg.adr, buf, 2);
+ return status;
+}
+
+#if 0
+static int read_io_data(struct cxd *ci, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[3] = { 2, 0, 0 };
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status)
+ status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n);
+ return 0;
+}
+
+static int write_io_data(struct cxd *ci, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[3] = {2, 0, 0};
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status) {
+ u8 buf[256] = {3};
+ memcpy(buf+1, data, n);
+ status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
+ }
+ return 0;
+}
+#endif
+
+static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask)
+{
+ int status;
+
+ status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg);
+ if (!status && reg >= 6 && reg <= 8 && mask != 0xff)
+ status = i2c_read_reg(ci->i2c, ci->cfg.adr, 1, &ci->regs[reg]);
+ ci->regs[reg] = (ci->regs[reg] & (~mask)) | val;
+ if (!status) {
+ ci->lastaddress = reg;
+ status = i2c_write_reg(ci->i2c, ci->cfg.adr, 1, ci->regs[reg]);
+ }
+ if (reg == 0x20)
+ ci->regs[reg] &= 0x7f;
+ return status;
+}
+
+static int write_reg(struct cxd *ci, u8 reg, u8 val)
+{
+ return write_regm(ci, reg, val, 0xff);
+}
+
+#ifdef BUFFER_MODE
+static int write_block(struct cxd *ci, u8 adr, u8 *data, int n)
+{
+ int status;
+ u8 buf[256] = {1};
+
+ status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
+ if (!status) {
+ ci->lastaddress = adr;
+ memcpy(buf + 1, data, n);
+ status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
+ }
+ return status;
+}
+#endif
+
+static void set_mode(struct cxd *ci, int mode)
+{
+ if (mode == ci->mode)
+ return;
+
+ switch (mode) {
+ case 0x00: /* IO mem */
+ write_regm(ci, 0x06, 0x00, 0x07);
+ break;
+ case 0x01: /* ATT mem */
+ write_regm(ci, 0x06, 0x02, 0x07);
+ break;
+ default:
+ break;
+ }
+ ci->mode = mode;
+}
+
+static void cam_mode(struct cxd *ci, int mode)
+{
+ if (mode == ci->cammode)
+ return;
+
+ switch (mode) {
+ case 0x00:
+ write_regm(ci, 0x20, 0x80, 0x80);
+ break;
+ case 0x01:
+#ifdef BUFFER_MODE
+ if (!ci->en.read_data)
+ return;
+ printk(KERN_INFO "enable cam buffer mode\n");
+ /* write_reg(ci, 0x0d, 0x00); */
+ /* write_reg(ci, 0x0e, 0x01); */
+ write_regm(ci, 0x08, 0x40, 0x40);
+ /* read_reg(ci, 0x12, &dummy); */
+ write_regm(ci, 0x08, 0x80, 0x80);
+#endif
+ break;
+ default:
+ break;
+ }
+ ci->cammode = mode;
+}
+
+
+
+static int init(struct cxd *ci)
+{
+ int status;
+
+ mutex_lock(&ci->lock);
+ ci->mode = -1;
+ do {
+ status = write_reg(ci, 0x00, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x01, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x02, 0x10);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x03, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x05, 0xFF);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x06, 0x1F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x07, 0x1F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x08, 0x28);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x14, 0x20);
+ if (status < 0)
+ break;
+
+#if 0
+ status = write_reg(ci, 0x09, 0x4D); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */
+ if (status < 0)
+ break;
+#endif
+ status = write_reg(ci, 0x0A, 0xA7); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */
+ if (status < 0)
+ break;
+
+ status = write_reg(ci, 0x0B, 0x33);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x0C, 0x33);
+ if (status < 0)
+ break;
+
+ status = write_regm(ci, 0x14, 0x00, 0x0F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x15, ci->clk_reg_b);
+ if (status < 0)
+ break;
+ status = write_regm(ci, 0x16, 0x00, 0x0F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x17, ci->clk_reg_f);
+ if (status < 0)
+ break;
+
+ if (ci->cfg.clock_mode) {
+ if (ci->cfg.polarity) {
+ status = write_reg(ci, 0x09, 0x6f);
+ if (status < 0)
+ break;
+ } else {
+ status = write_reg(ci, 0x09, 0x6d);
+ if (status < 0)
+ break;
+ }
+ status = write_reg(ci, 0x20, 0x68);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x21, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x22, 0x02);
+ if (status < 0)
+ break;
+ } else {
+ if (ci->cfg.polarity) {
+ status = write_reg(ci, 0x09, 0x4f);
+ if (status < 0)
+ break;
+ } else {
+ status = write_reg(ci, 0x09, 0x4d);
+ if (status < 0)
+ break;
+ }
+
+ status = write_reg(ci, 0x20, 0x28);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x21, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x22, 0x07);
+ if (status < 0)
+ break;
+ }
+
+ status = write_regm(ci, 0x20, 0x80, 0x80);
+ if (status < 0)
+ break;
+ status = write_regm(ci, 0x03, 0x02, 0x02);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x01, 0x04);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x00, 0x31);
+ if (status < 0)
+ break;
+
+ /* Put TS in bypass */
+ status = write_regm(ci, 0x09, 0x08, 0x08);
+ if (status < 0)
+ break;
+ ci->cammode = -1;
+ cam_mode(ci, 0);
+ } while (0);
+ mutex_unlock(&ci->lock);
+
+ return 0;
+}
+
+static int read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address)
+{
+ struct cxd *ci = ca->data;
+#if 0
+ if (ci->amem_read) {
+ if (address <= 0 || address > 1024)
+ return -EIO;
+ return ci->amem[address];
+ }
+
+ mutex_lock(&ci->lock);
+ write_regm(ci, 0x06, 0x00, 0x05);
+ read_pccard(ci, 0, &ci->amem[0], 128);
+ read_pccard(ci, 128, &ci->amem[0], 128);
+ read_pccard(ci, 256, &ci->amem[0], 128);
+ read_pccard(ci, 384, &ci->amem[0], 128);
+ write_regm(ci, 0x06, 0x05, 0x05);
+ mutex_unlock(&ci->lock);
+ return ci->amem[address];
+#else
+ u8 val;
+ mutex_lock(&ci->lock);
+ set_mode(ci, 1);
+ read_pccard(ci, address, &val, 1);
+ mutex_unlock(&ci->lock);
+ /* printk(KERN_INFO "%02x:%02x\n", address,val); */
+ return val;
+#endif
+}
+
+static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot,
+ int address, u8 value)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 1);
+ write_pccard(ci, address, &value, 1);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot, u8 address)
+{
+ struct cxd *ci = ca->data;
+ u8 val;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 0);
+ read_io(ci, address, &val);
+ mutex_unlock(&ci->lock);
+ return val;
+}
+
+static int write_cam_control(struct dvb_ca_en50221 *ca, int slot,
+ u8 address, u8 value)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 0);
+ write_io(ci, address, value);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+#if 0
+ write_reg(ci, 0x00, 0x21);
+ write_reg(ci, 0x06, 0x1F);
+ write_reg(ci, 0x00, 0x31);
+#else
+#if 0
+ write_reg(ci, 0x06, 0x1F);
+ write_reg(ci, 0x06, 0x2F);
+#else
+ cam_mode(ci, 0);
+ write_reg(ci, 0x00, 0x21);
+ write_reg(ci, 0x06, 0x1F);
+ write_reg(ci, 0x00, 0x31);
+ write_regm(ci, 0x20, 0x80, 0x80);
+ write_reg(ci, 0x03, 0x02);
+ ci->ready = 0;
+#endif
+#endif
+ ci->mode = -1;
+ {
+ int i;
+#if 0
+ u8 val;
+#endif
+ for (i = 0; i < 100; i++) {
+ msleep(10);
+#if 0
+ read_reg(ci, 0x06, &val);
+ printk(KERN_INFO "%d:%02x\n", i, val);
+ if (!(val&0x10))
+ break;
+#else
+ if (ci->ready)
+ break;
+#endif
+ }
+ }
+ mutex_unlock(&ci->lock);
+ /* msleep(500); */
+ return 0;
+}
+
+static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ printk(KERN_INFO "slot_shutdown\n");
+ mutex_lock(&ci->lock);
+ write_regm(ci, 0x09, 0x08, 0x08);
+ write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */
+ write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */
+ ci->mode = -1;
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ write_regm(ci, 0x09, 0x00, 0x08);
+ set_mode(ci, 0);
+#ifdef BUFFER_MODE
+ cam_mode(ci, 1);
+#endif
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+
+static int campoll(struct cxd *ci)
+{
+ u8 istat;
+
+ read_reg(ci, 0x04, &istat);
+ if (!istat)
+ return 0;
+ write_reg(ci, 0x05, istat);
+
+ if (istat&0x40) {
+ ci->dr = 1;
+ printk(KERN_INFO "DR\n");
+ }
+ if (istat&0x20)
+ printk(KERN_INFO "WC\n");
+
+ if (istat&2) {
+ u8 slotstat;
+
+ read_reg(ci, 0x01, &slotstat);
+ if (!(2&slotstat)) {
+ if (!ci->slot_stat) {
+ ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_PRESENT;
+ write_regm(ci, 0x03, 0x08, 0x08);
+ }
+
+ } else {
+ if (ci->slot_stat) {
+ ci->slot_stat = 0;
+ write_regm(ci, 0x03, 0x00, 0x08);
+ printk(KERN_INFO "NO CAM\n");
+ ci->ready = 0;
+ }
+ }
+ if (istat&8 && ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) {
+ ci->ready = 1;
+ ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY;
+ }
+ }
+ return 0;
+}
+
+
+static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct cxd *ci = ca->data;
+ u8 slotstat;
+
+ mutex_lock(&ci->lock);
+ campoll(ci);
+ read_reg(ci, 0x01, &slotstat);
+ mutex_unlock(&ci->lock);
+
+ return ci->slot_stat;
+}
+
+#ifdef BUFFER_MODE
+static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
+{
+ struct cxd *ci = ca->data;
+ u8 msb, lsb;
+ u16 len;
+
+ mutex_lock(&ci->lock);
+ campoll(ci);
+ mutex_unlock(&ci->lock);
+
+ printk(KERN_INFO "read_data\n");
+ if (!ci->dr)
+ return 0;
+
+ mutex_lock(&ci->lock);
+ read_reg(ci, 0x0f, &msb);
+ read_reg(ci, 0x10, &lsb);
+ len = (msb<<8)|lsb;
+ read_block(ci, 0x12, ebuf, len);
+ ci->dr = 0;
+ mutex_unlock(&ci->lock);
+
+ return len;
+}
+
+static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ printk(kern_INFO "write_data %d\n", ecount);
+ write_reg(ci, 0x0d, ecount>>8);
+ write_reg(ci, 0x0e, ecount&0xff);
+ write_block(ci, 0x11, ebuf, ecount);
+ mutex_unlock(&ci->lock);
+ return ecount;
+}
+#endif
+
+static struct dvb_ca_en50221 en_templ = {
+ .read_attribute_mem = read_attribute_mem,
+ .write_attribute_mem = write_attribute_mem,
+ .read_cam_control = read_cam_control,
+ .write_cam_control = write_cam_control,
+ .slot_reset = slot_reset,
+ .slot_shutdown = slot_shutdown,
+ .slot_ts_enable = slot_ts_enable,
+ .poll_slot_status = poll_slot_status,
+#ifdef BUFFER_MODE
+ .read_data = read_data,
+ .write_data = write_data,
+#endif
+
+};
+
+struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
+ void *priv,
+ struct i2c_adapter *i2c)
+{
+ struct cxd *ci = 0;
+ u8 val;
+
+ if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) {
+ printk(KERN_INFO "No CXD2099 detected at %02x\n", cfg->adr);
+ return 0;
+ }
+
+ ci = kmalloc(sizeof(struct cxd), GFP_KERNEL);
+ if (!ci)
+ return 0;
+ memset(ci, 0, sizeof(*ci));
+
+ mutex_init(&ci->lock);
+ memcpy(&ci->cfg, cfg, sizeof(struct cxd2099_cfg));
+ ci->i2c = i2c;
+ ci->lastaddress = 0xff;
+ ci->clk_reg_b = 0x4a;
+ ci->clk_reg_f = 0x1b;
+
+ memcpy(&ci->en, &en_templ, sizeof(en_templ));
+ ci->en.data = ci;
+ init(ci);
+ printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->cfg.adr);
+ return &ci->en;
+}
+EXPORT_SYMBOL(cxd2099_attach);
+
+MODULE_DESCRIPTION("cxd2099");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * cxd2099.h: Driver for the CXD2099AR Common Interface Controller
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _CXD2099_H_
+#define _CXD2099_H_
+
+#include <dvb_ca_en50221.h>
+
+struct cxd2099_cfg {
+ u32 bitrate;
+ u8 adr;
+ u8 polarity:1;
+ u8 clock_mode:1;
+};
+
+#if defined(CONFIG_DVB_CXD2099) || \
+ (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE))
+struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
+ void *priv, struct i2c_adapter *i2c);
+#else
+
+static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
+ void *priv, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
--- /dev/null
+config VIDEO_DT3155
+ tristate "DT3155 frame grabber, Video4Linux interface"
+ depends on PCI && VIDEO_DEV && VIDEO_V4L2
+ select VIDEOBUF2_DMA_CONTIG
+ default n
+ ---help---
+ Enables dt3155 device driver for the DataTranslation DT3155 frame grabber.
+ Say Y here if you have this hardware.
+ In doubt, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dt3155v4l.
+
+config DT3155_CCIR
+ bool "Selects CCIR/50Hz vertical refresh"
+ depends on VIDEO_DT3155
+ default y
+ ---help---
+ Select it for CCIR/50Hz (European region),
+ or leave it unselected for RS-170/60Hz (North America).
+
+config DT3155_STREAMING
+ bool "Selects streaming capture method"
+ depends on VIDEO_DT3155
+ default y
+ ---help---
+ Select it if you want to use streaming of memory mapped buffers
+ or leave it unselected if you want to use read method (one copy more).
--- /dev/null
+obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l.o
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2006-2010 by Marin Mitov *
+ * mitov@issp.bas.bg *
+ * *
+ * 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/module.h>
+#include <linux/version.h>
+#include <linux/stringify.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "dt3155v4l.h"
+
+#define DT3155_VENDOR_ID 0x8086
+#define DT3155_DEVICE_ID 0x1223
+
+/* DT3155_CHUNK_SIZE is 4M (2^22) 8 full size buffers */
+#define DT3155_CHUNK_SIZE (1U << 22)
+
+#define DT3155_COH_FLAGS (GFP_KERNEL | GFP_DMA32 | __GFP_COLD | __GFP_NOWARN)
+
+#define DT3155_BUF_SIZE (768 * 576)
+
+#ifdef CONFIG_DT3155_STREAMING
+#define DT3155_CAPTURE_METHOD V4L2_CAP_STREAMING
+#else
+#define DT3155_CAPTURE_METHOD V4L2_CAP_READWRITE
+#endif
+
+/* global initializers (for all boards) */
+#ifdef CONFIG_DT3155_CCIR
+static const u8 csr2_init = VT_50HZ;
+#define DT3155_CURRENT_NORM V4L2_STD_625_50
+static const unsigned int img_width = 768;
+static const unsigned int img_height = 576;
+static const unsigned int frames_per_sec = 25;
+static const struct v4l2_fmtdesc frame_std[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "CCIR/50Hz 8 bits gray",
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ },
+};
+#else
+static const u8 csr2_init = VT_60HZ;
+#define DT3155_CURRENT_NORM V4L2_STD_525_60
+static const unsigned int img_width = 640;
+static const unsigned int img_height = 480;
+static const unsigned int frames_per_sec = 30;
+static const struct v4l2_fmtdesc frame_std[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "RS-170/60Hz 8 bits gray",
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ },
+};
+#endif
+
+#define NUM_OF_FORMATS ARRAY_SIZE(frame_std)
+
+static u8 config_init = ACQ_MODE_EVEN;
+
+/**
+ * read_i2c_reg - reads an internal i2c register
+ *
+ * @addr: dt3155 mmio base address
+ * @index: index (internal address) of register to read
+ * @data: pointer to byte the read data will be placed in
+ *
+ * returns: zero on success or error code
+ *
+ * This function starts reading the specified (by index) register
+ * and busy waits for the process to finish. The result is placed
+ * in a byte pointed by data.
+ */
+static int
+read_i2c_reg(void __iomem *addr, u8 index, u8 *data)
+{
+ u32 tmp = index;
+
+ iowrite32((tmp<<17) | IIC_READ, addr + IIC_CSR2);
+ mmiowb();
+ udelay(45); /* wait at least 43 usec for NEW_CYCLE to clear */
+ if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
+ return -EIO; /* error: NEW_CYCLE not cleared */
+ tmp = ioread32(addr + IIC_CSR1);
+ if (tmp & DIRECT_ABORT) {
+ /* reset DIRECT_ABORT bit */
+ iowrite32(DIRECT_ABORT, addr + IIC_CSR1);
+ return -EIO; /* error: DIRECT_ABORT set */
+ }
+ *data = tmp>>24;
+ return 0;
+}
+
+/**
+ * write_i2c_reg - writes to an internal i2c register
+ *
+ * @addr: dt3155 mmio base address
+ * @index: index (internal address) of register to read
+ * @data: data to be written
+ *
+ * returns: zero on success or error code
+ *
+ * This function starts writting the specified (by index) register
+ * and busy waits for the process to finish.
+ */
+static int
+write_i2c_reg(void __iomem *addr, u8 index, u8 data)
+{
+ u32 tmp = index;
+
+ iowrite32((tmp<<17) | IIC_WRITE | data, addr + IIC_CSR2);
+ mmiowb();
+ udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */
+ if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
+ return -EIO; /* error: NEW_CYCLE not cleared */
+ if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) {
+ /* reset DIRECT_ABORT bit */
+ iowrite32(DIRECT_ABORT, addr + IIC_CSR1);
+ return -EIO; /* error: DIRECT_ABORT set */
+ }
+ return 0;
+}
+
+/**
+ * write_i2c_reg_nowait - writes to an internal i2c register
+ *
+ * @addr: dt3155 mmio base address
+ * @index: index (internal address) of register to read
+ * @data: data to be written
+ *
+ * This function starts writting the specified (by index) register
+ * and then returns.
+ */
+static void write_i2c_reg_nowait(void __iomem *addr, u8 index, u8 data)
+{
+ u32 tmp = index;
+
+ iowrite32((tmp<<17) | IIC_WRITE | data, addr + IIC_CSR2);
+ mmiowb();
+}
+
+/**
+ * wait_i2c_reg - waits the read/write to finish
+ *
+ * @addr: dt3155 mmio base address
+ *
+ * returns: zero on success or error code
+ *
+ * This function waits reading/writting to finish.
+ */
+static int wait_i2c_reg(void __iomem *addr)
+{
+ if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
+ udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */
+ if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
+ return -EIO; /* error: NEW_CYCLE not cleared */
+ if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) {
+ /* reset DIRECT_ABORT bit */
+ iowrite32(DIRECT_ABORT, addr + IIC_CSR1);
+ return -EIO; /* error: DIRECT_ABORT set */
+ }
+ return 0;
+}
+
+static int
+dt3155_start_acq(struct dt3155_priv *pd)
+{
+ struct vb2_buffer *vb = pd->curr_buf;
+ dma_addr_t dma_addr;
+
+ dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ iowrite32(dma_addr, pd->regs + EVEN_DMA_START);
+ iowrite32(dma_addr + img_width, pd->regs + ODD_DMA_START);
+ iowrite32(img_width, pd->regs + EVEN_DMA_STRIDE);
+ iowrite32(img_width, pd->regs + ODD_DMA_STRIDE);
+ /* enable interrupts, clear all irq flags */
+ iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START |
+ FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR);
+ iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |
+ FLD_DN_ODD | FLD_DN_EVEN | CAP_CONT_EVEN | CAP_CONT_ODD,
+ pd->regs + CSR1);
+ wait_i2c_reg(pd->regs);
+ write_i2c_reg(pd->regs, CONFIG, pd->config);
+ write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE);
+ write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_DONE);
+
+ /* start the board */
+ write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | BUSY_ODD);
+ return 0; /* success */
+}
+
+/*
+ * driver-specific callbacks (vb2_ops)
+ */
+static int
+dt3155_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned long sizes[],
+ void *alloc_ctxs[])
+{
+ struct dt3155_priv *pd = vb2_get_drv_priv(q);
+ void *ret;
+
+ if (*num_buffers == 0)
+ *num_buffers = 1;
+ *num_planes = 1;
+ sizes[0] = img_width * img_height;
+ if (pd->q->alloc_ctx[0])
+ return 0;
+ ret = vb2_dma_contig_init_ctx(&pd->pdev->dev);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ pd->q->alloc_ctx[0] = ret;
+ return 0;
+}
+
+static void
+dt3155_wait_prepare(struct vb2_queue *q)
+{
+ struct dt3155_priv *pd = vb2_get_drv_priv(q);
+
+ mutex_unlock(pd->vdev->lock);
+}
+
+static void
+dt3155_wait_finish(struct vb2_queue *q)
+{
+ struct dt3155_priv *pd = vb2_get_drv_priv(q);
+
+ mutex_lock(pd->vdev->lock);
+}
+
+static int
+dt3155_buf_prepare(struct vb2_buffer *vb)
+{
+ vb2_set_plane_payload(vb, 0, img_width * img_height);
+ return 0;
+}
+
+static int
+dt3155_start_streaming(struct vb2_queue *q)
+{
+ return 0;
+}
+
+static int
+dt3155_stop_streaming(struct vb2_queue *q)
+{
+ struct dt3155_priv *pd = vb2_get_drv_priv(q);
+ struct vb2_buffer *vb;
+
+ spin_lock_irq(&pd->lock);
+ while (!list_empty(&pd->dmaq)) {
+ vb = list_first_entry(&pd->dmaq, typeof(*vb), done_entry);
+ list_del(&vb->done_entry);
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irq(&pd->lock);
+ msleep(45); /* irq hendler will stop the hardware */
+ return 0;
+}
+
+static void
+dt3155_buf_queue(struct vb2_buffer *vb)
+{
+ struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue);
+
+ /* pd->q->streaming = 1 when dt3155_buf_queue() is invoked */
+ spin_lock_irq(&pd->lock);
+ if (pd->curr_buf)
+ list_add_tail(&vb->done_entry, &pd->dmaq);
+ else {
+ pd->curr_buf = vb;
+ dt3155_start_acq(pd);
+ }
+ spin_unlock_irq(&pd->lock);
+}
+/*
+ * end driver-specific callbacks
+ */
+
+const struct vb2_ops q_ops = {
+ .queue_setup = dt3155_queue_setup,
+ .wait_prepare = dt3155_wait_prepare,
+ .wait_finish = dt3155_wait_finish,
+ .buf_prepare = dt3155_buf_prepare,
+ .start_streaming = dt3155_start_streaming,
+ .stop_streaming = dt3155_stop_streaming,
+ .buf_queue = dt3155_buf_queue,
+};
+
+static irqreturn_t
+dt3155_irq_handler_even(int irq, void *dev_id)
+{
+ struct dt3155_priv *ipd = dev_id;
+ struct vb2_buffer *ivb;
+ dma_addr_t dma_addr;
+ u32 tmp;
+
+ tmp = ioread32(ipd->regs + INT_CSR) & (FLD_START | FLD_END_ODD);
+ if (!tmp)
+ return IRQ_NONE; /* not our irq */
+ if ((tmp & FLD_START) && !(tmp & FLD_END_ODD)) {
+ iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START,
+ ipd->regs + INT_CSR);
+ ipd->field_count++;
+ return IRQ_HANDLED; /* start of field irq */
+ }
+ if ((tmp & FLD_START) && (tmp & FLD_END_ODD))
+ ipd->stats.start_before_end++;
+ /* check for corrupted fields */
+/* write_i2c_reg(ipd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE); */
+/* write_i2c_reg(ipd->regs, ODD_CSR, CSR_ERROR | CSR_DONE); */
+ tmp = ioread32(ipd->regs + CSR1) & (FLD_CRPT_EVEN | FLD_CRPT_ODD);
+ if (tmp) {
+ ipd->stats.corrupted_fields++;
+ iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |
+ FLD_DN_ODD | FLD_DN_EVEN |
+ CAP_CONT_EVEN | CAP_CONT_ODD,
+ ipd->regs + CSR1);
+ mmiowb();
+ }
+
+ spin_lock(&ipd->lock);
+ if (ipd->curr_buf) {
+ do_gettimeofday(&ipd->curr_buf->v4l2_buf.timestamp);
+ ipd->curr_buf->v4l2_buf.sequence = (ipd->field_count) >> 1;
+ vb2_buffer_done(ipd->curr_buf, VB2_BUF_STATE_DONE);
+ }
+
+ if (!ipd->q->streaming || list_empty(&ipd->dmaq))
+ goto stop_dma;
+ ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry);
+ list_del(&ivb->done_entry);
+ ipd->curr_buf = ivb;
+ dma_addr = vb2_dma_contig_plane_dma_addr(ivb, 0);
+ iowrite32(dma_addr, ipd->regs + EVEN_DMA_START);
+ iowrite32(dma_addr + img_width, ipd->regs + ODD_DMA_START);
+ iowrite32(img_width, ipd->regs + EVEN_DMA_STRIDE);
+ iowrite32(img_width, ipd->regs + ODD_DMA_STRIDE);
+ mmiowb();
+ /* enable interrupts, clear all irq flags */
+ iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START |
+ FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR);
+ spin_unlock(&ipd->lock);
+ return IRQ_HANDLED;
+
+stop_dma:
+ ipd->curr_buf = NULL;
+ /* stop the board */
+ write_i2c_reg_nowait(ipd->regs, CSR2, ipd->csr2);
+ iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |
+ FLD_DN_ODD | FLD_DN_EVEN, ipd->regs + CSR1);
+ /* disable interrupts, clear all irq flags */
+ iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR);
+ spin_unlock(&ipd->lock);
+ return IRQ_HANDLED;
+}
+
+static int
+dt3155_open(struct file *filp)
+{
+ int ret = 0;
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ if (!pd->users) {
+ pd->q = kzalloc(sizeof(*pd->q), GFP_KERNEL);
+ if (!pd->q) {
+ ret = -ENOMEM;
+ goto err_alloc_queue;
+ }
+ pd->q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pd->q->io_modes = VB2_READ | VB2_MMAP;
+ pd->q->ops = &q_ops;
+ pd->q->mem_ops = &vb2_dma_contig_memops;
+ pd->q->drv_priv = pd;
+ pd->curr_buf = NULL;
+ pd->field_count = 0;
+ vb2_queue_init(pd->q); /* cannot fail */
+ INIT_LIST_HEAD(&pd->dmaq);
+ spin_lock_init(&pd->lock);
+ /* disable all irqs, clear all irq flags */
+ iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD,
+ pd->regs + INT_CSR);
+ ret = request_irq(pd->pdev->irq, dt3155_irq_handler_even,
+ IRQF_SHARED, DT3155_NAME, pd);
+ if (ret)
+ goto err_request_irq;
+ }
+ pd->users++;
+ return 0; /* success */
+err_request_irq:
+ kfree(pd->q);
+ pd->q = NULL;
+err_alloc_queue:
+ return ret;
+}
+
+static int
+dt3155_release(struct file *filp)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ pd->users--;
+ BUG_ON(pd->users < 0);
+ if (!pd->users) {
+ vb2_queue_release(pd->q);
+ free_irq(pd->pdev->irq, pd);
+ if (pd->q->alloc_ctx[0])
+ vb2_dma_contig_cleanup_ctx(pd->q->alloc_ctx[0]);
+ kfree(pd->q);
+ pd->q = NULL;
+ }
+ return 0;
+}
+
+static ssize_t
+dt3155_read(struct file *filp, char __user *user, size_t size, loff_t *loff)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_read(pd->q, user, size, loff, filp->f_flags & O_NONBLOCK);
+}
+
+static unsigned int
+dt3155_poll(struct file *filp, struct poll_table_struct *polltbl)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_poll(pd->q, filp, polltbl);
+}
+
+static int
+dt3155_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_mmap(pd->q, vma);
+}
+
+static const struct v4l2_file_operations dt3155_fops = {
+ .owner = THIS_MODULE,
+ .open = dt3155_open,
+ .release = dt3155_release,
+ .read = dt3155_read,
+ .poll = dt3155_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = dt3155_mmap,
+};
+
+static int
+dt3155_ioc_streamon(struct file *filp, void *p, enum v4l2_buf_type type)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_streamon(pd->q, type);
+}
+
+static int
+dt3155_ioc_streamoff(struct file *filp, void *p, enum v4l2_buf_type type)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_streamoff(pd->q, type);
+}
+
+static int
+dt3155_ioc_querycap(struct file *filp, void *p, struct v4l2_capability *cap)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ strcpy(cap->driver, DT3155_NAME);
+ strcpy(cap->card, DT3155_NAME " frame grabber");
+ sprintf(cap->bus_info, "PCI:%s", pci_name(pd->pdev));
+ cap->version =
+ KERNEL_VERSION(DT3155_VER_MAJ, DT3155_VER_MIN, DT3155_VER_EXT);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ DT3155_CAPTURE_METHOD;
+ return 0;
+}
+
+static int
+dt3155_ioc_enum_fmt_vid_cap(struct file *filp, void *p, struct v4l2_fmtdesc *f)
+{
+ if (f->index >= NUM_OF_FORMATS)
+ return -EINVAL;
+ *f = frame_std[f->index];
+ return 0;
+}
+
+static int
+dt3155_ioc_g_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ f->fmt.pix.width = img_width;
+ f->fmt.pix.height = img_height;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int
+dt3155_ioc_try_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (f->fmt.pix.width == img_width &&
+ f->fmt.pix.height == img_height &&
+ f->fmt.pix.pixelformat == V4L2_PIX_FMT_GREY &&
+ f->fmt.pix.field == V4L2_FIELD_NONE &&
+ f->fmt.pix.bytesperline == f->fmt.pix.width &&
+ f->fmt.pix.sizeimage == f->fmt.pix.width * f->fmt.pix.height)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int
+dt3155_ioc_s_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
+{
+ return dt3155_ioc_g_fmt_vid_cap(filp, p, f);
+}
+
+static int
+dt3155_ioc_reqbufs(struct file *filp, void *p, struct v4l2_requestbuffers *b)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_reqbufs(pd->q, b);
+}
+
+static int
+dt3155_ioc_querybuf(struct file *filp, void *p, struct v4l2_buffer *b)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_querybuf(pd->q, b);
+}
+
+static int
+dt3155_ioc_qbuf(struct file *filp, void *p, struct v4l2_buffer *b)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_qbuf(pd->q, b);
+}
+
+static int
+dt3155_ioc_dqbuf(struct file *filp, void *p, struct v4l2_buffer *b)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_dqbuf(pd->q, b, filp->f_flags & O_NONBLOCK);
+}
+
+static int
+dt3155_ioc_querystd(struct file *filp, void *p, v4l2_std_id *norm)
+{
+ *norm = DT3155_CURRENT_NORM;
+ return 0;
+}
+
+static int
+dt3155_ioc_g_std(struct file *filp, void *p, v4l2_std_id *norm)
+{
+ *norm = DT3155_CURRENT_NORM;
+ return 0;
+}
+
+static int
+dt3155_ioc_s_std(struct file *filp, void *p, v4l2_std_id *norm)
+{
+ if (*norm & DT3155_CURRENT_NORM)
+ return 0;
+ return -EINVAL;
+}
+
+static int
+dt3155_ioc_enum_input(struct file *filp, void *p, struct v4l2_input *input)
+{
+ if (input->index)
+ return -EINVAL;
+ strcpy(input->name, "Coax in");
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ /*
+ * FIXME: input->std = 0 according to v4l2 API
+ * VIDIOC_G_STD, VIDIOC_S_STD, VIDIOC_QUERYSTD and VIDIOC_ENUMSTD
+ * should return -EINVAL
+ */
+ input->std = DT3155_CURRENT_NORM;
+ input->status = 0;/* FIXME: add sync detection & V4L2_IN_ST_NO_H_LOCK */
+ return 0;
+}
+
+static int
+dt3155_ioc_g_input(struct file *filp, void *p, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int
+dt3155_ioc_s_input(struct file *filp, void *p, unsigned int i)
+{
+ if (i)
+ return -EINVAL;
+ return 0;
+}
+
+static int
+dt3155_ioc_g_parm(struct file *filp, void *p, struct v4l2_streamparm *parms)
+{
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parms->parm.capture.capturemode = 0;
+ parms->parm.capture.timeperframe.numerator = 1001;
+ parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000;
+ parms->parm.capture.extendedmode = 0;
+ parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */
+ return 0;
+}
+
+static int
+dt3155_ioc_s_parm(struct file *filp, void *p, struct v4l2_streamparm *parms)
+{
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parms->parm.capture.capturemode = 0;
+ parms->parm.capture.timeperframe.numerator = 1001;
+ parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000;
+ parms->parm.capture.extendedmode = 0;
+ parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops dt3155_ioctl_ops = {
+ .vidioc_streamon = dt3155_ioc_streamon,
+ .vidioc_streamoff = dt3155_ioc_streamoff,
+ .vidioc_querycap = dt3155_ioc_querycap,
+/*
+ .vidioc_g_priority = dt3155_ioc_g_priority,
+ .vidioc_s_priority = dt3155_ioc_s_priority,
+*/
+ .vidioc_enum_fmt_vid_cap = dt3155_ioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = dt3155_ioc_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = dt3155_ioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = dt3155_ioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = dt3155_ioc_reqbufs,
+ .vidioc_querybuf = dt3155_ioc_querybuf,
+ .vidioc_qbuf = dt3155_ioc_qbuf,
+ .vidioc_dqbuf = dt3155_ioc_dqbuf,
+ .vidioc_querystd = dt3155_ioc_querystd,
+ .vidioc_g_std = dt3155_ioc_g_std,
+ .vidioc_s_std = dt3155_ioc_s_std,
+ .vidioc_enum_input = dt3155_ioc_enum_input,
+ .vidioc_g_input = dt3155_ioc_g_input,
+ .vidioc_s_input = dt3155_ioc_s_input,
+/*
+ .vidioc_queryctrl = dt3155_ioc_queryctrl,
+ .vidioc_g_ctrl = dt3155_ioc_g_ctrl,
+ .vidioc_s_ctrl = dt3155_ioc_s_ctrl,
+ .vidioc_querymenu = dt3155_ioc_querymenu,
+ .vidioc_g_ext_ctrls = dt3155_ioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = dt3155_ioc_s_ext_ctrls,
+*/
+ .vidioc_g_parm = dt3155_ioc_g_parm,
+ .vidioc_s_parm = dt3155_ioc_s_parm,
+/*
+ .vidioc_cropcap = dt3155_ioc_cropcap,
+ .vidioc_g_crop = dt3155_ioc_g_crop,
+ .vidioc_s_crop = dt3155_ioc_s_crop,
+ .vidioc_enum_framesizes = dt3155_ioc_enum_framesizes,
+ .vidioc_enum_frameintervals = dt3155_ioc_enum_frameintervals,
+*/
+};
+
+static int __devinit
+dt3155_init_board(struct pci_dev *pdev)
+{
+ struct dt3155_priv *pd = pci_get_drvdata(pdev);
+ void *buf_cpu;
+ dma_addr_t buf_dma;
+ int i;
+ u8 tmp;
+
+ pci_set_master(pdev); /* dt3155 needs it */
+
+ /* resetting the adapter */
+ iowrite32(FLD_CRPT_ODD | FLD_CRPT_EVEN | FLD_DN_ODD | FLD_DN_EVEN,
+ pd->regs + CSR1);
+ mmiowb();
+ msleep(20);
+
+ /* initializing adaper registers */
+ iowrite32(FIFO_EN | SRST, pd->regs + CSR1);
+ mmiowb();
+ iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT);
+ iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT);
+ iowrite32(0x00000020, pd->regs + FIFO_TRIGER);
+ iowrite32(0x00000103, pd->regs + XFER_MODE);
+ iowrite32(0, pd->regs + RETRY_WAIT_CNT);
+ iowrite32(0, pd->regs + INT_CSR);
+ iowrite32(1, pd->regs + EVEN_FLD_MASK);
+ iowrite32(1, pd->regs + ODD_FLD_MASK);
+ iowrite32(0, pd->regs + MASK_LENGTH);
+ iowrite32(0x0005007C, pd->regs + FIFO_FLAG_CNT);
+ iowrite32(0x01010101, pd->regs + IIC_CLK_DUR);
+ mmiowb();
+
+ /* verifying that we have a DT3155 board (not just a SAA7116 chip) */
+ read_i2c_reg(pd->regs, DT_ID, &tmp);
+ if (tmp != DT3155_ID)
+ return -ENODEV;
+
+ /* initialize AD LUT */
+ write_i2c_reg(pd->regs, AD_ADDR, 0);
+ for (i = 0; i < 256; i++)
+ write_i2c_reg(pd->regs, AD_LUT, i);
+
+ /* initialize ADC references */
+ /* FIXME: pos_ref & neg_ref depend on VT_50HZ */
+ write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG);
+ write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3);
+ write_i2c_reg(pd->regs, AD_ADDR, AD_POS_REF);
+ write_i2c_reg(pd->regs, AD_CMD, 34);
+ write_i2c_reg(pd->regs, AD_ADDR, AD_NEG_REF);
+ write_i2c_reg(pd->regs, AD_CMD, 0);
+
+ /* initialize PM LUT */
+ write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM);
+ for (i = 0; i < 256; i++) {
+ write_i2c_reg(pd->regs, PM_LUT_ADDR, i);
+ write_i2c_reg(pd->regs, PM_LUT_DATA, i);
+ }
+ write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM | PM_LUT_SEL);
+ for (i = 0; i < 256; i++) {
+ write_i2c_reg(pd->regs, PM_LUT_ADDR, i);
+ write_i2c_reg(pd->regs, PM_LUT_DATA, i);
+ }
+ write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */
+
+ /* select chanel 1 for input and set sync level */
+ write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG);
+ write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3);
+
+ /* allocate memory, and initialize the DMA machine */
+ buf_cpu = dma_alloc_coherent(&pdev->dev, DT3155_BUF_SIZE, &buf_dma,
+ GFP_KERNEL);
+ if (!buf_cpu)
+ return -ENOMEM;
+ iowrite32(buf_dma, pd->regs + EVEN_DMA_START);
+ iowrite32(buf_dma, pd->regs + ODD_DMA_START);
+ iowrite32(0, pd->regs + EVEN_DMA_STRIDE);
+ iowrite32(0, pd->regs + ODD_DMA_STRIDE);
+
+ /* Perform a pseudo even field acquire */
+ iowrite32(FIFO_EN | SRST | CAP_CONT_ODD, pd->regs + CSR1);
+ write_i2c_reg(pd->regs, CSR2, pd->csr2 | SYNC_SNTL);
+ write_i2c_reg(pd->regs, CONFIG, pd->config);
+ write_i2c_reg(pd->regs, EVEN_CSR, CSR_SNGL);
+ write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | SYNC_SNTL);
+ msleep(100);
+ read_i2c_reg(pd->regs, CSR2, &tmp);
+ write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE);
+ write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE);
+ write_i2c_reg(pd->regs, CSR2, pd->csr2);
+ iowrite32(FIFO_EN | SRST | FLD_DN_EVEN | FLD_DN_ODD, pd->regs + CSR1);
+
+ /* deallocate memory */
+ dma_free_coherent(&pdev->dev, DT3155_BUF_SIZE, buf_cpu, buf_dma);
+ if (tmp & BUSY_EVEN)
+ return -EIO;
+ return 0;
+}
+
+static struct video_device dt3155_vdev = {
+ .name = DT3155_NAME,
+ .fops = &dt3155_fops,
+ .ioctl_ops = &dt3155_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+ .tvnorms = DT3155_CURRENT_NORM,
+ .current_norm = DT3155_CURRENT_NORM,
+};
+
+/* same as in drivers/base/dma-coherent.c */
+struct dma_coherent_mem {
+ void *virt_base;
+ dma_addr_t device_base;
+ int size;
+ int flags;
+ unsigned long *bitmap;
+};
+
+static int __devinit
+dt3155_alloc_coherent(struct device *dev, size_t size, int flags)
+{
+ struct dma_coherent_mem *mem;
+ dma_addr_t dev_base;
+ int pages = size >> PAGE_SHIFT;
+ int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
+
+ if ((flags & DMA_MEMORY_MAP) == 0)
+ goto out;
+ if (!size)
+ goto out;
+ if (dev->dma_mem)
+ goto out;
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (!mem)
+ goto out;
+ mem->virt_base = dma_alloc_coherent(dev, size, &dev_base,
+ DT3155_COH_FLAGS);
+ if (!mem->virt_base)
+ goto err_alloc_coherent;
+ mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+ if (!mem->bitmap)
+ goto err_bitmap;
+
+ /* coherent_dma_mask is already set to 32 bits */
+ mem->device_base = dev_base;
+ mem->size = pages;
+ mem->flags = flags;
+ dev->dma_mem = mem;
+ return DMA_MEMORY_MAP;
+
+err_bitmap:
+ dma_free_coherent(dev, size, mem->virt_base, dev_base);
+err_alloc_coherent:
+ kfree(mem);
+out:
+ return 0;
+}
+
+static void __devexit
+dt3155_free_coherent(struct device *dev)
+{
+ struct dma_coherent_mem *mem = dev->dma_mem;
+
+ if (!mem)
+ return;
+ dev->dma_mem = NULL;
+ dma_free_coherent(dev, mem->size << PAGE_SHIFT,
+ mem->virt_base, mem->device_base);
+ kfree(mem->bitmap);
+ kfree(mem);
+}
+
+static int __devinit
+dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int err;
+ struct dt3155_priv *pd;
+
+ err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (err)
+ return -ENODEV;
+ err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (err)
+ return -ENODEV;
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return -ENOMEM;
+ pd->vdev = video_device_alloc();
+ if (!pd->vdev)
+ goto err_video_device_alloc;
+ *pd->vdev = dt3155_vdev;
+ pci_set_drvdata(pdev, pd); /* for use in dt3155_remove() */
+ video_set_drvdata(pd->vdev, pd); /* for use in video_fops */
+ pd->users = 0;
+ pd->pdev = pdev;
+ INIT_LIST_HEAD(&pd->dmaq);
+ mutex_init(&pd->mux);
+ pd->vdev->lock = &pd->mux; /* for locking v4l2_file_operations */
+ spin_lock_init(&pd->lock);
+ pd->csr2 = csr2_init;
+ pd->config = config_init;
+ err = pci_enable_device(pdev);
+ if (err)
+ goto err_enable_dev;
+ err = pci_request_region(pdev, 0, pci_name(pdev));
+ if (err)
+ goto err_req_region;
+ pd->regs = pci_iomap(pdev, 0, pci_resource_len(pd->pdev, 0));
+ if (!pd->regs)
+ err = -ENOMEM;
+ goto err_pci_iomap;
+ err = dt3155_init_board(pdev);
+ if (err)
+ goto err_init_board;
+ err = video_register_device(pd->vdev, VFL_TYPE_GRABBER, -1);
+ if (err)
+ goto err_init_board;
+ if (dt3155_alloc_coherent(&pdev->dev, DT3155_CHUNK_SIZE,
+ DMA_MEMORY_MAP))
+ dev_info(&pdev->dev, "preallocated 8 buffers\n");
+ dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev->minor);
+ return 0; /* success */
+
+err_init_board:
+ pci_iounmap(pdev, pd->regs);
+err_pci_iomap:
+ pci_release_region(pdev, 0);
+err_req_region:
+ pci_disable_device(pdev);
+err_enable_dev:
+ video_device_release(pd->vdev);
+err_video_device_alloc:
+ kfree(pd);
+ return err;
+}
+
+static void __devexit
+dt3155_remove(struct pci_dev *pdev)
+{
+ struct dt3155_priv *pd = pci_get_drvdata(pdev);
+
+ dt3155_free_coherent(&pdev->dev);
+ video_unregister_device(pd->vdev);
+ pci_iounmap(pdev, pd->regs);
+ pci_release_region(pdev, 0);
+ pci_disable_device(pdev);
+ /*
+ * video_device_release() is invoked automatically
+ * see: struct video_device dt3155_vdev
+ */
+ kfree(pd);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
+ { PCI_DEVICE(DT3155_VENDOR_ID, DT3155_DEVICE_ID) },
+ { 0, /* zero marks the end */ },
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static struct pci_driver pci_driver = {
+ .name = DT3155_NAME,
+ .id_table = pci_ids,
+ .probe = dt3155_probe,
+ .remove = __devexit_p(dt3155_remove),
+};
+
+static int __init
+dt3155_init_module(void)
+{
+ return pci_register_driver(&pci_driver);
+}
+
+static void __exit
+dt3155_exit_module(void)
+{
+ pci_unregister_driver(&pci_driver);
+}
+
+module_init(dt3155_init_module);
+module_exit(dt3155_exit_module);
+
+MODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber");
+MODULE_AUTHOR("Marin Mitov <mitov@issp.bas.bg>");
+MODULE_VERSION(DT3155_VERSION);
+MODULE_LICENSE("GPL");
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2006-2010 by Marin Mitov *
+ * mitov@issp.bas.bg *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+/* DT3155 header file */
+#ifndef _DT3155_H_
+#define _DT3155_H_
+
+#ifdef __KERNEL__
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#define DT3155_NAME "dt3155"
+#define DT3155_VER_MAJ 1
+#define DT3155_VER_MIN 1
+#define DT3155_VER_EXT 0
+#define DT3155_VERSION __stringify(DT3155_VER_MAJ) "." \
+ __stringify(DT3155_VER_MIN) "." \
+ __stringify(DT3155_VER_EXT)
+
+/* DT3155 Base Register offsets (memory mapped) */
+#define EVEN_DMA_START 0x00
+#define ODD_DMA_START 0x0C
+#define EVEN_DMA_STRIDE 0x18
+#define ODD_DMA_STRIDE 0x24
+#define EVEN_PIXEL_FMT 0x30
+#define ODD_PIXEL_FMT 0x34
+#define FIFO_TRIGER 0x38
+#define XFER_MODE 0x3C
+#define CSR1 0x40
+#define RETRY_WAIT_CNT 0x44
+#define INT_CSR 0x48
+#define EVEN_FLD_MASK 0x4C
+#define ODD_FLD_MASK 0x50
+#define MASK_LENGTH 0x54
+#define FIFO_FLAG_CNT 0x58
+#define IIC_CLK_DUR 0x5C
+#define IIC_CSR1 0x60
+#define IIC_CSR2 0x64
+
+/* DT3155 Internal Registers indexes (i2c/IIC mapped) */
+#define CSR2 0x10
+#define EVEN_CSR 0x11
+#define ODD_CSR 0x12
+#define CONFIG 0x13
+#define DT_ID 0x1F
+#define X_CLIP_START 0x20
+#define Y_CLIP_START 0x22
+#define X_CLIP_END 0x24
+#define Y_CLIP_END 0x26
+#define AD_ADDR 0x30
+#define AD_LUT 0x31
+#define AD_CMD 0x32
+#define DIG_OUT 0x40
+#define PM_LUT_ADDR 0x50
+#define PM_LUT_DATA 0x51
+
+/* AD command register values */
+#define AD_CMD_REG 0x00
+#define AD_POS_REF 0x01
+#define AD_NEG_REF 0x02
+
+/* CSR1 bit masks */
+#define CRPT_DIS 0x00004000
+#define FLD_CRPT_ODD 0x00000200
+#define FLD_CRPT_EVEN 0x00000100
+#define FIFO_EN 0x00000080
+#define SRST 0x00000040
+#define FLD_DN_ODD 0x00000020
+#define FLD_DN_EVEN 0x00000010
+/* These should not be used.
+ * Use CAP_CONT_ODD/EVEN instead
+#define CAP_SNGL_ODD 0x00000008
+#define CAP_SNGL_EVEN 0x00000004
+*/
+#define CAP_CONT_ODD 0x00000002
+#define CAP_CONT_EVEN 0x00000001
+
+/* INT_CSR bit masks */
+#define FLD_START_EN 0x00000400
+#define FLD_END_ODD_EN 0x00000200
+#define FLD_END_EVEN_EN 0x00000100
+#define FLD_START 0x00000004
+#define FLD_END_ODD 0x00000002
+#define FLD_END_EVEN 0x00000001
+
+/* IIC_CSR1 bit masks */
+#define DIRECT_ABORT 0x00000200
+
+/* IIC_CSR2 bit masks */
+#define NEW_CYCLE 0x01000000
+#define DIR_RD 0x00010000
+#define IIC_READ 0x01010000
+#define IIC_WRITE 0x01000000
+
+/* CSR2 bit masks */
+#define DISP_PASS 0x40
+#define BUSY_ODD 0x20
+#define BUSY_EVEN 0x10
+#define SYNC_PRESENT 0x08
+#define VT_50HZ 0x04
+#define SYNC_SNTL 0x02
+#define CHROM_FILT 0x01
+#define VT_60HZ 0x00
+
+/* CSR_EVEN/ODD bit masks */
+#define CSR_ERROR 0x04
+#define CSR_SNGL 0x02
+#define CSR_DONE 0x01
+
+/* CONFIG bit masks */
+#define PM_LUT_PGM 0x80
+#define PM_LUT_SEL 0x40
+#define CLIP_EN 0x20
+#define HSCALE_EN 0x10
+#define EXT_TRIG_UP 0x0C
+#define EXT_TRIG_DOWN 0x04
+#define ACQ_MODE_NEXT 0x02
+#define ACQ_MODE_ODD 0x01
+#define ACQ_MODE_EVEN 0x00
+
+/* AD_CMD bit masks */
+#define VIDEO_CNL_1 0x00
+#define VIDEO_CNL_2 0x40
+#define VIDEO_CNL_3 0x80
+#define VIDEO_CNL_4 0xC0
+#define SYNC_CNL_1 0x00
+#define SYNC_CNL_2 0x10
+#define SYNC_CNL_3 0x20
+#define SYNC_CNL_4 0x30
+#define SYNC_LVL_1 0x00
+#define SYNC_LVL_2 0x04
+#define SYNC_LVL_3 0x08
+#define SYNC_LVL_4 0x0C
+
+/* DT3155 identificator */
+#define DT3155_ID 0x20
+
+#ifdef CONFIG_DT3155_CCIR
+#define DMA_STRIDE 768
+#else
+#define DMA_STRIDE 640
+#endif
+
+/**
+ * struct dt3155_stats - statistics structure
+ *
+ * @free_bufs_empty: no free image buffers
+ * @corrupted_fields: corrupted fields
+ * @dma_map_failed: dma mapping failed
+ * @start_before_end: new started before old ended
+ */
+struct dt3155_stats {
+ int free_bufs_empty;
+ int corrupted_fields;
+ int dma_map_failed;
+ int start_before_end;
+};
+
+/* per board private data structure */
+/**
+ * struct dt3155_priv - private data structure
+ *
+ * @vdev: pointer to video_device structure
+ * @pdev: pointer to pci_dev structure
+ * @q pointer to vb2_queue structure
+ * @curr_buf: pointer to curren buffer
+ * @mux: mutex to protect the instance
+ * @dmaq queue for dma buffers
+ * @lock spinlock for dma queue
+ * @field_count fields counter
+ * @stats: statistics structure
+ * @users open count
+ * @regs: local copy of mmio base register
+ * @csr2: local copy of csr2 register
+ * @config: local copy of config register
+ */
+struct dt3155_priv {
+ struct video_device *vdev;
+ struct pci_dev *pdev;
+ struct vb2_queue *q;
+ struct vb2_buffer *curr_buf;
+ struct mutex mux;
+ struct list_head dmaq;
+ spinlock_t lock;
+ unsigned int field_count;
+ struct dt3155_stats stats;
+ void __iomem *regs;
+ int users;
+ u8 csr2, config;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* _DT3155_H_ */
--- /dev/null
+config EASYCAP
+ tristate "EasyCAP USB ID 05e1:0408 support"
+ depends on USB && VIDEO_DEV && SND
+ select SND_PCM
+
+ ---help---
+ This is an integrated audio/video driver for EasyCAP cards with
+ USB ID 05e1:0408. It supports two hardware variants:
+
+ * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60,
+ having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R)
+
+ * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled
+ 1, 2, 3, 4 and an unlabelled input cable for a microphone.
+
+ To compile this driver as a module, choose M here: the
+ module will be called easycap
+
+config EASYCAP_DEBUG
+ bool "Enable EasyCAP driver debugging"
+ depends on EASYCAP
+
+ ---help---
+ This option enables debug printouts
+
+ To enable debug, pass the debug level to the debug module
+ parameter:
+
+ modprobe easycap debug=[0..9]
+
--- /dev/null
+easycap-objs := easycap_main.o
+easycap-objs += easycap_low.o
+easycap-objs += easycap_ioctl.o
+easycap-objs += easycap_settings.o
+easycap-objs += easycap_testcard.o
+easycap-objs += easycap_sound.o
+obj-$(CONFIG_EASYCAP) += easycap.o
+
+ccflags-y := -Wall
+
--- /dev/null
+
+ ***********************************************************
+ * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60 *
+ * and *
+ * EasyCAP002 4-Channel USB 2.0 DVR *
+ ***********************************************************
+ Mike Thomas <rmthomas@sciolus.org>
+
+
+
+SUPPORTED HARDWARE
+------------------
+
+This driver is intended for use with hardware having USB ID 05e1:0408.
+Two kinds of EasyCAP have this USB ID, namely:
+
+ * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60,
+ having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R)
+
+ * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled
+ 1, 2, 3, 4 and an unlabelled input cable for a microphone.
+
+
+BUILD OPTIONS AND DEPENDENCIES
+------------------------------
+
+Unless EASYCAP_DEBUG is defined during compilation it will not be possible
+to select a debug level at the time of module installation.
+
+
+KNOWN RUNTIME ISSUES
+--------------------
+
+(1) Intentionally, this driver will not stream material which is unambiguously
+identified by the hardware as copy-protected. Normal video output will be
+present for about a minute but will then freeze when this situation arises.
+
+(2) The controls for luminance, contrast, saturation, hue and volume may not
+always work properly.
+
+(3) Reduced-resolution S-Video seems to suffer from moire artefacts.
+
+
+INPUT NUMBERING
+---------------
+
+For the EasyCAP with S-VIDEO input cable the driver regards a request for
+inputs numbered 0 or 1 as referring to CVBS and a request for input
+numbered 5 as referring to S-VIDEO.
+
+For the EasyCAP with four CVBS inputs the driver expects to be asked for
+any one of inputs numbered 1,2,3,4. If input 0 is asked for, it is
+interpreted as input 1.
+
+
+MODULE PARAMETERS
+-----------------
+
+Three module parameters are defined:
+
+debug the easycap module is configured at diagnostic level n (0 to 9)
+gain audio gain level n (0 to 31, default is 16)
+bars whether to display testcard bars when incoming video signal is lost
+ 0 => no, 1 => yes (default)
+
+
+SUPPORTED TV STANDARDS AND RESOLUTIONS
+--------------------------------------
+
+The following TV standards are natively supported by the hardware and are
+usable as (for example) the "norm=" parameter in the mplayer command:
+
+ PAL_BGHIN, NTSC_N_443,
+ PAL_Nc, NTSC_N,
+ SECAM, NTSC_M, NTSC_M_JP,
+ PAL_60, NTSC_443,
+ PAL_M.
+
+In addition, the driver offers "custom" pseudo-standards with a framerate
+which is 20% of the usual framerate. These pseudo-standards are named:
+
+ PAL_BGHIN_SLOW, NTSC_N_443_SLOW,
+ PAL_Nc_SLOW, NTSC_N_SLOW,
+ SECAM_SLOW, NTSC_M_SLOW, NTSC_M_JP_SLOW,
+ PAL_60_SLOW, NTSC_443_SLOW,
+ PAL_M_SLOW.
+
+
+The available picture sizes are:
+
+ at 25 frames per second: 720x576, 704x576, 640x480, 360x288, 320x240;
+ at 30 frames per second: 720x480, 640x480, 360x240, 320x240.
+
+
+WHAT'S TESTED AND WHAT'S NOT
+----------------------------
+
+This driver is known to work with mplayer, mencoder, tvtime, zoneminder,
+xawtv, gstreamer and sufficiently recent versions of vlc. An interface
+to ffmpeg is implemented, but serious audio-video synchronization problems
+remain.
+
+The driver is designed to support all the TV standards accepted by the
+hardware, but as yet it has actually been tested on only a few of these.
+
+I have been unable to test and calibrate the S-video input myself because I
+do not possess any equipment with S-video output.
+
+
+UDEV RULES
+----------
+
+In order that the special files /dev/easycap0 and /dev/easysnd1 are created
+with conveniently relaxed permissions when the EasyCAP is plugged in, a file
+is preferably to be provided in directory /etc/udev/rules.d with content:
+
+ACTION!="add|change", GOTO="easycap_rules_end"
+ATTRS{idVendor}=="05e1", ATTRS{idProduct}=="0408", \
+ MODE="0666", OWNER="root", GROUP="root"
+LABEL="easycap_rules_end"
+
+
+MODPROBE CONFIGURATION
+----------------------
+
+The easycap module is in competition with the module snd-usb-audio for the
+EasyCAP's audio channel, and its installation can be aided by providing a
+file in directory /etc/modprobe.d with content:
+
+options easycap gain=16 bars=1
+install easycap /sbin/rmmod snd-usb-audio; /sbin/modprobe --ignore-install easycap
+
+
+ACKNOWLEGEMENTS AND REFERENCES
+------------------------------
+This driver makes use of information contained in the Syntek Semicon DC-1125
+Driver, presently maintained at http://sourceforge.net/projects/syntekdriver/
+by Nicolas Vivien. Particularly useful has been a patch to the latter driver
+provided by Ivor Hewitt in January 2009. The NTSC implementation is taken
+from the work of Ben Trask.
+
--- /dev/null
+/*****************************************************************************
+* *
+* easycap.h *
+* *
+*****************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This 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.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * THE FOLLOWING PARAMETERS ARE UNDEFINED:
+ *
+ * EASYCAP_DEBUG
+ *
+ * IF REQUIRED THEY MUST BE EXTERNALLY DEFINED, FOR EXAMPLE AS COMPILER
+ * OPTIONS.
+ */
+/*---------------------------------------------------------------------------*/
+
+#ifndef __EASYCAP_H__
+#define __EASYCAP_H__
+
+/*---------------------------------------------------------------------------*/
+/*
+ * THESE ARE NORMALLY DEFINED
+ */
+/*---------------------------------------------------------------------------*/
+#define PATIENCE 500
+#define PERSEVERE
+/*---------------------------------------------------------------------------*/
+/*
+ * THESE ARE FOR MAINTENANCE ONLY - NORMALLY UNDEFINED:
+ */
+/*---------------------------------------------------------------------------*/
+#undef EASYCAP_TESTCARD
+/*---------------------------------------------------------------------------*/
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/usb.h>
+#include <linux/uaccess.h>
+
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/poll.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+
+#include <linux/vmalloc.h>
+#include <linux/sound.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <linux/videodev2.h>
+#include <linux/soundcard.h>
+
+/*---------------------------------------------------------------------------*/
+/* VENDOR, PRODUCT: Syntek Semiconductor Co., Ltd
+ *
+ * EITHER EasyCAP USB 2.0 Video Adapter with Audio, Model No. DC60
+ * with input cabling: AUDIO(L), AUDIO(R), CVBS, S-VIDEO.
+ *
+ * OR EasyCAP 4CHANNEL USB 2.0 DVR, Model No. EasyCAP002
+ * with input cabling: MICROPHONE, CVBS1, CVBS2, CVBS3, CVBS4.
+ */
+/*---------------------------------------------------------------------------*/
+#define USB_EASYCAP_VENDOR_ID 0x05e1
+#define USB_EASYCAP_PRODUCT_ID 0x0408
+
+#define EASYCAP_DRIVER_VERSION "0.9.01"
+#define EASYCAP_DRIVER_DESCRIPTION "easycapdc60"
+
+#define USB_SKEL_MINOR_BASE 192
+#define DONGLE_MANY 8
+#define INPUT_MANY 6
+/*---------------------------------------------------------------------------*/
+/*
+ * DEFAULT LUMINANCE, CONTRAST, SATURATION AND HUE
+ */
+/*---------------------------------------------------------------------------*/
+#define SAA_0A_DEFAULT 0x7F
+#define SAA_0B_DEFAULT 0x3F
+#define SAA_0C_DEFAULT 0x2F
+#define SAA_0D_DEFAULT 0x00
+/*---------------------------------------------------------------------------*/
+/*
+ * VIDEO STREAMING PARAMETERS:
+ * USB 2.0 PROVIDES FOR HIGH-BANDWIDTH ENDPOINTS WITH AN UPPER LIMIT
+ * OF 3072 BYTES PER MICROFRAME for wMaxPacketSize.
+ */
+/*---------------------------------------------------------------------------*/
+#define VIDEO_ISOC_BUFFER_MANY 16
+#define VIDEO_ISOC_ORDER 3
+#define VIDEO_ISOC_FRAMESPERDESC ((unsigned int) 1 << VIDEO_ISOC_ORDER)
+#define USB_2_0_MAXPACKETSIZE 3072
+#if (USB_2_0_MAXPACKETSIZE > PAGE_SIZE)
+#error video_isoc_buffer[.] will not be big enough
+#endif
+#define VIDEO_JUNK_TOLERATE VIDEO_ISOC_BUFFER_MANY
+#define VIDEO_LOST_TOLERATE 50
+/*---------------------------------------------------------------------------*/
+/*
+ * VIDEO BUFFERS
+ */
+/*---------------------------------------------------------------------------*/
+#define FIELD_BUFFER_SIZE (203 * PAGE_SIZE)
+#define FRAME_BUFFER_SIZE (405 * PAGE_SIZE)
+#define FIELD_BUFFER_MANY 4
+#define FRAME_BUFFER_MANY 6
+/*---------------------------------------------------------------------------*/
+/*
+ * AUDIO STREAMING PARAMETERS
+ */
+/*---------------------------------------------------------------------------*/
+#define AUDIO_ISOC_BUFFER_MANY 16
+#define AUDIO_ISOC_ORDER 1
+#define AUDIO_ISOC_FRAMESPERDESC 32
+#define AUDIO_ISOC_BUFFER_SIZE (PAGE_SIZE << AUDIO_ISOC_ORDER)
+/*---------------------------------------------------------------------------*/
+/*
+ * AUDIO BUFFERS
+ */
+/*---------------------------------------------------------------------------*/
+#define AUDIO_FRAGMENT_MANY 32
+#define PAGES_PER_AUDIO_FRAGMENT 4
+/*---------------------------------------------------------------------------*/
+/*
+ * IT IS ESSENTIAL THAT EVEN-NUMBERED STANDARDS ARE 25 FRAMES PER SECOND,
+ * ODD-NUMBERED STANDARDS ARE 30 FRAMES PER SECOND.
+ * THE NUMBERING OF STANDARDS MUST NOT BE CHANGED WITHOUT DUE CARE. NOT
+ * ONLY MUST THE PARAMETER
+ * STANDARD_MANY
+ * BE CHANGED TO CORRESPOND TO THE NEW NUMBER OF STANDARDS, BUT ALSO THE
+ * NUMBERING MUST REMAIN AN UNBROKEN ASCENDING SEQUENCE: DUMMY STANDARDS
+ * MAY NEED TO BE ADDED. APPROPRIATE CHANGES WILL ALWAYS BE REQUIRED IN
+ * ROUTINE fillin_formats() AND POSSIBLY ELSEWHERE. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+#define PAL_BGHIN 0
+#define PAL_Nc 2
+#define SECAM 4
+#define NTSC_N 6
+#define NTSC_N_443 8
+#define NTSC_M 1
+#define NTSC_443 3
+#define NTSC_M_JP 5
+#define PAL_60 7
+#define PAL_M 9
+#define PAL_BGHIN_SLOW 10
+#define PAL_Nc_SLOW 12
+#define SECAM_SLOW 14
+#define NTSC_N_SLOW 16
+#define NTSC_N_443_SLOW 18
+#define NTSC_M_SLOW 11
+#define NTSC_443_SLOW 13
+#define NTSC_M_JP_SLOW 15
+#define PAL_60_SLOW 17
+#define PAL_M_SLOW 19
+#define STANDARD_MANY 20
+/*---------------------------------------------------------------------------*/
+/*
+ * ENUMS
+ */
+/*---------------------------------------------------------------------------*/
+enum {
+ AT_720x576,
+ AT_704x576,
+ AT_640x480,
+ AT_720x480,
+ AT_360x288,
+ AT_320x240,
+ AT_360x240,
+ RESOLUTION_MANY
+};
+enum {
+ FMT_UYVY,
+ FMT_YUY2,
+ FMT_RGB24,
+ FMT_RGB32,
+ FMT_BGR24,
+ FMT_BGR32,
+ PIXELFORMAT_MANY
+};
+enum {
+ FIELD_NONE,
+ FIELD_INTERLACED,
+ INTERLACE_MANY
+};
+#define SETTINGS_MANY (STANDARD_MANY * \
+ RESOLUTION_MANY * \
+ 2 * \
+ PIXELFORMAT_MANY * \
+ INTERLACE_MANY)
+/*---------------------------------------------------------------------------*/
+/*
+ * STRUCTURE DEFINITIONS
+ */
+/*---------------------------------------------------------------------------*/
+struct easycap_dongle {
+ struct easycap *peasycap;
+ struct mutex mutex_video;
+ struct mutex mutex_audio;
+};
+/*---------------------------------------------------------------------------*/
+struct data_buffer {
+ struct list_head list_head;
+ void *pgo;
+ void *pto;
+ u16 kount;
+ u16 input;
+};
+/*---------------------------------------------------------------------------*/
+struct data_urb {
+ struct list_head list_head;
+ struct urb *purb;
+ int isbuf;
+ int length;
+};
+/*---------------------------------------------------------------------------*/
+struct easycap_standard {
+ u16 mask;
+struct v4l2_standard v4l2_standard;
+};
+struct easycap_format {
+ u16 mask;
+ char name[128];
+struct v4l2_format v4l2_format;
+};
+struct inputset {
+ int input;
+ int input_ok;
+ int standard_offset;
+ int standard_offset_ok;
+ int format_offset;
+ int format_offset_ok;
+ int brightness;
+ int brightness_ok;
+ int contrast;
+ int contrast_ok;
+ int saturation;
+ int saturation_ok;
+ int hue;
+ int hue_ok;
+};
+/*---------------------------------------------------------------------------*/
+/*
+ * easycap.ilk == 0 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=256
+ * easycap.ilk == 2 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=9
+ * easycap.ilk == 3 => FOUR-CVBS HARDWARE, AUDIO wMaxPacketSize=9
+ */
+/*---------------------------------------------------------------------------*/
+struct easycap {
+ int isdongle;
+ int minor;
+
+ struct video_device video_device;
+ struct v4l2_device v4l2_device;
+
+ int status;
+ unsigned int audio_pages_per_fragment;
+ unsigned int audio_bytes_per_fragment;
+ unsigned int audio_buffer_page_many;
+
+#define UPSAMPLE
+#ifdef UPSAMPLE
+ s16 oldaudio;
+#endif /*UPSAMPLE*/
+
+ int ilk;
+ bool microphone;
+
+ struct usb_device *pusb_device;
+ struct usb_interface *pusb_interface;
+
+ struct kref kref;
+
+ int queued[FRAME_BUFFER_MANY];
+ int done[FRAME_BUFFER_MANY];
+
+ wait_queue_head_t wq_video;
+ wait_queue_head_t wq_audio;
+ wait_queue_head_t wq_trigger;
+
+ int input;
+ int polled;
+ int standard_offset;
+ int format_offset;
+ struct inputset inputset[INPUT_MANY];
+
+ bool ntsc;
+ int fps;
+ int usec;
+ int tolerate;
+ int skip;
+ int skipped;
+ int lost[INPUT_MANY];
+ int merit[180];
+
+ long long int dnbydt;
+
+ int video_interface;
+ int video_altsetting_on;
+ int video_altsetting_off;
+ int video_endpointnumber;
+ int video_isoc_maxframesize;
+ int video_isoc_buffer_size;
+ int video_isoc_framesperdesc;
+
+ int video_isoc_streaming;
+ int video_isoc_sequence;
+ int video_idle;
+ int video_eof;
+ int video_junk;
+
+ struct data_buffer video_isoc_buffer[VIDEO_ISOC_BUFFER_MANY];
+ struct data_buffer field_buffer[FIELD_BUFFER_MANY]
+ [(FIELD_BUFFER_SIZE/PAGE_SIZE)];
+ struct data_buffer frame_buffer[FRAME_BUFFER_MANY]
+ [(FRAME_BUFFER_SIZE/PAGE_SIZE)];
+
+ struct list_head urb_video_head;
+ struct list_head *purb_video_head;
+
+ u8 cache[8];
+ u8 *pcache;
+ int video_mt;
+ int audio_mt;
+ long long audio_bytes;
+ u32 isequence;
+
+ int vma_many;
+/*---------------------------------------------------------------------------*/
+/*
+ * BUFFER INDICATORS
+ */
+/*---------------------------------------------------------------------------*/
+ int field_fill; /* Field buffer being filled by easycap_complete(). */
+ /* Bumped only by easycap_complete(). */
+ int field_page; /* Page of field buffer page being filled by */
+ /* easycap_complete(). */
+ int field_read; /* Field buffer to be read by field2frame(). */
+ /* Bumped only by easycap_complete(). */
+ int frame_fill; /* Frame buffer being filled by field2frame(). */
+ /* Bumped only by easycap_dqbuf() when */
+ /* field2frame() has created a complete frame. */
+ int frame_read; /* Frame buffer offered to user by DQBUF. */
+ /* Set only by easycap_dqbuf() to trail frame_fill.*/
+ int frame_lock; /* Flag set to 1 by DQBUF and cleared by QBUF */
+/*---------------------------------------------------------------------------*/
+/*
+ * IMAGE PROPERTIES
+ */
+/*---------------------------------------------------------------------------*/
+ u32 pixelformat;
+ int width;
+ int height;
+ int bytesperpixel;
+ bool byteswaporder;
+ bool decimatepixel;
+ bool offerfields;
+ int frame_buffer_used;
+ int frame_buffer_many;
+ int videofieldamount;
+
+ int brightness;
+ int contrast;
+ int saturation;
+ int hue;
+
+ int allocation_video_urb;
+ int allocation_video_page;
+ int allocation_video_struct;
+ int registered_video;
+/*---------------------------------------------------------------------------*/
+/*
+ * ALSA
+ */
+/*---------------------------------------------------------------------------*/
+ struct snd_pcm_hardware alsa_hardware;
+ struct snd_card *psnd_card;
+ struct snd_pcm *psnd_pcm;
+ struct snd_pcm_substream *psubstream;
+ int dma_fill;
+ int dma_next;
+ int dma_read;
+/*---------------------------------------------------------------------------*/
+/*
+ * SOUND PROPERTIES
+ */
+/*---------------------------------------------------------------------------*/
+ int audio_interface;
+ int audio_altsetting_on;
+ int audio_altsetting_off;
+ int audio_endpointnumber;
+ int audio_isoc_maxframesize;
+ int audio_isoc_buffer_size;
+ int audio_isoc_framesperdesc;
+
+ int audio_isoc_streaming;
+ int audio_idle;
+ int audio_eof;
+ int volume;
+ int mute;
+ s8 gain;
+
+ struct data_buffer audio_isoc_buffer[AUDIO_ISOC_BUFFER_MANY];
+
+ struct list_head urb_audio_head;
+ struct list_head *purb_audio_head;
+/*---------------------------------------------------------------------------*/
+/*
+ * BUFFER INDICATORS
+ */
+/*---------------------------------------------------------------------------*/
+ int audio_fill; /* Audio buffer being filled by easycap_complete(). */
+ /* Bumped only by easycap_complete(). */
+ int audio_read; /* Audio buffer page being read by easycap_read(). */
+ /* Set by easycap_read() to trail audio_fill by */
+ /* one fragment. */
+/*---------------------------------------------------------------------------*/
+/*
+ * SOUND PROPERTIES
+ */
+/*---------------------------------------------------------------------------*/
+
+ int audio_buffer_many;
+
+ int allocation_audio_urb;
+ int allocation_audio_page;
+ int allocation_audio_struct;
+ int registered_audio;
+
+ long long int audio_sample;
+ long long int audio_niveau;
+ long long int audio_square;
+
+ struct data_buffer audio_buffer[];
+};
+/*---------------------------------------------------------------------------*/
+/*
+ * VIDEO FUNCTION PROTOTYPES
+ */
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+long easycap_unlocked_ioctl(struct file *, unsigned int, unsigned long);
+int easycap_dqbuf(struct easycap *, int);
+int submit_video_urbs(struct easycap *);
+int kill_video_urbs(struct easycap *);
+int field2frame(struct easycap *);
+int redaub(struct easycap *, void *, void *,
+ int, int, u8, u8, bool);
+void easycap_testcard(struct easycap *, int);
+int fillin_formats(void);
+int newinput(struct easycap *, int);
+int adjust_standard(struct easycap *, v4l2_std_id);
+int adjust_format(struct easycap *, u32, u32, u32,
+ int, bool);
+int adjust_brightness(struct easycap *, int);
+int adjust_contrast(struct easycap *, int);
+int adjust_saturation(struct easycap *, int);
+int adjust_hue(struct easycap *, int);
+int adjust_volume(struct easycap *, int);
+/*---------------------------------------------------------------------------*/
+/*
+ * AUDIO FUNCTION PROTOTYPES
+ */
+/*---------------------------------------------------------------------------*/
+int easycap_alsa_probe(struct easycap *);
+void easycap_alsa_complete(struct urb *);
+
+int easycap_sound_setup(struct easycap *);
+int submit_audio_urbs(struct easycap *);
+int kill_audio_urbs(struct easycap *);
+void easyoss_testtone(struct easycap *, int);
+int audio_setup(struct easycap *);
+/*---------------------------------------------------------------------------*/
+/*
+ * LOW-LEVEL FUNCTION PROTOTYPES
+ */
+/*---------------------------------------------------------------------------*/
+int audio_gainget(struct usb_device *);
+int audio_gainset(struct usb_device *, s8);
+
+int set_interface(struct usb_device *, u16);
+int wakeup_device(struct usb_device *);
+int confirm_resolution(struct usb_device *);
+int confirm_stream(struct usb_device *);
+
+int setup_stk(struct usb_device *, bool);
+int setup_saa(struct usb_device *, bool);
+int setup_vt(struct usb_device *);
+int check_stk(struct usb_device *, bool);
+int check_saa(struct usb_device *, bool);
+int ready_saa(struct usb_device *);
+int merit_saa(struct usb_device *);
+int check_vt(struct usb_device *);
+int select_input(struct usb_device *, int, int);
+int set_resolution(struct usb_device *,
+ u16, u16, u16, u16);
+
+int read_saa(struct usb_device *, u16);
+int read_stk(struct usb_device *, u32);
+int write_saa(struct usb_device *, u16, u16);
+int write_000(struct usb_device *, u16, u16);
+int start_100(struct usb_device *);
+int stop_100(struct usb_device *);
+int write_300(struct usb_device *);
+int read_vt(struct usb_device *, u16);
+int write_vt(struct usb_device *, u16, u16);
+int isdongle(struct easycap *);
+/*---------------------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------------*/
+/*
+ * MACROS SAM(...) AND JOM(...) ALLOW DIAGNOSTIC OUTPUT TO BE TAGGED WITH
+ * THE IDENTITY OF THE DONGLE TO WHICH IT APPLIES, BUT IF INVOKED WHEN THE
+ * POINTER peasycap IS INVALID AN Oops IS LIKELY, AND ITS CAUSE MAY NOT BE
+ * IMMEDIATELY OBVIOUS FROM A CASUAL READING OF THE SOURCE CODE. BEWARE.
+*/
+/*---------------------------------------------------------------------------*/
+const char *strerror(int err);
+
+#define SAY(format, args...) do { \
+ printk(KERN_DEBUG "easycap:: %s: " \
+ format, __func__, ##args); \
+} while (0)
+#define SAM(format, args...) do { \
+ printk(KERN_DEBUG "easycap::%i%s: " \
+ format, peasycap->isdongle, __func__, ##args);\
+} while (0)
+
+#ifdef CONFIG_EASYCAP_DEBUG
+extern int easycap_debug;
+#define JOT(n, format, args...) do { \
+ if (n <= easycap_debug) { \
+ printk(KERN_DEBUG "easycap:: %s: " \
+ format, __func__, ##args);\
+ } \
+} while (0)
+#define JOM(n, format, args...) do { \
+ if (n <= easycap_debug) { \
+ printk(KERN_DEBUG "easycap::%i%s: " \
+ format, peasycap->isdongle, __func__, ##args);\
+ } \
+} while (0)
+
+#else
+#define JOT(n, format, args...) do {} while (0)
+#define JOM(n, format, args...) do {} while (0)
+#endif /* CONFIG_EASYCAP_DEBUG */
+
+/*---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/* globals
+ */
+/*---------------------------------------------------------------------------*/
+
+extern bool easycap_readback;
+extern const struct easycap_standard easycap_standard[];
+extern struct easycap_format easycap_format[];
+extern struct v4l2_queryctrl easycap_control[];
+extern struct usb_driver easycap_usb_driver;
+extern struct easycap_dongle easycapdc60_dongle[];
+
+#endif /* !__EASYCAP_H__ */
--- /dev/null
+/******************************************************************************
+* *
+* easycap_ioctl.c *
+* *
+******************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This 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.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+
+#include <linux/version.h>
+#include "easycap.h"
+
+/*--------------------------------------------------------------------------*/
+/*
+ * UNLESS THERE IS A PREMATURE ERROR RETURN THIS ROUTINE UPDATES THE
+ * FOLLOWING:
+ * peasycap->standard_offset
+ * peasycap->inputset[peasycap->input].standard_offset
+ * peasycap->fps
+ * peasycap->usec
+ * peasycap->tolerate
+ * peasycap->skip
+ */
+/*---------------------------------------------------------------------------*/
+int adjust_standard(struct easycap *peasycap, v4l2_std_id std_id)
+{
+ struct easycap_standard const *peasycap_standard;
+ u16 reg, set;
+ int ir, rc, need, k;
+ unsigned int itwas, isnow;
+ bool resubmit;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ peasycap_standard = &easycap_standard[0];
+ while (0xFFFF != peasycap_standard->mask) {
+ if (std_id == peasycap_standard->v4l2_standard.id)
+ break;
+ peasycap_standard++;
+ }
+ if (0xFFFF == peasycap_standard->mask) {
+ peasycap_standard = &easycap_standard[0];
+ while (0xFFFF != peasycap_standard->mask) {
+ if (std_id & peasycap_standard->v4l2_standard.id)
+ break;
+ peasycap_standard++;
+ }
+ }
+ if (0xFFFF == peasycap_standard->mask) {
+ SAM("ERROR: 0x%08X=std_id: standard not found\n",
+ (unsigned int)std_id);
+ return -EINVAL;
+ }
+ SAM("selected standard: %s\n",
+ &(peasycap_standard->v4l2_standard.name[0]));
+ if (peasycap->standard_offset == peasycap_standard - easycap_standard) {
+ SAM("requested standard already in effect\n");
+ return 0;
+ }
+ peasycap->standard_offset = peasycap_standard - easycap_standard;
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].standard_offset_ok) {
+ peasycap->inputset[k].standard_offset =
+ peasycap->standard_offset;
+ }
+ }
+ if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
+ peasycap->inputset[peasycap->input].standard_offset =
+ peasycap->standard_offset;
+ peasycap->inputset[peasycap->input].standard_offset_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+
+ peasycap->fps = peasycap_standard->v4l2_standard.frameperiod.denominator /
+ peasycap_standard->v4l2_standard.frameperiod.numerator;
+ switch (peasycap->fps) {
+ case 6:
+ case 30: {
+ peasycap->ntsc = true;
+ break;
+ }
+ case 5:
+ case 25: {
+ peasycap->ntsc = false;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: %i=frames-per-second\n", peasycap->fps);
+ return -ENOENT;
+ }
+ }
+ JOM(8, "%i frames-per-second\n", peasycap->fps);
+ if (0x8000 & peasycap_standard->mask) {
+ peasycap->skip = 5;
+ peasycap->usec = 1000000 / (2 * (5 * peasycap->fps));
+ peasycap->tolerate = 1000 * (25 / (5 * peasycap->fps));
+ } else {
+ peasycap->skip = 0;
+ peasycap->usec = 1000000 / (2 * peasycap->fps);
+ peasycap->tolerate = 1000 * (25 / peasycap->fps);
+ }
+ if (peasycap->video_isoc_streaming) {
+ resubmit = true;
+ kill_video_urbs(peasycap);
+ } else
+ resubmit = false;
+/*--------------------------------------------------------------------------*/
+/*
+ * SAA7113H DATASHEET PAGE 44, TABLE 42
+ */
+/*--------------------------------------------------------------------------*/
+ need = 0;
+ itwas = 0;
+ reg = 0x00;
+ set = 0x00;
+ switch (peasycap_standard->mask & 0x000F) {
+ case NTSC_M_JP: {
+ reg = 0x0A;
+ set = 0x95;
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: cannot read SAA register 0x%02X\n", reg);
+ else
+ itwas = (unsigned int)ir;
+ rc = write_saa(peasycap->pusb_device, reg, set);
+ if (rc)
+ SAM("ERROR: failed to set SAA register "
+ "0x%02X to 0x%02X for JP standard\n", reg, set);
+ else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed "
+ "to 0x%02X\n", reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+
+ reg = 0x0B;
+ set = 0x48;
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: cannot read SAA register 0x%02X\n", reg);
+ else
+ itwas = (unsigned int)ir;
+ rc = write_saa(peasycap->pusb_device, reg, set);
+ if (rc)
+ SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X "
+ "for JP standard\n", reg, set);
+ else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed "
+ "to 0x%02X\n", reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * NOTE: NO break HERE: RUN ON TO NEXT CASE
+ */
+/*--------------------------------------------------------------------------*/
+ }
+ case NTSC_M:
+ case PAL_BGHIN: {
+ reg = 0x0E;
+ set = 0x01;
+ need = 1;
+ break;
+ }
+ case NTSC_N_443:
+ case PAL_60: {
+ reg = 0x0E;
+ set = 0x11;
+ need = 1;
+ break;
+ }
+ case NTSC_443:
+ case PAL_Nc: {
+ reg = 0x0E;
+ set = 0x21;
+ need = 1;
+ break;
+ }
+ case NTSC_N:
+ case PAL_M: {
+ reg = 0x0E;
+ set = 0x31;
+ need = 1;
+ break;
+ }
+ case SECAM: {
+ reg = 0x0E;
+ set = 0x51;
+ need = 1;
+ break;
+ }
+ default:
+ break;
+ }
+/*--------------------------------------------------------------------------*/
+ if (need) {
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: failed to read SAA register 0x%02X\n", reg);
+ else
+ itwas = (unsigned int)ir;
+ rc = write_saa(peasycap->pusb_device, reg, set);
+ if (0 != write_saa(peasycap->pusb_device, reg, set)) {
+ SAM("ERROR: failed to set SAA register "
+ "0x%02X to 0x%02X for table 42\n", reg, set);
+ } else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed "
+ "to 0x%02X\n", reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * SAA7113H DATASHEET PAGE 41
+ */
+/*--------------------------------------------------------------------------*/
+ reg = 0x08;
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: failed to read SAA register 0x%02X "
+ "so cannot reset\n", reg);
+ else {
+ itwas = (unsigned int)ir;
+ if (peasycap_standard->mask & 0x0001)
+ set = itwas | 0x40 ;
+ else
+ set = itwas & ~0x40 ;
+ rc = write_saa(peasycap->pusb_device, reg, set);
+ if (rc)
+ SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
+ reg, set);
+ else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed to 0x%02X\n",
+ reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * SAA7113H DATASHEET PAGE 51, TABLE 57
+ */
+/*---------------------------------------------------------------------------*/
+ reg = 0x40;
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: failed to read SAA register 0x%02X "
+ "so cannot reset\n", reg);
+ else {
+ itwas = (unsigned int)ir;
+ if (peasycap_standard->mask & 0x0001)
+ set = itwas | 0x80 ;
+ else
+ set = itwas & ~0x80 ;
+ rc = write_saa(peasycap->pusb_device, reg, set);
+ if (rc)
+ SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
+ reg, set);
+ else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed to 0x%02X\n",
+ reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * SAA7113H DATASHEET PAGE 53, TABLE 66
+ */
+/*--------------------------------------------------------------------------*/
+ reg = 0x5A;
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: failed to read SAA register 0x%02X but continuing\n", reg);
+ itwas = (unsigned int)ir;
+ if (peasycap_standard->mask & 0x0001)
+ set = 0x0A ;
+ else
+ set = 0x07 ;
+ if (0 != write_saa(peasycap->pusb_device, reg, set))
+ SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
+ reg, set);
+ else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed "
+ "to 0x%02X\n", reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+ if (resubmit)
+ submit_video_urbs(peasycap);
+ return 0;
+}
+/*****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * THE ALGORITHM FOR RESPONDING TO THE VIDIO_S_FMT IOCTL REQUIRES
+ * A VALID VALUE OF peasycap->standard_offset, OTHERWISE -EBUSY IS RETURNED.
+ *
+ * PROVIDED THE ARGUMENT try IS false AND THERE IS NO PREMATURE ERROR RETURN
+ * THIS ROUTINE UPDATES THE FOLLOWING:
+ * peasycap->format_offset
+ * peasycap->inputset[peasycap->input].format_offset
+ * peasycap->pixelformat
+ * peasycap->height
+ * peasycap->width
+ * peasycap->bytesperpixel
+ * peasycap->byteswaporder
+ * peasycap->decimatepixel
+ * peasycap->frame_buffer_used
+ * peasycap->videofieldamount
+ * peasycap->offerfields
+ *
+ * IF SUCCESSFUL THE FUNCTION RETURNS THE OFFSET IN easycap_format[]
+ * IDENTIFYING THE FORMAT WHICH IS TO RETURNED TO THE USER.
+ * ERRORS RETURN A NEGATIVE NUMBER.
+ */
+/*--------------------------------------------------------------------------*/
+int adjust_format(struct easycap *peasycap,
+ u32 width, u32 height, u32 pixelformat, int field, bool try)
+{
+ struct easycap_format *peasycap_format, *peasycap_best_format;
+ u16 mask;
+ struct usb_device *p;
+ int miss, multiplier, best, k;
+ char bf[5], fo[32], *pc;
+ u32 uc;
+ bool resubmit;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (0 > peasycap->standard_offset) {
+ JOM(8, "%i=peasycap->standard_offset\n", peasycap->standard_offset);
+ return -EBUSY;
+ }
+ p = peasycap->pusb_device;
+ if (!p) {
+ SAM("ERROR: peaycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ pc = &bf[0];
+ uc = pixelformat;
+ memcpy((void *)pc, (void *)(&uc), 4);
+ bf[4] = 0;
+ mask = 0xFF & easycap_standard[peasycap->standard_offset].mask;
+ SAM("sought: %ix%i,%s(0x%08X),%i=field,0x%02X=std mask\n",
+ width, height, pc, pixelformat, field, mask);
+ switch (field) {
+ case V4L2_FIELD_ANY: {
+ strcpy(&fo[0], "V4L2_FIELD_ANY ");
+ break;
+ }
+ case V4L2_FIELD_NONE: {
+ strcpy(&fo[0], "V4L2_FIELD_NONE");
+ break;
+ }
+ case V4L2_FIELD_TOP: {
+ strcpy(&fo[0], "V4L2_FIELD_TOP");
+ break;
+ }
+ case V4L2_FIELD_BOTTOM: {
+ strcpy(&fo[0], "V4L2_FIELD_BOTTOM");
+ break;
+ }
+ case V4L2_FIELD_INTERLACED: {
+ strcpy(&fo[0], "V4L2_FIELD_INTERLACED");
+ break;
+ }
+ case V4L2_FIELD_SEQ_TB: {
+ strcpy(&fo[0], "V4L2_FIELD_SEQ_TB");
+ break;
+ }
+ case V4L2_FIELD_SEQ_BT: {
+ strcpy(&fo[0], "V4L2_FIELD_SEQ_BT");
+ break;
+ }
+ case V4L2_FIELD_ALTERNATE: {
+ strcpy(&fo[0], "V4L2_FIELD_ALTERNATE");
+ break;
+ }
+ case V4L2_FIELD_INTERLACED_TB: {
+ strcpy(&fo[0], "V4L2_FIELD_INTERLACED_TB");
+ break;
+ }
+ case V4L2_FIELD_INTERLACED_BT: {
+ strcpy(&fo[0], "V4L2_FIELD_INTERLACED_BT");
+ break;
+ }
+ default: {
+ strcpy(&fo[0], "V4L2_FIELD_... UNKNOWN ");
+ break;
+ }
+ }
+ SAM("sought: %s\n", &fo[0]);
+ if (V4L2_FIELD_ANY == field) {
+ field = V4L2_FIELD_NONE;
+ SAM("prefer: V4L2_FIELD_NONE=field, was V4L2_FIELD_ANY\n");
+ }
+ peasycap_best_format = NULL;
+ peasycap_format = &easycap_format[0];
+ while (0 != peasycap_format->v4l2_format.fmt.pix.width) {
+ JOM(16, ".> %i %i 0x%08X %ix%i\n",
+ peasycap_format->mask & 0x01,
+ peasycap_format->v4l2_format.fmt.pix.field,
+ peasycap_format->v4l2_format.fmt.pix.pixelformat,
+ peasycap_format->v4l2_format.fmt.pix.width,
+ peasycap_format->v4l2_format.fmt.pix.height);
+
+ if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) &&
+ (peasycap_format->v4l2_format.fmt.pix.field == field) &&
+ (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat) &&
+ (peasycap_format->v4l2_format.fmt.pix.width == width) &&
+ (peasycap_format->v4l2_format.fmt.pix.height == height)) {
+
+ peasycap_best_format = peasycap_format;
+ break;
+ }
+ peasycap_format++;
+ }
+ if (0 == peasycap_format->v4l2_format.fmt.pix.width) {
+ SAM("cannot do: %ix%i with standard mask 0x%02X\n",
+ width, height, mask);
+ peasycap_format = &easycap_format[0];
+ best = -1;
+ while (0 != peasycap_format->v4l2_format.fmt.pix.width) {
+ if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) &&
+ (peasycap_format->v4l2_format.fmt.pix.field == field) &&
+ (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat)) {
+
+ miss = abs(peasycap_format->v4l2_format.fmt.pix.width - width);
+ if ((best > miss) || (best < 0)) {
+ best = miss;
+ peasycap_best_format = peasycap_format;
+ if (!miss)
+ break;
+ }
+ }
+ peasycap_format++;
+ }
+ if (-1 == best) {
+ SAM("cannot do %ix... with standard mask 0x%02X\n",
+ width, mask);
+ SAM("cannot do ...x%i with standard mask 0x%02X\n",
+ height, mask);
+ SAM(" %ix%i unmatched\n", width, height);
+ return peasycap->format_offset;
+ }
+ }
+ if (!peasycap_best_format) {
+ SAM("MISTAKE: peasycap_best_format is NULL");
+ return -EINVAL;
+ }
+ peasycap_format = peasycap_best_format;
+
+/*...........................................................................*/
+ if (try)
+ return peasycap_best_format - easycap_format;
+/*...........................................................................*/
+
+ if (false != try) {
+ SAM("MISTAKE: true==try where is should be false\n");
+ return -EINVAL;
+ }
+ SAM("actioning: %ix%i %s\n",
+ peasycap_format->v4l2_format.fmt.pix.width,
+ peasycap_format->v4l2_format.fmt.pix.height,
+ &peasycap_format->name[0]);
+ peasycap->height = peasycap_format->v4l2_format.fmt.pix.height;
+ peasycap->width = peasycap_format->v4l2_format.fmt.pix.width;
+ peasycap->pixelformat = peasycap_format->v4l2_format.fmt.pix.pixelformat;
+ peasycap->format_offset = peasycap_format - easycap_format;
+
+
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].format_offset_ok) {
+ peasycap->inputset[k].format_offset =
+ peasycap->format_offset;
+ }
+ }
+ if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
+ peasycap->inputset[peasycap->input].format_offset =
+ peasycap->format_offset;
+ peasycap->inputset[peasycap->input].format_offset_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+
+
+
+ peasycap->bytesperpixel = (0x00E0 & peasycap_format->mask) >> 5 ;
+ if (0x0100 & peasycap_format->mask)
+ peasycap->byteswaporder = true;
+ else
+ peasycap->byteswaporder = false;
+ if (0x0200 & peasycap_format->mask)
+ peasycap->skip = 5;
+ else
+ peasycap->skip = 0;
+ if (0x0800 & peasycap_format->mask)
+ peasycap->decimatepixel = true;
+ else
+ peasycap->decimatepixel = false;
+ if (0x1000 & peasycap_format->mask)
+ peasycap->offerfields = true;
+ else
+ peasycap->offerfields = false;
+ if (peasycap->decimatepixel)
+ multiplier = 2;
+ else
+ multiplier = 1;
+ peasycap->videofieldamount =
+ multiplier * peasycap->width * multiplier * peasycap->height;
+ peasycap->frame_buffer_used =
+ peasycap->bytesperpixel * peasycap->width * peasycap->height;
+ if (peasycap->video_isoc_streaming) {
+ resubmit = true;
+ kill_video_urbs(peasycap);
+ } else
+ resubmit = false;
+/*---------------------------------------------------------------------------*/
+/*
+ * PAL
+ */
+/*---------------------------------------------------------------------------*/
+ if (0 == (0x01 & peasycap_format->mask)) {
+ if (((720 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (576 == peasycap_format->v4l2_format.fmt.pix.height)) ||
+ ((360 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (288 == peasycap_format->v4l2_format.fmt.pix.height))) {
+ if (set_resolution(p, 0x0000, 0x0001, 0x05A0, 0x0121)) {
+ SAM("ERROR: set_resolution() failed\n");
+ return -EINVAL;
+ }
+ } else if ((704 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (576 == peasycap_format->v4l2_format.fmt.pix.height)) {
+ if (set_resolution(p, 0x0004, 0x0001, 0x0584, 0x0121)) {
+ SAM("ERROR: set_resolution() failed\n");
+ return -EINVAL;
+ }
+ } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
+ ((320 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
+ if (set_resolution(p, 0x0014, 0x0020, 0x0514, 0x0110)) {
+ SAM("ERROR: set_resolution() failed\n");
+ return -EINVAL;
+ }
+ } else {
+ SAM("MISTAKE: bad format, cannot set resolution\n");
+ return -EINVAL;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * NTSC
+ */
+/*---------------------------------------------------------------------------*/
+ } else {
+ if (((720 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
+ ((360 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
+ if (set_resolution(p, 0x0000, 0x0003, 0x05A0, 0x00F3)) {
+ SAM("ERROR: set_resolution() failed\n");
+ return -EINVAL;
+ }
+ } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
+ ((320 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
+ if (set_resolution(p, 0x0014, 0x0003, 0x0514, 0x00F3)) {
+ SAM("ERROR: set_resolution() failed\n");
+ return -EINVAL;
+ }
+ } else {
+ SAM("MISTAKE: bad format, cannot set resolution\n");
+ return -EINVAL;
+ }
+ }
+/*---------------------------------------------------------------------------*/
+ if (resubmit)
+ submit_video_urbs(peasycap);
+
+ return peasycap_best_format - easycap_format;
+}
+/*****************************************************************************/
+int adjust_brightness(struct easycap *peasycap, int value)
+{
+ unsigned int mood;
+ int i1, k;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_BRIGHTNESS == easycap_control[i1].id) {
+ if ((easycap_control[i1].minimum > value) ||
+ (easycap_control[i1].maximum < value))
+ value = easycap_control[i1].default_value;
+
+ if ((easycap_control[i1].minimum <= peasycap->brightness) &&
+ (easycap_control[i1].maximum >= peasycap->brightness)) {
+ if (peasycap->brightness == value) {
+ SAM("unchanged brightness at 0x%02X\n",
+ value);
+ return 0;
+ }
+ }
+ peasycap->brightness = value;
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].brightness_ok)
+ peasycap->inputset[k].brightness =
+ peasycap->brightness;
+ }
+ if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
+ peasycap->inputset[peasycap->input].brightness =
+ peasycap->brightness;
+ peasycap->inputset[peasycap->input].brightness_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+ mood = 0x00FF & (unsigned int)peasycap->brightness;
+ if (!write_saa(peasycap->pusb_device, 0x0A, mood)) {
+ SAM("adjusting brightness to 0x%02X\n", mood);
+ return 0;
+ } else {
+ SAM("WARNING: failed to adjust brightness "
+ "to 0x%02X\n", mood);
+ return -ENOENT;
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust brightness: control not found\n");
+ return -ENOENT;
+}
+/*****************************************************************************/
+int adjust_contrast(struct easycap *peasycap, int value)
+{
+ unsigned int mood;
+ int i1, k;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_CONTRAST == easycap_control[i1].id) {
+ if ((easycap_control[i1].minimum > value) ||
+ (easycap_control[i1].maximum < value))
+ value = easycap_control[i1].default_value;
+
+
+ if ((easycap_control[i1].minimum <= peasycap->contrast) &&
+ (easycap_control[i1].maximum >= peasycap->contrast)) {
+ if (peasycap->contrast == value) {
+ SAM("unchanged contrast at 0x%02X\n", value);
+ return 0;
+ }
+ }
+ peasycap->contrast = value;
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].contrast_ok)
+ peasycap->inputset[k].contrast = peasycap->contrast;
+ }
+
+ if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
+ peasycap->inputset[peasycap->input].contrast =
+ peasycap->contrast;
+ peasycap->inputset[peasycap->input].contrast_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+
+ mood = 0x00FF & (unsigned int) (peasycap->contrast - 128);
+ if (!write_saa(peasycap->pusb_device, 0x0B, mood)) {
+ SAM("adjusting contrast to 0x%02X\n", mood);
+ return 0;
+ } else {
+ SAM("WARNING: failed to adjust contrast to "
+ "0x%02X\n", mood);
+ return -ENOENT;
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust contrast: control not found\n");
+ return -ENOENT;
+}
+/*****************************************************************************/
+int adjust_saturation(struct easycap *peasycap, int value)
+{
+ unsigned int mood;
+ int i1, k;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_SATURATION == easycap_control[i1].id) {
+ if ((easycap_control[i1].minimum > value) ||
+ (easycap_control[i1].maximum < value))
+ value = easycap_control[i1].default_value;
+
+
+ if ((easycap_control[i1].minimum <= peasycap->saturation) &&
+ (easycap_control[i1].maximum >= peasycap->saturation)) {
+ if (peasycap->saturation == value) {
+ SAM("unchanged saturation at 0x%02X\n",
+ value);
+ return 0;
+ }
+ }
+ peasycap->saturation = value;
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].saturation_ok)
+ peasycap->inputset[k].saturation =
+ peasycap->saturation;
+ }
+ if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
+ peasycap->inputset[peasycap->input].saturation =
+ peasycap->saturation;
+ peasycap->inputset[peasycap->input].saturation_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+ mood = 0x00FF & (unsigned int) (peasycap->saturation - 128);
+ if (!write_saa(peasycap->pusb_device, 0x0C, mood)) {
+ SAM("adjusting saturation to 0x%02X\n", mood);
+ return 0;
+ } else {
+ SAM("WARNING: failed to adjust saturation to "
+ "0x%02X\n", mood);
+ return -ENOENT;
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust saturation: control not found\n");
+ return -ENOENT;
+}
+/*****************************************************************************/
+int adjust_hue(struct easycap *peasycap, int value)
+{
+ unsigned int mood;
+ int i1, i2, k;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_HUE == easycap_control[i1].id) {
+ if ((easycap_control[i1].minimum > value) ||
+ (easycap_control[i1].maximum < value))
+ value = easycap_control[i1].default_value;
+
+ if ((easycap_control[i1].minimum <= peasycap->hue) &&
+ (easycap_control[i1].maximum >= peasycap->hue)) {
+ if (peasycap->hue == value) {
+ SAM("unchanged hue at 0x%02X\n", value);
+ return 0;
+ }
+ }
+ peasycap->hue = value;
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].hue_ok)
+ peasycap->inputset[k].hue = peasycap->hue;
+ }
+ if (0 <= peasycap->input && INPUT_MANY > peasycap->input) {
+ peasycap->inputset[peasycap->input].hue = peasycap->hue;
+ peasycap->inputset[peasycap->input].hue_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+ i2 = peasycap->hue - 128;
+ mood = 0x00FF & ((int) i2);
+ if (!write_saa(peasycap->pusb_device, 0x0D, mood)) {
+ SAM("adjusting hue to 0x%02X\n", mood);
+ return 0;
+ } else {
+ SAM("WARNING: failed to adjust hue to 0x%02X\n", mood);
+ return -ENOENT;
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust hue: control not found\n");
+ return -ENOENT;
+}
+/*****************************************************************************/
+int adjust_volume(struct easycap *peasycap, int value)
+{
+ s8 mood;
+ int i1;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_AUDIO_VOLUME == easycap_control[i1].id) {
+ if ((easycap_control[i1].minimum > value) ||
+ (easycap_control[i1].maximum < value))
+ value = easycap_control[i1].default_value;
+
+ if ((easycap_control[i1].minimum <= peasycap->volume) &&
+ (easycap_control[i1].maximum >= peasycap->volume)) {
+ if (peasycap->volume == value) {
+ SAM("unchanged volume at 0x%02X\n", value);
+ return 0;
+ }
+ }
+ peasycap->volume = value;
+ mood = (16 > peasycap->volume) ? 16 :
+ ((31 < peasycap->volume) ? 31 :
+ (s8) peasycap->volume);
+ if (!audio_gainset(peasycap->pusb_device, mood)) {
+ SAM("adjusting volume to 0x%02X\n", mood);
+ return 0;
+ } else {
+ SAM("WARNING: failed to adjust volume to "
+ "0x%2X\n", mood);
+ return -ENOENT;
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust volume: control not found\n");
+ return -ENOENT;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * AN ALTERNATIVE METHOD OF MUTING MIGHT SEEM TO BE:
+ * usb_set_interface(peasycap->pusb_device,
+ * peasycap->audio_interface,
+ * peasycap->audio_altsetting_off);
+ * HOWEVER, AFTER THIS COMMAND IS ISSUED ALL SUBSEQUENT URBS RECEIVE STATUS
+ * -ESHUTDOWN. THE HANDLER ROUTINE easyxxx_complete() DECLINES TO RESUBMIT
+ * THE URB AND THE PIPELINE COLLAPSES IRRETRIEVABLY. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+static int adjust_mute(struct easycap *peasycap, int value)
+{
+ int i1;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_AUDIO_MUTE == easycap_control[i1].id) {
+ peasycap->mute = value;
+ switch (peasycap->mute) {
+ case 1: {
+ peasycap->audio_idle = 1;
+ SAM("adjusting mute: %i=peasycap->audio_idle\n",
+ peasycap->audio_idle);
+ return 0;
+ }
+ default: {
+ peasycap->audio_idle = 0;
+ SAM("adjusting mute: %i=peasycap->audio_idle\n",
+ peasycap->audio_idle);
+ return 0;
+ }
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust mute: control not found\n");
+ return -ENOENT;
+}
+/*---------------------------------------------------------------------------*/
+long easycap_unlocked_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct easycap *peasycap;
+ struct usb_device *p;
+ int kd;
+
+ if (!file) {
+ SAY("ERROR: file is NULL\n");
+ return -ERESTARTSYS;
+ }
+ peasycap = file->private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -1;
+ }
+ p = peasycap->pusb_device;
+ if (!p) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ kd = isdongle(peasycap);
+ if (0 <= kd && DONGLE_MANY > kd) {
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
+ SAY("ERROR: cannot lock "
+ "easycapdc60_dongle[%i].mutex_video\n", kd);
+ return -ERESTARTSYS;
+ }
+ JOM(4, "locked easycapdc60_dongle[%i].mutex_video\n", kd);
+/*---------------------------------------------------------------------------*/
+/*
+ * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
+ * IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
+ * IF NECESSARY, BAIL OUT.
+ */
+/*---------------------------------------------------------------------------*/
+ if (kd != isdongle(peasycap))
+ return -ERESTARTSYS;
+ if (!file) {
+ SAY("ERROR: file is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ peasycap = file->private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ } else {
+/*---------------------------------------------------------------------------*/
+/*
+ * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
+ * ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT.
+ */
+/*---------------------------------------------------------------------------*/
+ return -ERESTARTSYS;
+ }
+/*---------------------------------------------------------------------------*/
+ switch (cmd) {
+ case VIDIOC_QUERYCAP: {
+ struct v4l2_capability v4l2_capability;
+ char version[16], *p1, *p2;
+ int i, rc, k[3];
+ long lng;
+
+ JOM(8, "VIDIOC_QUERYCAP\n");
+
+ if (16 <= strlen(EASYCAP_DRIVER_VERSION)) {
+ SAM("ERROR: bad driver version string\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ strcpy(&version[0], EASYCAP_DRIVER_VERSION);
+ for (i = 0; i < 3; i++)
+ k[i] = 0;
+ p2 = &version[0];
+ i = 0;
+ while (*p2) {
+ p1 = p2;
+ while (*p2 && ('.' != *p2))
+ p2++;
+ if (*p2)
+ *p2++ = 0;
+ if (3 > i) {
+ rc = (int) strict_strtol(p1, 10, &lng);
+ if (rc) {
+ SAM("ERROR: %i=strict_strtol(%s,.,,)\n",
+ rc, p1);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ k[i] = (int)lng;
+ }
+ i++;
+ }
+
+ memset(&v4l2_capability, 0, sizeof(struct v4l2_capability));
+ strlcpy(&v4l2_capability.driver[0],
+ "easycap", sizeof(v4l2_capability.driver));
+
+ v4l2_capability.capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE;
+
+ v4l2_capability.version = KERNEL_VERSION(k[0], k[1], k[2]);
+ JOM(8, "v4l2_capability.version=(%i,%i,%i)\n", k[0], k[1], k[2]);
+
+ strlcpy(&v4l2_capability.card[0],
+ "EasyCAP DC60", sizeof(v4l2_capability.card));
+
+ if (usb_make_path(peasycap->pusb_device,
+ &v4l2_capability.bus_info[0],
+ sizeof(v4l2_capability.bus_info)) < 0) {
+
+ strlcpy(&v4l2_capability.bus_info[0], "EasyCAP bus_info",
+ sizeof(v4l2_capability.bus_info));
+ JOM(8, "%s=v4l2_capability.bus_info\n",
+ &v4l2_capability.bus_info[0]);
+ }
+ if (copy_to_user((void __user *)arg, &v4l2_capability,
+ sizeof(struct v4l2_capability))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUMINPUT: {
+ struct v4l2_input v4l2_input;
+ u32 index;
+
+ JOM(8, "VIDIOC_ENUMINPUT\n");
+
+ if (copy_from_user(&v4l2_input, (void __user *)arg,
+ sizeof(struct v4l2_input))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ index = v4l2_input.index;
+ memset(&v4l2_input, 0, sizeof(struct v4l2_input));
+
+ switch (index) {
+ case 0: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "CVBS0");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL |
+ V4L2_STD_SECAM |
+ V4L2_STD_NTSC ;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ case 1: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "CVBS1");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
+ V4L2_STD_NTSC;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ case 2: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "CVBS2");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
+ V4L2_STD_NTSC ;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ case 3: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "CVBS3");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
+ V4L2_STD_NTSC ;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ case 4: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "CVBS4");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
+ V4L2_STD_NTSC ;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ case 5: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "S-VIDEO");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
+ V4L2_STD_NTSC ;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ default: {
+ JOM(8, "%i=index: exhausts inputs\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+
+ if (copy_to_user((void __user *)arg, &v4l2_input,
+ sizeof(struct v4l2_input))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_INPUT: {
+ u32 index;
+
+ JOM(8, "VIDIOC_G_INPUT\n");
+ index = (u32)peasycap->input;
+ JOM(8, "user is told: %i\n", index);
+ if (copy_to_user((void __user *)arg, &index, sizeof(u32))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_INPUT:
+ {
+ u32 index;
+ int rc;
+
+ JOM(8, "VIDIOC_S_INPUT\n");
+
+ if (0 != copy_from_user(&index, (void __user *)arg, sizeof(u32))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ JOM(8, "user requests input %i\n", index);
+
+ if ((int)index == peasycap->input) {
+ SAM("requested input already in effect\n");
+ break;
+ }
+
+ if ((0 > index) || (INPUT_MANY <= index)) {
+ JOM(8, "ERROR: bad requested input: %i\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+
+ rc = newinput(peasycap, (int)index);
+ if (0 == rc) {
+ JOM(8, "newinput(.,%i) OK\n", (int)index);
+ } else {
+ SAM("ERROR: newinput(.,%i) returned %i\n", (int)index, rc);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUMAUDIO: {
+ JOM(8, "VIDIOC_ENUMAUDIO\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUMAUDOUT: {
+ struct v4l2_audioout v4l2_audioout;
+
+ JOM(8, "VIDIOC_ENUMAUDOUT\n");
+
+ if (copy_from_user(&v4l2_audioout, (void __user *)arg,
+ sizeof(struct v4l2_audioout))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (0 != v4l2_audioout.index) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ memset(&v4l2_audioout, 0, sizeof(struct v4l2_audioout));
+ v4l2_audioout.index = 0;
+ strcpy(&v4l2_audioout.name[0], "Soundtrack");
+
+ if (copy_to_user((void __user *)arg, &v4l2_audioout,
+ sizeof(struct v4l2_audioout))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_QUERYCTRL: {
+ int i1;
+ struct v4l2_queryctrl v4l2_queryctrl;
+
+ JOM(8, "VIDIOC_QUERYCTRL\n");
+
+ if (0 != copy_from_user(&v4l2_queryctrl, (void __user *)arg,
+ sizeof(struct v4l2_queryctrl))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (easycap_control[i1].id == v4l2_queryctrl.id) {
+ JOM(8, "VIDIOC_QUERYCTRL %s=easycap_control[%i]"
+ ".name\n", &easycap_control[i1].name[0], i1);
+ memcpy(&v4l2_queryctrl, &easycap_control[i1],
+ sizeof(struct v4l2_queryctrl));
+ break;
+ }
+ i1++;
+ }
+ if (0xFFFFFFFF == easycap_control[i1].id) {
+ JOM(8, "%i=index: exhausts controls\n", i1);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ if (copy_to_user((void __user *)arg, &v4l2_queryctrl,
+ sizeof(struct v4l2_queryctrl))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_QUERYMENU: {
+ JOM(8, "VIDIOC_QUERYMENU unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_CTRL: {
+ struct v4l2_control *pv4l2_control;
+
+ JOM(8, "VIDIOC_G_CTRL\n");
+ pv4l2_control = memdup_user((void __user *)arg,
+ sizeof(struct v4l2_control));
+ if (IS_ERR(pv4l2_control)) {
+ SAM("ERROR: copy from user failed\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return PTR_ERR(pv4l2_control);
+ }
+
+ switch (pv4l2_control->id) {
+ case V4L2_CID_BRIGHTNESS: {
+ pv4l2_control->value = peasycap->brightness;
+ JOM(8, "user enquires brightness: %i\n", pv4l2_control->value);
+ break;
+ }
+ case V4L2_CID_CONTRAST: {
+ pv4l2_control->value = peasycap->contrast;
+ JOM(8, "user enquires contrast: %i\n", pv4l2_control->value);
+ break;
+ }
+ case V4L2_CID_SATURATION: {
+ pv4l2_control->value = peasycap->saturation;
+ JOM(8, "user enquires saturation: %i\n", pv4l2_control->value);
+ break;
+ }
+ case V4L2_CID_HUE: {
+ pv4l2_control->value = peasycap->hue;
+ JOM(8, "user enquires hue: %i\n", pv4l2_control->value);
+ break;
+ }
+ case V4L2_CID_AUDIO_VOLUME: {
+ pv4l2_control->value = peasycap->volume;
+ JOM(8, "user enquires volume: %i\n", pv4l2_control->value);
+ break;
+ }
+ case V4L2_CID_AUDIO_MUTE: {
+ if (1 == peasycap->mute)
+ pv4l2_control->value = true;
+ else
+ pv4l2_control->value = false;
+ JOM(8, "user enquires mute: %i\n", pv4l2_control->value);
+ break;
+ }
+ default: {
+ SAM("ERROR: unknown V4L2 control: 0x%08X=id\n",
+ pv4l2_control->id);
+ kfree(pv4l2_control);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ if (copy_to_user((void __user *)arg, pv4l2_control,
+ sizeof(struct v4l2_control))) {
+ kfree(pv4l2_control);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ kfree(pv4l2_control);
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_CTRL: {
+ struct v4l2_control v4l2_control;
+
+ JOM(8, "VIDIOC_S_CTRL\n");
+
+ if (0 != copy_from_user(&v4l2_control, (void __user *)arg,
+ sizeof(struct v4l2_control))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ switch (v4l2_control.id) {
+ case V4L2_CID_BRIGHTNESS: {
+ JOM(8, "user requests brightness %i\n", v4l2_control.value);
+ if (0 != adjust_brightness(peasycap, v4l2_control.value))
+ ;
+ break;
+ }
+ case V4L2_CID_CONTRAST: {
+ JOM(8, "user requests contrast %i\n", v4l2_control.value);
+ if (0 != adjust_contrast(peasycap, v4l2_control.value))
+ ;
+ break;
+ }
+ case V4L2_CID_SATURATION: {
+ JOM(8, "user requests saturation %i\n", v4l2_control.value);
+ if (0 != adjust_saturation(peasycap, v4l2_control.value))
+ ;
+ break;
+ }
+ case V4L2_CID_HUE: {
+ JOM(8, "user requests hue %i\n", v4l2_control.value);
+ if (0 != adjust_hue(peasycap, v4l2_control.value))
+ ;
+ break;
+ }
+ case V4L2_CID_AUDIO_VOLUME: {
+ JOM(8, "user requests volume %i\n", v4l2_control.value);
+ if (0 != adjust_volume(peasycap, v4l2_control.value))
+ ;
+ break;
+ }
+ case V4L2_CID_AUDIO_MUTE: {
+ int mute;
+
+ JOM(8, "user requests mute %i\n", v4l2_control.value);
+ if (v4l2_control.value)
+ mute = 1;
+ else
+ mute = 0;
+
+ if (0 != adjust_mute(peasycap, mute))
+ SAM("WARNING: failed to adjust mute to %i\n", mute);
+ break;
+ }
+ default: {
+ SAM("ERROR: unknown V4L2 control: 0x%08X=id\n",
+ v4l2_control.id);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_EXT_CTRLS: {
+ JOM(8, "VIDIOC_S_EXT_CTRLS unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUM_FMT: {
+ u32 index;
+ struct v4l2_fmtdesc v4l2_fmtdesc;
+
+ JOM(8, "VIDIOC_ENUM_FMT\n");
+
+ if (0 != copy_from_user(&v4l2_fmtdesc, (void __user *)arg,
+ sizeof(struct v4l2_fmtdesc))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ index = v4l2_fmtdesc.index;
+ memset(&v4l2_fmtdesc, 0, sizeof(struct v4l2_fmtdesc));
+
+ v4l2_fmtdesc.index = index;
+ v4l2_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ switch (index) {
+ case 0: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "uyvy");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_UYVY;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ case 1: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "yuy2");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ case 2: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "rgb24");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB24;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ case 3: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "rgb32");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB32;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ case 4: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "bgr24");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR24;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ case 5: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "bgr32");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR32;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ default: {
+ JOM(8, "%i=index: exhausts formats\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ if (copy_to_user((void __user *)arg, &v4l2_fmtdesc,
+ sizeof(struct v4l2_fmtdesc))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ * THE RESPONSE TO VIDIOC_ENUM_FRAMESIZES MUST BE CONDITIONED ON THE
+ * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE.
+ */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUM_FRAMESIZES: {
+ u32 index;
+ struct v4l2_frmsizeenum v4l2_frmsizeenum;
+
+ JOM(8, "VIDIOC_ENUM_FRAMESIZES\n");
+
+ if (0 != copy_from_user(&v4l2_frmsizeenum, (void __user *)arg,
+ sizeof(struct v4l2_frmsizeenum))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ index = v4l2_frmsizeenum.index;
+
+ v4l2_frmsizeenum.type = (u32) V4L2_FRMSIZE_TYPE_DISCRETE;
+
+ if (peasycap->ntsc) {
+ switch (index) {
+ case 0: {
+ v4l2_frmsizeenum.discrete.width = 640;
+ v4l2_frmsizeenum.discrete.height = 480;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 1: {
+ v4l2_frmsizeenum.discrete.width = 320;
+ v4l2_frmsizeenum.discrete.height = 240;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 2: {
+ v4l2_frmsizeenum.discrete.width = 720;
+ v4l2_frmsizeenum.discrete.height = 480;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 3: {
+ v4l2_frmsizeenum.discrete.width = 360;
+ v4l2_frmsizeenum.discrete.height = 240;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ default: {
+ JOM(8, "%i=index: exhausts framesizes\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ } else {
+ switch (index) {
+ case 0: {
+ v4l2_frmsizeenum.discrete.width = 640;
+ v4l2_frmsizeenum.discrete.height = 480;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 1: {
+ v4l2_frmsizeenum.discrete.width = 320;
+ v4l2_frmsizeenum.discrete.height = 240;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 2: {
+ v4l2_frmsizeenum.discrete.width = 704;
+ v4l2_frmsizeenum.discrete.height = 576;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 3: {
+ v4l2_frmsizeenum.discrete.width = 720;
+ v4l2_frmsizeenum.discrete.height = 576;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 4: {
+ v4l2_frmsizeenum.discrete.width = 360;
+ v4l2_frmsizeenum.discrete.height = 288;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ default: {
+ JOM(8, "%i=index: exhausts framesizes\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ }
+ if (copy_to_user((void __user *)arg, &v4l2_frmsizeenum,
+ sizeof(struct v4l2_frmsizeenum))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ * THE RESPONSE TO VIDIOC_ENUM_FRAMEINTERVALS MUST BE CONDITIONED ON THE
+ * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE.
+ */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUM_FRAMEINTERVALS: {
+ u32 index;
+ int denominator;
+ struct v4l2_frmivalenum v4l2_frmivalenum;
+
+ JOM(8, "VIDIOC_ENUM_FRAMEINTERVALS\n");
+
+ if (peasycap->fps)
+ denominator = peasycap->fps;
+ else {
+ if (peasycap->ntsc)
+ denominator = 30;
+ else
+ denominator = 25;
+ }
+
+ if (0 != copy_from_user(&v4l2_frmivalenum, (void __user *)arg,
+ sizeof(struct v4l2_frmivalenum))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ index = v4l2_frmivalenum.index;
+
+ v4l2_frmivalenum.type = (u32) V4L2_FRMIVAL_TYPE_DISCRETE;
+
+ switch (index) {
+ case 0: {
+ v4l2_frmivalenum.discrete.numerator = 1;
+ v4l2_frmivalenum.discrete.denominator = denominator;
+ JOM(8, "%i=index: %i/%i\n", index,
+ (int)(v4l2_frmivalenum.discrete.numerator),
+ (int)(v4l2_frmivalenum.discrete.denominator));
+ break;
+ }
+ case 1: {
+ v4l2_frmivalenum.discrete.numerator = 1;
+ v4l2_frmivalenum.discrete.denominator = denominator/5;
+ JOM(8, "%i=index: %i/%i\n", index,
+ (int)(v4l2_frmivalenum.discrete.numerator),
+ (int)(v4l2_frmivalenum.discrete.denominator));
+ break;
+ }
+ default: {
+ JOM(8, "%i=index: exhausts frameintervals\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ if (copy_to_user((void __user *)arg, &v4l2_frmivalenum,
+ sizeof(struct v4l2_frmivalenum))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_FMT: {
+ struct v4l2_format *pv4l2_format;
+ struct v4l2_pix_format *pv4l2_pix_format;
+
+ JOM(8, "VIDIOC_G_FMT\n");
+ pv4l2_format = kzalloc(sizeof(struct v4l2_format), GFP_KERNEL);
+ if (!pv4l2_format) {
+ SAM("ERROR: out of memory\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ENOMEM;
+ }
+ pv4l2_pix_format = kzalloc(sizeof(struct v4l2_pix_format), GFP_KERNEL);
+ if (!pv4l2_pix_format) {
+ SAM("ERROR: out of memory\n");
+ kfree(pv4l2_format);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ENOMEM;
+ }
+ if (0 != copy_from_user(pv4l2_format, (void __user *)arg,
+ sizeof(struct v4l2_format))) {
+ kfree(pv4l2_format);
+ kfree(pv4l2_pix_format);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (pv4l2_format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ kfree(pv4l2_format);
+ kfree(pv4l2_pix_format);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+
+ memset(pv4l2_pix_format, 0, sizeof(struct v4l2_pix_format));
+ pv4l2_format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ memcpy(&pv4l2_format->fmt.pix,
+ &easycap_format[peasycap->format_offset]
+ .v4l2_format.fmt.pix, sizeof(struct v4l2_pix_format));
+ JOM(8, "user is told: %s\n",
+ &easycap_format[peasycap->format_offset].name[0]);
+
+ if (copy_to_user((void __user *)arg, pv4l2_format,
+ sizeof(struct v4l2_format))) {
+ kfree(pv4l2_format);
+ kfree(pv4l2_pix_format);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ kfree(pv4l2_format);
+ kfree(pv4l2_pix_format);
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT: {
+ struct v4l2_format v4l2_format;
+ struct v4l2_pix_format v4l2_pix_format;
+ bool try;
+ int best_format;
+
+ if (VIDIOC_TRY_FMT == cmd) {
+ JOM(8, "VIDIOC_TRY_FMT\n");
+ try = true;
+ } else {
+ JOM(8, "VIDIOC_S_FMT\n");
+ try = false;
+ }
+
+ if (0 != copy_from_user(&v4l2_format, (void __user *)arg,
+ sizeof(struct v4l2_format))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ best_format = adjust_format(peasycap,
+ v4l2_format.fmt.pix.width,
+ v4l2_format.fmt.pix.height,
+ v4l2_format.fmt.pix.pixelformat,
+ v4l2_format.fmt.pix.field,
+ try);
+ if (0 > best_format) {
+ if (-EBUSY == best_format) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EBUSY;
+ }
+ JOM(8, "WARNING: adjust_format() returned %i\n", best_format);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ENOENT;
+ }
+/*...........................................................................*/
+ memset(&v4l2_pix_format, 0, sizeof(struct v4l2_pix_format));
+ v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ memcpy(&(v4l2_format.fmt.pix),
+ &(easycap_format[best_format].v4l2_format.fmt.pix),
+ sizeof(v4l2_pix_format));
+ JOM(8, "user is told: %s\n", &easycap_format[best_format].name[0]);
+
+ if (copy_to_user((void __user *)arg, &v4l2_format,
+ sizeof(struct v4l2_format))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_CROPCAP: {
+ struct v4l2_cropcap v4l2_cropcap;
+
+ JOM(8, "VIDIOC_CROPCAP\n");
+
+ if (0 != copy_from_user(&v4l2_cropcap, (void __user *)arg,
+ sizeof(struct v4l2_cropcap))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ JOM(8, "v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+
+ memset(&v4l2_cropcap, 0, sizeof(struct v4l2_cropcap));
+ v4l2_cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_cropcap.bounds.left = 0;
+ v4l2_cropcap.bounds.top = 0;
+ v4l2_cropcap.bounds.width = peasycap->width;
+ v4l2_cropcap.bounds.height = peasycap->height;
+ v4l2_cropcap.defrect.left = 0;
+ v4l2_cropcap.defrect.top = 0;
+ v4l2_cropcap.defrect.width = peasycap->width;
+ v4l2_cropcap.defrect.height = peasycap->height;
+ v4l2_cropcap.pixelaspect.numerator = 1;
+ v4l2_cropcap.pixelaspect.denominator = 1;
+
+ JOM(8, "user is told: %ix%i\n", peasycap->width, peasycap->height);
+
+ if (copy_to_user((void __user *)arg, &v4l2_cropcap,
+ sizeof(struct v4l2_cropcap))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP: {
+ JOM(8, "VIDIOC_G_CROP|VIDIOC_S_CROP unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_QUERYSTD: {
+ JOM(8, "VIDIOC_QUERYSTD: "
+ "EasyCAP is incapable of detecting standard\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ break;
+ }
+ /*-------------------------------------------------------------------*/
+ /*
+ * THE MANIPULATIONS INVOLVING last0,last1,last2,last3
+ * CONSTITUTE A WORKAROUND * FOR WHAT APPEARS TO BE
+ * A BUG IN 64-BIT mplayer.
+ * NOT NEEDED, BUT HOPEFULLY HARMLESS, FOR 32-BIT mplayer.
+ */
+ /*------------------------------------------------------------------*/
+ case VIDIOC_ENUMSTD: {
+ int last0 = -1, last1 = -1, last2 = -1, last3 = -1;
+ struct v4l2_standard v4l2_standard;
+ u32 index;
+ struct easycap_standard const *peasycap_standard;
+
+ JOM(8, "VIDIOC_ENUMSTD\n");
+
+ if (0 != copy_from_user(&v4l2_standard, (void __user *)arg,
+ sizeof(struct v4l2_standard))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ index = v4l2_standard.index;
+
+ last3 = last2;
+ last2 = last1;
+ last1 = last0;
+ last0 = index;
+ if ((index == last3) && (index == last2) &&
+ (index == last1) && (index == last0)) {
+ index++;
+ last3 = last2;
+ last2 = last1;
+ last1 = last0;
+ last0 = index;
+ }
+
+ memset(&v4l2_standard, 0, sizeof(struct v4l2_standard));
+
+ peasycap_standard = &easycap_standard[0];
+ while (0xFFFF != peasycap_standard->mask) {
+ if ((int)(peasycap_standard - &easycap_standard[0]) == index)
+ break;
+ peasycap_standard++;
+ }
+ if (0xFFFF == peasycap_standard->mask) {
+ JOM(8, "%i=index: exhausts standards\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ JOM(8, "%i=index: %s\n", index,
+ &(peasycap_standard->v4l2_standard.name[0]));
+ memcpy(&v4l2_standard, &(peasycap_standard->v4l2_standard),
+ sizeof(struct v4l2_standard));
+
+ v4l2_standard.index = index;
+
+ if (copy_to_user((void __user *)arg, &v4l2_standard,
+ sizeof(struct v4l2_standard))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_STD: {
+ v4l2_std_id std_id;
+ struct easycap_standard const *peasycap_standard;
+
+ JOM(8, "VIDIOC_G_STD\n");
+
+ if (0 > peasycap->standard_offset) {
+ JOM(8, "%i=peasycap->standard_offset\n",
+ peasycap->standard_offset);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EBUSY;
+ }
+
+ if (0 != copy_from_user(&std_id, (void __user *)arg,
+ sizeof(v4l2_std_id))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ peasycap_standard = &easycap_standard[peasycap->standard_offset];
+ std_id = peasycap_standard->v4l2_standard.id;
+
+ JOM(8, "user is told: %s\n",
+ &peasycap_standard->v4l2_standard.name[0]);
+
+ if (copy_to_user((void __user *)arg, &std_id,
+ sizeof(v4l2_std_id))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_STD: {
+ v4l2_std_id std_id;
+ int rc;
+
+ JOM(8, "VIDIOC_S_STD\n");
+
+ if (0 != copy_from_user(&std_id, (void __user *)arg,
+ sizeof(v4l2_std_id))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ JOM(8, "User requests standard: 0x%08X%08X\n",
+ (int)((std_id & (((v4l2_std_id)0xFFFFFFFF) << 32)) >> 32),
+ (int)(std_id & ((v4l2_std_id)0xFFFFFFFF)));
+
+ rc = adjust_standard(peasycap, std_id);
+ if (0 > rc) {
+ JOM(8, "WARNING: adjust_standard() returned %i\n", rc);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ENOENT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_REQBUFS: {
+ int nbuffers;
+ struct v4l2_requestbuffers v4l2_requestbuffers;
+
+ JOM(8, "VIDIOC_REQBUFS\n");
+
+ if (0 != copy_from_user(&v4l2_requestbuffers,
+ (void __user *)arg,
+ sizeof(struct v4l2_requestbuffers))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ nbuffers = v4l2_requestbuffers.count;
+ JOM(8, " User requests %i buffers ...\n", nbuffers);
+ if (nbuffers < 2)
+ nbuffers = 2;
+ if (nbuffers > FRAME_BUFFER_MANY)
+ nbuffers = FRAME_BUFFER_MANY;
+ if (v4l2_requestbuffers.count == nbuffers) {
+ JOM(8, " ... agree to %i buffers\n",
+ nbuffers);
+ } else {
+ JOM(8, " ... insist on %i buffers\n",
+ nbuffers);
+ v4l2_requestbuffers.count = nbuffers;
+ }
+ peasycap->frame_buffer_many = nbuffers;
+
+ if (copy_to_user((void __user *)arg, &v4l2_requestbuffers,
+ sizeof(struct v4l2_requestbuffers))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_QUERYBUF: {
+ u32 index;
+ struct v4l2_buffer v4l2_buffer;
+
+ JOM(8, "VIDIOC_QUERYBUF\n");
+
+ if (peasycap->video_eof) {
+ JOM(8, "returning -EIO because %i=video_eof\n",
+ peasycap->video_eof);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EIO;
+ }
+
+ if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ index = v4l2_buffer.index;
+ if (index < 0 || index >= peasycap->frame_buffer_many)
+ return -EINVAL;
+ memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
+ v4l2_buffer.index = index;
+ v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_buffer.bytesused = peasycap->frame_buffer_used;
+ v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED |
+ peasycap->done[index] |
+ peasycap->queued[index];
+ v4l2_buffer.field = V4L2_FIELD_NONE;
+ v4l2_buffer.memory = V4L2_MEMORY_MMAP;
+ v4l2_buffer.m.offset = index * FRAME_BUFFER_SIZE;
+ v4l2_buffer.length = FRAME_BUFFER_SIZE;
+
+ JOM(16, " %10i=index\n", v4l2_buffer.index);
+ JOM(16, " 0x%08X=type\n", v4l2_buffer.type);
+ JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused);
+ JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags);
+ JOM(16, " %10i=field\n", v4l2_buffer.field);
+ JOM(16, " %10li=timestamp.tv_usec\n",
+ (long)v4l2_buffer.timestamp.tv_usec);
+ JOM(16, " %10i=sequence\n", v4l2_buffer.sequence);
+ JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory);
+ JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset);
+ JOM(16, " %10i=length\n", v4l2_buffer.length);
+
+ if (copy_to_user((void __user *)arg, &v4l2_buffer,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_QBUF: {
+ struct v4l2_buffer v4l2_buffer;
+
+ JOM(8, "VIDIOC_QBUF\n");
+
+ if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ if (v4l2_buffer.memory != V4L2_MEMORY_MMAP) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ if (v4l2_buffer.index < 0 ||
+ v4l2_buffer.index >= peasycap->frame_buffer_many) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED;
+
+ peasycap->done[v4l2_buffer.index] = 0;
+ peasycap->queued[v4l2_buffer.index] = V4L2_BUF_FLAG_QUEUED;
+
+ if (copy_to_user((void __user *)arg, &v4l2_buffer,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ JOM(8, "..... user queueing frame buffer %i\n",
+ (int)v4l2_buffer.index);
+
+ peasycap->frame_lock = 0;
+
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_DQBUF:
+ {
+ struct timeval timeval, timeval2;
+ int i, j;
+ struct v4l2_buffer v4l2_buffer;
+ int rcdq;
+ u16 input;
+
+ JOM(8, "VIDIOC_DQBUF\n");
+
+ if ((peasycap->video_idle) || (peasycap->video_eof)) {
+ JOM(8, "returning -EIO because "
+ "%i=video_idle %i=video_eof\n",
+ peasycap->video_idle, peasycap->video_eof);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EIO;
+ }
+
+ if (copy_from_user(&v4l2_buffer, (void __user *)arg,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+
+ if (peasycap->offerfields) {
+ /*---------------------------------------------------*/
+ /*
+ * IN ITS 50 "fps" MODE tvtime SEEMS ALWAYS TO REQUEST
+ * V4L2_FIELD_BOTTOM
+ */
+ /*---------------------------------------------------*/
+ if (V4L2_FIELD_TOP == v4l2_buffer.field)
+ JOM(8, "user wants V4L2_FIELD_TOP\n");
+ else if (V4L2_FIELD_BOTTOM == v4l2_buffer.field)
+ JOM(8, "user wants V4L2_FIELD_BOTTOM\n");
+ else if (V4L2_FIELD_ANY == v4l2_buffer.field)
+ JOM(8, "user wants V4L2_FIELD_ANY\n");
+ else
+ JOM(8, "user wants V4L2_FIELD_...UNKNOWN: %i\n",
+ v4l2_buffer.field);
+ }
+
+ if (!peasycap->video_isoc_streaming) {
+ JOM(16, "returning -EIO because video urbs not streaming\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EIO;
+ }
+ /*-------------------------------------------------------------------*/
+ /*
+ * IF THE USER HAS PREVIOUSLY CALLED easycap_poll(),
+ * AS DETERMINED BY FINDING
+ * THE FLAG peasycap->polled SET, THERE MUST BE
+ * NO FURTHER WAIT HERE. IN THIS
+ * CASE, JUST CHOOSE THE FRAME INDICATED BY peasycap->frame_read
+ */
+ /*-------------------------------------------------------------------*/
+
+ if (!peasycap->polled) {
+ do {
+ rcdq = easycap_dqbuf(peasycap, 0);
+ if (-EIO == rcdq) {
+ JOM(8, "returning -EIO because "
+ "dqbuf() returned -EIO\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EIO;
+ }
+ } while (0 != rcdq);
+ } else {
+ if (peasycap->video_eof) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EIO;
+ }
+ }
+ if (V4L2_BUF_FLAG_DONE != peasycap->done[peasycap->frame_read]) {
+ JOM(8, "V4L2_BUF_FLAG_DONE != 0x%08X\n",
+ peasycap->done[peasycap->frame_read]);
+ }
+ peasycap->polled = 0;
+
+ if (!(peasycap->isequence % 10)) {
+ for (i = 0; i < 179; i++)
+ peasycap->merit[i] = peasycap->merit[i+1];
+ peasycap->merit[179] = merit_saa(peasycap->pusb_device);
+ j = 0;
+ for (i = 0; i < 180; i++)
+ j += peasycap->merit[i];
+ if (90 < j) {
+ SAM("easycap driver shutting down "
+ "on condition blue\n");
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ }
+ }
+
+ v4l2_buffer.index = peasycap->frame_read;
+ v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_buffer.bytesused = peasycap->frame_buffer_used;
+ v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE;
+ if (peasycap->offerfields)
+ v4l2_buffer.field = V4L2_FIELD_BOTTOM;
+ else
+ v4l2_buffer.field = V4L2_FIELD_NONE;
+ do_gettimeofday(&timeval);
+ timeval2 = timeval;
+
+ v4l2_buffer.timestamp = timeval2;
+ v4l2_buffer.sequence = peasycap->isequence++;
+ v4l2_buffer.memory = V4L2_MEMORY_MMAP;
+ v4l2_buffer.m.offset = v4l2_buffer.index * FRAME_BUFFER_SIZE;
+ v4l2_buffer.length = FRAME_BUFFER_SIZE;
+
+ JOM(16, " %10i=index\n", v4l2_buffer.index);
+ JOM(16, " 0x%08X=type\n", v4l2_buffer.type);
+ JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused);
+ JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags);
+ JOM(16, " %10i=field\n", v4l2_buffer.field);
+ JOM(16, " %10li=timestamp.tv_sec\n",
+ (long)v4l2_buffer.timestamp.tv_sec);
+ JOM(16, " %10li=timestamp.tv_usec\n",
+ (long)v4l2_buffer.timestamp.tv_usec);
+ JOM(16, " %10i=sequence\n", v4l2_buffer.sequence);
+ JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory);
+ JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset);
+ JOM(16, " %10i=length\n", v4l2_buffer.length);
+
+ if (copy_to_user((void __user *)arg, &v4l2_buffer,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ input = peasycap->frame_buffer[peasycap->frame_read][0].input;
+ if (0x08 & input) {
+ JOM(8, "user is offered frame buffer %i, input %i\n",
+ peasycap->frame_read, (0x07 & input));
+ } else {
+ JOM(8, "user is offered frame buffer %i\n",
+ peasycap->frame_read);
+ }
+ peasycap->frame_lock = 1;
+ JOM(8, "%i=peasycap->frame_fill\n", peasycap->frame_fill);
+ if (peasycap->frame_read == peasycap->frame_fill) {
+ if (peasycap->frame_lock) {
+ JOM(8, "WORRY: filling frame buffer "
+ "while offered to user\n");
+ }
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_STREAMON: {
+ int i;
+
+ JOM(8, "VIDIOC_STREAMON\n");
+
+ peasycap->isequence = 0;
+ for (i = 0; i < 180; i++)
+ peasycap->merit[i] = 0;
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ submit_video_urbs(peasycap);
+ peasycap->video_idle = 0;
+ peasycap->audio_idle = 0;
+ peasycap->video_eof = 0;
+ peasycap->audio_eof = 0;
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_STREAMOFF: {
+ JOM(8, "VIDIOC_STREAMOFF\n");
+
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ peasycap->video_idle = 1;
+ peasycap->audio_idle = 1;
+/*---------------------------------------------------------------------------*/
+/*
+ * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO THE STREAMOFF COMMAND
+ * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+ JOM(8, "calling wake_up on wq_video and wq_audio\n");
+ wake_up_interruptible(&(peasycap->wq_video));
+ if (peasycap->psubstream)
+ snd_pcm_period_elapsed(peasycap->psubstream);
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_PARM: {
+ struct v4l2_streamparm *pv4l2_streamparm;
+
+ JOM(8, "VIDIOC_G_PARM\n");
+ pv4l2_streamparm = memdup_user((void __user *)arg,
+ sizeof(struct v4l2_streamparm));
+ if (IS_ERR(pv4l2_streamparm)) {
+ SAM("ERROR: copy from user failed\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return PTR_ERR(pv4l2_streamparm);
+ }
+
+ if (pv4l2_streamparm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ kfree(pv4l2_streamparm);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ pv4l2_streamparm->parm.capture.capability = 0;
+ pv4l2_streamparm->parm.capture.capturemode = 0;
+ pv4l2_streamparm->parm.capture.timeperframe.numerator = 1;
+
+ if (peasycap->fps) {
+ pv4l2_streamparm->parm.capture.timeperframe.
+ denominator = peasycap->fps;
+ } else {
+ if (peasycap->ntsc) {
+ pv4l2_streamparm->parm.capture.timeperframe.
+ denominator = 30;
+ } else {
+ pv4l2_streamparm->parm.capture.timeperframe.
+ denominator = 25;
+ }
+ }
+
+ pv4l2_streamparm->parm.capture.readbuffers =
+ peasycap->frame_buffer_many;
+ pv4l2_streamparm->parm.capture.extendedmode = 0;
+ if (copy_to_user((void __user *)arg,
+ pv4l2_streamparm,
+ sizeof(struct v4l2_streamparm))) {
+ kfree(pv4l2_streamparm);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ kfree(pv4l2_streamparm);
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_PARM: {
+ JOM(8, "VIDIOC_S_PARM unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_AUDIO: {
+ JOM(8, "VIDIOC_G_AUDIO unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_AUDIO: {
+ JOM(8, "VIDIOC_S_AUDIO unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_TUNER: {
+ JOM(8, "VIDIOC_S_TUNER unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_FBUF:
+ case VIDIOC_S_FBUF:
+ case VIDIOC_OVERLAY: {
+ JOM(8, "VIDIOC_G_FBUF|VIDIOC_S_FBUF|VIDIOC_OVERLAY unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_TUNER: {
+ JOM(8, "VIDIOC_G_TUNER unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY: {
+ JOM(8, "VIDIOC_G_FREQUENCY|VIDIOC_S_FREQUENCY unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ default: {
+ JOM(8, "ERROR: unrecognized V4L2 IOCTL command: 0x%08X\n", cmd);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ENOIOCTLCMD;
+ }
+ }
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ JOM(4, "unlocked easycapdc60_dongle[%i].mutex_video\n", kd);
+ return 0;
+}
+/*****************************************************************************/
--- /dev/null
+/*****************************************************************************
+* *
+* *
+* easycap_low.c *
+* *
+* *
+*****************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This 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.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+/*
+ * ACKNOWLEGEMENTS AND REFERENCES
+ * ------------------------------
+ * This driver makes use of register information contained in the Syntek
+ * Semicon DC-1125 driver hosted at
+ * http://sourceforge.net/projects/syntekdriver/.
+ * Particularly useful has been a patch to the latter driver provided by
+ * Ivor Hewitt in January 2009. The NTSC implementation is taken from the
+ * work of Ben Trask.
+*/
+/****************************************************************************/
+
+#include "easycap.h"
+
+#define GET(X, Y, Z) do { \
+ int __rc; \
+ *(Z) = (u16)0; \
+ __rc = regget(X, Y, Z, sizeof(u8)); \
+ if (0 > __rc) { \
+ JOT(8, ":-(%i\n", __LINE__); return __rc; \
+ } \
+} while (0)
+
+#define SET(X, Y, Z) do { \
+ int __rc; \
+ __rc = regset(X, Y, Z); \
+ if (0 > __rc) { \
+ JOT(8, ":-(%i\n", __LINE__); return __rc; \
+ } \
+} while (0)
+
+/*--------------------------------------------------------------------------*/
+static const struct stk1160config {
+ int reg;
+ int set;
+} stk1160configPAL[256] = {
+ {0x000, 0x0098},
+ {0x002, 0x0093},
+
+ {0x001, 0x0003},
+ {0x003, 0x0080},
+ {0x00D, 0x0000},
+ {0x00F, 0x0002},
+ {0x018, 0x0010},
+ {0x019, 0x0000},
+ {0x01A, 0x0014},
+ {0x01B, 0x000E},
+ {0x01C, 0x0046},
+
+ {0x100, 0x0033},
+ {0x103, 0x0000},
+ {0x104, 0x0000},
+ {0x105, 0x0000},
+ {0x106, 0x0000},
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ * RESOLUTION 640x480
+*/
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {0x110, 0x0008},
+ {0x111, 0x0000},
+ {0x112, 0x0020},
+ {0x113, 0x0000},
+ {0x114, 0x0508},
+ {0x115, 0x0005},
+ {0x116, 0x0110},
+ {0x117, 0x0001},
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+ {0x202, 0x000F},
+ {0x203, 0x004A},
+ {0x2FF, 0x0000},
+
+ {0xFFF, 0xFFFF}
+};
+/*--------------------------------------------------------------------------*/
+static const struct stk1160config stk1160configNTSC[256] = {
+ {0x000, 0x0098},
+ {0x002, 0x0093},
+
+ {0x001, 0x0003},
+ {0x003, 0x0080},
+ {0x00D, 0x0000},
+ {0x00F, 0x0002},
+ {0x018, 0x0010},
+ {0x019, 0x0000},
+ {0x01A, 0x0014},
+ {0x01B, 0x000E},
+ {0x01C, 0x0046},
+
+ {0x100, 0x0033},
+ {0x103, 0x0000},
+ {0x104, 0x0000},
+ {0x105, 0x0000},
+ {0x106, 0x0000},
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ * RESOLUTION 640x480
+*/
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {0x110, 0x0008},
+ {0x111, 0x0000},
+ {0x112, 0x0003},
+ {0x113, 0x0000},
+ {0x114, 0x0508},
+ {0x115, 0x0005},
+ {0x116, 0x00F3},
+ {0x117, 0x0000},
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+ {0x202, 0x000F},
+ {0x203, 0x004A},
+ {0x2FF, 0x0000},
+
+ {0xFFF, 0xFFFF}
+};
+/*--------------------------------------------------------------------------*/
+static const struct saa7113config {
+ int reg;
+ int set;
+} saa7113configPAL[256] = {
+ {0x01, 0x08},
+ {0x02, 0x80},
+ {0x03, 0x33},
+ {0x04, 0x00},
+ {0x05, 0x00},
+ {0x06, 0xE9},
+ {0x07, 0x0D},
+ {0x08, 0x38},
+ {0x09, 0x00},
+ {0x0A, SAA_0A_DEFAULT},
+ {0x0B, SAA_0B_DEFAULT},
+ {0x0C, SAA_0C_DEFAULT},
+ {0x0D, SAA_0D_DEFAULT},
+ {0x0E, 0x01},
+ {0x0F, 0x36},
+ {0x10, 0x00},
+ {0x11, 0x0C},
+ {0x12, 0xE7},
+ {0x13, 0x00},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x40, 0x02},
+ {0x41, 0xFF},
+ {0x42, 0xFF},
+ {0x43, 0xFF},
+ {0x44, 0xFF},
+ {0x45, 0xFF},
+ {0x46, 0xFF},
+ {0x47, 0xFF},
+ {0x48, 0xFF},
+ {0x49, 0xFF},
+ {0x4A, 0xFF},
+ {0x4B, 0xFF},
+ {0x4C, 0xFF},
+ {0x4D, 0xFF},
+ {0x4E, 0xFF},
+ {0x4F, 0xFF},
+ {0x50, 0xFF},
+ {0x51, 0xFF},
+ {0x52, 0xFF},
+ {0x53, 0xFF},
+ {0x54, 0xFF},
+ {0x55, 0xFF},
+ {0x56, 0xFF},
+ {0x57, 0xFF},
+ {0x58, 0x40},
+ {0x59, 0x54},
+ {0x5A, 0x07},
+ {0x5B, 0x83},
+
+ {0xFF, 0xFF}
+};
+/*--------------------------------------------------------------------------*/
+static const struct saa7113config saa7113configNTSC[256] = {
+ {0x01, 0x08},
+ {0x02, 0x80},
+ {0x03, 0x33},
+ {0x04, 0x00},
+ {0x05, 0x00},
+ {0x06, 0xE9},
+ {0x07, 0x0D},
+ {0x08, 0x78},
+ {0x09, 0x00},
+ {0x0A, SAA_0A_DEFAULT},
+ {0x0B, SAA_0B_DEFAULT},
+ {0x0C, SAA_0C_DEFAULT},
+ {0x0D, SAA_0D_DEFAULT},
+ {0x0E, 0x01},
+ {0x0F, 0x36},
+ {0x10, 0x00},
+ {0x11, 0x0C},
+ {0x12, 0xE7},
+ {0x13, 0x00},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x40, 0x82},
+ {0x41, 0xFF},
+ {0x42, 0xFF},
+ {0x43, 0xFF},
+ {0x44, 0xFF},
+ {0x45, 0xFF},
+ {0x46, 0xFF},
+ {0x47, 0xFF},
+ {0x48, 0xFF},
+ {0x49, 0xFF},
+ {0x4A, 0xFF},
+ {0x4B, 0xFF},
+ {0x4C, 0xFF},
+ {0x4D, 0xFF},
+ {0x4E, 0xFF},
+ {0x4F, 0xFF},
+ {0x50, 0xFF},
+ {0x51, 0xFF},
+ {0x52, 0xFF},
+ {0x53, 0xFF},
+ {0x54, 0xFF},
+ {0x55, 0xFF},
+ {0x56, 0xFF},
+ {0x57, 0xFF},
+ {0x58, 0x40},
+ {0x59, 0x54},
+ {0x5A, 0x0A},
+ {0x5B, 0x83},
+
+ {0xFF, 0xFF}
+};
+
+static int regget(struct usb_device *pusb_device,
+ u16 index, void *reg, int reg_size)
+{
+ int rc;
+
+ if (!pusb_device)
+ return -ENODEV;
+
+ rc = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0),
+ 0x00,
+ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
+ 0x00,
+ index, reg, reg_size, 50000);
+
+ return rc;
+}
+
+static int regset(struct usb_device *pusb_device, u16 index, u16 value)
+{
+ int rc;
+
+ if (!pusb_device)
+ return -ENODEV;
+
+ rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
+ 0x01,
+ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
+ value, index, NULL, 0, 500);
+
+ if (rc < 0)
+ return rc;
+
+ if (easycap_readback) {
+ u16 igot = 0;
+ rc = regget(pusb_device, index, &igot, sizeof(igot));
+ igot = 0xFF & igot;
+ switch (index) {
+ case 0x000:
+ case 0x500:
+ case 0x502:
+ case 0x503:
+ case 0x504:
+ case 0x506:
+ case 0x507:
+ break;
+
+ case 0x204:
+ case 0x205:
+ case 0x350:
+ case 0x351:
+ if (igot)
+ JOT(8, "unexpected 0x%02X "
+ "for STK register 0x%03X\n",
+ igot, index);
+ break;
+
+ default:
+ if ((0xFF & value) != igot)
+ JOT(8, "unexpected 0x%02X != 0x%02X "
+ "for STK register 0x%03X\n",
+ igot, value, index);
+ break;
+ }
+ }
+
+ return rc;
+}
+/*--------------------------------------------------------------------------*/
+/*
+ * FUNCTION wait_i2c() RETURNS 0 ON SUCCESS
+*/
+/*--------------------------------------------------------------------------*/
+static int wait_i2c(struct usb_device *p)
+{
+ u16 get0;
+ u8 igot;
+ const int max = 2;
+ int k;
+
+ if (!p)
+ return -ENODEV;
+
+ for (k = 0; k < max; k++) {
+ GET(p, 0x0201, &igot); get0 = igot;
+ switch (get0) {
+ case 0x04:
+ case 0x01:
+ return 0;
+ case 0x00:
+ msleep(20);
+ continue;
+ default:
+ return get0 - 1;
+ }
+ }
+ return -1;
+}
+
+/****************************************************************************/
+int confirm_resolution(struct usb_device *p)
+{
+ u8 get0, get1, get2, get3, get4, get5, get6, get7;
+
+ if (!p)
+ return -ENODEV;
+ GET(p, 0x0110, &get0);
+ GET(p, 0x0111, &get1);
+ GET(p, 0x0112, &get2);
+ GET(p, 0x0113, &get3);
+ GET(p, 0x0114, &get4);
+ GET(p, 0x0115, &get5);
+ GET(p, 0x0116, &get6);
+ GET(p, 0x0117, &get7);
+ JOT(8, "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X\n",
+ get0, get1, get2, get3, get4, get5, get6, get7);
+ JOT(8, "....cf PAL_720x526: "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X\n",
+ 0x000, 0x000, 0x001, 0x000, 0x5A0, 0x005, 0x121, 0x001);
+ JOT(8, "....cf PAL_704x526: "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X\n",
+ 0x004, 0x000, 0x001, 0x000, 0x584, 0x005, 0x121, 0x001);
+ JOT(8, "....cf VGA_640x480: "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X\n",
+ 0x008, 0x000, 0x020, 0x000, 0x508, 0x005, 0x110, 0x001);
+ return 0;
+}
+/****************************************************************************/
+int confirm_stream(struct usb_device *p)
+{
+ u16 get2;
+ u8 igot;
+
+ if (!p)
+ return -ENODEV;
+ GET(p, 0x0100, &igot); get2 = 0x80 & igot;
+ if (0x80 == get2)
+ JOT(8, "confirm_stream: OK\n");
+ else
+ JOT(8, "confirm_stream: STUCK\n");
+ return 0;
+}
+/****************************************************************************/
+int setup_stk(struct usb_device *p, bool ntsc)
+{
+ int i;
+ const struct stk1160config *cfg;
+ if (!p)
+ return -ENODEV;
+ cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL;
+ for (i = 0; cfg[i].reg != 0xFFF; i++)
+ SET(p, cfg[i].reg, cfg[i].set);
+
+ write_300(p);
+
+ return 0;
+}
+/****************************************************************************/
+int setup_saa(struct usb_device *p, bool ntsc)
+{
+ int i, ir;
+ const struct saa7113config *cfg;
+ if (!p)
+ return -ENODEV;
+ cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL;
+ for (i = 0; cfg[i].reg != 0xFF; i++)
+ ir = write_saa(p, cfg[i].reg, cfg[i].set);
+ return 0;
+}
+/****************************************************************************/
+int write_000(struct usb_device *p, u16 set2, u16 set0)
+{
+ u8 igot0, igot2;
+
+ if (!p)
+ return -ENODEV;
+ GET(p, 0x0002, &igot2);
+ GET(p, 0x0000, &igot0);
+ SET(p, 0x0002, set2);
+ SET(p, 0x0000, set0);
+ return 0;
+}
+/****************************************************************************/
+int write_saa(struct usb_device *p, u16 reg0, u16 set0)
+{
+ if (!p)
+ return -ENODEV;
+ SET(p, 0x200, 0x00);
+ SET(p, 0x204, reg0);
+ SET(p, 0x205, set0);
+ SET(p, 0x200, 0x01);
+ return wait_i2c(p);
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?)
+ * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A
+ * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO SET
+ * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO SET
+ * REGISTER 504: TARGET ADDRESS ON VT1612A
+ */
+/*--------------------------------------------------------------------------*/
+int
+write_vt(struct usb_device *p, u16 reg0, u16 set0)
+{
+ u8 igot;
+ u16 got502, got503;
+ u16 set502, set503;
+
+ if (!p)
+ return -ENODEV;
+ SET(p, 0x0504, reg0);
+ SET(p, 0x0500, 0x008B);
+
+ GET(p, 0x0502, &igot); got502 = (0xFF & igot);
+ GET(p, 0x0503, &igot); got503 = (0xFF & igot);
+
+ JOT(16, "write_vt(., 0x%04X, 0x%04X): was 0x%04X\n",
+ reg0, set0, ((got503 << 8) | got502));
+
+ set502 = (0x00FF & set0);
+ set503 = ((0xFF00 & set0) >> 8);
+
+ SET(p, 0x0504, reg0);
+ SET(p, 0x0502, set502);
+ SET(p, 0x0503, set503);
+ SET(p, 0x0500, 0x008C);
+
+ return 0;
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?)
+ * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A
+ * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO GET
+ * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO GET
+ * REGISTER 504: TARGET ADDRESS ON VT1612A
+ */
+/*--------------------------------------------------------------------------*/
+int read_vt(struct usb_device *p, u16 reg0)
+{
+ u8 igot;
+ u16 got502, got503;
+
+ if (!p)
+ return -ENODEV;
+ SET(p, 0x0504, reg0);
+ SET(p, 0x0500, 0x008B);
+
+ GET(p, 0x0502, &igot); got502 = (0xFF & igot);
+ GET(p, 0x0503, &igot); got503 = (0xFF & igot);
+
+ JOT(16, "read_vt(., 0x%04X): has 0x%04X\n",
+ reg0, ((got503 << 8) | got502));
+
+ return (got503 << 8) | got502;
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * THESE APPEAR TO HAVE NO EFFECT ON EITHER VIDEO OR AUDIO.
+ */
+/*--------------------------------------------------------------------------*/
+int write_300(struct usb_device *p)
+{
+ if (!p)
+ return -ENODEV;
+ SET(p, 0x300, 0x0012);
+ SET(p, 0x350, 0x002D);
+ SET(p, 0x351, 0x0001);
+ SET(p, 0x352, 0x0000);
+ SET(p, 0x353, 0x0000);
+ SET(p, 0x300, 0x0080);
+ return 0;
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * NOTE: THE FOLLOWING IS NOT CHECKED:
+ * REGISTER 0x0F, WHICH IS INVOLVED IN CHROMINANCE AUTOMATIC GAIN CONTROL.
+ */
+/*--------------------------------------------------------------------------*/
+int check_saa(struct usb_device *p, bool ntsc)
+{
+ int i, ir, rc = 0;
+ struct saa7113config const *cfg;
+ if (!p)
+ return -ENODEV;
+
+ cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL;
+ for (i = 0; cfg[i].reg != 0xFF; i++) {
+ if (0x0F == cfg[i].reg)
+ continue;
+ ir = read_saa(p, cfg[i].reg);
+ if (ir != cfg[i].set) {
+ SAY("SAA register 0x%02X has 0x%02X, expected 0x%02X\n",
+ cfg[i].reg, ir, cfg[i].set);
+ rc--;
+ }
+ }
+
+ return (rc < -8) ? rc : 0;
+}
+/****************************************************************************/
+int merit_saa(struct usb_device *p)
+{
+ int rc;
+
+ if (!p)
+ return -ENODEV;
+ rc = read_saa(p, 0x1F);
+ return ((0 > rc) || (0x02 & rc)) ? 1 : 0;
+}
+/****************************************************************************/
+int ready_saa(struct usb_device *p)
+{
+ int j, rc, rate;
+ const int max = 5, marktime = PATIENCE/5;
+/*--------------------------------------------------------------------------*/
+/*
+ * RETURNS 0 FOR INTERLACED 50 Hz
+ * 1 FOR NON-INTERLACED 50 Hz
+ * 2 FOR INTERLACED 60 Hz
+ * 3 FOR NON-INTERLACED 60 Hz
+*/
+/*--------------------------------------------------------------------------*/
+ if (!p)
+ return -ENODEV;
+ j = 0;
+ while (max > j) {
+ rc = read_saa(p, 0x1F);
+ if (0 <= rc) {
+ if (0 == (0x40 & rc))
+ break;
+ if (1 == (0x01 & rc))
+ break;
+ }
+ msleep(marktime);
+ j++;
+ }
+ if (max == j)
+ return -1;
+ else {
+ if (0x20 & rc) {
+ rate = 2;
+ JOT(8, "hardware detects 60 Hz\n");
+ } else {
+ rate = 0;
+ JOT(8, "hardware detects 50 Hz\n");
+ }
+ if (0x80 & rc)
+ JOT(8, "hardware detects interlacing\n");
+ else {
+ rate++;
+ JOT(8, "hardware detects no interlacing\n");
+ }
+ }
+ return 0;
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * NOTE: THE FOLLOWING ARE NOT CHECKED:
+ * REGISTERS 0x000, 0x002: FUNCTIONALITY IS NOT KNOWN
+ * REGISTER 0x100: ACCEPT ALSO (0x80 | stk1160config....[.].set)
+ */
+/*--------------------------------------------------------------------------*/
+int check_stk(struct usb_device *p, bool ntsc)
+{
+ int i, ir;
+ const struct stk1160config *cfg;
+
+ if (!p)
+ return -ENODEV;
+ cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL;
+
+ for (i = 0; 0xFFF != cfg[i].reg; i++) {
+ if (0x000 == cfg[i].reg || 0x002 == cfg[i].reg)
+ continue;
+
+
+ ir = read_stk(p, cfg[i].reg);
+ if (0x100 == cfg[i].reg) {
+ if ((ir != (0xFF & cfg[i].set)) &&
+ (ir != (0x80 | (0xFF & cfg[i].set))) &&
+ (0xFFFF != cfg[i].set)) {
+ SAY("STK reg[0x%03X]=0x%02X expected 0x%02X\n",
+ cfg[i].reg, ir, cfg[i].set);
+ }
+ continue;
+ }
+ if ((ir != (0xFF & cfg[i].set)) && (0xFFFF != cfg[i].set))
+ SAY("STK register 0x%03X has 0x%02X,expected 0x%02X\n",
+ cfg[i].reg, ir, cfg[i].set);
+ }
+ return 0;
+}
+/****************************************************************************/
+int read_saa(struct usb_device *p, u16 reg0)
+{
+ u8 igot;
+
+ if (!p)
+ return -ENODEV;
+ SET(p, 0x208, reg0);
+ SET(p, 0x200, 0x20);
+ if (0 != wait_i2c(p))
+ return -1;
+ igot = 0;
+ GET(p, 0x0209, &igot);
+ return igot;
+}
+/****************************************************************************/
+int read_stk(struct usb_device *p, u32 reg0)
+{
+ u8 igot;
+
+ if (!p)
+ return -ENODEV;
+ igot = 0;
+ GET(p, reg0, &igot);
+ return igot;
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * HARDWARE USERSPACE INPUT NUMBER PHYSICAL INPUT DRIVER input VALUE
+ *
+ * CVBS+S-VIDEO 0 or 1 CVBS 1
+ * FOUR-CVBS 0 or 1 CVBS1 1
+ * FOUR-CVBS 2 CVBS2 2
+ * FOUR-CVBS 3 CVBS3 3
+ * FOUR-CVBS 4 CVBS4 4
+ * CVBS+S-VIDEO 5 S-VIDEO 5
+ *
+ * WHEN 5==input THE ARGUMENT mode MUST ALSO BE SUPPLIED:
+ *
+ * mode 7 => GAIN TO BE SET EXPLICITLY USING REGISTER 0x05 (UNTESTED)
+ * mode 9 => USE AUTOMATIC GAIN CONTROL (DEFAULT)
+ *
+*/
+/*---------------------------------------------------------------------------*/
+int
+select_input(struct usb_device *p, int input, int mode)
+{
+ int ir;
+
+ if (!p)
+ return -ENODEV;
+ stop_100(p);
+ switch (input) {
+ case 0:
+ case 1: {
+ if (0 != write_saa(p, 0x02, 0x80))
+ SAY("ERROR: failed to set SAA register 0x02 "
+ "for input %i\n", input);
+
+ SET(p, 0x0000, 0x0098);
+ SET(p, 0x0002, 0x0078);
+ break;
+ }
+ case 2: {
+ if (0 != write_saa(p, 0x02, 0x80))
+ SAY("ERROR: failed to set SAA register 0x02 "
+ "for input %i\n", input);
+
+ SET(p, 0x0000, 0x0090);
+ SET(p, 0x0002, 0x0078);
+ break;
+ }
+ case 3: {
+ if (0 != write_saa(p, 0x02, 0x80))
+ SAY("ERROR: failed to set SAA register 0x02 "
+ " for input %i\n", input);
+
+ SET(p, 0x0000, 0x0088);
+ SET(p, 0x0002, 0x0078);
+ break;
+ }
+ case 4: {
+ if (0 != write_saa(p, 0x02, 0x80)) {
+ SAY("ERROR: failed to set SAA register 0x02 "
+ "for input %i\n", input);
+ }
+ SET(p, 0x0000, 0x0080);
+ SET(p, 0x0002, 0x0078);
+ break;
+ }
+ case 5: {
+ if (9 != mode)
+ mode = 7;
+ switch (mode) {
+ case 7: {
+ if (0 != write_saa(p, 0x02, 0x87))
+ SAY("ERROR: failed to set SAA register 0x02 "
+ "for input %i\n", input);
+
+ if (0 != write_saa(p, 0x05, 0xFF))
+ SAY("ERROR: failed to set SAA register 0x05 "
+ "for input %i\n", input);
+
+ break;
+ }
+ case 9: {
+ if (0 != write_saa(p, 0x02, 0x89))
+ SAY("ERROR: failed to set SAA register 0x02 "
+ "for input %i\n", input);
+
+ if (0 != write_saa(p, 0x05, 0x00))
+ SAY("ERROR: failed to set SAA register 0x05 "
+ "for input %i\n", input);
+
+ break;
+ }
+ default:
+ SAY("MISTAKE: bad mode: %i\n", mode);
+ return -1;
+ }
+
+ if (0 != write_saa(p, 0x04, 0x00))
+ SAY("ERROR: failed to set SAA register 0x04 "
+ "for input %i\n", input);
+
+ if (0 != write_saa(p, 0x09, 0x80))
+ SAY("ERROR: failed to set SAA register 0x09 "
+ "for input %i\n", input);
+
+ SET(p, 0x0002, 0x0093);
+ break;
+ }
+ default:
+ SAY("ERROR: bad input: %i\n", input);
+ return -1;
+ }
+
+ ir = read_stk(p, 0x00);
+ JOT(8, "STK register 0x00 has 0x%02X\n", ir);
+ ir = read_saa(p, 0x02);
+ JOT(8, "SAA register 0x02 has 0x%02X\n", ir);
+
+ start_100(p);
+
+ return 0;
+}
+/****************************************************************************/
+int set_resolution(struct usb_device *p,
+ u16 set0, u16 set1, u16 set2, u16 set3)
+{
+ u16 u0x0111, u0x0113, u0x0115, u0x0117;
+
+ if (!p)
+ return -ENODEV;
+ u0x0111 = ((0xFF00 & set0) >> 8);
+ u0x0113 = ((0xFF00 & set1) >> 8);
+ u0x0115 = ((0xFF00 & set2) >> 8);
+ u0x0117 = ((0xFF00 & set3) >> 8);
+
+ SET(p, 0x0110, (0x00FF & set0));
+ SET(p, 0x0111, u0x0111);
+ SET(p, 0x0112, (0x00FF & set1));
+ SET(p, 0x0113, u0x0113);
+ SET(p, 0x0114, (0x00FF & set2));
+ SET(p, 0x0115, u0x0115);
+ SET(p, 0x0116, (0x00FF & set3));
+ SET(p, 0x0117, u0x0117);
+
+ return 0;
+}
+/****************************************************************************/
+int start_100(struct usb_device *p)
+{
+ u16 get116, get117, get0;
+ u8 igot116, igot117, igot;
+
+ if (!p)
+ return -ENODEV;
+ GET(p, 0x0116, &igot116);
+ get116 = igot116;
+ GET(p, 0x0117, &igot117);
+ get117 = igot117;
+ SET(p, 0x0116, 0x0000);
+ SET(p, 0x0117, 0x0000);
+
+ GET(p, 0x0100, &igot);
+ get0 = igot;
+ SET(p, 0x0100, (0x80 | get0));
+
+ SET(p, 0x0116, get116);
+ SET(p, 0x0117, get117);
+
+ return 0;
+}
+/****************************************************************************/
+int stop_100(struct usb_device *p)
+{
+ u16 get0;
+ u8 igot;
+
+ if (!p)
+ return -ENODEV;
+ GET(p, 0x0100, &igot);
+ get0 = igot;
+ SET(p, 0x0100, (0x7F & get0));
+ return 0;
+}
+/****************************************************************************/
+/****************************************************************************/
+/*****************************************************************************/
+int wakeup_device(struct usb_device *pusb_device)
+{
+ if (!pusb_device)
+ return -ENODEV;
+ return usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
+ USB_REQ_SET_FEATURE,
+ USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP,
+ 0, NULL, 0, 50000);
+}
+/*****************************************************************************/
+int
+audio_setup(struct easycap *peasycap)
+{
+ struct usb_device *pusb_device;
+ u8 buffer[1];
+ int rc, id1, id2;
+/*---------------------------------------------------------------------------*/
+/*
+ * IMPORTANT:
+ * THE MESSAGE OF TYPE (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+ * CAUSES MUTING IF THE VALUE 0x0100 IS SENT.
+ * TO ENABLE AUDIO THE VALUE 0x0200 MUST BE SENT.
+ */
+/*---------------------------------------------------------------------------*/
+ const u8 request = 0x01;
+ const u8 requesttype = USB_DIR_OUT |
+ USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE;
+ const u16 value_unmute = 0x0200;
+ const u16 index = 0x0301;
+ const u16 length = 1;
+
+ if (!peasycap)
+ return -EFAULT;
+
+ pusb_device = peasycap->pusb_device;
+ if (!pusb_device)
+ return -ENODEV;
+
+ JOM(8, "%02X %02X %02X %02X %02X %02X %02X %02X\n",
+ requesttype, request,
+ (0x00FF & value_unmute),
+ (0xFF00 & value_unmute) >> 8,
+ (0x00FF & index),
+ (0xFF00 & index) >> 8,
+ (0x00FF & length),
+ (0xFF00 & length) >> 8);
+
+ buffer[0] = 0x01;
+
+ rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
+ request, requesttype, value_unmute,
+ index, &buffer[0], length, 50000);
+
+ JOT(8, "0x%02X=buffer\n", buffer[0]);
+ if (rc != (int)length) {
+ switch (rc) {
+ case -EPIPE:
+ SAY("usb_control_msg returned -EPIPE\n");
+ break;
+ default:
+ SAY("ERROR: usb_control_msg returned %i\n", rc);
+ break;
+ }
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * REGISTER 500: SETTING VALUE TO 0x0094 RESETS AUDIO CONFIGURATION ???
+ * REGISTER 506: ANALOGUE AUDIO ATTENTUATOR ???
+ * FOR THE CVBS+S-VIDEO HARDWARE:
+ * SETTING VALUE TO 0x0000 GIVES QUIET SOUND.
+ * THE UPPER BYTE SEEMS TO HAVE NO EFFECT.
+ * FOR THE FOUR-CVBS HARDWARE:
+ * SETTING VALUE TO 0x0000 SEEMS TO HAVE NO EFFECT.
+ * REGISTER 507: ANALOGUE AUDIO PREAMPLIFIER ON/OFF ???
+ * FOR THE CVBS-S-VIDEO HARDWARE:
+ * SETTING VALUE TO 0x0001 GIVES VERY LOUD, DISTORTED SOUND.
+ * THE UPPER BYTE SEEMS TO HAVE NO EFFECT.
+ */
+/*--------------------------------------------------------------------------*/
+ SET(pusb_device, 0x0500, 0x0094);
+ SET(pusb_device, 0x0500, 0x008C);
+ SET(pusb_device, 0x0506, 0x0001);
+ SET(pusb_device, 0x0507, 0x0000);
+ id1 = read_vt(pusb_device, 0x007C);
+ id2 = read_vt(pusb_device, 0x007E);
+ SAM("0x%04X:0x%04X is audio vendor id\n", id1, id2);
+/*---------------------------------------------------------------------------*/
+/*
+ * SELECT AUDIO SOURCE "LINE IN" AND SET THE AUDIO GAIN.
+*/
+/*---------------------------------------------------------------------------*/
+ if (0 != audio_gainset(pusb_device, peasycap->gain))
+ SAY("ERROR: audio_gainset() failed\n");
+ check_vt(pusb_device);
+ return 0;
+}
+/*****************************************************************************/
+int check_vt(struct usb_device *pusb_device)
+{
+ int igot;
+
+ if (!pusb_device)
+ return -ENODEV;
+ igot = read_vt(pusb_device, 0x0002);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x02\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x02);
+
+ igot = read_vt(pusb_device, 0x000E);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x0E\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x0E);
+
+ igot = read_vt(pusb_device, 0x0010);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x10\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x10);
+
+ igot = read_vt(pusb_device, 0x0012);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x12\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x12);
+
+ igot = read_vt(pusb_device, 0x0014);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x14\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x14);
+
+ igot = read_vt(pusb_device, 0x0016);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x16\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x16);
+
+ igot = read_vt(pusb_device, 0x0018);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x18\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x18);
+
+ igot = read_vt(pusb_device, 0x001C);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x1C\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x1C);
+
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/* NOTE: THIS DOES INCREASE THE VOLUME DRAMATICALLY:
+ * audio_gainset(pusb_device, 0x000F);
+ *
+ * loud dB register 0x10 dB register 0x1C dB total
+ * 0 -34.5 0 -34.5
+ * .. .... . ....
+ * 15 10.5 0 10.5
+ * 16 12.0 0 12.0
+ * 17 12.0 1.5 13.5
+ * .. .... .... ....
+ * 31 12.0 22.5 34.5
+*/
+/*---------------------------------------------------------------------------*/
+int audio_gainset(struct usb_device *pusb_device, s8 loud)
+{
+ int igot;
+ u8 tmp;
+ u16 mute;
+
+ if (!pusb_device)
+ return -ENODEV;
+ if (0 > loud)
+ loud = 0;
+ if (31 < loud)
+ loud = 31;
+
+ write_vt(pusb_device, 0x0002, 0x8000);
+/*---------------------------------------------------------------------------*/
+ igot = read_vt(pusb_device, 0x000E);
+ if (0 > igot) {
+ SAY("ERROR: failed to read VT1612A register 0x0E\n");
+ mute = 0x0000;
+ } else
+ mute = 0x8000 & ((unsigned int)igot);
+ mute = 0;
+
+ if (16 > loud)
+ tmp = 0x01 | (0x001F & (((u8)(15 - loud)) << 1));
+ else
+ tmp = 0;
+
+ JOT(8, "0x%04X=(mute|tmp) for VT1612A register 0x0E\n", mute | tmp);
+ write_vt(pusb_device, 0x000E, (mute | tmp));
+/*---------------------------------------------------------------------------*/
+ igot = read_vt(pusb_device, 0x0010);
+ if (0 > igot) {
+ SAY("ERROR: failed to read VT1612A register 0x10\n");
+ mute = 0x0000;
+ } else
+ mute = 0x8000 & ((unsigned int)igot);
+ mute = 0;
+
+ JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x10,...0x18\n",
+ mute | tmp | (tmp << 8));
+ write_vt(pusb_device, 0x0010, (mute | tmp | (tmp << 8)));
+ write_vt(pusb_device, 0x0012, (mute | tmp | (tmp << 8)));
+ write_vt(pusb_device, 0x0014, (mute | tmp | (tmp << 8)));
+ write_vt(pusb_device, 0x0016, (mute | tmp | (tmp << 8)));
+ write_vt(pusb_device, 0x0018, (mute | tmp | (tmp << 8)));
+/*---------------------------------------------------------------------------*/
+ igot = read_vt(pusb_device, 0x001C);
+ if (0 > igot) {
+ SAY("ERROR: failed to read VT1612A register 0x1C\n");
+ mute = 0x0000;
+ } else
+ mute = 0x8000 & ((unsigned int)igot);
+ mute = 0;
+
+ if (16 <= loud)
+ tmp = 0x000F & (u8)(loud - 16);
+ else
+ tmp = 0;
+
+ JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x1C\n",
+ mute | tmp | (tmp << 8));
+ write_vt(pusb_device, 0x001C, (mute | tmp | (tmp << 8)));
+ write_vt(pusb_device, 0x001A, 0x0404);
+ write_vt(pusb_device, 0x0002, 0x0000);
+ return 0;
+}
+/*****************************************************************************/
+int audio_gainget(struct usb_device *pusb_device)
+{
+ int igot;
+
+ if (!pusb_device)
+ return -ENODEV;
+ igot = read_vt(pusb_device, 0x001C);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x1C\n");
+ return igot;
+}
+/*****************************************************************************/
--- /dev/null
+/******************************************************************************
+* *
+* easycap_main.c *
+* *
+* Video driver for EasyCAP USB2.0 Video Capture Device DC60 *
+* *
+* *
+******************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This 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.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+
+#include "easycap.h"
+#include <linux/usb/audio.h>
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("R.M. Thomas <rmthomas@sciolus.org>");
+MODULE_DESCRIPTION(EASYCAP_DRIVER_DESCRIPTION);
+MODULE_VERSION(EASYCAP_DRIVER_VERSION);
+
+#ifdef CONFIG_EASYCAP_DEBUG
+int easycap_debug;
+module_param_named(debug, easycap_debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level: 0(default),1,2,...,9");
+#endif /* CONFIG_EASYCAP_DEBUG */
+
+bool easycap_readback;
+module_param_named(readback, easycap_readback, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(readback, "read back written registers: (default false)");
+
+static int easycap_bars = 1;
+module_param_named(bars, easycap_bars, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(bars,
+ "Testcard bars on input signal failure: 0=>no, 1=>yes(default)");
+
+static int easycap_gain = 16;
+module_param_named(gain, easycap_gain, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(gain, "Audio gain: 0,...,16(default),...31");
+
+static bool easycap_ntsc;
+module_param_named(ntsc, easycap_ntsc, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ntsc, "NTSC default encoding (default PAL)");
+
+
+
+struct easycap_dongle easycapdc60_dongle[DONGLE_MANY];
+static struct mutex mutex_dongle;
+static void easycap_complete(struct urb *purb);
+static int reset(struct easycap *peasycap);
+
+const char *strerror(int err)
+{
+#define ERRNOSTR(_e) case _e: return # _e
+ switch (err) {
+ case 0: return "OK";
+ ERRNOSTR(ENOMEM);
+ ERRNOSTR(ENODEV);
+ ERRNOSTR(ENXIO);
+ ERRNOSTR(EINVAL);
+ ERRNOSTR(EAGAIN);
+ ERRNOSTR(EFBIG);
+ ERRNOSTR(EPIPE);
+ ERRNOSTR(EMSGSIZE);
+ ERRNOSTR(ENOSPC);
+ ERRNOSTR(EINPROGRESS);
+ ERRNOSTR(ENOSR);
+ ERRNOSTR(EOVERFLOW);
+ ERRNOSTR(EPROTO);
+ ERRNOSTR(EILSEQ);
+ ERRNOSTR(ETIMEDOUT);
+ ERRNOSTR(EOPNOTSUPP);
+ ERRNOSTR(EPFNOSUPPORT);
+ ERRNOSTR(EAFNOSUPPORT);
+ ERRNOSTR(EADDRINUSE);
+ ERRNOSTR(EADDRNOTAVAIL);
+ ERRNOSTR(ENOBUFS);
+ ERRNOSTR(EISCONN);
+ ERRNOSTR(ENOTCONN);
+ ERRNOSTR(ESHUTDOWN);
+ ERRNOSTR(ENOENT);
+ ERRNOSTR(ECONNRESET);
+ ERRNOSTR(ETIME);
+ ERRNOSTR(ECOMM);
+ ERRNOSTR(EREMOTEIO);
+ ERRNOSTR(EXDEV);
+ ERRNOSTR(EPERM);
+ default: return "unknown";
+ }
+
+#undef ERRNOSTR
+}
+
+/*---------------------------------------------------------------------------*/
+/*
+ * PARAMETERS USED WHEN REGISTERING THE VIDEO INTERFACE
+ *
+ * NOTE: SOME KERNELS IGNORE usb_class_driver.minor_base, AS MENTIONED BY
+ * CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGE 253.
+ * THIS IS THE CASE FOR OpenSUSE.
+ */
+/*---------------------------------------------------------------------------*/
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+/****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * THIS ROUTINE DOES NOT DETECT DUPLICATE OCCURRENCES OF POINTER peasycap
+*/
+/*---------------------------------------------------------------------------*/
+int isdongle(struct easycap *peasycap)
+{
+ int k;
+ if (!peasycap)
+ return -2;
+ for (k = 0; k < DONGLE_MANY; k++) {
+ if (easycapdc60_dongle[k].peasycap == peasycap) {
+ peasycap->isdongle = k;
+ return k;
+ }
+ }
+ return -1;
+}
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+static int easycap_open(struct inode *inode, struct file *file)
+{
+ struct video_device *pvideo_device;
+ struct easycap *peasycap;
+ int rc;
+
+ JOT(4, "\n");
+ SAY("==========OPEN=========\n");
+
+ pvideo_device = video_devdata(file);
+ if (!pvideo_device) {
+ SAY("ERROR: pvideo_device is NULL.\n");
+ return -EFAULT;
+ }
+ peasycap = (struct easycap *)video_get_drvdata(pvideo_device);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ } else {
+ JOM(16, "peasycap->pusb_device=%p\n", peasycap->pusb_device);
+ }
+ file->private_data = peasycap;
+ rc = wakeup_device(peasycap->pusb_device);
+ if (0 == rc)
+ JOM(8, "wakeup_device() OK\n");
+ else {
+ SAM("ERROR: wakeup_device() rc = %i\n", rc);
+ if (-ENODEV == rc)
+ SAM("ERROR: wakeup_device() returned -ENODEV\n");
+ else
+ SAM("ERROR: wakeup_device() rc = %i\n", rc);
+ return rc;
+ }
+ peasycap->input = 0;
+ rc = reset(peasycap);
+ if (rc) {
+ SAM("ERROR: reset() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * RESET THE HARDWARE TO ITS REFERENCE STATE.
+ *
+ * THIS ROUTINE MAY BE CALLED REPEATEDLY IF easycap_complete() DETECTS
+ * A BAD VIDEO FRAME SIZE.
+*/
+/*---------------------------------------------------------------------------*/
+static int reset(struct easycap *peasycap)
+{
+ struct easycap_standard const *peasycap_standard;
+ int fmtidx, input, rate;
+ bool ntsc, other;
+ int rc;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ input = peasycap->input;
+
+/*---------------------------------------------------------------------------*/
+/*
+ * IF THE SAA7113H HAS ALREADY ACQUIRED SYNC, USE ITS HARDWARE-DETECTED
+ * FIELD FREQUENCY TO DISTINGUISH NTSC FROM PAL. THIS IS ESSENTIAL FOR
+ * gstreamer AND OTHER USERSPACE PROGRAMS WHICH MAY NOT ATTEMPT TO INITIATE
+ * A SWITCH BETWEEN PAL AND NTSC.
+ *
+ * FUNCTION ready_saa() MAY REQUIRE A SUBSTANTIAL FRACTION OF A SECOND TO
+ * COMPLETE, SO SHOULD NOT BE INVOKED WITHOUT GOOD REASON.
+*/
+/*---------------------------------------------------------------------------*/
+ other = false;
+ JOM(8, "peasycap->ntsc=%d\n", peasycap->ntsc);
+
+ rate = ready_saa(peasycap->pusb_device);
+ if (rate < 0) {
+ JOM(8, "not ready to capture after %i ms ...\n", PATIENCE);
+ ntsc = !peasycap->ntsc;
+ JOM(8, "... trying %s ..\n", ntsc ? "NTSC" : "PAL");
+ rc = setup_stk(peasycap->pusb_device, ntsc);
+ if (rc) {
+ SAM("ERROR: setup_stk() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ rc = setup_saa(peasycap->pusb_device, ntsc);
+ if (rc) {
+ SAM("ERROR: setup_saa() rc = %i\n", rc);
+ return -EFAULT;
+ }
+
+ rate = ready_saa(peasycap->pusb_device);
+ if (rate < 0) {
+ JOM(8, "not ready to capture after %i ms\n", PATIENCE);
+ JOM(8, "... saa register 0x1F has 0x%02X\n",
+ read_saa(peasycap->pusb_device, 0x1F));
+ ntsc = peasycap->ntsc;
+ } else {
+ JOM(8, "... success at second try: %i=rate\n", rate);
+ ntsc = (0 < (rate/2)) ? true : false ;
+ other = true;
+ }
+ } else {
+ JOM(8, "... success at first try: %i=rate\n", rate);
+ ntsc = (0 < rate/2) ? true : false ;
+ }
+ JOM(8, "ntsc=%d\n", ntsc);
+/*---------------------------------------------------------------------------*/
+
+ rc = setup_stk(peasycap->pusb_device, ntsc);
+ if (rc) {
+ SAM("ERROR: setup_stk() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ rc = setup_saa(peasycap->pusb_device, ntsc);
+ if (rc) {
+ SAM("ERROR: setup_saa() rc = %i\n", rc);
+ return -EFAULT;
+ }
+
+ memset(peasycap->merit, 0, sizeof(peasycap->merit));
+
+ peasycap->video_eof = 0;
+ peasycap->audio_eof = 0;
+/*---------------------------------------------------------------------------*/
+/*
+ * RESTORE INPUT AND FORCE REFRESH OF STANDARD, FORMAT, ETC.
+ *
+ * WHILE THIS PROCEDURE IS IN PROGRESS, SOME IOCTL COMMANDS WILL RETURN -EBUSY.
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->input = -8192;
+ peasycap->standard_offset = -8192;
+ fmtidx = ntsc ? NTSC_M : PAL_BGHIN;
+ if (other) {
+ peasycap_standard = &easycap_standard[0];
+ while (0xFFFF != peasycap_standard->mask) {
+ if (fmtidx == peasycap_standard->v4l2_standard.index) {
+ peasycap->inputset[input].standard_offset =
+ peasycap_standard - easycap_standard;
+ break;
+ }
+ peasycap_standard++;
+ }
+ if (0xFFFF == peasycap_standard->mask) {
+ SAM("ERROR: standard not found\n");
+ return -EINVAL;
+ }
+ JOM(8, "%i=peasycap->inputset[%i].standard_offset\n",
+ peasycap->inputset[input].standard_offset, input);
+ }
+ peasycap->format_offset = -8192;
+ peasycap->brightness = -8192;
+ peasycap->contrast = -8192;
+ peasycap->saturation = -8192;
+ peasycap->hue = -8192;
+
+ rc = newinput(peasycap, input);
+
+ if (rc) {
+ SAM("ERROR: newinput(.,%i) rc = %i\n", rc, input);
+ return -EFAULT;
+ }
+ JOM(4, "restored input, standard and format\n");
+
+ JOM(8, "true=peasycap->ntsc %d\n", peasycap->ntsc);
+
+ if (0 > peasycap->input) {
+ SAM("MISTAKE: %i=peasycap->input\n", peasycap->input);
+ return -ENOENT;
+ }
+ if (0 > peasycap->standard_offset) {
+ SAM("MISTAKE: %i=peasycap->standard_offset\n",
+ peasycap->standard_offset);
+ return -ENOENT;
+ }
+ if (0 > peasycap->format_offset) {
+ SAM("MISTAKE: %i=peasycap->format_offset\n",
+ peasycap->format_offset);
+ return -ENOENT;
+ }
+ if (0 > peasycap->brightness) {
+ SAM("MISTAKE: %i=peasycap->brightness\n",
+ peasycap->brightness);
+ return -ENOENT;
+ }
+ if (0 > peasycap->contrast) {
+ SAM("MISTAKE: %i=peasycap->contrast\n", peasycap->contrast);
+ return -ENOENT;
+ }
+ if (0 > peasycap->saturation) {
+ SAM("MISTAKE: %i=peasycap->saturation\n",
+ peasycap->saturation);
+ return -ENOENT;
+ }
+ if (0 > peasycap->hue) {
+ SAM("MISTAKE: %i=peasycap->hue\n", peasycap->hue);
+ return -ENOENT;
+ }
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * IF THE REQUESTED INPUT IS THE SAME AS THE EXISTING INPUT, DO NOTHING.
+ * OTHERWISE:
+ * KILL URBS, CLEAR FIELD AND FRAME BUFFERS AND RESET THEIR
+ * _read AND _fill POINTERS.
+ * SELECT THE NEW INPUT.
+ * ADJUST THE STANDARD, FORMAT, BRIGHTNESS, CONTRAST, SATURATION AND HUE
+ * ON THE BASIS OF INFORMATION IN STRUCTURE easycap.inputset[input].
+ * RESUBMIT THE URBS IF STREAMING WAS ALREADY IN PROGRESS.
+ *
+ * NOTE:
+ * THIS ROUTINE MAY BE CALLED FREQUENTLY BY ZONEMINDER VIA IOCTL,
+ * SO IT SHOULD WRITE ONLY SPARINGLY TO THE LOGFILE.
+*/
+/*---------------------------------------------------------------------------*/
+int
+newinput(struct easycap *peasycap, int input)
+{
+ int rc, k, m, mood, off;
+ int inputnow, video_idlenow, audio_idlenow;
+ bool resubmit;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ JOM(8, "%i=input sought\n", input);
+
+ if (0 > input && INPUT_MANY <= input)
+ return -ENOENT;
+ inputnow = peasycap->input;
+ if (input == inputnow)
+ return 0;
+/*---------------------------------------------------------------------------*/
+/*
+ * IF STREAMING IS IN PROGRESS THE URBS ARE KILLED AT THIS
+ * STAGE AND WILL BE RESUBMITTED PRIOR TO EXIT FROM THE ROUTINE.
+ * IF NO STREAMING IS IN PROGRESS NO URBS WILL BE SUBMITTED BY THE
+ * ROUTINE.
+*/
+/*---------------------------------------------------------------------------*/
+ video_idlenow = peasycap->video_idle;
+ audio_idlenow = peasycap->audio_idle;
+
+ peasycap->video_idle = 1;
+ peasycap->audio_idle = 1;
+ if (peasycap->video_isoc_streaming) {
+ resubmit = true;
+ kill_video_urbs(peasycap);
+ } else {
+ resubmit = false;
+ }
+/*---------------------------------------------------------------------------*/
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -ENODEV;
+ }
+ rc = usb_set_interface(peasycap->pusb_device,
+ peasycap->video_interface,
+ peasycap->video_altsetting_off);
+ if (rc) {
+ SAM("ERROR: usb_set_interface() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ rc = stop_100(peasycap->pusb_device);
+ if (rc) {
+ SAM("ERROR: stop_100() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ for (k = 0; k < FIELD_BUFFER_MANY; k++) {
+ for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++)
+ memset(peasycap->field_buffer[k][m].pgo, 0, PAGE_SIZE);
+ }
+ for (k = 0; k < FRAME_BUFFER_MANY; k++) {
+ for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++)
+ memset(peasycap->frame_buffer[k][m].pgo, 0, PAGE_SIZE);
+ }
+ peasycap->field_page = 0;
+ peasycap->field_read = 0;
+ peasycap->field_fill = 0;
+
+ peasycap->frame_read = 0;
+ peasycap->frame_fill = 0;
+ for (k = 0; k < peasycap->input; k++) {
+ (peasycap->frame_fill)++;
+ if (peasycap->frame_buffer_many <= peasycap->frame_fill)
+ peasycap->frame_fill = 0;
+ }
+ peasycap->input = input;
+ select_input(peasycap->pusb_device, peasycap->input, 9);
+/*---------------------------------------------------------------------------*/
+ if (input == peasycap->inputset[input].input) {
+ off = peasycap->inputset[input].standard_offset;
+ if (off != peasycap->standard_offset) {
+ rc = adjust_standard(peasycap,
+ easycap_standard[off].v4l2_standard.id);
+ if (rc) {
+ SAM("ERROR: adjust_standard() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->standard_offset\n",
+ peasycap->standard_offset);
+ } else {
+ JOM(8, "%i=peasycap->standard_offset unchanged\n",
+ peasycap->standard_offset);
+ }
+ off = peasycap->inputset[input].format_offset;
+ if (off != peasycap->format_offset) {
+ struct v4l2_pix_format *pix =
+ &easycap_format[off].v4l2_format.fmt.pix;
+ rc = adjust_format(peasycap,
+ pix->width, pix->height,
+ pix->pixelformat, pix->field, false);
+ if (0 > rc) {
+ SAM("ERROR: adjust_format() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->format_offset\n",
+ peasycap->format_offset);
+ } else {
+ JOM(8, "%i=peasycap->format_offset unchanged\n",
+ peasycap->format_offset);
+ }
+ mood = peasycap->inputset[input].brightness;
+ if (mood != peasycap->brightness) {
+ rc = adjust_brightness(peasycap, mood);
+ if (rc) {
+ SAM("ERROR: adjust_brightness rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->brightness\n",
+ peasycap->brightness);
+ }
+ mood = peasycap->inputset[input].contrast;
+ if (mood != peasycap->contrast) {
+ rc = adjust_contrast(peasycap, mood);
+ if (rc) {
+ SAM("ERROR: adjust_contrast rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->contrast\n", peasycap->contrast);
+ }
+ mood = peasycap->inputset[input].saturation;
+ if (mood != peasycap->saturation) {
+ rc = adjust_saturation(peasycap, mood);
+ if (rc) {
+ SAM("ERROR: adjust_saturation rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->saturation\n",
+ peasycap->saturation);
+ }
+ mood = peasycap->inputset[input].hue;
+ if (mood != peasycap->hue) {
+ rc = adjust_hue(peasycap, mood);
+ if (rc) {
+ SAM("ERROR: adjust_hue rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->hue\n", peasycap->hue);
+ }
+ } else {
+ SAM("MISTAKE: easycap.inputset[%i] unpopulated\n", input);
+ return -ENOENT;
+ }
+/*---------------------------------------------------------------------------*/
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -ENODEV;
+ }
+ rc = usb_set_interface(peasycap->pusb_device,
+ peasycap->video_interface,
+ peasycap->video_altsetting_on);
+ if (rc) {
+ SAM("ERROR: usb_set_interface() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ rc = start_100(peasycap->pusb_device);
+ if (rc) {
+ SAM("ERROR: start_100() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ if (resubmit)
+ submit_video_urbs(peasycap);
+
+ peasycap->video_isoc_sequence = VIDEO_ISOC_BUFFER_MANY - 1;
+ peasycap->video_idle = video_idlenow;
+ peasycap->audio_idle = audio_idlenow;
+ peasycap->video_junk = 0;
+
+ return 0;
+}
+/*****************************************************************************/
+int submit_video_urbs(struct easycap *peasycap)
+{
+ struct data_urb *pdata_urb;
+ struct urb *purb;
+ struct list_head *plist_head;
+ int j, isbad, nospc, m, rc;
+ int isbuf;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+
+ if (!peasycap->purb_video_head) {
+ SAY("ERROR: peasycap->urb_video_head uninitialized\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAY("ERROR: peasycap->pusb_device is NULL\n");
+ return -ENODEV;
+ }
+ if (!peasycap->video_isoc_streaming) {
+ JOM(4, "submission of all video urbs\n");
+ isbad = 0; nospc = 0; m = 0;
+ list_for_each(plist_head, (peasycap->purb_video_head)) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb && pdata_urb->purb) {
+ purb = pdata_urb->purb;
+ isbuf = pdata_urb->isbuf;
+ purb->interval = 1;
+ purb->dev = peasycap->pusb_device;
+ purb->pipe =
+ usb_rcvisocpipe(peasycap->pusb_device,
+ peasycap->video_endpointnumber);
+ purb->transfer_flags = URB_ISO_ASAP;
+ purb->transfer_buffer =
+ peasycap->video_isoc_buffer[isbuf].pgo;
+ purb->transfer_buffer_length =
+ peasycap->video_isoc_buffer_size;
+ purb->complete = easycap_complete;
+ purb->context = peasycap;
+ purb->start_frame = 0;
+ purb->number_of_packets =
+ peasycap->video_isoc_framesperdesc;
+
+ for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) {
+ purb->iso_frame_desc[j]. offset =
+ j * peasycap->video_isoc_maxframesize;
+ purb->iso_frame_desc[j]. length =
+ peasycap->video_isoc_maxframesize;
+ }
+
+ rc = usb_submit_urb(purb, GFP_KERNEL);
+ if (rc) {
+ isbad++;
+ SAM("ERROR: usb_submit_urb() failed "
+ "for urb with rc:-%s\n",
+ strerror(rc));
+ if (rc == -ENOSPC)
+ nospc++;
+ } else {
+ m++;
+ }
+ } else {
+ isbad++;
+ }
+ }
+ if (nospc) {
+ SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc);
+ SAM("..... possibly inadequate USB bandwidth\n");
+ peasycap->video_eof = 1;
+ }
+
+ if (isbad) {
+ JOM(4, "attempting cleanup instead of submitting\n");
+ list_for_each(plist_head, (peasycap->purb_video_head)) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb) {
+ purb = pdata_urb->purb;
+ if (purb)
+ usb_kill_urb(purb);
+ }
+ }
+ peasycap->video_isoc_streaming = 0;
+ } else {
+ peasycap->video_isoc_streaming = 1;
+ JOM(4, "submitted %i video urbs\n", m);
+ }
+ } else {
+ JOM(4, "already streaming video urbs\n");
+ }
+ return 0;
+}
+/*****************************************************************************/
+int kill_video_urbs(struct easycap *peasycap)
+{
+ int m;
+ struct list_head *plist_head;
+ struct data_urb *pdata_urb;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->video_isoc_streaming) {
+ JOM(8, "%i=video_isoc_streaming, no video urbs killed\n",
+ peasycap->video_isoc_streaming);
+ return 0;
+ }
+ if (!peasycap->purb_video_head) {
+ SAM("ERROR: peasycap->purb_video_head is NULL\n");
+ return -EFAULT;
+ }
+
+ peasycap->video_isoc_streaming = 0;
+ JOM(4, "killing video urbs\n");
+ m = 0;
+ list_for_each(plist_head, (peasycap->purb_video_head)) {
+ pdata_urb = list_entry(plist_head, struct data_urb, list_head);
+ if (pdata_urb && pdata_urb->purb) {
+ usb_kill_urb(pdata_urb->purb);
+ m++;
+ }
+ }
+ JOM(4, "%i video urbs killed\n", m);
+
+ return 0;
+}
+/****************************************************************************/
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+/*--------------------------------------------------------------------------*/
+static int easycap_open_noinode(struct file *file)
+{
+ return easycap_open(NULL, file);
+}
+
+static int videodev_release(struct video_device *pvideo_device)
+{
+ struct easycap *peasycap;
+
+ peasycap = video_get_drvdata(pvideo_device);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ SAY("ending unsuccessfully\n");
+ return -EFAULT;
+ }
+ if (0 != kill_video_urbs(peasycap)) {
+ SAM("ERROR: kill_video_urbs() failed\n");
+ return -EFAULT;
+ }
+ JOM(4, "ending successfully\n");
+ return 0;
+}
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+/*****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect() AND IS
+ * PROTECTED BY SEMAPHORES SET AND CLEARED BY easycap_usb_disconnect().
+ *
+ * BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED, SO
+ * peasycap->pusb_device IS NO LONGER VALID.
+ */
+/*---------------------------------------------------------------------------*/
+static void easycap_delete(struct kref *pkref)
+{
+ struct easycap *peasycap;
+ struct data_urb *pdata_urb;
+ struct list_head *plist_head, *plist_next;
+ int k, m, gone, kd;
+ int allocation_video_urb;
+ int allocation_video_page;
+ int allocation_video_struct;
+ int allocation_audio_urb;
+ int allocation_audio_page;
+ int allocation_audio_struct;
+ int registered_video, registered_audio;
+
+ peasycap = container_of(pkref, struct easycap, kref);
+ if (!peasycap) {
+ SAM("ERROR: peasycap is NULL: cannot perform deletions\n");
+ return;
+ }
+ kd = isdongle(peasycap);
+/*---------------------------------------------------------------------------*/
+/*
+ * FREE VIDEO.
+ */
+/*---------------------------------------------------------------------------*/
+ if (peasycap->purb_video_head) {
+ JOM(4, "freeing video urbs\n");
+ m = 0;
+ list_for_each(plist_head, (peasycap->purb_video_head)) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (!pdata_urb) {
+ JOM(4, "ERROR: pdata_urb is NULL\n");
+ } else {
+ if (pdata_urb->purb) {
+ usb_free_urb(pdata_urb->purb);
+ pdata_urb->purb = NULL;
+ peasycap->allocation_video_urb -= 1;
+ m++;
+ }
+ }
+ }
+
+ JOM(4, "%i video urbs freed\n", m);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing video data_urb structures.\n");
+ m = 0;
+ list_for_each_safe(plist_head, plist_next,
+ peasycap->purb_video_head) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb) {
+ peasycap->allocation_video_struct -=
+ sizeof(struct data_urb);
+ kfree(pdata_urb);
+ pdata_urb = NULL;
+ m++;
+ }
+ }
+ JOM(4, "%i video data_urb structures freed\n", m);
+ JOM(4, "setting peasycap->purb_video_head=NULL\n");
+ peasycap->purb_video_head = NULL;
+ }
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing video isoc buffers.\n");
+ m = 0;
+ for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
+ if (peasycap->video_isoc_buffer[k].pgo) {
+ free_pages((unsigned long)
+ peasycap->video_isoc_buffer[k].pgo,
+ VIDEO_ISOC_ORDER);
+ peasycap->video_isoc_buffer[k].pgo = NULL;
+ peasycap->allocation_video_page -=
+ BIT(VIDEO_ISOC_ORDER);
+ m++;
+ }
+ }
+ JOM(4, "isoc video buffers freed: %i pages\n",
+ m * (0x01 << VIDEO_ISOC_ORDER));
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing video field buffers.\n");
+ gone = 0;
+ for (k = 0; k < FIELD_BUFFER_MANY; k++) {
+ for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) {
+ if (peasycap->field_buffer[k][m].pgo) {
+ free_page((unsigned long)
+ peasycap->field_buffer[k][m].pgo);
+ peasycap->field_buffer[k][m].pgo = NULL;
+ peasycap->allocation_video_page -= 1;
+ gone++;
+ }
+ }
+ }
+ JOM(4, "video field buffers freed: %i pages\n", gone);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing video frame buffers.\n");
+ gone = 0;
+ for (k = 0; k < FRAME_BUFFER_MANY; k++) {
+ for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) {
+ if (peasycap->frame_buffer[k][m].pgo) {
+ free_page((unsigned long)
+ peasycap->frame_buffer[k][m].pgo);
+ peasycap->frame_buffer[k][m].pgo = NULL;
+ peasycap->allocation_video_page -= 1;
+ gone++;
+ }
+ }
+ }
+ JOM(4, "video frame buffers freed: %i pages\n", gone);
+/*---------------------------------------------------------------------------*/
+/*
+ * FREE AUDIO.
+ */
+/*---------------------------------------------------------------------------*/
+ if (peasycap->purb_audio_head) {
+ JOM(4, "freeing audio urbs\n");
+ m = 0;
+ list_for_each(plist_head, (peasycap->purb_audio_head)) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (!pdata_urb)
+ JOM(4, "ERROR: pdata_urb is NULL\n");
+ else {
+ if (pdata_urb->purb) {
+ usb_free_urb(pdata_urb->purb);
+ pdata_urb->purb = NULL;
+ peasycap->allocation_audio_urb -= 1;
+ m++;
+ }
+ }
+ }
+ JOM(4, "%i audio urbs freed\n", m);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing audio data_urb structures.\n");
+ m = 0;
+ list_for_each_safe(plist_head, plist_next,
+ peasycap->purb_audio_head) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb) {
+ peasycap->allocation_audio_struct -=
+ sizeof(struct data_urb);
+ kfree(pdata_urb);
+ pdata_urb = NULL;
+ m++;
+ }
+ }
+ JOM(4, "%i audio data_urb structures freed\n", m);
+ JOM(4, "setting peasycap->purb_audio_head=NULL\n");
+ peasycap->purb_audio_head = NULL;
+ }
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing audio isoc buffers.\n");
+ m = 0;
+ for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
+ if (peasycap->audio_isoc_buffer[k].pgo) {
+ free_pages((unsigned long)
+ (peasycap->audio_isoc_buffer[k].pgo),
+ AUDIO_ISOC_ORDER);
+ peasycap->audio_isoc_buffer[k].pgo = NULL;
+ peasycap->allocation_audio_page -=
+ BIT(AUDIO_ISOC_ORDER);
+ m++;
+ }
+ }
+ JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n",
+ m * (0x01 << AUDIO_ISOC_ORDER));
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing easycap structure.\n");
+ allocation_video_urb = peasycap->allocation_video_urb;
+ allocation_video_page = peasycap->allocation_video_page;
+ allocation_video_struct = peasycap->allocation_video_struct;
+ registered_video = peasycap->registered_video;
+ allocation_audio_urb = peasycap->allocation_audio_urb;
+ allocation_audio_page = peasycap->allocation_audio_page;
+ allocation_audio_struct = peasycap->allocation_audio_struct;
+ registered_audio = peasycap->registered_audio;
+
+ if (0 <= kd && DONGLE_MANY > kd) {
+ if (mutex_lock_interruptible(&mutex_dongle)) {
+ SAY("ERROR: cannot down mutex_dongle\n");
+ } else {
+ JOM(4, "locked mutex_dongle\n");
+ easycapdc60_dongle[kd].peasycap = NULL;
+ mutex_unlock(&mutex_dongle);
+ JOM(4, "unlocked mutex_dongle\n");
+ JOT(4, " null-->dongle[%i].peasycap\n", kd);
+ allocation_video_struct -= sizeof(struct easycap);
+ }
+ } else {
+ SAY("ERROR: cannot purge dongle[].peasycap");
+ }
+
+ kfree(peasycap);
+
+/*---------------------------------------------------------------------------*/
+ SAY("%8i=video urbs after all deletions\n", allocation_video_urb);
+ SAY("%8i=video pages after all deletions\n", allocation_video_page);
+ SAY("%8i=video structs after all deletions\n", allocation_video_struct);
+ SAY("%8i=video devices after all deletions\n", registered_video);
+ SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb);
+ SAY("%8i=audio pages after all deletions\n", allocation_audio_page);
+ SAY("%8i=audio structs after all deletions\n", allocation_audio_struct);
+ SAY("%8i=audio devices after all deletions\n", registered_audio);
+
+ JOT(4, "ending.\n");
+ return;
+}
+/*****************************************************************************/
+static unsigned int easycap_poll(struct file *file, poll_table *wait)
+{
+ struct easycap *peasycap;
+ int rc, kd;
+
+ JOT(8, "\n");
+
+ if (NULL == ((poll_table *)wait))
+ JOT(8, "WARNING: poll table pointer is NULL ... continuing\n");
+ if (!file) {
+ SAY("ERROR: file pointer is NULL\n");
+ return -ERESTARTSYS;
+ }
+ peasycap = file->private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAY("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+ kd = isdongle(peasycap);
+ if (0 <= kd && DONGLE_MANY > kd) {
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
+ SAY("ERROR: cannot down dongle[%i].mutex_video\n", kd);
+ return -ERESTARTSYS;
+ }
+ JOM(4, "locked dongle[%i].mutex_video\n", kd);
+ /*
+ * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER
+ * peasycap, IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
+ * IF NECESSARY, BAIL OUT.
+ */
+ if (kd != isdongle(peasycap)) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ if (!file) {
+ SAY("ERROR: file is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ peasycap = file->private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ } else
+ /*
+ * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap
+ * BEFORE THE ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL
+ * HAVE FAILED. BAIL OUT.
+ */
+ return -ERESTARTSYS;
+/*---------------------------------------------------------------------------*/
+ rc = easycap_dqbuf(peasycap, 0);
+ peasycap->polled = 1;
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ if (0 == rc)
+ return POLLIN | POLLRDNORM;
+ else
+ return POLLERR;
+ }
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * IF mode IS NONZERO THIS ROUTINE RETURNS -EAGAIN RATHER THAN BLOCKING.
+ */
+/*---------------------------------------------------------------------------*/
+int easycap_dqbuf(struct easycap *peasycap, int mode)
+{
+ int input, ifield, miss, rc;
+
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAY("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ ifield = 0;
+ JOM(8, "%i=ifield\n", ifield);
+/*---------------------------------------------------------------------------*/
+/*
+ * CHECK FOR LOST INPUT SIGNAL.
+ *
+ * FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED.
+ * IF INPUT 0 IS PRESENT AND SYNC ACQUIRED, UNPLUGGING INPUT 4 DOES NOT
+ * RESULT IN SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE
+ * IS FLYWHEELING ON INPUT 0. THE UPSHOT IS:
+ *
+ * INPUT 0 PLUGGED, INPUT 4 PLUGGED => SCREEN 0 OK, SCREEN 4 OK
+ * INPUT 0 PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK, SCREEN 4 BLACK
+ * INPUT 0 UNPLUGGED, INPUT 4 PLUGGED => SCREEN 0 BARS, SCREEN 4 OK
+ * INPUT 0 UNPLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 BARS, SCREEN 4 BARS
+*/
+/*---------------------------------------------------------------------------*/
+ input = peasycap->input;
+ if (0 <= input && INPUT_MANY > input) {
+ rc = read_saa(peasycap->pusb_device, 0x1F);
+ if (0 <= rc) {
+ if (rc & 0x40)
+ peasycap->lost[input] += 1;
+ else
+ peasycap->lost[input] -= 2;
+
+ if (0 > peasycap->lost[input])
+ peasycap->lost[input] = 0;
+ else if ((2 * VIDEO_LOST_TOLERATE) < peasycap->lost[input])
+ peasycap->lost[input] = (2 * VIDEO_LOST_TOLERATE);
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * WAIT FOR FIELD ifield (0 => TOP, 1 => BOTTOM)
+ */
+/*---------------------------------------------------------------------------*/
+ miss = 0;
+ while ((peasycap->field_read == peasycap->field_fill) ||
+ (0 != (0xFF00 & peasycap->field_buffer
+ [peasycap->field_read][0].kount)) ||
+ (ifield != (0x00FF & peasycap->field_buffer
+ [peasycap->field_read][0].kount))) {
+ if (mode)
+ return -EAGAIN;
+
+ JOM(8, "first wait on wq_video, %i=field_read %i=field_fill\n",
+ peasycap->field_read, peasycap->field_fill);
+
+ if (0 != (wait_event_interruptible(peasycap->wq_video,
+ (peasycap->video_idle || peasycap->video_eof ||
+ ((peasycap->field_read != peasycap->field_fill) &&
+ (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) &&
+ (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) {
+ SAM("aborted by signal\n");
+ return -EIO;
+ }
+ if (peasycap->video_idle) {
+ JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n",
+ peasycap->video_idle);
+ return -EAGAIN;
+ }
+ if (peasycap->video_eof) {
+ JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof);
+ #if defined(PERSEVERE)
+ if (1 == peasycap->status) {
+ JOM(8, "persevering ...\n");
+ peasycap->video_eof = 0;
+ peasycap->audio_eof = 0;
+ if (0 != reset(peasycap)) {
+ JOM(8, " ... failed returning -EIO\n");
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ kill_video_urbs(peasycap);
+ return -EIO;
+ }
+ peasycap->status = 0;
+ JOM(8, " ... OK returning -EAGAIN\n");
+ return -EAGAIN;
+ }
+ #endif /*PERSEVERE*/
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ kill_video_urbs(peasycap);
+ JOM(8, "returning -EIO\n");
+ return -EIO;
+ }
+ miss++;
+ }
+ JOM(8, "first awakening on wq_video after %i waits\n", miss);
+
+ rc = field2frame(peasycap);
+ if (rc)
+ SAM("ERROR: field2frame() rc = %i\n", rc);
+/*---------------------------------------------------------------------------*/
+/*
+ * WAIT FOR THE OTHER FIELD
+ */
+/*---------------------------------------------------------------------------*/
+ if (ifield)
+ ifield = 0;
+ else
+ ifield = 1;
+ miss = 0;
+ while ((peasycap->field_read == peasycap->field_fill) ||
+ (0 != (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) ||
+ (ifield != (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))) {
+ if (mode)
+ return -EAGAIN;
+
+ JOM(8, "second wait on wq_video %i=field_read %i=field_fill\n",
+ peasycap->field_read, peasycap->field_fill);
+ if (0 != (wait_event_interruptible(peasycap->wq_video,
+ (peasycap->video_idle || peasycap->video_eof ||
+ ((peasycap->field_read != peasycap->field_fill) &&
+ (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) &&
+ (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) {
+ SAM("aborted by signal\n");
+ return -EIO;
+ }
+ if (peasycap->video_idle) {
+ JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n",
+ peasycap->video_idle);
+ return -EAGAIN;
+ }
+ if (peasycap->video_eof) {
+ JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof);
+#if defined(PERSEVERE)
+ if (1 == peasycap->status) {
+ JOM(8, "persevering ...\n");
+ peasycap->video_eof = 0;
+ peasycap->audio_eof = 0;
+ if (0 != reset(peasycap)) {
+ JOM(8, " ... failed returning -EIO\n");
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ kill_video_urbs(peasycap);
+ return -EIO;
+ }
+ peasycap->status = 0;
+ JOM(8, " ... OK ... returning -EAGAIN\n");
+ return -EAGAIN;
+ }
+#endif /*PERSEVERE*/
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ kill_video_urbs(peasycap);
+ JOM(8, "returning -EIO\n");
+ return -EIO;
+ }
+ miss++;
+ }
+ JOM(8, "second awakening on wq_video after %i waits\n", miss);
+
+ rc = field2frame(peasycap);
+ if (rc)
+ SAM("ERROR: field2frame() rc = %i\n", rc);
+/*---------------------------------------------------------------------------*/
+/*
+ * WASTE THIS FRAME
+*/
+/*---------------------------------------------------------------------------*/
+ if (peasycap->skip) {
+ peasycap->skipped++;
+ if (peasycap->skip != peasycap->skipped)
+ return peasycap->skip - peasycap->skipped;
+ else
+ peasycap->skipped = 0;
+ }
+/*---------------------------------------------------------------------------*/
+ peasycap->frame_read = peasycap->frame_fill;
+ peasycap->queued[peasycap->frame_read] = 0;
+ peasycap->done[peasycap->frame_read] = V4L2_BUF_FLAG_DONE;
+
+ peasycap->frame_fill++;
+ if (peasycap->frame_buffer_many <= peasycap->frame_fill)
+ peasycap->frame_fill = 0;
+
+ if (0x01 & easycap_standard[peasycap->standard_offset].mask)
+ peasycap->frame_buffer[peasycap->frame_read][0].kount =
+ V4L2_FIELD_TOP;
+ else
+ peasycap->frame_buffer[peasycap->frame_read][0].kount =
+ V4L2_FIELD_BOTTOM;
+
+
+ JOM(8, "setting: %i=peasycap->frame_read\n", peasycap->frame_read);
+ JOM(8, "bumped to: %i=peasycap->frame_fill\n", peasycap->frame_fill);
+
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * BY DEFINITION, odd IS true FOR THE FIELD OCCUPYING LINES 1,3,5,...,479
+ * odd IS false FOR THE FIELD OCCUPYING LINES 0,2,4,...,478
+ *
+ * WHEN BOOLEAN PARAMETER decimatepixel IS true, ONLY THE FIELD FOR WHICH
+ * odd==false IS TRANSFERRED TO THE FRAME BUFFER.
+ *
+ * THE BOOLEAN PARAMETER offerfields IS true ONLY WHEN THE USER PROGRAM
+ * CHOOSES THE OPTION V4L2_FIELD_INTERLACED.
+ */
+/*---------------------------------------------------------------------------*/
+int
+field2frame(struct easycap *peasycap)
+{
+
+ void *pex, *pad;
+ int kex, kad, mex, mad, rex, rad, rad2;
+ int c2, c3, w2, w3, cz, wz;
+ int rc, bytesperpixel, multiplier;
+ int much, more, over, rump, caches, input;
+ u8 mask, margin;
+ bool odd, isuy, decimatepixel, offerfields, badinput;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+
+ badinput = false;
+ input = 0x07 & peasycap->field_buffer[peasycap->field_read][0].input;
+
+ JOM(8, "===== parity %i, input 0x%02X, field buffer %i --> "
+ "frame buffer %i\n",
+ peasycap->field_buffer[peasycap->field_read][0].kount,
+ peasycap->field_buffer[peasycap->field_read][0].input,
+ peasycap->field_read, peasycap->frame_fill);
+ JOM(8, "===== %i=bytesperpixel\n", peasycap->bytesperpixel);
+ if (peasycap->offerfields)
+ JOM(8, "===== offerfields\n");
+
+/*---------------------------------------------------------------------------*/
+/*
+ * REJECT OR CLEAN BAD FIELDS
+ */
+/*---------------------------------------------------------------------------*/
+ if (peasycap->field_read == peasycap->field_fill) {
+ SAM("ERROR: on entry, still filling field buffer %i\n",
+ peasycap->field_read);
+ return 0;
+ }
+#ifdef EASYCAP_TESTCARD
+ easycap_testcard(peasycap, peasycap->field_read);
+#else
+ if (0 <= input && INPUT_MANY > input) {
+ if (easycap_bars && VIDEO_LOST_TOLERATE <= peasycap->lost[input])
+ easycap_testcard(peasycap, peasycap->field_read);
+ }
+#endif /*EASYCAP_TESTCARD*/
+/*---------------------------------------------------------------------------*/
+
+ offerfields = peasycap->offerfields;
+ bytesperpixel = peasycap->bytesperpixel;
+ decimatepixel = peasycap->decimatepixel;
+
+ if ((2 != bytesperpixel) &&
+ (3 != bytesperpixel) &&
+ (4 != bytesperpixel)) {
+ SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel);
+ return -EFAULT;
+ }
+ if (decimatepixel)
+ multiplier = 2;
+ else
+ multiplier = 1;
+
+ w2 = 2 * multiplier * (peasycap->width);
+ w3 = bytesperpixel * multiplier * (peasycap->width);
+ wz = multiplier * (peasycap->height) *
+ multiplier * (peasycap->width);
+
+ kex = peasycap->field_read; mex = 0;
+ kad = peasycap->frame_fill; mad = 0;
+
+ pex = peasycap->field_buffer[kex][0].pgo; rex = PAGE_SIZE;
+ pad = peasycap->frame_buffer[kad][0].pgo; rad = PAGE_SIZE;
+ odd = !!(peasycap->field_buffer[kex][0].kount);
+
+ if (odd && (!decimatepixel)) {
+ JOM(8, "initial skipping %4i bytes p.%4i\n",
+ w3/multiplier, mad);
+ pad += (w3 / multiplier); rad -= (w3 / multiplier);
+ }
+ isuy = true;
+ mask = 0; rump = 0; caches = 0;
+
+ cz = 0;
+ while (cz < wz) {
+ /*
+ * PROCESS ONE LINE OF FRAME AT FULL RESOLUTION:
+ * READ w2 BYTES FROM FIELD BUFFER,
+ * WRITE w3 BYTES TO FRAME BUFFER
+ */
+ if (!decimatepixel) {
+ over = w2;
+ do {
+ much = over; more = 0;
+ margin = 0; mask = 0x00;
+ if (rex < much)
+ much = rex;
+ rump = 0;
+
+ if (much % 2) {
+ SAM("MISTAKE: much is odd\n");
+ return -EFAULT;
+ }
+
+ more = (bytesperpixel *
+ much) / 2;
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ if (1 < bytesperpixel) {
+ if (rad * 2 < much * bytesperpixel) {
+ /*
+ * INJUDICIOUS ALTERATION OF
+ * THIS STATEMENT BLOCK WILL
+ * CAUSE BREAKAGE. BEWARE.
+ */
+ rad2 = rad + bytesperpixel - 1;
+ much = ((((2 * rad2)/bytesperpixel)/2) * 2);
+ rump = ((bytesperpixel * much) / 2) - rad;
+ more = rad;
+ }
+ mask = (u8)rump;
+ margin = 0;
+ if (much == rex) {
+ mask |= 0x04;
+ if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE)
+ margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo));
+ else
+ mask |= 0x08;
+ }
+ } else {
+ SAM("MISTAKE: %i=bytesperpixel\n",
+ bytesperpixel);
+ return -EFAULT;
+ }
+ if (rump)
+ caches++;
+ if (badinput) {
+ JOM(8, "ERROR: 0x%02X=->field_buffer"
+ "[%i][%i].input, "
+ "0x%02X=(0x08|->input)\n",
+ peasycap->field_buffer
+ [kex][mex].input, kex, mex,
+ (0x08|peasycap->input));
+ }
+ rc = redaub(peasycap, pad, pex, much, more,
+ mask, margin, isuy);
+ if (0 > rc) {
+ SAM("ERROR: redaub() failed\n");
+ return -EFAULT;
+ }
+ if (much % 4)
+ isuy = !isuy;
+
+ over -= much; cz += much;
+ pex += much; rex -= much;
+ if (!rex) {
+ mex++;
+ pex = peasycap->field_buffer[kex][mex].pgo;
+ rex = PAGE_SIZE;
+ if (peasycap->field_buffer[kex][mex].input != (0x08|peasycap->input))
+ badinput = true;
+ }
+ pad += more;
+ rad -= more;
+ if (!rad) {
+ mad++;
+ pad = peasycap->frame_buffer[kad][mad].pgo;
+ rad = PAGE_SIZE;
+ if (rump) {
+ pad += rump;
+ rad -= rump;
+ }
+ }
+ } while (over);
+/*---------------------------------------------------------------------------*/
+/*
+ * SKIP w3 BYTES IN TARGET FRAME BUFFER,
+ * UNLESS IT IS THE LAST LINE OF AN ODD FRAME
+ */
+/*---------------------------------------------------------------------------*/
+ if (!odd || (cz != wz)) {
+ over = w3;
+ do {
+ if (!rad) {
+ mad++;
+ pad = peasycap->frame_buffer
+ [kad][mad].pgo;
+ rad = PAGE_SIZE;
+ }
+ more = over;
+ if (rad < more)
+ more = rad;
+ over -= more;
+ pad += more;
+ rad -= more;
+ } while (over);
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * PROCESS ONE LINE OF FRAME AT REDUCED RESOLUTION:
+ * ONLY IF false==odd,
+ * READ w2 BYTES FROM FIELD BUFFER,
+ * WRITE w3 / 2 BYTES TO FRAME BUFFER
+ */
+/*---------------------------------------------------------------------------*/
+ } else if (!odd) {
+ over = w2;
+ do {
+ much = over; more = 0; margin = 0; mask = 0x00;
+ if (rex < much)
+ much = rex;
+ rump = 0;
+
+ if (much % 2) {
+ SAM("MISTAKE: much is odd\n");
+ return -EFAULT;
+ }
+
+ more = (bytesperpixel * much) / 4;
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ if (1 < bytesperpixel) {
+ if (rad * 4 < much * bytesperpixel) {
+ /*
+ * INJUDICIOUS ALTERATION OF
+ * THIS STATEMENT BLOCK
+ * WILL CAUSE BREAKAGE.
+ * BEWARE.
+ */
+ rad2 = rad + bytesperpixel - 1;
+ much = ((((2 * rad2) / bytesperpixel) / 2) * 4);
+ rump = ((bytesperpixel * much) / 4) - rad;
+ more = rad;
+ }
+ mask = (u8)rump;
+ margin = 0;
+ if (much == rex) {
+ mask |= 0x04;
+ if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE)
+ margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo));
+ else
+ mask |= 0x08;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ } else {
+ SAM("MISTAKE: %i=bytesperpixel\n",
+ bytesperpixel);
+ return -EFAULT;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ if (rump)
+ caches++;
+
+ if (badinput) {
+ JOM(8, "ERROR: 0x%02X=->field_buffer"
+ "[%i][%i].input, "
+ "0x%02X=(0x08|->input)\n",
+ peasycap->field_buffer
+ [kex][mex].input, kex, mex,
+ (0x08|peasycap->input));
+ }
+ rc = redaub(peasycap, pad, pex, much, more,
+ mask, margin, isuy);
+ if (0 > rc) {
+ SAM("ERROR: redaub() failed\n");
+ return -EFAULT;
+ }
+ over -= much; cz += much;
+ pex += much; rex -= much;
+ if (!rex) {
+ mex++;
+ pex = peasycap->field_buffer[kex][mex].pgo;
+ rex = PAGE_SIZE;
+ if (peasycap->field_buffer[kex][mex].input !=
+ (0x08|peasycap->input))
+ badinput = true;
+ }
+ pad += more;
+ rad -= more;
+ if (!rad) {
+ mad++;
+ pad = peasycap->frame_buffer[kad][mad].pgo;
+ rad = PAGE_SIZE;
+ if (rump) {
+ pad += rump;
+ rad -= rump;
+ }
+ }
+ } while (over);
+/*---------------------------------------------------------------------------*/
+/*
+ * OTHERWISE JUST
+ * READ w2 BYTES FROM FIELD BUFFER AND DISCARD THEM
+ */
+/*---------------------------------------------------------------------------*/
+ } else {
+ over = w2;
+ do {
+ if (!rex) {
+ mex++;
+ pex = peasycap->field_buffer[kex][mex].pgo;
+ rex = PAGE_SIZE;
+ if (peasycap->field_buffer[kex][mex].input !=
+ (0x08|peasycap->input)) {
+ JOM(8, "ERROR: 0x%02X=->field_buffer"
+ "[%i][%i].input, "
+ "0x%02X=(0x08|->input)\n",
+ peasycap->field_buffer
+ [kex][mex].input, kex, mex,
+ (0x08|peasycap->input));
+ badinput = true;
+ }
+ }
+ much = over;
+ if (rex < much)
+ much = rex;
+ over -= much;
+ cz += much;
+ pex += much;
+ rex -= much;
+ } while (over);
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * SANITY CHECKS
+ */
+/*---------------------------------------------------------------------------*/
+ c2 = (mex + 1)*PAGE_SIZE - rex;
+ if (cz != c2)
+ SAM("ERROR: discrepancy %i in bytes read\n", c2 - cz);
+ c3 = (mad + 1)*PAGE_SIZE - rad;
+
+ if (!decimatepixel) {
+ if (bytesperpixel * cz != c3)
+ SAM("ERROR: discrepancy %i in bytes written\n",
+ c3 - (bytesperpixel * cz));
+ } else {
+ if (!odd) {
+ if (bytesperpixel *
+ cz != (4 * c3))
+ SAM("ERROR: discrepancy %i in bytes written\n",
+ (2*c3)-(bytesperpixel * cz));
+ } else {
+ if (0 != c3)
+ SAM("ERROR: discrepancy %i "
+ "in bytes written\n", c3);
+ }
+ }
+ if (rump)
+ SAM("WORRY: undischarged cache at end of line in frame buffer\n");
+
+ JOM(8, "===== field2frame(): %i bytes --> %i bytes (incl skip)\n", c2, c3);
+ JOM(8, "===== field2frame(): %i=mad %i=rad\n", mad, rad);
+
+ if (odd)
+ JOM(8, "+++++ field2frame(): frame buffer %i is full\n", kad);
+
+ if (peasycap->field_read == peasycap->field_fill)
+ SAM("WARNING: on exit, filling field buffer %i\n",
+ peasycap->field_read);
+
+ if (caches)
+ JOM(8, "%i=caches\n", caches);
+ return 0;
+}
+/*---------------------------------------------------------------------------*/
+/*
+ * DECIMATION AND COLOURSPACE CONVERSION.
+ *
+ * THIS ROUTINE REQUIRES THAT ALL THE DATA TO BE READ RESIDES ON ONE PAGE
+ * AND THAT ALL THE DATA TO BE WRITTEN RESIDES ON ONE (DIFFERENT) PAGE.
+ * THE CALLING ROUTINE MUST ENSURE THAT THIS REQUIREMENT IS MET, AND MUST
+ * ALSO ENSURE THAT much IS EVEN.
+ *
+ * much BYTES ARE READ, AT LEAST (bytesperpixel * much)/2 BYTES ARE WRITTEN
+ * IF THERE IS NO DECIMATION, HALF THIS AMOUNT IF THERE IS DECIMATION.
+ *
+ * mask IS ZERO WHEN NO SPECIAL BEHAVIOUR REQUIRED. OTHERWISE IT IS SET THUS:
+ * 0x03 & mask = number of bytes to be written to cache instead of to
+ * frame buffer
+ * 0x04 & mask => use argument margin to set the chrominance for last pixel
+ * 0x08 & mask => do not set the chrominance for last pixel
+ *
+ * YUV to RGB CONVERSION IS (OR SHOULD BE) ITU-R BT 601.
+ *
+ * THERE IS A LOT OF CODE REPETITION IN THIS ROUTINE IN ORDER TO AVOID
+ * INEFFICIENT SWITCHING INSIDE INNER LOOPS. REARRANGING THE LOGIC TO
+ * REDUCE CODE LENGTH WILL GENERALLY IMPAIR RUNTIME PERFORMANCE. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+int
+redaub(struct easycap *peasycap, void *pad, void *pex, int much, int more,
+ u8 mask, u8 margin, bool isuy)
+{
+ static s32 ay[256], bu[256], rv[256], gu[256], gv[256];
+ u8 *pcache;
+ u8 r, g, b, y, u, v, c, *p2, *p3, *pz, *pr;
+ int bytesperpixel;
+ bool byteswaporder, decimatepixel, last;
+ int j, rump;
+ s32 tmp;
+
+ if (much % 2) {
+ SAM("MISTAKE: much is odd\n");
+ return -EFAULT;
+ }
+ bytesperpixel = peasycap->bytesperpixel;
+ byteswaporder = peasycap->byteswaporder;
+ decimatepixel = peasycap->decimatepixel;
+
+/*---------------------------------------------------------------------------*/
+ if (!bu[255]) {
+ for (j = 0; j < 112; j++) {
+ tmp = (0xFF00 & (453 * j)) >> 8;
+ bu[j + 128] = tmp; bu[127 - j] = -tmp;
+ tmp = (0xFF00 & (359 * j)) >> 8;
+ rv[j + 128] = tmp; rv[127 - j] = -tmp;
+ tmp = (0xFF00 & (88 * j)) >> 8;
+ gu[j + 128] = tmp; gu[127 - j] = -tmp;
+ tmp = (0xFF00 & (183 * j)) >> 8;
+ gv[j + 128] = tmp; gv[127 - j] = -tmp;
+ }
+ for (j = 0; j < 16; j++) {
+ bu[j] = bu[16]; rv[j] = rv[16];
+ gu[j] = gu[16]; gv[j] = gv[16];
+ }
+ for (j = 240; j < 256; j++) {
+ bu[j] = bu[239]; rv[j] = rv[239];
+ gu[j] = gu[239]; gv[j] = gv[239];
+ }
+ for (j = 16; j < 236; j++)
+ ay[j] = j;
+ for (j = 0; j < 16; j++)
+ ay[j] = ay[16];
+ for (j = 236; j < 256; j++)
+ ay[j] = ay[235];
+ JOM(8, "lookup tables are prepared\n");
+ }
+ pcache = peasycap->pcache;
+ if (!pcache)
+ pcache = &peasycap->cache[0];
+/*---------------------------------------------------------------------------*/
+/*
+ * TRANSFER CONTENTS OF CACHE TO THE FRAME BUFFER
+ */
+/*---------------------------------------------------------------------------*/
+ if (!pcache) {
+ SAM("MISTAKE: pcache is NULL\n");
+ return -EFAULT;
+ }
+
+ if (pcache != &peasycap->cache[0])
+ JOM(16, "cache has %i bytes\n", (int)(pcache - &peasycap->cache[0]));
+ p2 = &peasycap->cache[0];
+ p3 = (u8 *)pad - (int)(pcache - &peasycap->cache[0]);
+ while (p2 < pcache) {
+ *p3++ = *p2; p2++;
+ }
+ pcache = &peasycap->cache[0];
+ if (p3 != pad) {
+ SAM("MISTAKE: pointer misalignment\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+ rump = (int)(0x03 & mask);
+ u = 0; v = 0;
+ p2 = (u8 *)pex; pz = p2 + much; pr = p3 + more; last = false;
+ p2++;
+
+ if (isuy)
+ u = *(p2 - 1);
+ else
+ v = *(p2 - 1);
+
+ if (rump)
+ JOM(16, "%4i=much %4i=more %i=rump\n", much, more, rump);
+
+/*---------------------------------------------------------------------------*/
+ switch (bytesperpixel) {
+ case 2: {
+ if (!decimatepixel) {
+ memcpy(pad, pex, (size_t)much);
+ if (!byteswaporder) {
+ /* UYVY */
+ return 0;
+ } else {
+ /* YUYV */
+ p3 = (u8 *)pad; pz = p3 + much;
+ while (pz > p3) {
+ c = *p3;
+ *p3 = *(p3 + 1);
+ *(p3 + 1) = c;
+ p3 += 2;
+ }
+ return 0;
+ }
+ } else {
+ if (!byteswaporder) {
+ /* UYVY DECIMATED */
+ p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much;
+ while (pz > p2) {
+ *p3 = *p2;
+ *(p3 + 1) = *(p2 + 1);
+ *(p3 + 2) = *(p2 + 2);
+ *(p3 + 3) = *(p2 + 3);
+ p3 += 4; p2 += 8;
+ }
+ return 0;
+ } else {
+ /* YUYV DECIMATED */
+ p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much;
+ while (pz > p2) {
+ *p3 = *(p2 + 1);
+ *(p3 + 1) = *p2;
+ *(p3 + 2) = *(p2 + 3);
+ *(p3 + 3) = *(p2 + 2);
+ p3 += 4; p2 += 8;
+ }
+ return 0;
+ }
+ }
+ break;
+ }
+ case 3:
+ {
+ if (!decimatepixel) {
+ if (!byteswaporder) {
+ /* RGB */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = r;
+ *pcache++ = g;
+ *pcache++ = b;
+ break;
+ }
+ case 2: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *pcache++ = b;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: %i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ }
+ p2 += 2;
+ if (isuy)
+ isuy = false;
+ else
+ isuy = true;
+ p3 += bytesperpixel;
+ }
+ return 0;
+ } else {
+ /* BGR */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ }
+ else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = b;
+ *pcache++ = g;
+ *pcache++ = r;
+ break;
+ }
+ case 2: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *pcache++ = r;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: %i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ }
+ p2 += 2;
+ if (isuy)
+ isuy = false;
+ else
+ isuy = true;
+ p3 += bytesperpixel;
+ }
+ }
+ return 0;
+ } else {
+ if (!byteswaporder) {
+ /* RGB DECIMATED */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ if (isuy) {
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] -
+ gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = r;
+ *pcache++ = g;
+ *pcache++ = b;
+ break;
+ }
+ case 2: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *pcache++ = b;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: "
+ "%i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ }
+ isuy = false;
+ p3 += bytesperpixel;
+ } else {
+ isuy = true;
+ }
+ p2 += 2;
+ }
+ return 0;
+ } else {
+ /* BGR DECIMATED */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ if (isuy) {
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] -
+ gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = b;
+ *pcache++ = g;
+ *pcache++ = r;
+ break;
+ }
+ case 2: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *pcache++ = r;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: "
+ "%i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ }
+ isuy = false;
+ p3 += bytesperpixel;
+ }
+ else
+ isuy = true;
+ p2 += 2;
+ }
+ return 0;
+ }
+ }
+ break;
+ }
+ case 4:
+ {
+ if (!decimatepixel) {
+ if (!byteswaporder) {
+ /* RGBA */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = r;
+ *pcache++ = g;
+ *pcache++ = b;
+ *pcache++ = 0;
+ break;
+ }
+ case 2: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *pcache++ = b;
+ *pcache++ = 0;
+ break;
+ }
+ case 3: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ *pcache++ = 0;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: %i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ *(p3 + 3) = 0;
+ }
+ p2 += 2;
+ if (isuy)
+ isuy = false;
+ else
+ isuy = true;
+ p3 += bytesperpixel;
+ }
+ return 0;
+ } else {
+ /*
+ * BGRA
+ */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = b;
+ *pcache++ = g;
+ *pcache++ = r;
+ *pcache++ = 0;
+ break;
+ }
+ case 2: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *pcache++ = r;
+ *pcache++ = 0;
+ break;
+ }
+ case 3: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ *pcache++ = 0;
+ break;
+ }
+ default:
+ SAM("MISTAKE: %i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ } else {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ *(p3 + 3) = 0;
+ }
+ p2 += 2;
+ if (isuy)
+ isuy = false;
+ else
+ isuy = true;
+ p3 += bytesperpixel;
+ }
+ }
+ return 0;
+ } else {
+ if (!byteswaporder) {
+ /*
+ * RGBA DECIMATED
+ */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ if (isuy) {
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] -
+ gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = r;
+ *pcache++ = g;
+ *pcache++ = b;
+ *pcache++ = 0;
+ break;
+ }
+ case 2: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *pcache++ = b;
+ *pcache++ = 0;
+ break;
+ }
+ case 3: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ *pcache++ = 0;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: "
+ "%i=rump\n",
+ bytesperpixel -
+ rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ *(p3 + 3) = 0;
+ }
+ isuy = false;
+ p3 += bytesperpixel;
+ } else
+ isuy = true;
+ p2 += 2;
+ }
+ return 0;
+ } else {
+ /*
+ * BGRA DECIMATED
+ */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ if (isuy) {
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] -
+ gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = b;
+ *pcache++ = g;
+ *pcache++ = r;
+ *pcache++ = 0;
+ break;
+ }
+ case 2: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *pcache++ = r;
+ *pcache++ = 0;
+ break;
+ }
+ case 3: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ *pcache++ = 0;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: "
+ "%i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ *(p3 + 3) = 0;
+ }
+ isuy = false;
+ p3 += bytesperpixel;
+ } else
+ isuy = true;
+ p2 += 2;
+ }
+ return 0;
+ }
+ }
+ break;
+ }
+ default: {
+ SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel);
+ return -EFAULT;
+ }
+ }
+ return 0;
+}
+/*****************************************************************************/
+/*
+ * SEE CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGES 430-434
+ */
+/*****************************************************************************/
+static void easycap_vma_open(struct vm_area_struct *pvma)
+{
+ struct easycap *peasycap;
+
+ peasycap = pvma->vm_private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return;
+ }
+ peasycap->vma_many++;
+ JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many);
+ return;
+}
+/*****************************************************************************/
+static void easycap_vma_close(struct vm_area_struct *pvma)
+{
+ struct easycap *peasycap;
+
+ peasycap = pvma->vm_private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return;
+ }
+ peasycap->vma_many--;
+ JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many);
+ return;
+}
+/*****************************************************************************/
+static int easycap_vma_fault(struct vm_area_struct *pvma, struct vm_fault *pvmf)
+{
+ int k, m, retcode;
+ void *pbuf;
+ struct page *page;
+ struct easycap *peasycap;
+
+ retcode = VM_FAULT_NOPAGE;
+
+ if (!pvma) {
+ SAY("pvma is NULL\n");
+ return retcode;
+ }
+ if (!pvmf) {
+ SAY("pvmf is NULL\n");
+ return retcode;
+ }
+
+ k = (pvmf->pgoff) / (FRAME_BUFFER_SIZE/PAGE_SIZE);
+ m = (pvmf->pgoff) % (FRAME_BUFFER_SIZE/PAGE_SIZE);
+
+ if (!m)
+ JOT(4, "%4i=k, %4i=m\n", k, m);
+ else
+ JOT(16, "%4i=k, %4i=m\n", k, m);
+
+ if ((0 > k) || (FRAME_BUFFER_MANY <= k)) {
+ SAY("ERROR: buffer index %i out of range\n", k);
+ return retcode;
+ }
+ if ((0 > m) || (FRAME_BUFFER_SIZE/PAGE_SIZE <= m)) {
+ SAY("ERROR: page number %i out of range\n", m);
+ return retcode;
+ }
+ peasycap = pvma->vm_private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return retcode;
+ }
+/*---------------------------------------------------------------------------*/
+ pbuf = peasycap->frame_buffer[k][m].pgo;
+ if (!pbuf) {
+ SAM("ERROR: pbuf is NULL\n");
+ return retcode;
+ }
+ page = virt_to_page(pbuf);
+ if (!page) {
+ SAM("ERROR: page is NULL\n");
+ return retcode;
+ }
+ get_page(page);
+/*---------------------------------------------------------------------------*/
+ if (!page) {
+ SAM("ERROR: page is NULL after get_page(page)\n");
+ } else {
+ pvmf->page = page;
+ retcode = VM_FAULT_MINOR;
+ }
+ return retcode;
+}
+
+static const struct vm_operations_struct easycap_vm_ops = {
+ .open = easycap_vma_open,
+ .close = easycap_vma_close,
+ .fault = easycap_vma_fault,
+};
+
+static int easycap_mmap(struct file *file, struct vm_area_struct *pvma)
+{
+ JOT(8, "\n");
+
+ pvma->vm_ops = &easycap_vm_ops;
+ pvma->vm_flags |= VM_RESERVED;
+ if (file)
+ pvma->vm_private_data = file->private_data;
+ easycap_vma_open(pvma);
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * ON COMPLETION OF A VIDEO URB ITS DATA IS COPIED TO THE FIELD BUFFERS
+ * PROVIDED peasycap->video_idle IS ZERO. REGARDLESS OF THIS BEING TRUE,
+ * IT IS RESUBMITTED PROVIDED peasycap->video_isoc_streaming IS NOT ZERO.
+ *
+ * THIS FUNCTION IS AN INTERRUPT SERVICE ROUTINE AND MUST NOT SLEEP.
+ *
+ * INFORMATION ABOUT THE VALIDITY OF THE CONTENTS OF THE FIELD BUFFER ARE
+ * STORED IN THE TWO-BYTE STATUS PARAMETER
+ * peasycap->field_buffer[peasycap->field_fill][0].kount
+ * NOTICE THAT THE INFORMATION IS STORED ONLY WITH PAGE 0 OF THE FIELD BUFFER.
+ *
+ * THE LOWER BYTE CONTAINS THE FIELD PARITY BYTE FURNISHED BY THE SAA7113H
+ * CHIP.
+ *
+ * THE UPPER BYTE IS ZERO IF NO PROBLEMS, OTHERWISE:
+ * 0 != (kount & 0x8000) => AT LEAST ONE URB COMPLETED WITH ERRORS
+ * 0 != (kount & 0x4000) => BUFFER HAS TOO MUCH DATA
+ * 0 != (kount & 0x2000) => BUFFER HAS NOT ENOUGH DATA
+ * 0 != (kount & 0x1000) => BUFFER HAS DATA FROM DISPARATE INPUTS
+ * 0 != (kount & 0x0400) => RESERVED
+ * 0 != (kount & 0x0200) => FIELD BUFFER NOT YET CHECKED
+ * 0 != (kount & 0x0100) => BUFFER HAS TWO EXTRA BYTES - WHY?
+ */
+/*---------------------------------------------------------------------------*/
+static void easycap_complete(struct urb *purb)
+{
+ struct easycap *peasycap;
+ struct data_buffer *pfield_buffer;
+ char errbuf[16];
+ int i, more, much, leap, rc, last;
+ int videofieldamount;
+ unsigned int override, bad;
+ int framestatus, framelength, frameactual, frameoffset;
+ u8 *pu;
+
+ if (!purb) {
+ SAY("ERROR: easycap_complete(): purb is NULL\n");
+ return;
+ }
+ peasycap = purb->context;
+ if (!peasycap) {
+ SAY("ERROR: easycap_complete(): peasycap is NULL\n");
+ return;
+ }
+ if (peasycap->video_eof)
+ return;
+ for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++)
+ if (purb->transfer_buffer == peasycap->video_isoc_buffer[i].pgo)
+ break;
+ JOM(16, "%2i=urb\n", i);
+ last = peasycap->video_isoc_sequence;
+ if ((((VIDEO_ISOC_BUFFER_MANY - 1) == last) && (0 != i)) ||
+ (((VIDEO_ISOC_BUFFER_MANY - 1) != last) && ((last + 1) != i))) {
+ JOM(16, "ERROR: out-of-order urbs %i,%i ... continuing\n",
+ last, i);
+ }
+ peasycap->video_isoc_sequence = i;
+
+ if (peasycap->video_idle) {
+ JOM(16, "%i=video_idle %i=video_isoc_streaming\n",
+ peasycap->video_idle, peasycap->video_isoc_streaming);
+ if (peasycap->video_isoc_streaming) {
+ rc = usb_submit_urb(purb, GFP_ATOMIC);
+ if (rc) {
+ SAM("%s:%d ENOMEM\n", strerror(rc), rc);
+ if (-ENODEV != rc)
+ SAM("ERROR: while %i=video_idle, "
+ "usb_submit_urb() "
+ "failed with rc:\n",
+ peasycap->video_idle);
+ }
+ }
+ return;
+ }
+ override = 0;
+/*---------------------------------------------------------------------------*/
+ if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
+ SAM("ERROR: bad peasycap->field_fill\n");
+ return;
+ }
+ if (purb->status) {
+ if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
+ JOM(8, "urb status -ESHUTDOWN or -ENOENT\n");
+ return;
+ }
+
+ (peasycap->field_buffer[peasycap->field_fill][0].kount) |= 0x8000 ;
+ SAM("ERROR: bad urb status -%s: %d\n",
+ strerror(purb->status), purb->status);
+/*---------------------------------------------------------------------------*/
+ } else {
+ for (i = 0; i < purb->number_of_packets; i++) {
+ if (0 != purb->iso_frame_desc[i].status) {
+ (peasycap->field_buffer
+ [peasycap->field_fill][0].kount) |= 0x8000 ;
+ /* FIXME: 1. missing '-' check boundaries */
+ strcpy(&errbuf[0],
+ strerror(purb->iso_frame_desc[i].status));
+ }
+ framestatus = purb->iso_frame_desc[i].status;
+ framelength = purb->iso_frame_desc[i].length;
+ frameactual = purb->iso_frame_desc[i].actual_length;
+ frameoffset = purb->iso_frame_desc[i].offset;
+
+ JOM(16, "frame[%2i]:"
+ "%4i=status "
+ "%4i=actual "
+ "%4i=length "
+ "%5i=offset\n",
+ i, framestatus, frameactual, framelength, frameoffset);
+ if (!purb->iso_frame_desc[i].status) {
+ more = purb->iso_frame_desc[i].actual_length;
+ pfield_buffer = &peasycap->field_buffer
+ [peasycap->field_fill][peasycap->field_page];
+ videofieldamount = (peasycap->field_page *
+ PAGE_SIZE) +
+ (int)(pfield_buffer->pto - pfield_buffer->pgo);
+ if (4 == more)
+ peasycap->video_mt++;
+ if (4 < more) {
+ if (peasycap->video_mt) {
+ JOM(8, "%4i empty video urb frames\n",
+ peasycap->video_mt);
+ peasycap->video_mt = 0;
+ }
+ if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
+ SAM("ERROR: bad peasycap->field_fill\n");
+ return;
+ }
+ if (FIELD_BUFFER_SIZE/PAGE_SIZE <=
+ peasycap->field_page) {
+ SAM("ERROR: bad peasycap->field_page\n");
+ return;
+ }
+ pfield_buffer = &peasycap->field_buffer
+ [peasycap->field_fill][peasycap->field_page];
+ pu = (u8 *)(purb->transfer_buffer +
+ purb->iso_frame_desc[i].offset);
+ if (0x80 & *pu)
+ leap = 8;
+ else
+ leap = 4;
+/*--------------------------------------------------------------------------*/
+/*
+ * EIGHT-BYTE END-OF-VIDEOFIELD MARKER.
+ * NOTE: A SUCCESSION OF URB FRAMES FOLLOWING THIS ARE EMPTY,
+ * CORRESPONDING TO THE FIELD FLYBACK (VERTICAL BLANKING) PERIOD.
+ *
+ * PROVIDED THE FIELD BUFFER CONTAINS GOOD DATA AS INDICATED BY A ZERO UPPER
+ * BYTE OF
+ * peasycap->field_buffer[peasycap->field_fill][0].kount
+ * THE CONTENTS OF THE FIELD BUFFER ARE OFFERED TO dqbuf(), field_read IS
+ * UPDATED AND field_fill IS BUMPED. IF THE FIELD BUFFER CONTAINS BAD DATA
+ * NOTHING IS OFFERED TO dqbuf().
+ *
+ * THE DECISION ON WHETHER THE PARITY OF THE OFFERED FIELD BUFFER IS RIGHT
+ * RESTS WITH dqbuf().
+ */
+/*---------------------------------------------------------------------------*/
+ if ((8 == more) || override) {
+ if (videofieldamount >
+ peasycap->videofieldamount) {
+ if (2 == videofieldamount -
+ peasycap->
+ videofieldamount) {
+ (peasycap->field_buffer
+ [peasycap->field_fill]
+ [0].kount) |= 0x0100;
+ peasycap->video_junk += (1 +
+ VIDEO_JUNK_TOLERATE);
+ } else
+ (peasycap->field_buffer
+ [peasycap->field_fill]
+ [0].kount) |= 0x4000;
+ } else if (videofieldamount <
+ peasycap->
+ videofieldamount) {
+ (peasycap->field_buffer
+ [peasycap->field_fill]
+ [0].kount) |= 0x2000;
+ }
+ bad = 0xFF00 & peasycap->field_buffer
+ [peasycap->field_fill]
+ [0].kount;
+ if (!bad) {
+ (peasycap->video_junk)--;
+ if (-VIDEO_JUNK_TOLERATE >
+ peasycap->video_junk)
+ peasycap->video_junk =
+ -VIDEO_JUNK_TOLERATE;
+ peasycap->field_read =
+ (peasycap->
+ field_fill)++;
+ if (FIELD_BUFFER_MANY <=
+ peasycap->
+ field_fill)
+ peasycap->
+ field_fill = 0;
+ peasycap->field_page = 0;
+ pfield_buffer = &peasycap->
+ field_buffer
+ [peasycap->
+ field_fill]
+ [peasycap->
+ field_page];
+ pfield_buffer->pto =
+ pfield_buffer->pgo;
+ JOM(8, "bumped to: %i="
+ "peasycap->"
+ "field_fill %i="
+ "parity\n",
+ peasycap->field_fill,
+ 0x00FF &
+ pfield_buffer->kount);
+ JOM(8, "field buffer %i has "
+ "%i bytes fit to be "
+ "read\n",
+ peasycap->field_read,
+ videofieldamount);
+ JOM(8, "wakeup call to "
+ "wq_video, "
+ "%i=field_read "
+ "%i=field_fill "
+ "%i=parity\n",
+ peasycap->field_read,
+ peasycap->field_fill,
+ 0x00FF & peasycap->
+ field_buffer
+ [peasycap->
+ field_read][0].kount);
+ wake_up_interruptible
+ (&(peasycap->
+ wq_video));
+ } else {
+ peasycap->video_junk++;
+ if (bad & 0x0010)
+ peasycap->video_junk +=
+ (1 + VIDEO_JUNK_TOLERATE/2);
+ JOM(8, "field buffer %i had %i "
+ "bytes, now discarded: "
+ "0x%04X\n",
+ peasycap->field_fill,
+ videofieldamount,
+ (0xFF00 &
+ peasycap->field_buffer
+ [peasycap->field_fill][0].
+ kount));
+ (peasycap->field_fill)++;
+
+ if (FIELD_BUFFER_MANY <=
+ peasycap->field_fill)
+ peasycap->field_fill = 0;
+ peasycap->field_page = 0;
+ pfield_buffer =
+ &peasycap->field_buffer
+ [peasycap->field_fill]
+ [peasycap->field_page];
+ pfield_buffer->pto =
+ pfield_buffer->pgo;
+
+ JOM(8, "bumped to: %i=peasycap->"
+ "field_fill %i=parity\n",
+ peasycap->field_fill,
+ 0x00FF & pfield_buffer->kount);
+ }
+ if (8 == more) {
+ JOM(8, "end-of-field: received "
+ "parity byte 0x%02X\n",
+ (0xFF & *pu));
+ if (0x40 & *pu)
+ pfield_buffer->kount = 0x0000;
+ else
+ pfield_buffer->kount = 0x0001;
+ pfield_buffer->input = 0x08 |
+ (0x07 & peasycap->input);
+ JOM(8, "end-of-field: 0x%02X=kount\n",
+ 0xFF & pfield_buffer->kount);
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * COPY more BYTES FROM ISOC BUFFER TO FIELD BUFFER
+ */
+/*---------------------------------------------------------------------------*/
+ pu += leap;
+ more -= leap;
+
+ if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
+ SAM("ERROR: bad peasycap->field_fill\n");
+ return;
+ }
+ if (FIELD_BUFFER_SIZE/PAGE_SIZE <= peasycap->field_page) {
+ SAM("ERROR: bad peasycap->field_page\n");
+ return;
+ }
+ pfield_buffer = &peasycap->field_buffer
+ [peasycap->field_fill][peasycap->field_page];
+ while (more) {
+ pfield_buffer = &peasycap->field_buffer
+ [peasycap->field_fill]
+ [peasycap->field_page];
+ if (PAGE_SIZE < (pfield_buffer->pto -
+ pfield_buffer->pgo)) {
+ SAM("ERROR: bad pfield_buffer->pto\n");
+ return;
+ }
+ if (PAGE_SIZE == (pfield_buffer->pto -
+ pfield_buffer->pgo)) {
+ (peasycap->field_page)++;
+ if (FIELD_BUFFER_SIZE/PAGE_SIZE <=
+ peasycap->field_page) {
+ JOM(16, "wrapping peasycap->"
+ "field_page\n");
+ peasycap->field_page = 0;
+ }
+ pfield_buffer = &peasycap->
+ field_buffer
+ [peasycap->field_fill]
+ [peasycap->field_page];
+ pfield_buffer->pto = pfield_buffer->pgo;
+ pfield_buffer->input = 0x08 |
+ (0x07 & peasycap->input);
+ if ((peasycap->field_buffer[peasycap->
+ field_fill][0]).
+ input !=
+ pfield_buffer->input)
+ (peasycap->field_buffer
+ [peasycap->field_fill]
+ [0]).kount |= 0x1000;
+ }
+
+ much = PAGE_SIZE -
+ (int)(pfield_buffer->pto -
+ pfield_buffer->pgo);
+
+ if (much > more)
+ much = more;
+ memcpy(pfield_buffer->pto, pu, much);
+ pu += much;
+ (pfield_buffer->pto) += much;
+ more -= much;
+ }
+ }
+ }
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * RESUBMIT THIS URB, UNLESS A SEVERE PERSISTENT ERROR CONDITION EXISTS.
+ *
+ * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO AN ERROR CONDITION
+ * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+ if (VIDEO_ISOC_BUFFER_MANY <= peasycap->video_junk) {
+ SAM("easycap driver shutting down on condition green\n");
+ peasycap->status = 1;
+ peasycap->video_eof = 1;
+ peasycap->video_junk = 0;
+ wake_up_interruptible(&peasycap->wq_video);
+#if !defined(PERSEVERE)
+ peasycap->audio_eof = 1;
+ wake_up_interruptible(&peasycap->wq_audio);
+#endif /*PERSEVERE*/
+ return;
+ }
+ if (peasycap->video_isoc_streaming) {
+ rc = usb_submit_urb(purb, GFP_ATOMIC);
+ if (rc) {
+ SAM("%s: %d\n", strerror(rc), rc);
+ if (-ENODEV != rc)
+ SAM("ERROR: while %i=video_idle, "
+ "usb_submit_urb() "
+ "failed with rc:\n",
+ peasycap->video_idle);
+ }
+ }
+ return;
+}
+static const struct file_operations easycap_fops = {
+ .owner = THIS_MODULE,
+ .open = easycap_open,
+ .unlocked_ioctl = easycap_unlocked_ioctl,
+ .poll = easycap_poll,
+ .mmap = easycap_mmap,
+ .llseek = no_llseek,
+};
+static const struct usb_class_driver easycap_class = {
+ .name = "usb/easycap%d",
+ .fops = &easycap_fops,
+ .minor_base = USB_SKEL_MINOR_BASE,
+};
+/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
+static const struct v4l2_file_operations v4l2_fops = {
+ .owner = THIS_MODULE,
+ .open = easycap_open_noinode,
+ .unlocked_ioctl = easycap_unlocked_ioctl,
+ .poll = easycap_poll,
+ .mmap = easycap_mmap,
+};
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * WHEN THE EasyCAP IS PHYSICALLY PLUGGED IN, THIS FUNCTION IS CALLED THREE
+ * TIMES, ONCE FOR EACH OF THE THREE INTERFACES. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+static int easycap_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev;
+ struct usb_host_interface *alt;
+ struct usb_endpoint_descriptor *ep;
+ struct usb_interface_descriptor *interface;
+ struct urb *purb;
+ struct easycap *peasycap;
+ int ndong;
+ struct data_urb *pdata_urb;
+ int i, j, k, m, rc;
+ u8 bInterfaceNumber;
+ u8 bInterfaceClass;
+ u8 bInterfaceSubClass;
+ void *pbuf;
+ int okalt[8], isokalt;
+ int okepn[8];
+ int okmps[8];
+ int maxpacketsize;
+ u16 mask;
+ s32 value;
+ struct easycap_format *peasycap_format;
+ int fmtidx;
+ struct inputset *inputset;
+
+ usbdev = interface_to_usbdev(intf);
+
+/*---------------------------------------------------------------------------*/
+ alt = usb_altnum_to_altsetting(intf, 0);
+ if (!alt) {
+ SAY("ERROR: usb_host_interface not found\n");
+ return -EFAULT;
+ }
+ interface = &alt->desc;
+ if (!interface) {
+ SAY("ERROR: intf_descriptor is NULL\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * GET PROPERTIES OF PROBED INTERFACE
+ */
+/*---------------------------------------------------------------------------*/
+ bInterfaceNumber = interface->bInterfaceNumber;
+ bInterfaceClass = interface->bInterfaceClass;
+ bInterfaceSubClass = interface->bInterfaceSubClass;
+
+ JOT(4, "intf[%i]: num_altsetting=%i\n",
+ bInterfaceNumber, intf->num_altsetting);
+ JOT(4, "intf[%i]: cur_altsetting - altsetting=%li\n",
+ bInterfaceNumber,
+ (long int)(intf->cur_altsetting - intf->altsetting));
+ JOT(4, "intf[%i]: bInterfaceClass=0x%02X bInterfaceSubClass=0x%02X\n",
+ bInterfaceNumber, bInterfaceClass, bInterfaceSubClass);
+/*---------------------------------------------------------------------------*/
+/*
+ * A NEW struct easycap IS ALWAYS ALLOCATED WHEN INTERFACE 0 IS PROBED.
+ * IT IS NOT POSSIBLE HERE TO FREE ANY EXISTING struct easycap. THIS
+ * SHOULD HAVE BEEN DONE BY easycap_delete() WHEN THE EasyCAP WAS
+ * PHYSICALLY UNPLUGGED.
+ *
+ * THE POINTER peasycap TO THE struct easycap IS REMEMBERED WHEN
+ * INTERFACES 1 AND 2 ARE PROBED.
+*/
+/*---------------------------------------------------------------------------*/
+ if (0 == bInterfaceNumber) {
+ peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL);
+ if (!peasycap) {
+ SAY("ERROR: Could not allocate peasycap\n");
+ return -ENOMEM;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * PERFORM URGENT INTIALIZATIONS ...
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->minor = -1;
+ kref_init(&peasycap->kref);
+ JOM(8, "intf[%i]: after kref_init(..._video) "
+ "%i=peasycap->kref.refcount.counter\n",
+ bInterfaceNumber, peasycap->kref.refcount.counter);
+
+ /* module params */
+ peasycap->gain = (s8)clamp(easycap_gain, 0, 31);
+
+ init_waitqueue_head(&peasycap->wq_video);
+ init_waitqueue_head(&peasycap->wq_audio);
+ init_waitqueue_head(&peasycap->wq_trigger);
+
+ if (mutex_lock_interruptible(&mutex_dongle)) {
+ SAY("ERROR: cannot down mutex_dongle\n");
+ return -ERESTARTSYS;
+ } else {
+/*---------------------------------------------------------------------------*/
+ /*
+ * FOR INTERFACES 1 AND 2 THE POINTER peasycap WILL NEED TO
+ * TO BE THE SAME AS THAT ALLOCATED NOW FOR INTERFACE 0.
+ *
+ * NORMALLY ndong WILL NOT HAVE CHANGED SINCE INTERFACE 0 WAS
+ * PROBED, BUT THIS MAY NOT BE THE CASE IF, FOR EXAMPLE, TWO
+ * EASYCAPs ARE PLUGGED IN SIMULTANEOUSLY.
+ */
+/*---------------------------------------------------------------------------*/
+ for (ndong = 0; ndong < DONGLE_MANY; ndong++) {
+ if ((!easycapdc60_dongle[ndong].peasycap) &&
+ (!mutex_is_locked(&easycapdc60_dongle
+ [ndong].mutex_video)) &&
+ (!mutex_is_locked(&easycapdc60_dongle
+ [ndong].mutex_audio))) {
+ easycapdc60_dongle[ndong].peasycap = peasycap;
+ peasycap->isdongle = ndong;
+ JOM(8, "intf[%i]: peasycap-->easycap"
+ "_dongle[%i].peasycap\n",
+ bInterfaceNumber, ndong);
+ break;
+ }
+ }
+ if (DONGLE_MANY <= ndong) {
+ SAM("ERROR: too many dongles\n");
+ mutex_unlock(&mutex_dongle);
+ return -ENOMEM;
+ }
+ mutex_unlock(&mutex_dongle);
+ }
+ peasycap->allocation_video_struct = sizeof(struct easycap);
+ peasycap->allocation_video_page = 0;
+ peasycap->allocation_video_urb = 0;
+ peasycap->allocation_audio_struct = 0;
+ peasycap->allocation_audio_page = 0;
+ peasycap->allocation_audio_urb = 0;
+
+/*---------------------------------------------------------------------------*/
+/*
+ * ... AND FURTHER INITIALIZE THE STRUCTURE
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->pusb_device = usbdev;
+ peasycap->pusb_interface = intf;
+
+ peasycap->ilk = 0;
+ peasycap->microphone = false;
+
+ peasycap->video_interface = -1;
+ peasycap->video_altsetting_on = -1;
+ peasycap->video_altsetting_off = -1;
+ peasycap->video_endpointnumber = -1;
+ peasycap->video_isoc_maxframesize = -1;
+ peasycap->video_isoc_buffer_size = -1;
+
+ peasycap->audio_interface = -1;
+ peasycap->audio_altsetting_on = -1;
+ peasycap->audio_altsetting_off = -1;
+ peasycap->audio_endpointnumber = -1;
+ peasycap->audio_isoc_maxframesize = -1;
+ peasycap->audio_isoc_buffer_size = -1;
+
+ peasycap->frame_buffer_many = FRAME_BUFFER_MANY;
+
+ for (k = 0; k < INPUT_MANY; k++)
+ peasycap->lost[k] = 0;
+ peasycap->skip = 0;
+ peasycap->skipped = 0;
+ peasycap->offerfields = 0;
+/*---------------------------------------------------------------------------*/
+/*
+ * DYNAMICALLY FILL IN THE AVAILABLE FORMATS ...
+ */
+/*---------------------------------------------------------------------------*/
+ rc = fillin_formats();
+ if (0 > rc) {
+ SAM("ERROR: fillin_formats() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(4, "%i formats available\n", rc);
+/*---------------------------------------------------------------------------*/
+/*
+ * ... AND POPULATE easycap.inputset[]
+*/
+/*---------------------------------------------------------------------------*/
+ /* FIXME: maybe we just use memset 0 */
+ inputset = peasycap->inputset;
+ for (k = 0; k < INPUT_MANY; k++) {
+ inputset[k].input_ok = 0;
+ inputset[k].standard_offset_ok = 0;
+ inputset[k].format_offset_ok = 0;
+ inputset[k].brightness_ok = 0;
+ inputset[k].contrast_ok = 0;
+ inputset[k].saturation_ok = 0;
+ inputset[k].hue_ok = 0;
+ }
+
+ fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN;
+ m = 0;
+ mask = 0;
+ for (i = 0; 0xFFFF != easycap_standard[i].mask; i++) {
+ if (fmtidx == easycap_standard[i].v4l2_standard.index) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].standard_offset = i;
+
+ mask = easycap_standard[i].mask;
+ }
+ }
+
+ if (1 != m) {
+ SAM("ERROR: "
+ "inputset->standard_offset unpopulated, %i=m\n", m);
+ return -ENOENT;
+ }
+
+ peasycap_format = &easycap_format[0];
+ m = 0;
+ for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) {
+ struct v4l2_pix_format *pix =
+ &peasycap_format->v4l2_format.fmt.pix;
+ if (((peasycap_format->mask & 0x0F) == (mask & 0x0F)) &&
+ pix->field == V4L2_FIELD_NONE &&
+ pix->pixelformat == V4L2_PIX_FMT_UYVY &&
+ pix->width == 640 && pix->height == 480) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].format_offset = i;
+ break;
+ }
+ peasycap_format++;
+ }
+ if (1 != m) {
+ SAM("ERROR: inputset[]->format_offset unpopulated\n");
+ return -ENOENT;
+ }
+
+ m = 0;
+ for (i = 0; 0xFFFFFFFF != easycap_control[i].id; i++) {
+ value = easycap_control[i].default_value;
+ if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].brightness = value;
+ } else if (V4L2_CID_CONTRAST == easycap_control[i].id) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].contrast = value;
+ } else if (V4L2_CID_SATURATION == easycap_control[i].id) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].saturation = value;
+ } else if (V4L2_CID_HUE == easycap_control[i].id) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].hue = value;
+ }
+ }
+
+ if (4 != m) {
+ SAM("ERROR: inputset[]->brightness underpopulated\n");
+ return -ENOENT;
+ }
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].input = k;
+ JOM(4, "populated inputset[]\n");
+ JOM(4, "finished initialization\n");
+ } else {
+/*---------------------------------------------------------------------------*/
+/*
+ * FIXME
+ *
+ * IDENTIFY THE APPROPRIATE POINTER peasycap FOR INTERFACES 1 AND 2.
+ * THE ADDRESS OF peasycap->pusb_device IS RELUCTANTLY USED FOR THIS PURPOSE.
+ */
+/*---------------------------------------------------------------------------*/
+ for (ndong = 0; ndong < DONGLE_MANY; ndong++) {
+ if (usbdev == easycapdc60_dongle[ndong].peasycap->
+ pusb_device) {
+ peasycap = easycapdc60_dongle[ndong].peasycap;
+ JOT(8, "intf[%i]: dongle[%i].peasycap\n",
+ bInterfaceNumber, ndong);
+ break;
+ }
+ }
+ if (DONGLE_MANY <= ndong) {
+ SAY("ERROR: peasycap is unknown when probing interface %i\n",
+ bInterfaceNumber);
+ return -ENODEV;
+ }
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL when probing interface %i\n",
+ bInterfaceNumber);
+ return -ENODEV;
+ }
+ }
+/*---------------------------------------------------------------------------*/
+ if ((USB_CLASS_VIDEO == bInterfaceClass) ||
+ (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) {
+ if (-1 == peasycap->video_interface) {
+ peasycap->video_interface = bInterfaceNumber;
+ JOM(4, "setting peasycap->video_interface=%i\n",
+ peasycap->video_interface);
+ } else {
+ if (peasycap->video_interface != bInterfaceNumber) {
+ SAM("ERROR: attempting to reset "
+ "peasycap->video_interface\n");
+ SAM("...... continuing with "
+ "%i=peasycap->video_interface\n",
+ peasycap->video_interface);
+ }
+ }
+ } else if ((USB_CLASS_AUDIO == bInterfaceClass) &&
+ (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) {
+ if (-1 == peasycap->audio_interface) {
+ peasycap->audio_interface = bInterfaceNumber;
+ JOM(4, "setting peasycap->audio_interface=%i\n",
+ peasycap->audio_interface);
+ } else {
+ if (peasycap->audio_interface != bInterfaceNumber) {
+ SAM("ERROR: attempting to reset "
+ "peasycap->audio_interface\n");
+ SAM("...... continuing with "
+ "%i=peasycap->audio_interface\n",
+ peasycap->audio_interface);
+ }
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * INVESTIGATE ALL ALTSETTINGS.
+ * DONE IN DETAIL BECAUSE USB DEVICE 05e1:0408 HAS DISPARATE INCARNATIONS.
+ */
+/*---------------------------------------------------------------------------*/
+ isokalt = 0;
+
+ for (i = 0; i < intf->num_altsetting; i++) {
+ alt = usb_altnum_to_altsetting(intf, i);
+ if (!alt) {
+ SAM("ERROR: alt is NULL\n");
+ return -EFAULT;
+ }
+ interface = &alt->desc;
+ if (!interface) {
+ SAM("ERROR: intf_descriptor is NULL\n");
+ return -EFAULT;
+ }
+
+ if (0 == interface->bNumEndpoints)
+ JOM(4, "intf[%i]alt[%i] has no endpoints\n",
+ bInterfaceNumber, i);
+/*---------------------------------------------------------------------------*/
+ for (j = 0; j < interface->bNumEndpoints; j++) {
+ ep = &alt->endpoint[j].desc;
+ if (!ep) {
+ SAM("ERROR: ep is NULL.\n");
+ SAM("...... skipping\n");
+ continue;
+ }
+
+ if (!usb_endpoint_is_isoc_in(ep)) {
+ JOM(4, "intf[%i]alt[%i]end[%i] is a %d endpoint\n",
+ bInterfaceNumber,
+ i, j, ep->bmAttributes);
+ if (usb_endpoint_dir_out(ep)) {
+ SAM("ERROR: OUT endpoint unexpected\n");
+ SAM("...... continuing\n");
+ }
+ continue;
+ }
+ switch (bInterfaceClass) {
+ case USB_CLASS_VIDEO:
+ case USB_CLASS_VENDOR_SPEC: {
+ if (ep->wMaxPacketSize) {
+ if (8 > isokalt) {
+ okalt[isokalt] = i;
+ JOM(4,
+ "%i=okalt[%i]\n",
+ okalt[isokalt],
+ isokalt);
+ okepn[isokalt] =
+ ep->
+ bEndpointAddress &
+ 0x0F;
+ JOM(4,
+ "%i=okepn[%i]\n",
+ okepn[isokalt],
+ isokalt);
+ okmps[isokalt] =
+ le16_to_cpu(ep->
+ wMaxPacketSize);
+ JOM(4,
+ "%i=okmps[%i]\n",
+ okmps[isokalt],
+ isokalt);
+ isokalt++;
+ }
+ } else {
+ if (-1 == peasycap->
+ video_altsetting_off) {
+ peasycap->
+ video_altsetting_off =
+ i;
+ JOM(4, "%i=video_"
+ "altsetting_off "
+ "<====\n",
+ peasycap->
+ video_altsetting_off);
+ } else {
+ SAM("ERROR: peasycap"
+ "->video_altsetting_"
+ "off already set\n");
+ SAM("...... "
+ "continuing with "
+ "%i=peasycap->video_"
+ "altsetting_off\n",
+ peasycap->
+ video_altsetting_off);
+ }
+ }
+ break;
+ }
+ case USB_CLASS_AUDIO: {
+ if (bInterfaceSubClass !=
+ USB_SUBCLASS_AUDIOSTREAMING)
+ break;
+ if (!peasycap) {
+ SAM("MISTAKE: "
+ "peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (ep->wMaxPacketSize) {
+ if (8 > isokalt) {
+ okalt[isokalt] = i ;
+ JOM(4,
+ "%i=okalt[%i]\n",
+ okalt[isokalt],
+ isokalt);
+ okepn[isokalt] =
+ ep->
+ bEndpointAddress &
+ 0x0F;
+ JOM(4,
+ "%i=okepn[%i]\n",
+ okepn[isokalt],
+ isokalt);
+ okmps[isokalt] =
+ le16_to_cpu(ep->
+ wMaxPacketSize);
+ JOM(4,
+ "%i=okmps[%i]\n",
+ okmps[isokalt],
+ isokalt);
+ isokalt++;
+ }
+ } else {
+ if (-1 == peasycap->
+ audio_altsetting_off) {
+ peasycap->
+ audio_altsetting_off =
+ i;
+ JOM(4, "%i=audio_"
+ "altsetting_off "
+ "<====\n",
+ peasycap->
+ audio_altsetting_off);
+ } else {
+ SAM("ERROR: peasycap"
+ "->audio_altsetting_"
+ "off already set\n");
+ SAM("...... "
+ "continuing with "
+ "%i=peasycap->"
+ "audio_altsetting_"
+ "off\n",
+ peasycap->
+ audio_altsetting_off);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (0 == ep->wMaxPacketSize) {
+ JOM(4, "intf[%i]alt[%i]end[%i] "
+ "has zero packet size\n",
+ bInterfaceNumber, i, j);
+ }
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * PERFORM INITIALIZATION OF THE PROBED INTERFACE
+ */
+/*---------------------------------------------------------------------------*/
+ JOM(4, "initialization begins for interface %i\n",
+ interface->bInterfaceNumber);
+ switch (bInterfaceNumber) {
+/*---------------------------------------------------------------------------*/
+/*
+ * INTERFACE 0 IS THE VIDEO INTERFACE
+ */
+/*---------------------------------------------------------------------------*/
+ case 0: {
+ if (!peasycap) {
+ SAM("MISTAKE: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!isokalt) {
+ SAM("ERROR: no viable video_altsetting_on\n");
+ return -ENOENT;
+ } else {
+ peasycap->video_altsetting_on = okalt[isokalt - 1];
+ JOM(4, "%i=video_altsetting_on <====\n",
+ peasycap->video_altsetting_on);
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * DECIDE THE VIDEO STREAMING PARAMETERS
+ */
+/*---------------------------------------------------------------------------*/
+ peasycap->video_endpointnumber = okepn[isokalt - 1];
+ JOM(4, "%i=video_endpointnumber\n", peasycap->video_endpointnumber);
+ maxpacketsize = okmps[isokalt - 1];
+
+ peasycap->video_isoc_maxframesize =
+ min(maxpacketsize, USB_2_0_MAXPACKETSIZE);
+ if (0 >= peasycap->video_isoc_maxframesize) {
+ SAM("ERROR: bad video_isoc_maxframesize\n");
+ SAM(" possibly because port is USB 1.1\n");
+ return -ENOENT;
+ }
+ JOM(4, "%i=video_isoc_maxframesize\n",
+ peasycap->video_isoc_maxframesize);
+
+ peasycap->video_isoc_framesperdesc = VIDEO_ISOC_FRAMESPERDESC;
+ JOM(4, "%i=video_isoc_framesperdesc\n",
+ peasycap->video_isoc_framesperdesc);
+ if (0 >= peasycap->video_isoc_framesperdesc) {
+ SAM("ERROR: bad video_isoc_framesperdesc\n");
+ return -ENOENT;
+ }
+ peasycap->video_isoc_buffer_size =
+ peasycap->video_isoc_maxframesize *
+ peasycap->video_isoc_framesperdesc;
+ JOM(4, "%i=video_isoc_buffer_size\n",
+ peasycap->video_isoc_buffer_size);
+ if ((PAGE_SIZE << VIDEO_ISOC_ORDER) <
+ peasycap->video_isoc_buffer_size) {
+ SAM("MISTAKE: peasycap->video_isoc_buffer_size too big\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+ if (-1 == peasycap->video_interface) {
+ SAM("MISTAKE: video_interface is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->video_altsetting_on) {
+ SAM("MISTAKE: video_altsetting_on is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->video_altsetting_off) {
+ SAM("MISTAKE: video_interface_off is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->video_endpointnumber) {
+ SAM("MISTAKE: video_endpointnumber is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->video_isoc_maxframesize) {
+ SAM("MISTAKE: video_isoc_maxframesize is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->video_isoc_buffer_size) {
+ SAM("MISTAKE: video_isoc_buffer_size is unset\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * ALLOCATE MEMORY FOR VIDEO BUFFERS. LISTS MUST BE INITIALIZED FIRST.
+ */
+/*---------------------------------------------------------------------------*/
+ INIT_LIST_HEAD(&(peasycap->urb_video_head));
+ peasycap->purb_video_head = &(peasycap->urb_video_head);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i frame buffers of size %li\n",
+ FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE);
+ JOM(4, ".... each scattered over %li pages\n",
+ FRAME_BUFFER_SIZE/PAGE_SIZE);
+
+ for (k = 0; k < FRAME_BUFFER_MANY; k++) {
+ for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) {
+ if (peasycap->frame_buffer[k][m].pgo)
+ SAM("attempting to reallocate frame "
+ " buffers\n");
+ else {
+ pbuf = (void *)__get_free_page(GFP_KERNEL);
+ if (!pbuf) {
+ SAM("ERROR: Could not allocate frame "
+ "buffer %i page %i\n", k, m);
+ return -ENOMEM;
+ } else
+ peasycap->allocation_video_page += 1;
+ peasycap->frame_buffer[k][m].pgo = pbuf;
+ }
+ peasycap->frame_buffer[k][m].pto =
+ peasycap->frame_buffer[k][m].pgo;
+ }
+ }
+
+ peasycap->frame_fill = 0;
+ peasycap->frame_read = 0;
+ JOM(4, "allocation of frame buffers done: %i pages\n", k *
+ m);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i field buffers of size %li\n",
+ FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE);
+ JOM(4, ".... each scattered over %li pages\n",
+ FIELD_BUFFER_SIZE/PAGE_SIZE);
+
+ for (k = 0; k < FIELD_BUFFER_MANY; k++) {
+ for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) {
+ if (peasycap->field_buffer[k][m].pgo) {
+ SAM("ERROR: attempting to reallocate "
+ "field buffers\n");
+ } else {
+ pbuf = (void *) __get_free_page(GFP_KERNEL);
+ if (!pbuf) {
+ SAM("ERROR: Could not allocate field"
+ " buffer %i page %i\n", k, m);
+ return -ENOMEM;
+ }
+ else
+ peasycap->allocation_video_page += 1;
+ peasycap->field_buffer[k][m].pgo = pbuf;
+ }
+ peasycap->field_buffer[k][m].pto =
+ peasycap->field_buffer[k][m].pgo;
+ }
+ peasycap->field_buffer[k][0].kount = 0x0200;
+ }
+ peasycap->field_fill = 0;
+ peasycap->field_page = 0;
+ peasycap->field_read = 0;
+ JOM(4, "allocation of field buffers done: %i pages\n", k *
+ m);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i isoc video buffers of size %i\n",
+ VIDEO_ISOC_BUFFER_MANY,
+ peasycap->video_isoc_buffer_size);
+ JOM(4, ".... each occupying contiguous memory pages\n");
+
+ for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
+ pbuf = (void *)__get_free_pages(GFP_KERNEL,
+ VIDEO_ISOC_ORDER);
+ if (!pbuf) {
+ SAM("ERROR: Could not allocate isoc video buffer "
+ "%i\n", k);
+ return -ENOMEM;
+ } else
+ peasycap->allocation_video_page +=
+ BIT(VIDEO_ISOC_ORDER);
+
+ peasycap->video_isoc_buffer[k].pgo = pbuf;
+ peasycap->video_isoc_buffer[k].pto =
+ pbuf + peasycap->video_isoc_buffer_size;
+ peasycap->video_isoc_buffer[k].kount = k;
+ }
+ JOM(4, "allocation of isoc video buffers done: %i pages\n",
+ k * (0x01 << VIDEO_ISOC_ORDER));
+/*---------------------------------------------------------------------------*/
+/*
+ * ALLOCATE AND INITIALIZE MULTIPLE struct urb ...
+ */
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY);
+ JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n",
+ peasycap->video_isoc_framesperdesc);
+ JOM(4, "using %i=peasycap->video_isoc_maxframesize\n",
+ peasycap->video_isoc_maxframesize);
+ JOM(4, "using %i=peasycap->video_isoc_buffer_sizen",
+ peasycap->video_isoc_buffer_size);
+
+ for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
+ purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc,
+ GFP_KERNEL);
+ if (!purb) {
+ SAM("ERROR: usb_alloc_urb returned NULL for buffer "
+ "%i\n", k);
+ return -ENOMEM;
+ } else
+ peasycap->allocation_video_urb += 1;
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
+ if (!pdata_urb) {
+ SAM("ERROR: Could not allocate struct data_urb.\n");
+ return -ENOMEM;
+ } else
+ peasycap->allocation_video_struct +=
+ sizeof(struct data_urb);
+
+ pdata_urb->purb = purb;
+ pdata_urb->isbuf = k;
+ pdata_urb->length = 0;
+ list_add_tail(&(pdata_urb->list_head),
+ peasycap->purb_video_head);
+/*---------------------------------------------------------------------------*/
+/*
+ * ... AND INITIALIZE THEM
+ */
+/*---------------------------------------------------------------------------*/
+ if (!k) {
+ JOM(4, "initializing video urbs thus:\n");
+ JOM(4, " purb->interval = 1;\n");
+ JOM(4, " purb->dev = peasycap->pusb_device;\n");
+ JOM(4, " purb->pipe = usb_rcvisocpipe"
+ "(peasycap->pusb_device,%i);\n",
+ peasycap->video_endpointnumber);
+ JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n");
+ JOM(4, " purb->transfer_buffer = peasycap->"
+ "video_isoc_buffer[.].pgo;\n");
+ JOM(4, " purb->transfer_buffer_length = %i;\n",
+ peasycap->video_isoc_buffer_size);
+ JOM(4, " purb->complete = easycap_complete;\n");
+ JOM(4, " purb->context = peasycap;\n");
+ JOM(4, " purb->start_frame = 0;\n");
+ JOM(4, " purb->number_of_packets = %i;\n",
+ peasycap->video_isoc_framesperdesc);
+ JOM(4, " for (j = 0; j < %i; j++)\n",
+ peasycap->video_isoc_framesperdesc);
+ JOM(4, " {\n");
+ JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n",
+ peasycap->video_isoc_maxframesize);
+ JOM(4, " purb->iso_frame_desc[j].length = %i;\n",
+ peasycap->video_isoc_maxframesize);
+ JOM(4, " }\n");
+ }
+
+ purb->interval = 1;
+ purb->dev = peasycap->pusb_device;
+ purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
+ peasycap->video_endpointnumber);
+ purb->transfer_flags = URB_ISO_ASAP;
+ purb->transfer_buffer = peasycap->video_isoc_buffer[k].pgo;
+ purb->transfer_buffer_length =
+ peasycap->video_isoc_buffer_size;
+ purb->complete = easycap_complete;
+ purb->context = peasycap;
+ purb->start_frame = 0;
+ purb->number_of_packets = peasycap->video_isoc_framesperdesc;
+ for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) {
+ purb->iso_frame_desc[j].offset = j *
+ peasycap->video_isoc_maxframesize;
+ purb->iso_frame_desc[j].length =
+ peasycap->video_isoc_maxframesize;
+ }
+ }
+ JOM(4, "allocation of %i struct urb done.\n", k);
+/*--------------------------------------------------------------------------*/
+/*
+ * SAVE POINTER peasycap IN THIS INTERFACE.
+ */
+/*--------------------------------------------------------------------------*/
+ usb_set_intfdata(intf, peasycap);
+/*---------------------------------------------------------------------------*/
+/*
+ * IT IS ESSENTIAL TO INITIALIZE THE HARDWARE BEFORE, RATHER THAN AFTER,
+ * THE DEVICE IS REGISTERED, BECAUSE SOME VERSIONS OF THE videodev MODULE
+ * CALL easycap_open() IMMEDIATELY AFTER REGISTRATION, CAUSING A CLASH.
+ * BEWARE.
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->ntsc = easycap_ntsc;
+ JOM(8, "defaulting initially to %s\n",
+ easycap_ntsc ? "NTSC" : "PAL");
+ rc = reset(peasycap);
+ if (rc) {
+ SAM("ERROR: reset() rc = %i\n", rc);
+ return -EFAULT;
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * THE VIDEO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
+ */
+/*--------------------------------------------------------------------------*/
+ if (v4l2_device_register(&intf->dev, &peasycap->v4l2_device)) {
+ SAM("v4l2_device_register() failed\n");
+ return -ENODEV;
+ }
+ JOM(4, "registered device instance: %s\n",
+ peasycap->v4l2_device.name);
+/*---------------------------------------------------------------------------*/
+/*
+ * FIXME
+ *
+ *
+ * THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG:
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->video_device.v4l2_dev = NULL;
+/*---------------------------------------------------------------------------*/
+
+
+ strcpy(&peasycap->video_device.name[0], "easycapdc60");
+ peasycap->video_device.fops = &v4l2_fops;
+ peasycap->video_device.minor = -1;
+ peasycap->video_device.release = (void *)(&videodev_release);
+
+ video_set_drvdata(&(peasycap->video_device), (void *)peasycap);
+
+ if (0 != (video_register_device(&(peasycap->video_device),
+ VFL_TYPE_GRABBER, -1))) {
+ err("Not able to register with videodev");
+ videodev_release(&(peasycap->video_device));
+ return -ENODEV;
+ } else {
+ (peasycap->registered_video)++;
+ SAM("registered with videodev: %i=minor\n",
+ peasycap->video_device.minor);
+ peasycap->minor = peasycap->video_device.minor;
+ }
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+
+ break;
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * INTERFACE 1 IS THE AUDIO CONTROL INTERFACE
+ * INTERFACE 2 IS THE AUDIO STREAMING INTERFACE
+ */
+/*--------------------------------------------------------------------------*/
+ case 1: {
+ if (!peasycap) {
+ SAM("MISTAKE: peasycap is NULL\n");
+ return -EFAULT;
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * SAVE POINTER peasycap IN INTERFACE 1
+ */
+/*--------------------------------------------------------------------------*/
+ usb_set_intfdata(intf, peasycap);
+ JOM(4, "no initialization required for interface %i\n",
+ interface->bInterfaceNumber);
+ break;
+ }
+/*--------------------------------------------------------------------------*/
+ case 2: {
+ if (!peasycap) {
+ SAM("MISTAKE: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!isokalt) {
+ SAM("ERROR: no viable audio_altsetting_on\n");
+ return -ENOENT;
+ } else {
+ peasycap->audio_altsetting_on = okalt[isokalt - 1];
+ JOM(4, "%i=audio_altsetting_on <====\n",
+ peasycap->audio_altsetting_on);
+ }
+
+ peasycap->audio_endpointnumber = okepn[isokalt - 1];
+ JOM(4, "%i=audio_endpointnumber\n", peasycap->audio_endpointnumber);
+
+ peasycap->audio_isoc_maxframesize = okmps[isokalt - 1];
+ JOM(4, "%i=audio_isoc_maxframesize\n",
+ peasycap->audio_isoc_maxframesize);
+ if (0 >= peasycap->audio_isoc_maxframesize) {
+ SAM("ERROR: bad audio_isoc_maxframesize\n");
+ return -ENOENT;
+ }
+ if (9 == peasycap->audio_isoc_maxframesize) {
+ peasycap->ilk |= 0x02;
+ SAM("audio hardware is microphone\n");
+ peasycap->microphone = true;
+ peasycap->audio_pages_per_fragment =
+ PAGES_PER_AUDIO_FRAGMENT;
+ } else if (256 == peasycap->audio_isoc_maxframesize) {
+ peasycap->ilk &= ~0x02;
+ SAM("audio hardware is AC'97\n");
+ peasycap->microphone = false;
+ peasycap->audio_pages_per_fragment =
+ PAGES_PER_AUDIO_FRAGMENT;
+ } else {
+ SAM("hardware is unidentified:\n");
+ SAM("%i=audio_isoc_maxframesize\n",
+ peasycap->audio_isoc_maxframesize);
+ return -ENOENT;
+ }
+
+ peasycap->audio_bytes_per_fragment =
+ peasycap->audio_pages_per_fragment * PAGE_SIZE;
+ peasycap->audio_buffer_page_many = (AUDIO_FRAGMENT_MANY *
+ peasycap->audio_pages_per_fragment);
+
+ JOM(4, "%6i=AUDIO_FRAGMENT_MANY\n", AUDIO_FRAGMENT_MANY);
+ JOM(4, "%6i=audio_pages_per_fragment\n",
+ peasycap->audio_pages_per_fragment);
+ JOM(4, "%6i=audio_bytes_per_fragment\n",
+ peasycap->audio_bytes_per_fragment);
+ JOM(4, "%6i=audio_buffer_page_many\n",
+ peasycap->audio_buffer_page_many);
+
+ peasycap->audio_isoc_framesperdesc = AUDIO_ISOC_FRAMESPERDESC;
+
+ JOM(4, "%i=audio_isoc_framesperdesc\n",
+ peasycap->audio_isoc_framesperdesc);
+ if (0 >= peasycap->audio_isoc_framesperdesc) {
+ SAM("ERROR: bad audio_isoc_framesperdesc\n");
+ return -ENOENT;
+ }
+
+ peasycap->audio_isoc_buffer_size =
+ peasycap->audio_isoc_maxframesize *
+ peasycap->audio_isoc_framesperdesc;
+ JOM(4, "%i=audio_isoc_buffer_size\n",
+ peasycap->audio_isoc_buffer_size);
+ if (AUDIO_ISOC_BUFFER_SIZE < peasycap->audio_isoc_buffer_size) {
+ SAM("MISTAKE: audio_isoc_buffer_size bigger "
+ "than %li=AUDIO_ISOC_BUFFER_SIZE\n",
+ AUDIO_ISOC_BUFFER_SIZE);
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_interface) {
+ SAM("MISTAKE: audio_interface is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_altsetting_on) {
+ SAM("MISTAKE: audio_altsetting_on is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_altsetting_off) {
+ SAM("MISTAKE: audio_interface_off is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_endpointnumber) {
+ SAM("MISTAKE: audio_endpointnumber is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_isoc_maxframesize) {
+ SAM("MISTAKE: audio_isoc_maxframesize is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_isoc_buffer_size) {
+ SAM("MISTAKE: audio_isoc_buffer_size is unset\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * ALLOCATE MEMORY FOR AUDIO BUFFERS. LISTS MUST BE INITIALIZED FIRST.
+ */
+/*---------------------------------------------------------------------------*/
+ INIT_LIST_HEAD(&(peasycap->urb_audio_head));
+ peasycap->purb_audio_head = &(peasycap->urb_audio_head);
+
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i isoc audio buffers of size %i\n",
+ AUDIO_ISOC_BUFFER_MANY,
+ peasycap->audio_isoc_buffer_size);
+ JOM(4, ".... each occupying contiguous memory pages\n");
+
+ for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
+ pbuf = (void *)__get_free_pages(GFP_KERNEL,
+ AUDIO_ISOC_ORDER);
+ if (!pbuf) {
+ SAM("ERROR: Could not allocate isoc audio buffer "
+ "%i\n", k);
+ return -ENOMEM;
+ } else
+ peasycap->allocation_audio_page +=
+ BIT(AUDIO_ISOC_ORDER);
+
+ peasycap->audio_isoc_buffer[k].pgo = pbuf;
+ peasycap->audio_isoc_buffer[k].pto = pbuf +
+ peasycap->audio_isoc_buffer_size;
+ peasycap->audio_isoc_buffer[k].kount = k;
+ }
+ JOM(4, "allocation of isoc audio buffers done.\n");
+/*---------------------------------------------------------------------------*/
+/*
+ * ALLOCATE AND INITIALIZE MULTIPLE struct urb ...
+ */
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY);
+ JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n",
+ peasycap->audio_isoc_framesperdesc);
+ JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n",
+ peasycap->audio_isoc_maxframesize);
+ JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n",
+ peasycap->audio_isoc_buffer_size);
+
+ for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
+ purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc,
+ GFP_KERNEL);
+ if (!purb) {
+ SAM("ERROR: usb_alloc_urb returned NULL for buffer "
+ "%i\n", k);
+ return -ENOMEM;
+ }
+ peasycap->allocation_audio_urb += 1 ;
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
+ if (!pdata_urb) {
+ SAM("ERROR: Could not allocate struct data_urb.\n");
+ return -ENOMEM;
+ }
+ peasycap->allocation_audio_struct +=
+ sizeof(struct data_urb);
+
+ pdata_urb->purb = purb;
+ pdata_urb->isbuf = k;
+ pdata_urb->length = 0;
+ list_add_tail(&(pdata_urb->list_head),
+ peasycap->purb_audio_head);
+/*---------------------------------------------------------------------------*/
+/*
+ * ... AND INITIALIZE THEM
+ */
+/*---------------------------------------------------------------------------*/
+ if (!k) {
+ JOM(4, "initializing audio urbs thus:\n");
+ JOM(4, " purb->interval = 1;\n");
+ JOM(4, " purb->dev = peasycap->pusb_device;\n");
+ JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->"
+ "pusb_device,%i);\n",
+ peasycap->audio_endpointnumber);
+ JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n");
+ JOM(4, " purb->transfer_buffer = "
+ "peasycap->audio_isoc_buffer[.].pgo;\n");
+ JOM(4, " purb->transfer_buffer_length = %i;\n",
+ peasycap->audio_isoc_buffer_size);
+ JOM(4, " purb->complete = easycap_alsa_complete;\n");
+ JOM(4, " purb->context = peasycap;\n");
+ JOM(4, " purb->start_frame = 0;\n");
+ JOM(4, " purb->number_of_packets = %i;\n",
+ peasycap->audio_isoc_framesperdesc);
+ JOM(4, " for (j = 0; j < %i; j++)\n",
+ peasycap->audio_isoc_framesperdesc);
+ JOM(4, " {\n");
+ JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n",
+ peasycap->audio_isoc_maxframesize);
+ JOM(4, " purb->iso_frame_desc[j].length = %i;\n",
+ peasycap->audio_isoc_maxframesize);
+ JOM(4, " }\n");
+ }
+
+ purb->interval = 1;
+ purb->dev = peasycap->pusb_device;
+ purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
+ peasycap->audio_endpointnumber);
+ purb->transfer_flags = URB_ISO_ASAP;
+ purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo;
+ purb->transfer_buffer_length =
+ peasycap->audio_isoc_buffer_size;
+ purb->complete = easycap_alsa_complete;
+ purb->context = peasycap;
+ purb->start_frame = 0;
+ purb->number_of_packets = peasycap->audio_isoc_framesperdesc;
+ for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) {
+ purb->iso_frame_desc[j].offset = j *
+ peasycap->audio_isoc_maxframesize;
+ purb->iso_frame_desc[j].length =
+ peasycap->audio_isoc_maxframesize;
+ }
+ }
+ JOM(4, "allocation of %i struct urb done.\n", k);
+/*---------------------------------------------------------------------------*/
+/*
+ * SAVE POINTER peasycap IN THIS INTERFACE.
+ */
+/*---------------------------------------------------------------------------*/
+ usb_set_intfdata(intf, peasycap);
+/*---------------------------------------------------------------------------*/
+/*
+ * THE AUDIO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
+ */
+/*---------------------------------------------------------------------------*/
+ JOM(4, "initializing ALSA card\n");
+
+ rc = easycap_alsa_probe(peasycap);
+ if (rc) {
+ err("easycap_alsa_probe() rc = %i\n", rc);
+ return -ENODEV;
+ }
+
+
+ JOM(8, "kref_get() with %i=kref.refcount.counter\n",
+ peasycap->kref.refcount.counter);
+ kref_get(&peasycap->kref);
+ peasycap->registered_audio++;
+ break;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * INTERFACES OTHER THAN 0, 1 AND 2 ARE UNEXPECTED
+ */
+/*---------------------------------------------------------------------------*/
+ default:
+ JOM(4, "ERROR: unexpected interface %i\n", bInterfaceNumber);
+ return -EINVAL;
+ }
+ SAM("ends successfully for interface %i\n", bInterfaceNumber);
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * WHEN THIS FUNCTION IS CALLED THE EasyCAP HAS ALREADY BEEN PHYSICALLY
+ * UNPLUGGED. HENCE peasycap->pusb_device IS NO LONGER VALID.
+ *
+ * THIS FUNCTION AFFECTS ALSA. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+static void easycap_usb_disconnect(struct usb_interface *pusb_interface)
+{
+ struct usb_host_interface *pusb_host_interface;
+ struct usb_interface_descriptor *pusb_interface_descriptor;
+ u8 bInterfaceNumber;
+ struct easycap *peasycap;
+
+ struct list_head *plist_head;
+ struct data_urb *pdata_urb;
+ int minor, m, kd;
+
+ JOT(4, "\n");
+
+ pusb_host_interface = pusb_interface->cur_altsetting;
+ if (!pusb_host_interface) {
+ JOT(4, "ERROR: pusb_host_interface is NULL\n");
+ return;
+ }
+ pusb_interface_descriptor = &(pusb_host_interface->desc);
+ if (!pusb_interface_descriptor) {
+ JOT(4, "ERROR: pusb_interface_descriptor is NULL\n");
+ return;
+ }
+ bInterfaceNumber = pusb_interface_descriptor->bInterfaceNumber;
+ minor = pusb_interface->minor;
+ JOT(4, "intf[%i]: minor=%i\n", bInterfaceNumber, minor);
+
+ if (1 == bInterfaceNumber)
+ return;
+
+ peasycap = usb_get_intfdata(pusb_interface);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * IF THE WAIT QUEUES ARE NOT CLEARED A DEADLOCK IS POSSIBLE. BEWARE.
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ wake_up_interruptible(&(peasycap->wq_video));
+ wake_up_interruptible(&(peasycap->wq_audio));
+/*---------------------------------------------------------------------------*/
+ switch (bInterfaceNumber) {
+ case 0: {
+ if (peasycap->purb_video_head) {
+ JOM(4, "killing video urbs\n");
+ m = 0;
+ list_for_each(plist_head, peasycap->purb_video_head) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb) {
+ if (pdata_urb->purb) {
+ usb_kill_urb(pdata_urb->purb);
+ m++;
+ }
+ }
+ }
+ JOM(4, "%i video urbs killed\n", m);
+ }
+ break;
+ }
+/*---------------------------------------------------------------------------*/
+ case 2: {
+ if (peasycap->purb_audio_head) {
+ JOM(4, "killing audio urbs\n");
+ m = 0;
+ list_for_each(plist_head, peasycap->purb_audio_head) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb) {
+ if (pdata_urb->purb) {
+ usb_kill_urb(pdata_urb->purb);
+ m++;
+ }
+ }
+ }
+ JOM(4, "%i audio urbs killed\n", m);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * DEREGISTER
+ *
+ * THIS PROCEDURE WILL BLOCK UNTIL easycap_poll(), VIDEO IOCTL AND AUDIO
+ * IOCTL ARE ALL UNLOCKED. IF THIS IS NOT DONE AN Oops CAN OCCUR WHEN
+ * AN EasyCAP IS UNPLUGGED WHILE THE URBS ARE RUNNING. BEWARE.
+ */
+/*--------------------------------------------------------------------------*/
+ kd = isdongle(peasycap);
+ switch (bInterfaceNumber) {
+ case 0: {
+ if (0 <= kd && DONGLE_MANY > kd) {
+ wake_up_interruptible(&peasycap->wq_video);
+ JOM(4, "about to lock dongle[%i].mutex_video\n", kd);
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].
+ mutex_video)) {
+ SAY("ERROR: "
+ "cannot lock dongle[%i].mutex_video\n", kd);
+ return;
+ }
+ JOM(4, "locked dongle[%i].mutex_video\n", kd);
+ } else {
+ SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
+ }
+/*---------------------------------------------------------------------------*/
+ if (!peasycap->v4l2_device.name[0]) {
+ SAM("ERROR: peasycap->v4l2_device.name is empty\n");
+ if (0 <= kd && DONGLE_MANY > kd)
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return;
+ }
+ v4l2_device_disconnect(&peasycap->v4l2_device);
+ JOM(4, "v4l2_device_disconnect() OK\n");
+ v4l2_device_unregister(&peasycap->v4l2_device);
+ JOM(4, "v4l2_device_unregister() OK\n");
+
+ video_unregister_device(&peasycap->video_device);
+ JOM(4, "intf[%i]: video_unregister_device() minor=%i\n",
+ bInterfaceNumber, minor);
+ peasycap->registered_video--;
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+
+ if (0 <= kd && DONGLE_MANY > kd) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ JOM(4, "unlocked dongle[%i].mutex_video\n", kd);
+ }
+ break;
+ }
+ case 2: {
+ if (0 <= kd && DONGLE_MANY > kd) {
+ wake_up_interruptible(&peasycap->wq_audio);
+ JOM(4, "about to lock dongle[%i].mutex_audio\n", kd);
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].
+ mutex_audio)) {
+ SAY("ERROR: "
+ "cannot lock dongle[%i].mutex_audio\n", kd);
+ return;
+ }
+ JOM(4, "locked dongle[%i].mutex_audio\n", kd);
+ } else
+ SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
+ if (0 != snd_card_free(peasycap->psnd_card)) {
+ SAY("ERROR: snd_card_free() failed\n");
+ } else {
+ peasycap->psnd_card = NULL;
+ (peasycap->registered_audio)--;
+ }
+ if (0 <= kd && DONGLE_MANY > kd) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
+ JOM(4, "unlocked dongle[%i].mutex_audio\n", kd);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap
+ * (ALSO WHEN ALSA HAS BEEN IN USE)
+ */
+/*---------------------------------------------------------------------------*/
+ if (!peasycap->kref.refcount.counter) {
+ SAM("ERROR: peasycap->kref.refcount.counter is zero "
+ "so cannot call kref_put()\n");
+ SAM("ending unsuccessfully: may cause memory leak\n");
+ return;
+ }
+ if (0 <= kd && DONGLE_MANY > kd) {
+ JOM(4, "about to lock dongle[%i].mutex_video\n", kd);
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
+ SAY("ERROR: cannot lock dongle[%i].mutex_video\n", kd);
+ SAM("ending unsuccessfully: may cause memory leak\n");
+ return;
+ }
+ JOM(4, "locked dongle[%i].mutex_video\n", kd);
+ JOM(4, "about to lock dongle[%i].mutex_audio\n", kd);
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_audio)) {
+ SAY("ERROR: cannot lock dongle[%i].mutex_audio\n", kd);
+ mutex_unlock(&(easycapdc60_dongle[kd].mutex_video));
+ JOM(4, "unlocked dongle[%i].mutex_video\n", kd);
+ SAM("ending unsuccessfully: may cause memory leak\n");
+ return;
+ }
+ JOM(4, "locked dongle[%i].mutex_audio\n", kd);
+ }
+ JOM(4, "intf[%i]: %i=peasycap->kref.refcount.counter\n",
+ bInterfaceNumber, (int)peasycap->kref.refcount.counter);
+ kref_put(&peasycap->kref, easycap_delete);
+ JOT(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber);
+ if (0 <= kd && DONGLE_MANY > kd) {
+ mutex_unlock(&(easycapdc60_dongle[kd].mutex_audio));
+ JOT(4, "unlocked dongle[%i].mutex_audio\n", kd);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ JOT(4, "unlocked dongle[%i].mutex_video\n", kd);
+ }
+/*---------------------------------------------------------------------------*/
+ JOM(4, "ends\n");
+ return;
+}
+/*****************************************************************************/
+
+/*---------------------------------------------------------------------------*/
+/*
+ * PARAMETERS APPLICABLE TO ENTIRE DRIVER, I.E. BOTH VIDEO AND AUDIO
+ */
+/*---------------------------------------------------------------------------*/
+static struct usb_device_id easycap_usb_device_id_table[] = {
+ {USB_DEVICE(USB_EASYCAP_VENDOR_ID, USB_EASYCAP_PRODUCT_ID)},
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, easycap_usb_device_id_table);
+struct usb_driver easycap_usb_driver = {
+ .name = "easycap",
+ .id_table = easycap_usb_device_id_table,
+ .probe = easycap_usb_probe,
+ .disconnect = easycap_usb_disconnect,
+};
+
+static int __init easycap_module_init(void)
+{
+ int k, rc;
+
+ printk(KERN_INFO "Easycap version: "EASYCAP_DRIVER_VERSION "\n");
+
+ JOT(4, "begins. %i=debug %i=bars %i=gain\n",
+ easycap_debug, easycap_bars, easycap_gain);
+
+ mutex_init(&mutex_dongle);
+ for (k = 0; k < DONGLE_MANY; k++) {
+ easycapdc60_dongle[k].peasycap = NULL;
+ mutex_init(&easycapdc60_dongle[k].mutex_video);
+ mutex_init(&easycapdc60_dongle[k].mutex_audio);
+ }
+ rc = usb_register(&easycap_usb_driver);
+ if (rc)
+ printk(KERN_ERR "Easycap: usb_register failed rc=%d\n", rc);
+
+ return rc;
+}
+/*****************************************************************************/
+static void __exit easycap_module_exit(void)
+{
+ usb_deregister(&easycap_usb_driver);
+}
+/*****************************************************************************/
+
+module_init(easycap_module_init);
+module_exit(easycap_module_exit);
+
+/*****************************************************************************/
--- /dev/null
+/******************************************************************************
+* *
+* easycap_settings.c *
+* *
+******************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This 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.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+
+#include "easycap.h"
+
+/*---------------------------------------------------------------------------*/
+/*
+ * THE LEAST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING:
+ * 0 => 25 fps
+ * 1 => 30 fps
+ *
+ * THE MOST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING:
+ * 0 => full framerate
+ * 1 => 20% framerate
+ */
+/*---------------------------------------------------------------------------*/
+const struct easycap_standard easycap_standard[] = {
+ {
+ .mask = 0x00FF & PAL_BGHIN ,
+ .v4l2_standard = {
+ .index = PAL_BGHIN,
+ .id = (V4L2_STD_PAL_B |
+ V4L2_STD_PAL_G | V4L2_STD_PAL_H |
+ V4L2_STD_PAL_I | V4L2_STD_PAL_N),
+ .name = "PAL_BGHIN",
+ .frameperiod = {1, 25},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & NTSC_N_443 ,
+ .v4l2_standard = {
+ .index = NTSC_N_443,
+ .id = V4L2_STD_UNKNOWN,
+ .name = "NTSC_N_443",
+ .frameperiod = {1, 25},
+ .framelines = 480,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & PAL_Nc ,
+ .v4l2_standard = {
+ .index = PAL_Nc,
+ .id = V4L2_STD_PAL_Nc,
+ .name = "PAL_Nc",
+ .frameperiod = {1, 25},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & NTSC_N ,
+ .v4l2_standard = {
+ .index = NTSC_N,
+ .id = V4L2_STD_UNKNOWN,
+ .name = "NTSC_N",
+ .frameperiod = {1, 25},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & SECAM ,
+ .v4l2_standard = {
+ .index = SECAM,
+ .id = V4L2_STD_SECAM,
+ .name = "SECAM",
+ .frameperiod = {1, 25},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & NTSC_M ,
+ .v4l2_standard = {
+ .index = NTSC_M,
+ .id = V4L2_STD_NTSC_M,
+ .name = "NTSC_M",
+ .frameperiod = {1, 30},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & NTSC_M_JP ,
+ .v4l2_standard = {
+ .index = NTSC_M_JP,
+ .id = V4L2_STD_NTSC_M_JP,
+ .name = "NTSC_M_JP",
+ .frameperiod = {1, 30},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & PAL_60 ,
+ .v4l2_standard = {
+ .index = PAL_60,
+ .id = V4L2_STD_PAL_60,
+ .name = "PAL_60",
+ .frameperiod = {1, 30},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & NTSC_443 ,
+ .v4l2_standard = {
+ .index = NTSC_443,
+ .id = V4L2_STD_NTSC_443,
+ .name = "NTSC_443",
+ .frameperiod = {1, 30},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & PAL_M ,
+ .v4l2_standard = {
+ .index = PAL_M,
+ .id = V4L2_STD_PAL_M,
+ .name = "PAL_M",
+ .frameperiod = {1, 30},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & PAL_BGHIN_SLOW),
+ .v4l2_standard = {
+ .index = PAL_BGHIN_SLOW,
+ .id = (V4L2_STD_PAL_B | V4L2_STD_PAL_G |
+ V4L2_STD_PAL_H |
+ V4L2_STD_PAL_I | V4L2_STD_PAL_N |
+ (((v4l2_std_id)0x01) << 32)),
+ .name = "PAL_BGHIN_SLOW",
+ .frameperiod = {1, 5},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & NTSC_N_443_SLOW),
+ .v4l2_standard = {
+ .index = NTSC_N_443_SLOW,
+ .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x11) << 32)),
+ .name = "NTSC_N_443_SLOW",
+ .frameperiod = {1, 5},
+ .framelines = 480,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & PAL_Nc_SLOW),
+ .v4l2_standard = {
+ .index = PAL_Nc_SLOW,
+ .id = (V4L2_STD_PAL_Nc | (((v4l2_std_id)0x01) << 32)),
+ .name = "PAL_Nc_SLOW",
+ .frameperiod = {1, 5},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & NTSC_N_SLOW),
+ .v4l2_standard = {
+ .index = NTSC_N_SLOW,
+ .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x21) << 32)),
+ .name = "NTSC_N_SLOW",
+ .frameperiod = {1, 5},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & SECAM_SLOW),
+ .v4l2_standard = {
+ .index = SECAM_SLOW,
+ .id = (V4L2_STD_SECAM | (((v4l2_std_id)0x01) << 32)),
+ .name = "SECAM_SLOW",
+ .frameperiod = {1, 5},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & NTSC_M_SLOW),
+ .v4l2_standard = {
+ .index = NTSC_M_SLOW,
+ .id = (V4L2_STD_NTSC_M | (((v4l2_std_id)0x01) << 32)),
+ .name = "NTSC_M_SLOW",
+ .frameperiod = {1, 6},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & NTSC_M_JP_SLOW),
+ .v4l2_standard = {
+ .index = NTSC_M_JP_SLOW,
+ .id = (V4L2_STD_NTSC_M_JP |
+ (((v4l2_std_id)0x01) << 32)),
+ .name = "NTSC_M_JP_SLOW",
+ .frameperiod = {1, 6},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & PAL_60_SLOW),
+ .v4l2_standard = {
+ .index = PAL_60_SLOW,
+ .id = (V4L2_STD_PAL_60 | (((v4l2_std_id)0x01) << 32)),
+ .name = "PAL_60_SLOW",
+ .frameperiod = {1, 6},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & NTSC_443_SLOW),
+ .v4l2_standard = {
+ .index = NTSC_443_SLOW,
+ .id = (V4L2_STD_NTSC_443 | (((v4l2_std_id)0x01) << 32)),
+ .name = "NTSC_443_SLOW",
+ .frameperiod = {1, 6},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & PAL_M_SLOW),
+ .v4l2_standard = {
+ .index = PAL_M_SLOW,
+ .id = (V4L2_STD_PAL_M | (((v4l2_std_id)0x01) << 32)),
+ .name = "PAL_M_SLOW",
+ .frameperiod = {1, 6},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0xFFFF
+ }
+};
+/*---------------------------------------------------------------------------*/
+/*
+ * THE 16-BIT easycap_format.mask HAS MEANING:
+ * (least significant) BIT 0: 0 => PAL, 25 FPS; 1 => NTSC, 30 FPS
+ * BITS 2-4: RESERVED FOR DIFFERENTIATING STANDARDS
+ * BITS 5-7: NUMBER OF BYTES PER PIXEL
+ * BIT 8: 0 => NATIVE BYTE ORDER; 1 => SWAPPED
+ * BITS 9-10: RESERVED FOR OTHER BYTE PERMUTATIONS
+ * BIT 11: 0 => UNDECIMATED; 1 => DECIMATED
+ * BIT 12: 0 => OFFER FRAMES; 1 => OFFER FIELDS
+ * BIT 13: 0 => FULL FRAMERATE; 1 => REDUCED
+ * (most significant) BITS 14-15: RESERVED FOR OTHER FIELD/FRAME OPTIONS
+ * IT FOLLOWS THAT:
+ * bytesperpixel IS ((0x00E0 & easycap_format.mask) >> 5)
+ * byteswaporder IS true IF (0 != (0x0100 & easycap_format.mask))
+ *
+ * decimatepixel IS true IF (0 != (0x0800 & easycap_format.mask))
+ *
+ * offerfields IS true IF (0 != (0x1000 & easycap_format.mask))
+ */
+/*---------------------------------------------------------------------------*/
+
+struct easycap_format easycap_format[1 + SETTINGS_MANY];
+
+int fillin_formats(void)
+{
+ const char *name1, *name2, *name3, *name4;
+ struct v4l2_format *fmt;
+ int i, j, k, m, n;
+ u32 width, height, pixelformat, bytesperline, sizeimage;
+ u16 mask1, mask2, mask3, mask4;
+ enum v4l2_field field;
+ enum v4l2_colorspace colorspace;
+
+ for (i = 0, n = 0; i < STANDARD_MANY; i++) {
+ mask1 = 0x0000;
+ switch (i) {
+ case PAL_BGHIN: {
+ mask1 = 0x1F & PAL_BGHIN;
+ name1 = "PAL_BGHIN";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case SECAM: {
+ mask1 = 0x1F & SECAM;
+ name1 = "SECAM";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_Nc: {
+ mask1 = 0x1F & PAL_Nc;
+ name1 = "PAL_Nc";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_60: {
+ mask1 = 0x1F & PAL_60;
+ name1 = "PAL_60";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_M: {
+ mask1 = 0x1F & PAL_M;
+ name1 = "PAL_M";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case NTSC_M: {
+ mask1 = 0x1F & NTSC_M;
+ name1 = "NTSC_M";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_443: {
+ mask1 = 0x1F & NTSC_443;
+ name1 = "NTSC_443";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_M_JP: {
+ mask1 = 0x1F & NTSC_M_JP;
+ name1 = "NTSC_M_JP";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_N: {
+ mask1 = 0x1F & NTSC_M;
+ name1 = "NTSC_N";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_N_443: {
+ mask1 = 0x1F & NTSC_N_443;
+ name1 = "NTSC_N_443";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case PAL_BGHIN_SLOW: {
+ mask1 = 0x001F & PAL_BGHIN_SLOW;
+ mask1 |= 0x0200;
+ name1 = "PAL_BGHIN_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case SECAM_SLOW: {
+ mask1 = 0x001F & SECAM_SLOW;
+ mask1 |= 0x0200;
+ name1 = "SECAM_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_Nc_SLOW: {
+ mask1 = 0x001F & PAL_Nc_SLOW;
+ mask1 |= 0x0200;
+ name1 = "PAL_Nc_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_60_SLOW: {
+ mask1 = 0x001F & PAL_60_SLOW;
+ mask1 |= 0x0200;
+ name1 = "PAL_60_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_M_SLOW: {
+ mask1 = 0x001F & PAL_M_SLOW;
+ mask1 |= 0x0200;
+ name1 = "PAL_M_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case NTSC_M_SLOW: {
+ mask1 = 0x001F & NTSC_M_SLOW;
+ mask1 |= 0x0200;
+ name1 = "NTSC_M_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_443_SLOW: {
+ mask1 = 0x001F & NTSC_443_SLOW;
+ mask1 |= 0x0200;
+ name1 = "NTSC_443_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_M_JP_SLOW: {
+ mask1 = 0x001F & NTSC_M_JP_SLOW;
+ mask1 |= 0x0200;
+ name1 = "NTSC_M_JP_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_N_SLOW: {
+ mask1 = 0x001F & NTSC_N_SLOW;
+ mask1 |= 0x0200;
+ name1 = "NTSC_N_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_N_443_SLOW: {
+ mask1 = 0x001F & NTSC_N_443_SLOW;
+ mask1 |= 0x0200;
+ name1 = "NTSC_N_443_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ default:
+ return -1;
+ }
+
+ for (j = 0; j < RESOLUTION_MANY; j++) {
+ mask2 = 0x0000;
+ switch (j) {
+ case AT_720x576: {
+ if (0x1 & mask1)
+ continue;
+ name2 = "_AT_720x576";
+ width = 720;
+ height = 576;
+ break;
+ }
+ case AT_704x576: {
+ if (0x1 & mask1)
+ continue;
+ name2 = "_AT_704x576";
+ width = 704;
+ height = 576;
+ break;
+ }
+ case AT_640x480: {
+ name2 = "_AT_640x480";
+ width = 640;
+ height = 480;
+ break;
+ }
+ case AT_720x480: {
+ if (!(0x1 & mask1))
+ continue;
+ name2 = "_AT_720x480";
+ width = 720;
+ height = 480;
+ break;
+ }
+ case AT_360x288: {
+ if (0x1 & mask1)
+ continue;
+ name2 = "_AT_360x288";
+ width = 360;
+ height = 288;
+ mask2 = 0x0800;
+ break;
+ }
+ case AT_320x240: {
+ name2 = "_AT_320x240";
+ width = 320;
+ height = 240;
+ mask2 = 0x0800;
+ break;
+ }
+ case AT_360x240: {
+ if (!(0x1 & mask1))
+ continue;
+ name2 = "_AT_360x240";
+ width = 360;
+ height = 240;
+ mask2 = 0x0800;
+ break;
+ }
+ default:
+ return -2;
+ }
+
+ for (k = 0; k < PIXELFORMAT_MANY; k++) {
+ mask3 = 0x0000;
+ switch (k) {
+ case FMT_UYVY: {
+ name3 = __stringify(FMT_UYVY);
+ pixelformat = V4L2_PIX_FMT_UYVY;
+ mask3 |= (0x02 << 5);
+ break;
+ }
+ case FMT_YUY2: {
+ name3 = __stringify(FMT_YUY2);
+ pixelformat = V4L2_PIX_FMT_YUYV;
+ mask3 |= (0x02 << 5);
+ mask3 |= 0x0100;
+ break;
+ }
+ case FMT_RGB24: {
+ name3 = __stringify(FMT_RGB24);
+ pixelformat = V4L2_PIX_FMT_RGB24;
+ mask3 |= (0x03 << 5);
+ break;
+ }
+ case FMT_RGB32: {
+ name3 = __stringify(FMT_RGB32);
+ pixelformat = V4L2_PIX_FMT_RGB32;
+ mask3 |= (0x04 << 5);
+ break;
+ }
+ case FMT_BGR24: {
+ name3 = __stringify(FMT_BGR24);
+ pixelformat = V4L2_PIX_FMT_BGR24;
+ mask3 |= (0x03 << 5);
+ mask3 |= 0x0100;
+ break;
+ }
+ case FMT_BGR32: {
+ name3 = __stringify(FMT_BGR32);
+ pixelformat = V4L2_PIX_FMT_BGR32;
+ mask3 |= (0x04 << 5);
+ mask3 |= 0x0100;
+ break;
+ }
+ default:
+ return -3;
+ }
+ bytesperline = width * ((mask3 & 0x00E0) >> 5);
+ sizeimage = bytesperline * height;
+
+ for (m = 0; m < INTERLACE_MANY; m++) {
+ mask4 = 0x0000;
+ switch (m) {
+ case FIELD_NONE: {
+ name4 = "-n";
+ field = V4L2_FIELD_NONE;
+ break;
+ }
+ case FIELD_INTERLACED: {
+ name4 = "-i";
+ mask4 |= 0x1000;
+ field = V4L2_FIELD_INTERLACED;
+ break;
+ }
+ default:
+ return -4;
+ }
+ if (SETTINGS_MANY <= n)
+ return -5;
+
+ strcpy(easycap_format[n].name, name1);
+ strcat(easycap_format[n].name, name2);
+ strcat(easycap_format[n].name, "_");
+ strcat(easycap_format[n].name, name3);
+ strcat(easycap_format[n].name, name4);
+ easycap_format[n].mask =
+ mask1 | mask2 | mask3 | mask4;
+ fmt = &easycap_format[n].v4l2_format;
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt->fmt.pix.width = width;
+ fmt->fmt.pix.height = height;
+ fmt->fmt.pix.pixelformat = pixelformat;
+ fmt->fmt.pix.field = field;
+ fmt->fmt.pix.bytesperline = bytesperline;
+ fmt->fmt.pix.sizeimage = sizeimage;
+ fmt->fmt.pix.colorspace = colorspace;
+ fmt->fmt.pix.priv = 0;
+ n++;
+ }
+ }
+ }
+ }
+ if ((1 + SETTINGS_MANY) <= n)
+ return -6;
+ easycap_format[n].mask = 0xFFFF;
+ return n;
+}
+/*---------------------------------------------------------------------------*/
+struct v4l2_queryctrl easycap_control[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = SAA_0A_DEFAULT,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = SAA_0B_DEFAULT + 128,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = SAA_0C_DEFAULT + 128,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = SAA_0D_DEFAULT + 128,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 16,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mute",
+ .default_value = true,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = 0xFFFFFFFF
+ }
+};
+/*****************************************************************************/
--- /dev/null
+/******************************************************************************
+* *
+* easycap_sound.c *
+* *
+* Audio driver for EasyCAP USB2.0 Video Capture Device DC60 *
+* *
+* *
+******************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This 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.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+
+#include "easycap.h"
+
+/*--------------------------------------------------------------------------*/
+/*
+ * PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE
+ */
+/*--------------------------------------------------------------------------*/
+static const struct snd_pcm_hardware alsa_hardware = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+ .rate_min = 32000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = PAGE_SIZE *
+ PAGES_PER_AUDIO_FRAGMENT *
+ AUDIO_FRAGMENT_MANY,
+ .period_bytes_min = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT,
+ .period_bytes_max = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT * 2,
+ .periods_min = AUDIO_FRAGMENT_MANY,
+ .periods_max = AUDIO_FRAGMENT_MANY * 2,
+};
+
+
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE DAM BUFFER
+ * PROVIDED peasycap->audio_idle IS ZERO. REGARDLESS OF THIS BEING TRUE,
+ * IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO.
+ */
+/*---------------------------------------------------------------------------*/
+void
+easycap_alsa_complete(struct urb *purb)
+{
+ struct easycap *peasycap;
+ struct snd_pcm_substream *pss;
+ struct snd_pcm_runtime *prt;
+ int dma_bytes, fragment_bytes;
+ int isfragment;
+ u8 *p1, *p2;
+ s16 tmp;
+ int i, j, more, much, rc;
+#ifdef UPSAMPLE
+ int k;
+ s16 oldaudio, newaudio, delta;
+#endif /*UPSAMPLE*/
+
+ JOT(16, "\n");
+
+ if (!purb) {
+ SAY("ERROR: purb is NULL\n");
+ return;
+ }
+ peasycap = purb->context;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return;
+ }
+ much = 0;
+ if (peasycap->audio_idle) {
+ JOM(16, "%i=audio_idle %i=audio_isoc_streaming\n",
+ peasycap->audio_idle, peasycap->audio_isoc_streaming);
+ if (peasycap->audio_isoc_streaming)
+ goto resubmit;
+ }
+/*---------------------------------------------------------------------------*/
+ pss = peasycap->psubstream;
+ if (!pss)
+ goto resubmit;
+ prt = pss->runtime;
+ if (!prt)
+ goto resubmit;
+ dma_bytes = (int)prt->dma_bytes;
+ if (0 == dma_bytes)
+ goto resubmit;
+ fragment_bytes = 4 * ((int)prt->period_size);
+ if (0 == fragment_bytes)
+ goto resubmit;
+/* -------------------------------------------------------------------------*/
+ if (purb->status) {
+ if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
+ JOM(16, "urb status -ESHUTDOWN or -ENOENT\n");
+ return;
+ }
+ SAM("ERROR: non-zero urb status: -%s: %d\n",
+ strerror(purb->status), purb->status);
+ goto resubmit;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * PROCEED HERE WHEN NO ERROR
+ */
+/*---------------------------------------------------------------------------*/
+
+#ifdef UPSAMPLE
+ oldaudio = peasycap->oldaudio;
+#endif /*UPSAMPLE*/
+
+ for (i = 0; i < purb->number_of_packets; i++) {
+ if (purb->iso_frame_desc[i].status < 0) {
+ SAM("-%s: %d\n",
+ strerror(purb->iso_frame_desc[i].status),
+ purb->iso_frame_desc[i].status);
+ }
+ if (purb->iso_frame_desc[i].status) {
+ JOM(12, "discarding audio samples because "
+ "%i=purb->iso_frame_desc[i].status\n",
+ purb->iso_frame_desc[i].status);
+ continue;
+ }
+ more = purb->iso_frame_desc[i].actual_length;
+ if (more == 0) {
+ peasycap->audio_mt++;
+ continue;
+ }
+ if (0 > more) {
+ SAM("MISTAKE: more is negative\n");
+ return;
+ }
+
+ if (peasycap->audio_mt) {
+ JOM(12, "%4i empty audio urb frames\n",
+ peasycap->audio_mt);
+ peasycap->audio_mt = 0;
+ }
+
+ p1 = (u8 *)(purb->transfer_buffer +
+ purb->iso_frame_desc[i].offset);
+
+ /*
+ * COPY more BYTES FROM ISOC BUFFER
+ * TO THE DMA BUFFER, CONVERTING
+ * 8-BIT MONO TO 16-BIT SIGNED
+ * LITTLE-ENDIAN SAMPLES IF NECESSARY
+ */
+ while (more) {
+ much = dma_bytes - peasycap->dma_fill;
+ if (0 > much) {
+ SAM("MISTAKE: much is negative\n");
+ return;
+ }
+ if (0 == much) {
+ peasycap->dma_fill = 0;
+ peasycap->dma_next = fragment_bytes;
+ JOM(8, "wrapped dma buffer\n");
+ }
+ if (!peasycap->microphone) {
+ if (much > more)
+ much = more;
+ memcpy(prt->dma_area + peasycap->dma_fill,
+ p1, much);
+ p1 += much;
+ more -= much;
+ } else {
+#ifdef UPSAMPLE
+ if (much % 16)
+ JOM(8, "MISTAKE? much"
+ " is not divisible by 16\n");
+ if (much > (16 * more))
+ much = 16 * more;
+ p2 = (u8 *)(prt->dma_area + peasycap->dma_fill);
+
+ for (j = 0; j < (much / 16); j++) {
+ newaudio = ((int) *p1) - 128;
+ newaudio = 128 * newaudio;
+
+ delta = (newaudio - oldaudio) / 4;
+ tmp = oldaudio + delta;
+
+ for (k = 0; k < 4; k++) {
+ *p2 = (0x00FF & tmp);
+ *(p2 + 1) = (0xFF00 & tmp) >> 8;
+ p2 += 2;
+ *p2 = (0x00FF & tmp);
+ *(p2 + 1) = (0xFF00 & tmp) >> 8;
+ p2 += 2;
+ tmp += delta;
+ }
+ p1++;
+ more--;
+ oldaudio = tmp;
+ }
+#else /*!UPSAMPLE*/
+ if (much > (2 * more))
+ much = 2 * more;
+ p2 = (u8 *)(prt->dma_area + peasycap->dma_fill);
+
+ for (j = 0; j < (much / 2); j++) {
+ tmp = ((int) *p1) - 128;
+ tmp = 128 * tmp;
+ *p2 = (0x00FF & tmp);
+ *(p2 + 1) = (0xFF00 & tmp) >> 8;
+ p1++;
+ p2 += 2;
+ more--;
+ }
+#endif /*UPSAMPLE*/
+ }
+ peasycap->dma_fill += much;
+ if (peasycap->dma_fill >= peasycap->dma_next) {
+ isfragment = peasycap->dma_fill / fragment_bytes;
+ if (0 > isfragment) {
+ SAM("MISTAKE: isfragment is negative\n");
+ return;
+ }
+ peasycap->dma_read = (isfragment - 1) * fragment_bytes;
+ peasycap->dma_next = (isfragment + 1) * fragment_bytes;
+ if (dma_bytes < peasycap->dma_next)
+ peasycap->dma_next = fragment_bytes;
+
+ if (0 <= peasycap->dma_read) {
+ JOM(8, "snd_pcm_period_elapsed(), %i="
+ "isfragment\n", isfragment);
+ snd_pcm_period_elapsed(pss);
+ }
+ }
+ }
+
+#ifdef UPSAMPLE
+ peasycap->oldaudio = oldaudio;
+#endif /*UPSAMPLE*/
+
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * RESUBMIT THIS URB
+ */
+/*---------------------------------------------------------------------------*/
+resubmit:
+ if (peasycap->audio_isoc_streaming == 0)
+ return;
+
+ rc = usb_submit_urb(purb, GFP_ATOMIC);
+ if (rc) {
+ if ((-ENODEV != rc) && (-ENOENT != rc)) {
+ SAM("ERROR: while %i=audio_idle, usb_submit_urb failed "
+ "with rc: -%s :%d\n",
+ peasycap->audio_idle, strerror(rc), rc);
+ }
+ if (0 < peasycap->audio_isoc_streaming)
+ peasycap->audio_isoc_streaming--;
+ }
+ return;
+}
+/*****************************************************************************/
+static int easycap_alsa_open(struct snd_pcm_substream *pss)
+{
+ struct snd_pcm *psnd_pcm;
+ struct snd_card *psnd_card;
+ struct easycap *peasycap;
+
+ JOT(4, "\n");
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ psnd_pcm = pss->pcm;
+ if (!psnd_pcm) {
+ SAY("ERROR: psnd_pcm is NULL\n");
+ return -EFAULT;
+ }
+ psnd_card = psnd_pcm->card;
+ if (!psnd_card) {
+ SAY("ERROR: psnd_card is NULL\n");
+ return -EFAULT;
+ }
+
+ peasycap = psnd_card->private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (peasycap->psnd_card != psnd_card) {
+ SAM("ERROR: bad peasycap->psnd_card\n");
+ return -EFAULT;
+ }
+ if (peasycap->psubstream) {
+ SAM("ERROR: bad peasycap->psubstream\n");
+ return -EFAULT;
+ }
+ pss->private_data = peasycap;
+ peasycap->psubstream = pss;
+ pss->runtime->hw = peasycap->alsa_hardware;
+ pss->runtime->private_data = peasycap;
+ pss->private_data = peasycap;
+
+ if (0 != easycap_sound_setup(peasycap)) {
+ JOM(4, "ending unsuccessfully\n");
+ return -EFAULT;
+ }
+ JOM(4, "ending successfully\n");
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_close(struct snd_pcm_substream *pss)
+{
+ struct easycap *peasycap;
+
+ JOT(4, "\n");
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ peasycap = snd_pcm_substream_chip(pss);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ pss->private_data = NULL;
+ peasycap->psubstream = NULL;
+ JOT(4, "ending successfully\n");
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_vmalloc(struct snd_pcm_substream *pss, size_t sz)
+{
+ struct snd_pcm_runtime *prt;
+ JOT(4, "\n");
+
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ prt = pss->runtime;
+ if (!prt) {
+ SAY("ERROR: substream.runtime is NULL\n");
+ return -EFAULT;
+ }
+ if (prt->dma_area) {
+ if (prt->dma_bytes > sz)
+ return 0;
+ vfree(prt->dma_area);
+ }
+ prt->dma_area = vmalloc(sz);
+ if (!prt->dma_area)
+ return -ENOMEM;
+ prt->dma_bytes = sz;
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_hw_params(struct snd_pcm_substream *pss,
+ struct snd_pcm_hw_params *phw)
+{
+ int rc;
+
+ JOT(4, "%i\n", (params_buffer_bytes(phw)));
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ rc = easycap_alsa_vmalloc(pss, params_buffer_bytes(phw));
+ if (rc)
+ return rc;
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_hw_free(struct snd_pcm_substream *pss)
+{
+ struct snd_pcm_runtime *prt;
+ JOT(4, "\n");
+
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ prt = pss->runtime;
+ if (!prt) {
+ SAY("ERROR: substream.runtime is NULL\n");
+ return -EFAULT;
+ }
+ if (prt->dma_area) {
+ JOT(8, "prt->dma_area = %p\n", prt->dma_area);
+ vfree(prt->dma_area);
+ prt->dma_area = NULL;
+ } else
+ JOT(8, "dma_area already freed\n");
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_prepare(struct snd_pcm_substream *pss)
+{
+ struct easycap *peasycap;
+ struct snd_pcm_runtime *prt;
+
+ JOT(4, "\n");
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ prt = pss->runtime;
+ peasycap = snd_pcm_substream_chip(pss);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+
+ JOM(16, "ALSA decides %8i Hz=rate\n", pss->runtime->rate);
+ JOM(16, "ALSA decides %8ld =period_size\n", pss->runtime->period_size);
+ JOM(16, "ALSA decides %8i =periods\n", pss->runtime->periods);
+ JOM(16, "ALSA decides %8ld =buffer_size\n", pss->runtime->buffer_size);
+ JOM(16, "ALSA decides %8zd =dma_bytes\n", pss->runtime->dma_bytes);
+ JOM(16, "ALSA decides %8ld =boundary\n", pss->runtime->boundary);
+ JOM(16, "ALSA decides %8i =period_step\n", pss->runtime->period_step);
+ JOM(16, "ALSA decides %8i =sample_bits\n", pss->runtime->sample_bits);
+ JOM(16, "ALSA decides %8i =frame_bits\n", pss->runtime->frame_bits);
+ JOM(16, "ALSA decides %8ld =min_align\n", pss->runtime->min_align);
+ JOM(12, "ALSA decides %8ld =hw_ptr_base\n", pss->runtime->hw_ptr_base);
+ JOM(12, "ALSA decides %8ld =hw_ptr_interrupt\n",
+ pss->runtime->hw_ptr_interrupt);
+
+ if (prt->dma_bytes != 4 * ((int)prt->period_size) * ((int)prt->periods)) {
+ SAY("MISTAKE: unexpected ALSA parameters\n");
+ return -ENOENT;
+ }
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_ack(struct snd_pcm_substream *pss)
+{
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_trigger(struct snd_pcm_substream *pss, int cmd)
+{
+ struct easycap *peasycap;
+ int retval;
+
+ JOT(4, "%i=cmd cf %i=START %i=STOP\n", cmd, SNDRV_PCM_TRIGGER_START,
+ SNDRV_PCM_TRIGGER_STOP);
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ peasycap = snd_pcm_substream_chip(pss);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START: {
+ peasycap->audio_idle = 0;
+ break;
+ }
+ case SNDRV_PCM_TRIGGER_STOP: {
+ peasycap->audio_idle = 1;
+ break;
+ }
+ default:
+ retval = -EINVAL;
+ }
+ return 0;
+}
+/*****************************************************************************/
+static snd_pcm_uframes_t easycap_alsa_pointer(struct snd_pcm_substream *pss)
+{
+ struct easycap *peasycap;
+ snd_pcm_uframes_t offset;
+
+ JOT(16, "\n");
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ peasycap = snd_pcm_substream_chip(pss);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if ((0 != peasycap->audio_eof) || (0 != peasycap->audio_idle)) {
+ JOM(8, "returning -EIO because "
+ "%i=audio_idle %i=audio_eof\n",
+ peasycap->audio_idle, peasycap->audio_eof);
+ return -EIO;
+ }
+/*---------------------------------------------------------------------------*/
+ if (0 > peasycap->dma_read) {
+ JOM(8, "returning -EBUSY\n");
+ return -EBUSY;
+ }
+ offset = ((snd_pcm_uframes_t)peasycap->dma_read)/4;
+ JOM(8, "ALSA decides %8i =hw_ptr_base\n", (int)pss->runtime->hw_ptr_base);
+ JOM(8, "ALSA decides %8i =hw_ptr_interrupt\n",
+ (int)pss->runtime->hw_ptr_interrupt);
+ JOM(8, "%7i=offset %7i=dma_read %7i=dma_next\n",
+ (int)offset, peasycap->dma_read, peasycap->dma_next);
+ return offset;
+}
+/*****************************************************************************/
+static struct page *
+easycap_alsa_page(struct snd_pcm_substream *pss, unsigned long offset)
+{
+ return vmalloc_to_page(pss->runtime->dma_area + offset);
+}
+/*****************************************************************************/
+
+static struct snd_pcm_ops easycap_alsa_pcm_ops = {
+ .open = easycap_alsa_open,
+ .close = easycap_alsa_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = easycap_alsa_hw_params,
+ .hw_free = easycap_alsa_hw_free,
+ .prepare = easycap_alsa_prepare,
+ .ack = easycap_alsa_ack,
+ .trigger = easycap_alsa_trigger,
+ .pointer = easycap_alsa_pointer,
+ .page = easycap_alsa_page,
+};
+
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * THE FUNCTION snd_card_create() HAS THIS_MODULE AS AN ARGUMENT. THIS
+ * MEANS MODULE easycap. BEWARE.
+*/
+/*---------------------------------------------------------------------------*/
+int easycap_alsa_probe(struct easycap *peasycap)
+{
+ int rc;
+ struct snd_card *psnd_card;
+ struct snd_pcm *psnd_pcm;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -ENODEV;
+ }
+ if (0 > peasycap->minor) {
+ SAY("ERROR: no minor\n");
+ return -ENODEV;
+ }
+
+ peasycap->alsa_hardware = alsa_hardware;
+ if (peasycap->microphone) {
+ peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_32000;
+ peasycap->alsa_hardware.rate_min = 32000;
+ peasycap->alsa_hardware.rate_max = 32000;
+ } else {
+ peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_48000;
+ peasycap->alsa_hardware.rate_min = 48000;
+ peasycap->alsa_hardware.rate_max = 48000;
+ }
+
+ if (0 != snd_card_create(SNDRV_DEFAULT_IDX1, "easycap_alsa",
+ THIS_MODULE, 0, &psnd_card)) {
+ SAY("ERROR: Cannot do ALSA snd_card_create()\n");
+ return -EFAULT;
+ }
+
+ sprintf(&psnd_card->id[0], "EasyALSA%i", peasycap->minor);
+ strcpy(&psnd_card->driver[0], EASYCAP_DRIVER_DESCRIPTION);
+ strcpy(&psnd_card->shortname[0], "easycap_alsa");
+ sprintf(&psnd_card->longname[0], "%s", &psnd_card->shortname[0]);
+
+ psnd_card->dev = &peasycap->pusb_device->dev;
+ psnd_card->private_data = peasycap;
+ peasycap->psnd_card = psnd_card;
+
+ rc = snd_pcm_new(psnd_card, "easycap_pcm", 0, 0, 1, &psnd_pcm);
+ if (rc) {
+ SAM("ERROR: Cannot do ALSA snd_pcm_new()\n");
+ snd_card_free(psnd_card);
+ return -EFAULT;
+ }
+
+ snd_pcm_set_ops(psnd_pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &easycap_alsa_pcm_ops);
+ psnd_pcm->info_flags = 0;
+ strcpy(&psnd_pcm->name[0], &psnd_card->id[0]);
+ psnd_pcm->private_data = peasycap;
+ peasycap->psnd_pcm = psnd_pcm;
+ peasycap->psubstream = NULL;
+
+ rc = snd_card_register(psnd_card);
+ if (rc) {
+ SAM("ERROR: Cannot do ALSA snd_card_register()\n");
+ snd_card_free(psnd_card);
+ return -EFAULT;
+ }
+
+ SAM("registered %s\n", &psnd_card->id[0]);
+ return 0;
+}
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * COMMON AUDIO INITIALIZATION
+ */
+/*---------------------------------------------------------------------------*/
+int
+easycap_sound_setup(struct easycap *peasycap)
+{
+ int rc;
+
+ JOM(4, "starting initialization\n");
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL.\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -ENODEV;
+ }
+ JOM(16, "0x%08lX=peasycap->pusb_device\n", (long int)peasycap->pusb_device);
+
+ rc = audio_setup(peasycap);
+ JOM(8, "audio_setup() returned %i\n", rc);
+
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device has become NULL\n");
+ return -ENODEV;
+ }
+/*---------------------------------------------------------------------------*/
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device has become NULL\n");
+ return -ENODEV;
+ }
+ rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface,
+ peasycap->audio_altsetting_on);
+ JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface,
+ peasycap->audio_altsetting_on, rc);
+
+ rc = wakeup_device(peasycap->pusb_device);
+ JOM(8, "wakeup_device() returned %i\n", rc);
+
+ peasycap->audio_eof = 0;
+ peasycap->audio_idle = 0;
+
+ submit_audio_urbs(peasycap);
+
+ JOM(4, "finished initialization\n");
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * SUBMIT ALL AUDIO URBS.
+ */
+/*---------------------------------------------------------------------------*/
+int
+submit_audio_urbs(struct easycap *peasycap)
+{
+ struct data_urb *pdata_urb;
+ struct urb *purb;
+ struct list_head *plist_head;
+ int j, isbad, nospc, m, rc;
+ int isbuf;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->purb_audio_head) {
+ SAM("ERROR: peasycap->urb_audio_head uninitialized\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+
+ if (peasycap->audio_isoc_streaming) {
+ JOM(4, "already streaming audio urbs\n");
+ return 0;
+ }
+
+ JOM(4, "initial submission of all audio urbs\n");
+ rc = usb_set_interface(peasycap->pusb_device,
+ peasycap->audio_interface,
+ peasycap->audio_altsetting_on);
+ JOM(8, "usb_set_interface(.,%i,%i) returned %i\n",
+ peasycap->audio_interface,
+ peasycap->audio_altsetting_on, rc);
+
+ isbad = 0;
+ nospc = 0;
+ m = 0;
+ list_for_each(plist_head, peasycap->purb_audio_head) {
+ pdata_urb = list_entry(plist_head, struct data_urb, list_head);
+ if (pdata_urb && pdata_urb->purb) {
+ purb = pdata_urb->purb;
+ isbuf = pdata_urb->isbuf;
+
+ purb->interval = 1;
+ purb->dev = peasycap->pusb_device;
+ purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
+ peasycap->audio_endpointnumber);
+ purb->transfer_flags = URB_ISO_ASAP;
+ purb->transfer_buffer = peasycap->audio_isoc_buffer[isbuf].pgo;
+ purb->transfer_buffer_length = peasycap->audio_isoc_buffer_size;
+ purb->complete = easycap_alsa_complete;
+ purb->context = peasycap;
+ purb->start_frame = 0;
+ purb->number_of_packets = peasycap->audio_isoc_framesperdesc;
+ for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) {
+ purb->iso_frame_desc[j].offset = j * peasycap->audio_isoc_maxframesize;
+ purb->iso_frame_desc[j].length = peasycap->audio_isoc_maxframesize;
+ }
+
+ rc = usb_submit_urb(purb, GFP_KERNEL);
+ if (rc) {
+ isbad++;
+ SAM("ERROR: usb_submit_urb() failed"
+ " for urb with rc: -%s: %d\n",
+ strerror(rc), rc);
+ } else {
+ m++;
+ }
+ } else {
+ isbad++;
+ }
+ }
+ if (nospc) {
+ SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc);
+ SAM("..... possibly inadequate USB bandwidth\n");
+ peasycap->audio_eof = 1;
+ }
+ if (isbad) {
+ JOM(4, "attempting cleanup instead of submitting\n");
+ list_for_each(plist_head, (peasycap->purb_audio_head)) {
+ pdata_urb = list_entry(plist_head, struct data_urb, list_head);
+ if (pdata_urb && pdata_urb->purb)
+ usb_kill_urb(pdata_urb->purb);
+ }
+ peasycap->audio_isoc_streaming = 0;
+ } else {
+ peasycap->audio_isoc_streaming = m;
+ JOM(4, "submitted %i audio urbs\n", m);
+ }
+
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * KILL ALL AUDIO URBS.
+ */
+/*---------------------------------------------------------------------------*/
+int
+kill_audio_urbs(struct easycap *peasycap)
+{
+ int m;
+ struct list_head *plist_head;
+ struct data_urb *pdata_urb;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+
+ if (!peasycap->audio_isoc_streaming) {
+ JOM(8, "%i=audio_isoc_streaming, no audio urbs killed\n",
+ peasycap->audio_isoc_streaming);
+ return 0;
+ }
+
+ if (!peasycap->purb_audio_head) {
+ SAM("ERROR: peasycap->purb_audio_head is NULL\n");
+ return -EFAULT;
+ }
+
+ peasycap->audio_isoc_streaming = 0;
+ JOM(4, "killing audio urbs\n");
+ m = 0;
+ list_for_each(plist_head, (peasycap->purb_audio_head)) {
+ pdata_urb = list_entry(plist_head, struct data_urb, list_head);
+ if (pdata_urb && pdata_urb->purb) {
+ usb_kill_urb(pdata_urb->purb);
+ m++;
+ }
+ }
+ JOM(4, "%i audio urbs killed\n", m);
+
+ return 0;
+}
+/*****************************************************************************/
--- /dev/null
+/******************************************************************************
+* *
+* easycap_testcard.c *
+* *
+******************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This 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.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+
+#include "easycap.h"
+
+/*****************************************************************************/
+#define TESTCARD_BYTESPERLINE (2 * 720)
+void
+easycap_testcard(struct easycap *peasycap, int field)
+{
+ int total;
+ int y, u, v, r, g, b;
+ unsigned char uyvy[4];
+ int i1, line, k, m, n, more, much, barwidth, barheight;
+ unsigned char bfbar[TESTCARD_BYTESPERLINE / 8], *p1, *p2;
+ struct data_buffer *pfield_buffer;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return;
+ }
+ JOM(8, "%i=field\n", field);
+ switch (peasycap->width) {
+ case 720:
+ case 360: {
+ barwidth = (2 * 720) / 8;
+ break;
+ }
+ case 704:
+ case 352: {
+ barwidth = (2 * 704) / 8;
+ break;
+ }
+ case 640:
+ case 320: {
+ barwidth = (2 * 640) / 8;
+ break;
+ }
+ default: {
+ SAM("ERROR: cannot set barwidth\n");
+ return;
+ }
+ }
+ if (TESTCARD_BYTESPERLINE < barwidth) {
+ SAM("ERROR: barwidth is too large\n");
+ return;
+ }
+ switch (peasycap->height) {
+ case 576:
+ case 288: {
+ barheight = 576;
+ break;
+ }
+ case 480:
+ case 240: {
+ barheight = 480;
+ break;
+ }
+ default: {
+ SAM("ERROR: cannot set barheight\n");
+ return;
+ }
+ }
+ total = 0;
+ k = field;
+ m = 0;
+ n = 0;
+
+ for (line = 0; line < (barheight / 2); line++) {
+ for (i1 = 0; i1 < 8; i1++) {
+ r = (i1 * 256)/8;
+ g = (i1 * 256)/8;
+ b = (i1 * 256)/8;
+
+ y = 299*r/1000 + 587*g/1000 + 114*b/1000 ;
+ u = -147*r/1000 - 289*g/1000 + 436*b/1000 ;
+ u = u + 128;
+ v = 615*r/1000 - 515*g/1000 - 100*b/1000 ;
+ v = v + 128;
+
+ uyvy[0] = 0xFF & u ;
+ uyvy[1] = 0xFF & y ;
+ uyvy[2] = 0xFF & v ;
+ uyvy[3] = 0xFF & y ;
+
+ p1 = &bfbar[0];
+ while (p1 < &bfbar[barwidth]) {
+ *p1++ = uyvy[0] ;
+ *p1++ = uyvy[1] ;
+ *p1++ = uyvy[2] ;
+ *p1++ = uyvy[3] ;
+ total += 4;
+ }
+
+ p1 = &bfbar[0];
+ more = barwidth;
+
+ while (more) {
+ if ((FIELD_BUFFER_SIZE/PAGE_SIZE) <= m) {
+ SAM("ERROR: bad m reached\n");
+ return;
+ }
+ if (PAGE_SIZE < n) {
+ SAM("ERROR: bad n reached\n");
+ return;
+ }
+
+ if (0 > more) {
+ SAM("ERROR: internal fault\n");
+ return;
+ }
+
+ much = PAGE_SIZE - n;
+ if (much > more)
+ much = more;
+ pfield_buffer = &peasycap->field_buffer[k][m];
+ p2 = pfield_buffer->pgo + n;
+ memcpy(p2, p1, much);
+
+ p1 += much;
+ n += much;
+ more -= much;
+ if (PAGE_SIZE == n) {
+ m++;
+ n = 0;
+ }
+ }
+ }
+ }
+ return;
+}
--- /dev/null
+config VIDEO_GO7007
+ tristate "WIS GO7007 MPEG encoder support"
+ depends on VIDEO_DEV && PCI && I2C
+ depends on SND
+ select VIDEOBUF_DMA_SG
+ depends on RC_CORE
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select SND_PCM
+ select CRC32
+ default N
+ ---help---
+ This is a video4linux driver for the WIS GO7007 MPEG
+ encoder chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called go7007
+
+config VIDEO_GO7007_USB
+ tristate "WIS GO7007 USB support"
+ depends on VIDEO_GO7007 && USB
+ default N
+ ---help---
+ This is a video4linux driver for the WIS GO7007 MPEG
+ encoder chip over USB.
+
+ To compile this driver as a module, choose M here: the
+ module will be called go7007-usb
+
+config VIDEO_GO7007_USB_S2250_BOARD
+ tristate "Sensoray 2250/2251 support"
+ depends on VIDEO_GO7007_USB && DVB_USB
+ default N
+ ---help---
+ This is a video4linux driver for the Sensoray 2250/2251 device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s2250
+
+config VIDEO_GO7007_OV7640
+ tristate "OV7640 subdev support"
+ depends on VIDEO_GO7007
+ default N
+ ---help---
+ This is a video4linux driver for the OV7640 sub-device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wis-ov7640
+
+config VIDEO_GO7007_SAA7113
+ tristate "SAA7113 subdev support"
+ depends on VIDEO_GO7007
+ default N
+ ---help---
+ This is a video4linux driver for the SAA7113 sub-device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wis-saa7113
+
+config VIDEO_GO7007_SAA7115
+ tristate "SAA7115 subdev support"
+ depends on VIDEO_GO7007
+ default N
+ ---help---
+ This is a video4linux driver for the SAA7115 sub-device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wis-saa7115
+
+config VIDEO_GO7007_TW9903
+ tristate "TW9903 subdev support"
+ depends on VIDEO_GO7007
+ default N
+ ---help---
+ This is a video4linux driver for the TW9903 sub-device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wis-tw9903
+
+config VIDEO_GO7007_UDA1342
+ tristate "UDA1342 subdev support"
+ depends on VIDEO_GO7007
+ default N
+ ---help---
+ This is a video4linux driver for the UDA1342 sub-device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wis-uda1342
+
+config VIDEO_GO7007_SONY_TUNER
+ tristate "Sony tuner subdev support"
+ depends on VIDEO_GO7007
+ default N
+ ---help---
+ This is a video4linux driver for the Sony Tuner sub-device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wis-sony-tuner
+
+config VIDEO_GO7007_TW2804
+ tristate "TW2804 subdev support"
+ depends on VIDEO_GO7007
+ default N
+ ---help---
+ This is a video4linux driver for the TW2804 sub-device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wis-tw2804
+
--- /dev/null
+#obj-m += go7007.o go7007-usb.o snd-go7007.o wis-saa7115.o wis-tw9903.o \
+ wis-uda1342.o wis-sony-tuner.o wis-saa7113.o wis-ov7640.o \
+ wis-tw2804.o
+
+
+obj-$(CONFIG_VIDEO_GO7007) += go7007.o
+obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o
+obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o s2250-loader.o
+obj-$(CONFIG_VIDEO_GO7007_SAA7113) += wis-saa7113.o
+obj-$(CONFIG_VIDEO_GO7007_OV7640) += wis-ov7640.o
+obj-$(CONFIG_VIDEO_GO7007_SAA7115) += wis-saa7115.o
+obj-$(CONFIG_VIDEO_GO7007_TW9903) += wis-tw9903.o
+obj-$(CONFIG_VIDEO_GO7007_UDA1342) += wis-uda1342.o
+obj-$(CONFIG_VIDEO_GO7007_SONY_TUNER) += wis-sony-tuner.o
+obj-$(CONFIG_VIDEO_GO7007_TW2804) += wis-tw2804.o
+
+go7007-y := go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \
+ snd-go7007.o
+
+s2250-y := s2250-board.o
+
+# Uncomment when the saa7134 patches get into upstream
+#obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o
+#ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/video/saa7134 -DSAA7134_MPEG_GO7007=3
+
+# S2250 needs cypress ezusb loader from dvb-usb
+ccflags-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD:m=y) += -Idrivers/media/dvb/dvb-usb
+
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-core
--- /dev/null
+Todo:
+ - checkpatch.pl cleanups
+ - sparse cleanups
+ - lots of little modules, should be merged together
+ and added to the build.
+ - testing?
+ - handle churn in v4l layer.
+
+Please send patchs to Greg Kroah-Hartman <greg@kroah.com> and Cc: Ross
+Cohen <rcohen@snurgle.org> as well.
+
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <asm/system.h>
+#include <linux/videodev2.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+
+#include "go7007-priv.h"
+#include "wis-i2c.h"
+
+/*
+ * Wait for an interrupt to be delivered from the GO7007SB and return
+ * the associated value and data.
+ *
+ * Must be called with the hw_lock held.
+ */
+int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data)
+{
+ go->interrupt_available = 0;
+ go->hpi_ops->read_interrupt(go);
+ if (wait_event_timeout(go->interrupt_waitq,
+ go->interrupt_available, 5*HZ) < 0) {
+ v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n");
+ return -1;
+ }
+ if (!go->interrupt_available)
+ return -1;
+ go->interrupt_available = 0;
+ *value = go->interrupt_value & 0xfffe;
+ *data = go->interrupt_data;
+ return 0;
+}
+EXPORT_SYMBOL(go7007_read_interrupt);
+
+/*
+ * Read a register/address on the GO7007SB.
+ *
+ * Must be called with the hw_lock held.
+ */
+int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data)
+{
+ int count = 100;
+ u16 value;
+
+ if (go7007_write_interrupt(go, 0x0010, addr) < 0)
+ return -EIO;
+ while (count-- > 0) {
+ if (go7007_read_interrupt(go, &value, data) == 0 &&
+ value == 0xa000)
+ return 0;
+ }
+ return -EIO;
+}
+EXPORT_SYMBOL(go7007_read_addr);
+
+/*
+ * Send the boot firmware to the encoder, which just wakes it up and lets
+ * us talk to the GPIO pins and on-board I2C adapter.
+ *
+ * Must be called with the hw_lock held.
+ */
+static int go7007_load_encoder(struct go7007 *go)
+{
+ const struct firmware *fw_entry;
+ char fw_name[] = "go7007fw.bin";
+ void *bounce;
+ int fw_len, rv = 0;
+ u16 intr_val, intr_data;
+
+ if (request_firmware(&fw_entry, fw_name, go->dev)) {
+ v4l2_err(go, "unable to load firmware from file "
+ "\"%s\"\n", fw_name);
+ return -1;
+ }
+ if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) {
+ v4l2_err(go, "file \"%s\" does not appear to be "
+ "go7007 firmware\n", fw_name);
+ release_firmware(fw_entry);
+ return -1;
+ }
+ fw_len = fw_entry->size - 16;
+ bounce = kmalloc(fw_len, GFP_KERNEL);
+ if (bounce == NULL) {
+ v4l2_err(go, "unable to allocate %d bytes for "
+ "firmware transfer\n", fw_len);
+ release_firmware(fw_entry);
+ return -1;
+ }
+ memcpy(bounce, fw_entry->data + 16, fw_len);
+ release_firmware(fw_entry);
+ if (go7007_interface_reset(go) < 0 ||
+ go7007_send_firmware(go, bounce, fw_len) < 0 ||
+ go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
+ (intr_val & ~0x1) != 0x5a5a) {
+ v4l2_err(go, "error transferring firmware\n");
+ rv = -1;
+ }
+ kfree(bounce);
+ return rv;
+}
+
+MODULE_FIRMWARE("go7007fw.bin");
+
+/*
+ * Boot the encoder and register the I2C adapter if requested. Do the
+ * minimum initialization necessary, since the board-specific code may
+ * still need to probe the board ID.
+ *
+ * Must NOT be called with the hw_lock held.
+ */
+int go7007_boot_encoder(struct go7007 *go, int init_i2c)
+{
+ int ret;
+
+ mutex_lock(&go->hw_lock);
+ ret = go7007_load_encoder(go);
+ mutex_unlock(&go->hw_lock);
+ if (ret < 0)
+ return -1;
+ if (!init_i2c)
+ return 0;
+ if (go7007_i2c_init(go) < 0)
+ return -1;
+ go->i2c_adapter_online = 1;
+ return 0;
+}
+EXPORT_SYMBOL(go7007_boot_encoder);
+
+/*
+ * Configure any hardware-related registers in the GO7007, such as GPIO
+ * pins and bus parameters, which are board-specific. This assumes
+ * the boot firmware has already been downloaded.
+ *
+ * Must be called with the hw_lock held.
+ */
+static int go7007_init_encoder(struct go7007 *go)
+{
+ if (go->board_info->audio_flags & GO7007_AUDIO_I2S_MASTER) {
+ go7007_write_addr(go, 0x1000, 0x0811);
+ go7007_write_addr(go, 0x1000, 0x0c11);
+ }
+ if (go->board_id == GO7007_BOARDID_MATRIX_REV) {
+ /* Set GPIO pin 0 to be an output (audio clock control) */
+ go7007_write_addr(go, 0x3c82, 0x0001);
+ go7007_write_addr(go, 0x3c80, 0x00fe);
+ }
+ return 0;
+}
+
+/*
+ * Send the boot firmware to the GO7007 and configure the registers. This
+ * is the only way to stop the encoder once it has started streaming video.
+ *
+ * Must be called with the hw_lock held.
+ */
+int go7007_reset_encoder(struct go7007 *go)
+{
+ if (go7007_load_encoder(go) < 0)
+ return -1;
+ return go7007_init_encoder(go);
+}
+
+/*
+ * Attempt to instantiate an I2C client by ID, probably loading a module.
+ */
+static int init_i2c_module(struct i2c_adapter *adapter, const char *type,
+ int addr)
+{
+ struct go7007 *go = i2c_get_adapdata(adapter);
+ struct v4l2_device *v4l2_dev = &go->v4l2_dev;
+
+ if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL))
+ return 0;
+
+ printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type);
+ return -1;
+}
+
+/*
+ * Finalize the GO7007 hardware setup, register the on-board I2C adapter
+ * (if used on this board), load the I2C client driver for the sensor
+ * (SAA7115 or whatever) and other devices, and register the ALSA and V4L2
+ * interfaces.
+ *
+ * Must NOT be called with the hw_lock held.
+ */
+int go7007_register_encoder(struct go7007 *go)
+{
+ int i, ret;
+
+ printk(KERN_INFO "go7007: registering new %s\n", go->name);
+
+ mutex_lock(&go->hw_lock);
+ ret = go7007_init_encoder(go);
+ mutex_unlock(&go->hw_lock);
+ if (ret < 0)
+ return -1;
+
+ /* v4l2 init must happen before i2c subdevs */
+ ret = go7007_v4l2_init(go);
+ if (ret < 0)
+ return ret;
+
+ if (!go->i2c_adapter_online &&
+ go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) {
+ if (go7007_i2c_init(go) < 0)
+ return -1;
+ go->i2c_adapter_online = 1;
+ }
+ if (go->i2c_adapter_online) {
+ for (i = 0; i < go->board_info->num_i2c_devs; ++i)
+ init_i2c_module(&go->i2c_adapter,
+ go->board_info->i2c_devs[i].type,
+ go->board_info->i2c_devs[i].addr);
+ if (go->board_id == GO7007_BOARDID_ADLINK_MPG24)
+ i2c_clients_command(&go->i2c_adapter,
+ DECODER_SET_CHANNEL, &go->channel_number);
+ }
+ if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) {
+ go->audio_enabled = 1;
+ go7007_snd_init(go);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(go7007_register_encoder);
+
+/*
+ * Send the encode firmware to the encoder, which will cause it
+ * to immediately start delivering the video and audio streams.
+ *
+ * Must be called with the hw_lock held.
+ */
+int go7007_start_encoder(struct go7007 *go)
+{
+ u8 *fw;
+ int fw_len, rv = 0, i;
+ u16 intr_val, intr_data;
+
+ go->modet_enable = 0;
+ if (!go->dvd_mode)
+ for (i = 0; i < 4; ++i) {
+ if (go->modet[i].enable) {
+ go->modet_enable = 1;
+ continue;
+ }
+ go->modet[i].pixel_threshold = 32767;
+ go->modet[i].motion_threshold = 32767;
+ go->modet[i].mb_threshold = 32767;
+ }
+
+ if (go7007_construct_fw_image(go, &fw, &fw_len) < 0)
+ return -1;
+
+ if (go7007_send_firmware(go, fw, fw_len) < 0 ||
+ go7007_read_interrupt(go, &intr_val, &intr_data) < 0) {
+ v4l2_err(&go->v4l2_dev, "error transferring firmware\n");
+ rv = -1;
+ goto start_error;
+ }
+
+ go->state = STATE_DATA;
+ go->parse_length = 0;
+ go->seen_frame = 0;
+ if (go7007_stream_start(go) < 0) {
+ v4l2_err(&go->v4l2_dev, "error starting stream transfer\n");
+ rv = -1;
+ goto start_error;
+ }
+
+start_error:
+ kfree(fw);
+ return rv;
+}
+
+/*
+ * Store a byte in the current video buffer, if there is one.
+ */
+static inline void store_byte(struct go7007_buffer *gobuf, u8 byte)
+{
+ if (gobuf != NULL && gobuf->bytesused < GO7007_BUF_SIZE) {
+ unsigned int pgidx = gobuf->offset >> PAGE_SHIFT;
+ unsigned int pgoff = gobuf->offset & ~PAGE_MASK;
+
+ *((u8 *)page_address(gobuf->pages[pgidx]) + pgoff) = byte;
+ ++gobuf->offset;
+ ++gobuf->bytesused;
+ }
+}
+
+/*
+ * Deliver the last video buffer and get a new one to start writing to.
+ */
+static void frame_boundary(struct go7007 *go)
+{
+ struct go7007_buffer *gobuf;
+ int i;
+
+ if (go->active_buf) {
+ if (go->active_buf->modet_active) {
+ if (go->active_buf->bytesused + 216 < GO7007_BUF_SIZE) {
+ for (i = 0; i < 216; ++i)
+ store_byte(go->active_buf,
+ go->active_map[i]);
+ go->active_buf->bytesused -= 216;
+ } else
+ go->active_buf->modet_active = 0;
+ }
+ go->active_buf->state = BUF_STATE_DONE;
+ wake_up_interruptible(&go->frame_waitq);
+ go->active_buf = NULL;
+ }
+ list_for_each_entry(gobuf, &go->stream, stream)
+ if (gobuf->state == BUF_STATE_QUEUED) {
+ gobuf->seq = go->next_seq;
+ do_gettimeofday(&gobuf->timestamp);
+ go->active_buf = gobuf;
+ break;
+ }
+ ++go->next_seq;
+}
+
+static void write_bitmap_word(struct go7007 *go)
+{
+ int x, y, i, stride = ((go->width >> 4) + 7) >> 3;
+
+ for (i = 0; i < 16; ++i) {
+ y = (((go->parse_length - 1) << 3) + i) / (go->width >> 4);
+ x = (((go->parse_length - 1) << 3) + i) % (go->width >> 4);
+ if (stride * y + (x >> 3) < sizeof(go->active_map))
+ go->active_map[stride * y + (x >> 3)] |=
+ (go->modet_word & 1) << (x & 0x7);
+ go->modet_word >>= 1;
+ }
+}
+
+/*
+ * Parse a chunk of the video stream into frames. The frames are not
+ * delimited by the hardware, so we have to parse the frame boundaries
+ * based on the type of video stream we're receiving.
+ */
+void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length)
+{
+ int i, seq_start_code = -1, frame_start_code = -1;
+
+ spin_lock(&go->spinlock);
+
+ switch (go->format) {
+ case GO7007_FORMAT_MPEG4:
+ seq_start_code = 0xB0;
+ frame_start_code = 0xB6;
+ break;
+ case GO7007_FORMAT_MPEG1:
+ case GO7007_FORMAT_MPEG2:
+ seq_start_code = 0xB3;
+ frame_start_code = 0x00;
+ break;
+ }
+
+ for (i = 0; i < length; ++i) {
+ if (go->active_buf != NULL &&
+ go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) {
+ v4l2_info(&go->v4l2_dev, "dropping oversized frame\n");
+ go->active_buf->offset -= go->active_buf->bytesused;
+ go->active_buf->bytesused = 0;
+ go->active_buf->modet_active = 0;
+ go->active_buf = NULL;
+ }
+
+ switch (go->state) {
+ case STATE_DATA:
+ switch (buf[i]) {
+ case 0x00:
+ go->state = STATE_00;
+ break;
+ case 0xFF:
+ go->state = STATE_FF;
+ break;
+ default:
+ store_byte(go->active_buf, buf[i]);
+ break;
+ }
+ break;
+ case STATE_00:
+ switch (buf[i]) {
+ case 0x00:
+ go->state = STATE_00_00;
+ break;
+ case 0xFF:
+ store_byte(go->active_buf, 0x00);
+ go->state = STATE_FF;
+ break;
+ default:
+ store_byte(go->active_buf, 0x00);
+ store_byte(go->active_buf, buf[i]);
+ go->state = STATE_DATA;
+ break;
+ }
+ break;
+ case STATE_00_00:
+ switch (buf[i]) {
+ case 0x00:
+ store_byte(go->active_buf, 0x00);
+ /* go->state remains STATE_00_00 */
+ break;
+ case 0x01:
+ go->state = STATE_00_00_01;
+ break;
+ case 0xFF:
+ store_byte(go->active_buf, 0x00);
+ store_byte(go->active_buf, 0x00);
+ go->state = STATE_FF;
+ break;
+ default:
+ store_byte(go->active_buf, 0x00);
+ store_byte(go->active_buf, 0x00);
+ store_byte(go->active_buf, buf[i]);
+ go->state = STATE_DATA;
+ break;
+ }
+ break;
+ case STATE_00_00_01:
+ if (buf[i] == 0xF8 && go->modet_enable == 0) {
+ /* MODET start code, but MODET not enabled */
+ store_byte(go->active_buf, 0x00);
+ store_byte(go->active_buf, 0x00);
+ store_byte(go->active_buf, 0x01);
+ store_byte(go->active_buf, 0xF8);
+ go->state = STATE_DATA;
+ break;
+ }
+ /* If this is the start of a new MPEG frame,
+ * get a new buffer */
+ if ((go->format == GO7007_FORMAT_MPEG1 ||
+ go->format == GO7007_FORMAT_MPEG2 ||
+ go->format == GO7007_FORMAT_MPEG4) &&
+ (buf[i] == seq_start_code ||
+ buf[i] == 0xB8 || /* GOP code */
+ buf[i] == frame_start_code)) {
+ if (go->active_buf == NULL || go->seen_frame)
+ frame_boundary(go);
+ if (buf[i] == frame_start_code) {
+ if (go->active_buf != NULL)
+ go->active_buf->frame_offset =
+ go->active_buf->offset;
+ go->seen_frame = 1;
+ } else {
+ go->seen_frame = 0;
+ }
+ }
+ /* Handle any special chunk types, or just write the
+ * start code to the (potentially new) buffer */
+ switch (buf[i]) {
+ case 0xF5: /* timestamp */
+ go->parse_length = 12;
+ go->state = STATE_UNPARSED;
+ break;
+ case 0xF6: /* vbi */
+ go->state = STATE_VBI_LEN_A;
+ break;
+ case 0xF8: /* MD map */
+ go->parse_length = 0;
+ memset(go->active_map, 0,
+ sizeof(go->active_map));
+ go->state = STATE_MODET_MAP;
+ break;
+ case 0xFF: /* Potential JPEG start code */
+ store_byte(go->active_buf, 0x00);
+ store_byte(go->active_buf, 0x00);
+ store_byte(go->active_buf, 0x01);
+ go->state = STATE_FF;
+ break;
+ default:
+ store_byte(go->active_buf, 0x00);
+ store_byte(go->active_buf, 0x00);
+ store_byte(go->active_buf, 0x01);
+ store_byte(go->active_buf, buf[i]);
+ go->state = STATE_DATA;
+ break;
+ }
+ break;
+ case STATE_FF:
+ switch (buf[i]) {
+ case 0x00:
+ store_byte(go->active_buf, 0xFF);
+ go->state = STATE_00;
+ break;
+ case 0xFF:
+ store_byte(go->active_buf, 0xFF);
+ /* go->state remains STATE_FF */
+ break;
+ case 0xD8:
+ if (go->format == GO7007_FORMAT_MJPEG)
+ frame_boundary(go);
+ /* fall through */
+ default:
+ store_byte(go->active_buf, 0xFF);
+ store_byte(go->active_buf, buf[i]);
+ go->state = STATE_DATA;
+ break;
+ }
+ break;
+ case STATE_VBI_LEN_A:
+ go->parse_length = buf[i] << 8;
+ go->state = STATE_VBI_LEN_B;
+ break;
+ case STATE_VBI_LEN_B:
+ go->parse_length |= buf[i];
+ if (go->parse_length > 0)
+ go->state = STATE_UNPARSED;
+ else
+ go->state = STATE_DATA;
+ break;
+ case STATE_MODET_MAP:
+ if (go->parse_length < 204) {
+ if (go->parse_length & 1) {
+ go->modet_word |= buf[i];
+ write_bitmap_word(go);
+ } else
+ go->modet_word = buf[i] << 8;
+ } else if (go->parse_length == 207 && go->active_buf) {
+ go->active_buf->modet_active = buf[i];
+ }
+ if (++go->parse_length == 208)
+ go->state = STATE_DATA;
+ break;
+ case STATE_UNPARSED:
+ if (--go->parse_length == 0)
+ go->state = STATE_DATA;
+ break;
+ }
+ }
+
+ spin_unlock(&go->spinlock);
+}
+EXPORT_SYMBOL(go7007_parse_video_stream);
+
+/*
+ * Allocate a new go7007 struct. Used by the hardware-specific probe.
+ */
+struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev)
+{
+ struct go7007 *go;
+ int i;
+
+ go = kmalloc(sizeof(struct go7007), GFP_KERNEL);
+ if (go == NULL)
+ return NULL;
+ go->dev = dev;
+ go->board_info = board;
+ go->board_id = 0;
+ go->tuner_type = -1;
+ go->channel_number = 0;
+ go->name[0] = 0;
+ mutex_init(&go->hw_lock);
+ init_waitqueue_head(&go->frame_waitq);
+ spin_lock_init(&go->spinlock);
+ go->video_dev = NULL;
+ go->ref_count = 0;
+ go->status = STATUS_INIT;
+ memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter));
+ go->i2c_adapter_online = 0;
+ go->interrupt_available = 0;
+ init_waitqueue_head(&go->interrupt_waitq);
+ go->in_use = 0;
+ go->input = 0;
+ if (board->sensor_flags & GO7007_SENSOR_TV) {
+ go->standard = GO7007_STD_NTSC;
+ go->width = 720;
+ go->height = 480;
+ go->sensor_framerate = 30000;
+ } else {
+ go->standard = GO7007_STD_OTHER;
+ go->width = board->sensor_width;
+ go->height = board->sensor_height;
+ go->sensor_framerate = board->sensor_framerate;
+ }
+ go->encoder_v_offset = board->sensor_v_offset;
+ go->encoder_h_offset = board->sensor_h_offset;
+ go->encoder_h_halve = 0;
+ go->encoder_v_halve = 0;
+ go->encoder_subsample = 0;
+ go->streaming = 0;
+ go->format = GO7007_FORMAT_MJPEG;
+ go->bitrate = 1500000;
+ go->fps_scale = 1;
+ go->pali = 0;
+ go->aspect_ratio = GO7007_RATIO_1_1;
+ go->gop_size = 0;
+ go->ipb = 0;
+ go->closed_gop = 0;
+ go->repeat_seqhead = 0;
+ go->seq_header_enable = 0;
+ go->gop_header_enable = 0;
+ go->dvd_mode = 0;
+ go->interlace_coding = 0;
+ for (i = 0; i < 4; ++i)
+ go->modet[i].enable = 0;
+ for (i = 0; i < 1624; ++i)
+ go->modet_map[i] = 0;
+ go->audio_deliver = NULL;
+ go->audio_enabled = 0;
+ INIT_LIST_HEAD(&go->stream);
+
+ return go;
+}
+EXPORT_SYMBOL(go7007_alloc);
+
+/*
+ * Detach and unregister the encoder. The go7007 struct won't be freed
+ * until v4l2 finishes releasing its resources and all associated fds are
+ * closed by applications.
+ */
+void go7007_remove(struct go7007 *go)
+{
+ if (go->i2c_adapter_online) {
+ if (i2c_del_adapter(&go->i2c_adapter) == 0)
+ go->i2c_adapter_online = 0;
+ else
+ v4l2_err(&go->v4l2_dev,
+ "error removing I2C adapter!\n");
+ }
+
+ if (go->audio_enabled)
+ go7007_snd_remove(go);
+ go7007_v4l2_remove(go);
+}
+EXPORT_SYMBOL(go7007_remove);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * This file contains code to generate a firmware image for the GO7007SB
+ * encoder. Much of the firmware is read verbatim from a file, but some of
+ * it concerning bitrate control and other things that can be configured at
+ * run-time are generated dynamically. Note that the format headers
+ * generated here do not affect the functioning of the encoder; they are
+ * merely parroted back to the host at the start of each frame.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+
+#include "go7007-priv.h"
+
+/* Constants used in the source firmware image to describe code segments */
+
+#define FLAG_MODE_MJPEG (1)
+#define FLAG_MODE_MPEG1 (1<<1)
+#define FLAG_MODE_MPEG2 (1<<2)
+#define FLAG_MODE_MPEG4 (1<<3)
+#define FLAG_MODE_H263 (1<<4)
+#define FLAG_MODE_ALL (FLAG_MODE_MJPEG | FLAG_MODE_MPEG1 | \
+ FLAG_MODE_MPEG2 | FLAG_MODE_MPEG4 | \
+ FLAG_MODE_H263)
+#define FLAG_SPECIAL (1<<8)
+
+#define SPECIAL_FRM_HEAD 0
+#define SPECIAL_BRC_CTRL 1
+#define SPECIAL_CONFIG 2
+#define SPECIAL_SEQHEAD 3
+#define SPECIAL_AV_SYNC 4
+#define SPECIAL_FINAL 5
+#define SPECIAL_AUDIO 6
+#define SPECIAL_MODET 7
+
+/* Little data class for creating MPEG headers bit-by-bit */
+
+struct code_gen {
+ unsigned char *p; /* destination */
+ u32 a; /* collects bits at the top of the variable */
+ int b; /* bit position of most recently-written bit */
+ int len; /* written out so far */
+};
+
+#define CODE_GEN(name, dest) struct code_gen name = { dest, 0, 32, 0 }
+
+#define CODE_ADD(name, val, length) do { \
+ name.b -= (length); \
+ name.a |= (val) << name.b; \
+ while (name.b <= 24) { \
+ *name.p = name.a >> 24; \
+ ++name.p; \
+ name.a <<= 8; \
+ name.b += 8; \
+ name.len += 8; \
+ } \
+} while (0)
+
+#define CODE_LENGTH(name) (name.len + (32 - name.b))
+
+/* Tables for creating the bitrate control data */
+
+static const s16 converge_speed_ip[101] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
+ 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+ 5, 5, 5, 6, 6, 6, 7, 7, 8, 8,
+ 9, 10, 10, 11, 12, 13, 14, 15, 16, 17,
+ 19, 20, 22, 23, 25, 27, 30, 32, 35, 38,
+ 41, 45, 49, 53, 58, 63, 69, 76, 83, 91,
+ 100
+};
+
+static const s16 converge_speed_ipb[101] = {
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 5, 5, 5, 5, 5, 6,
+ 6, 6, 6, 7, 7, 7, 7, 8, 8, 9,
+ 9, 9, 10, 10, 11, 12, 12, 13, 14, 14,
+ 15, 16, 17, 18, 19, 20, 22, 23, 25, 26,
+ 28, 30, 32, 34, 37, 40, 42, 46, 49, 53,
+ 57, 61, 66, 71, 77, 83, 90, 97, 106, 115,
+ 125, 135, 147, 161, 175, 191, 209, 228, 249, 273,
+ 300
+};
+
+static const s16 LAMBDA_table[4][101] = {
+ { 16, 16, 16, 16, 17, 17, 17, 18, 18, 18,
+ 19, 19, 19, 20, 20, 20, 21, 21, 22, 22,
+ 22, 23, 23, 24, 24, 25, 25, 25, 26, 26,
+ 27, 27, 28, 28, 29, 29, 30, 31, 31, 32,
+ 32, 33, 33, 34, 35, 35, 36, 37, 37, 38,
+ 39, 39, 40, 41, 42, 42, 43, 44, 45, 46,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 67, 68, 69, 70, 72, 73, 74, 76, 77, 78,
+ 80, 81, 83, 84, 86, 87, 89, 90, 92, 94,
+ 96
+ },
+ {
+ 20, 20, 20, 21, 21, 21, 22, 22, 23, 23,
+ 23, 24, 24, 25, 25, 26, 26, 27, 27, 28,
+ 28, 29, 29, 30, 30, 31, 31, 32, 33, 33,
+ 34, 34, 35, 36, 36, 37, 38, 38, 39, 40,
+ 40, 41, 42, 43, 43, 44, 45, 46, 47, 48,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 64, 65, 66, 67, 68,
+ 70, 71, 72, 73, 75, 76, 78, 79, 80, 82,
+ 83, 85, 86, 88, 90, 91, 93, 95, 96, 98,
+ 100, 102, 103, 105, 107, 109, 111, 113, 115, 117,
+ 120
+ },
+ {
+ 24, 24, 24, 25, 25, 26, 26, 27, 27, 28,
+ 28, 29, 29, 30, 30, 31, 31, 32, 33, 33,
+ 34, 34, 35, 36, 36, 37, 38, 38, 39, 40,
+ 41, 41, 42, 43, 44, 44, 45, 46, 47, 48,
+ 49, 50, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 62, 63, 64, 65, 66, 67, 69,
+ 70, 71, 72, 74, 75, 76, 78, 79, 81, 82,
+ 84, 85, 87, 88, 90, 92, 93, 95, 97, 98,
+ 100, 102, 104, 106, 108, 110, 112, 114, 116, 118,
+ 120, 122, 124, 127, 129, 131, 134, 136, 138, 141,
+ 144
+ },
+ {
+ 32, 32, 33, 33, 34, 34, 35, 36, 36, 37,
+ 38, 38, 39, 40, 41, 41, 42, 43, 44, 44,
+ 45, 46, 47, 48, 49, 50, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 62, 63, 64,
+ 65, 66, 67, 69, 70, 71, 72, 74, 75, 76,
+ 78, 79, 81, 82, 84, 85, 87, 88, 90, 92,
+ 93, 95, 97, 98, 100, 102, 104, 106, 108, 110,
+ 112, 114, 116, 118, 120, 122, 124, 127, 129, 131,
+ 134, 136, 139, 141, 144, 146, 149, 152, 154, 157,
+ 160, 163, 166, 169, 172, 175, 178, 181, 185, 188,
+ 192
+ }
+};
+
+/* MPEG blank frame generation tables */
+
+enum mpeg_frame_type {
+ PFRAME,
+ BFRAME_PRE,
+ BFRAME_POST,
+ BFRAME_BIDIR,
+ BFRAME_EMPTY
+};
+
+static const u32 addrinctab[33][2] = {
+ { 0x01, 1 }, { 0x03, 3 }, { 0x02, 3 }, { 0x03, 4 },
+ { 0x02, 4 }, { 0x03, 5 }, { 0x02, 5 }, { 0x07, 7 },
+ { 0x06, 7 }, { 0x0b, 8 }, { 0x0a, 8 }, { 0x09, 8 },
+ { 0x08, 8 }, { 0x07, 8 }, { 0x06, 8 }, { 0x17, 10 },
+ { 0x16, 10 }, { 0x15, 10 }, { 0x14, 10 }, { 0x13, 10 },
+ { 0x12, 10 }, { 0x23, 11 }, { 0x22, 11 }, { 0x21, 11 },
+ { 0x20, 11 }, { 0x1f, 11 }, { 0x1e, 11 }, { 0x1d, 11 },
+ { 0x1c, 11 }, { 0x1b, 11 }, { 0x1a, 11 }, { 0x19, 11 },
+ { 0x18, 11 }
+};
+
+/* Standard JPEG tables */
+
+static const u8 default_intra_quant_table[] = {
+ 8, 16, 19, 22, 26, 27, 29, 34,
+ 16, 16, 22, 24, 27, 29, 34, 37,
+ 19, 22, 26, 27, 29, 34, 34, 38,
+ 22, 22, 26, 27, 29, 34, 37, 40,
+ 22, 26, 27, 29, 32, 35, 40, 48,
+ 26, 27, 29, 32, 35, 40, 48, 58,
+ 26, 27, 29, 34, 38, 46, 56, 69,
+ 27, 29, 35, 38, 46, 56, 69, 83
+};
+
+static const u8 bits_dc_luminance[] = {
+ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const u8 val_dc_luminance[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+};
+
+static const u8 bits_dc_chrominance[] = {
+ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
+};
+
+static const u8 val_dc_chrominance[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+};
+
+static const u8 bits_ac_luminance[] = {
+ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
+};
+
+static const u8 val_ac_luminance[] = {
+ 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
+};
+
+static const u8 bits_ac_chrominance[] = {
+ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
+};
+
+static const u8 val_ac_chrominance[] = {
+ 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
+};
+
+/* Zig-zag mapping for quant table
+ *
+ * OK, let's do this mapping on the actual table above so it doesn't have
+ * to be done on the fly.
+ */
+static const int zz[64] = {
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
+};
+
+static int copy_packages(__le16 *dest, u16 *src, int pkg_cnt, int space)
+{
+ int i, cnt = pkg_cnt * 32;
+
+ if (space < cnt)
+ return -1;
+
+ for (i = 0; i < cnt; ++i)
+ dest[i] = cpu_to_le16p(src + i);
+
+ return cnt;
+}
+
+static int mjpeg_frame_header(struct go7007 *go, unsigned char *buf, int q)
+{
+ int i, p = 0;
+
+ buf[p++] = 0xff;
+ buf[p++] = 0xd8;
+ buf[p++] = 0xff;
+ buf[p++] = 0xdb;
+ buf[p++] = 0;
+ buf[p++] = 2 + 65;
+ buf[p++] = 0;
+ buf[p++] = default_intra_quant_table[0];
+ for (i = 1; i < 64; ++i)
+ /* buf[p++] = (default_intra_quant_table[i] * q) >> 3; */
+ buf[p++] = (default_intra_quant_table[zz[i]] * q) >> 3;
+ buf[p++] = 0xff;
+ buf[p++] = 0xc0;
+ buf[p++] = 0;
+ buf[p++] = 17;
+ buf[p++] = 8;
+ buf[p++] = go->height >> 8;
+ buf[p++] = go->height & 0xff;
+ buf[p++] = go->width >> 8;
+ buf[p++] = go->width & 0xff;
+ buf[p++] = 3;
+ buf[p++] = 1;
+ buf[p++] = 0x22;
+ buf[p++] = 0;
+ buf[p++] = 2;
+ buf[p++] = 0x11;
+ buf[p++] = 0;
+ buf[p++] = 3;
+ buf[p++] = 0x11;
+ buf[p++] = 0;
+ buf[p++] = 0xff;
+ buf[p++] = 0xc4;
+ buf[p++] = 418 >> 8;
+ buf[p++] = 418 & 0xff;
+ buf[p++] = 0x00;
+ memcpy(buf + p, bits_dc_luminance + 1, 16);
+ p += 16;
+ memcpy(buf + p, val_dc_luminance, sizeof(val_dc_luminance));
+ p += sizeof(val_dc_luminance);
+ buf[p++] = 0x01;
+ memcpy(buf + p, bits_dc_chrominance + 1, 16);
+ p += 16;
+ memcpy(buf + p, val_dc_chrominance, sizeof(val_dc_chrominance));
+ p += sizeof(val_dc_chrominance);
+ buf[p++] = 0x10;
+ memcpy(buf + p, bits_ac_luminance + 1, 16);
+ p += 16;
+ memcpy(buf + p, val_ac_luminance, sizeof(val_ac_luminance));
+ p += sizeof(val_ac_luminance);
+ buf[p++] = 0x11;
+ memcpy(buf + p, bits_ac_chrominance + 1, 16);
+ p += 16;
+ memcpy(buf + p, val_ac_chrominance, sizeof(val_ac_chrominance));
+ p += sizeof(val_ac_chrominance);
+ buf[p++] = 0xff;
+ buf[p++] = 0xda;
+ buf[p++] = 0;
+ buf[p++] = 12;
+ buf[p++] = 3;
+ buf[p++] = 1;
+ buf[p++] = 0x00;
+ buf[p++] = 2;
+ buf[p++] = 0x11;
+ buf[p++] = 3;
+ buf[p++] = 0x11;
+ buf[p++] = 0;
+ buf[p++] = 63;
+ buf[p++] = 0;
+ return p;
+}
+
+static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space)
+{
+ u8 *buf;
+ u16 mem = 0x3e00;
+ unsigned int addr = 0x19;
+ int size = 0, i, off = 0, chunk;
+
+ buf = kzalloc(4096, GFP_KERNEL);
+ if (buf == NULL) {
+ printk(KERN_ERR "go7007: unable to allocate 4096 bytes for "
+ "firmware construction\n");
+ return -1;
+ }
+
+ for (i = 1; i < 32; ++i) {
+ mjpeg_frame_header(go, buf + size, i);
+ size += 80;
+ }
+ chunk = mjpeg_frame_header(go, buf + size, 1);
+ memmove(buf + size, buf + size + 80, chunk - 80);
+ size += chunk - 80;
+
+ for (i = 0; i < size; i += chunk * 2) {
+ if (space - off < 32) {
+ off = -1;
+ goto done;
+ }
+
+ code[off + 1] = __cpu_to_le16(0x8000 | mem);
+
+ chunk = 28;
+ if (mem + chunk > 0x4000)
+ chunk = 0x4000 - mem;
+ if (i + 2 * chunk > size)
+ chunk = (size - i) / 2;
+
+ if (chunk < 28) {
+ code[off] = __cpu_to_le16(0x4000 | chunk);
+ code[off + 31] = __cpu_to_le16(addr++);
+ mem = 0x3e00;
+ } else {
+ code[off] = __cpu_to_le16(0x1000 | 28);
+ code[off + 31] = 0;
+ mem += 28;
+ }
+
+ memcpy(&code[off + 2], buf + i, chunk * 2);
+ off += 32;
+ }
+done:
+ kfree(buf);
+ return off;
+}
+
+static int mpeg1_frame_header(struct go7007 *go, unsigned char *buf,
+ int modulo, int pict_struct, enum mpeg_frame_type frame)
+{
+ int i, j, mb_code, mb_len;
+ int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
+ CODE_GEN(c, buf + 6);
+
+ switch (frame) {
+ case PFRAME:
+ mb_code = 0x1;
+ mb_len = 3;
+ break;
+ case BFRAME_PRE:
+ mb_code = 0x2;
+ mb_len = 4;
+ break;
+ case BFRAME_POST:
+ mb_code = 0x2;
+ mb_len = 3;
+ break;
+ case BFRAME_BIDIR:
+ mb_code = 0x2;
+ mb_len = 2;
+ break;
+ default: /* keep the compiler happy */
+ mb_code = mb_len = 0;
+ break;
+ }
+
+ CODE_ADD(c, frame == PFRAME ? 0x2 : 0x3, 13);
+ CODE_ADD(c, 0xffff, 16);
+ CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4);
+ if (frame != PFRAME)
+ CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4);
+ else
+ CODE_ADD(c, 0, 4); /* Is this supposed to be here?? */
+ CODE_ADD(c, 0, 3); /* What is this?? */
+ /* Byte-align with zeros */
+ j = 8 - (CODE_LENGTH(c) % 8);
+ if (j != 8)
+ CODE_ADD(c, 0, j);
+
+ if (go->format == GO7007_FORMAT_MPEG2) {
+ CODE_ADD(c, 0x1, 24);
+ CODE_ADD(c, 0xb5, 8);
+ CODE_ADD(c, 0x844, 12);
+ CODE_ADD(c, frame == PFRAME ? 0xff : 0x44, 8);
+ if (go->interlace_coding) {
+ CODE_ADD(c, pict_struct, 4);
+ if (go->dvd_mode)
+ CODE_ADD(c, 0x000, 11);
+ else
+ CODE_ADD(c, 0x200, 11);
+ } else {
+ CODE_ADD(c, 0x3, 4);
+ CODE_ADD(c, 0x20c, 11);
+ }
+ /* Byte-align with zeros */
+ j = 8 - (CODE_LENGTH(c) % 8);
+ if (j != 8)
+ CODE_ADD(c, 0, j);
+ }
+
+ for (i = 0; i < rows; ++i) {
+ CODE_ADD(c, 1, 24);
+ CODE_ADD(c, i + 1, 8);
+ CODE_ADD(c, 0x2, 6);
+ CODE_ADD(c, 0x1, 1);
+ CODE_ADD(c, mb_code, mb_len);
+ if (go->interlace_coding) {
+ CODE_ADD(c, 0x1, 2);
+ CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
+ }
+ if (frame == BFRAME_BIDIR) {
+ CODE_ADD(c, 0x3, 2);
+ if (go->interlace_coding)
+ CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
+ }
+ CODE_ADD(c, 0x3, 2);
+ for (j = (go->width >> 4) - 2; j >= 33; j -= 33)
+ CODE_ADD(c, 0x8, 11);
+ CODE_ADD(c, addrinctab[j][0], addrinctab[j][1]);
+ CODE_ADD(c, mb_code, mb_len);
+ if (go->interlace_coding) {
+ CODE_ADD(c, 0x1, 2);
+ CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
+ }
+ if (frame == BFRAME_BIDIR) {
+ CODE_ADD(c, 0x3, 2);
+ if (go->interlace_coding)
+ CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
+ }
+ CODE_ADD(c, 0x3, 2);
+
+ /* Byte-align with zeros */
+ j = 8 - (CODE_LENGTH(c) % 8);
+ if (j != 8)
+ CODE_ADD(c, 0, j);
+ }
+
+ i = CODE_LENGTH(c) + 4 * 8;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ buf[4] = 0x01;
+ buf[5] = 0x00;
+ return i;
+}
+
+static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext)
+{
+ int i, aspect_ratio, picture_rate;
+ CODE_GEN(c, buf + 6);
+
+ if (go->format == GO7007_FORMAT_MPEG1) {
+ switch (go->aspect_ratio) {
+ case GO7007_RATIO_4_3:
+ aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2;
+ break;
+ case GO7007_RATIO_16_9:
+ aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4;
+ break;
+ default:
+ aspect_ratio = 1;
+ break;
+ }
+ } else {
+ switch (go->aspect_ratio) {
+ case GO7007_RATIO_4_3:
+ aspect_ratio = 2;
+ break;
+ case GO7007_RATIO_16_9:
+ aspect_ratio = 3;
+ break;
+ default:
+ aspect_ratio = 1;
+ break;
+ }
+ }
+ switch (go->sensor_framerate) {
+ case 24000:
+ picture_rate = 1;
+ break;
+ case 24024:
+ picture_rate = 2;
+ break;
+ case 25025:
+ picture_rate = go->interlace_coding ? 6 : 3;
+ break;
+ case 30000:
+ picture_rate = go->interlace_coding ? 7 : 4;
+ break;
+ case 30030:
+ picture_rate = go->interlace_coding ? 8 : 5;
+ break;
+ default:
+ picture_rate = 5; /* 30 fps seems like a reasonable default */
+ break;
+ }
+
+ CODE_ADD(c, go->width, 12);
+ CODE_ADD(c, go->height, 12);
+ CODE_ADD(c, aspect_ratio, 4);
+ CODE_ADD(c, picture_rate, 4);
+ CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 20000 : 0x3ffff, 18);
+ CODE_ADD(c, 1, 1);
+ CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 112 : 20, 10);
+ CODE_ADD(c, 0, 3);
+
+ /* Byte-align with zeros */
+ i = 8 - (CODE_LENGTH(c) % 8);
+ if (i != 8)
+ CODE_ADD(c, 0, i);
+
+ if (go->format == GO7007_FORMAT_MPEG2) {
+ CODE_ADD(c, 0x1, 24);
+ CODE_ADD(c, 0xb5, 8);
+ CODE_ADD(c, 0x148, 12);
+ if (go->interlace_coding)
+ CODE_ADD(c, 0x20001, 20);
+ else
+ CODE_ADD(c, 0xa0001, 20);
+ CODE_ADD(c, 0, 16);
+
+ /* Byte-align with zeros */
+ i = 8 - (CODE_LENGTH(c) % 8);
+ if (i != 8)
+ CODE_ADD(c, 0, i);
+
+ if (ext) {
+ CODE_ADD(c, 0x1, 24);
+ CODE_ADD(c, 0xb52, 12);
+ CODE_ADD(c, go->standard == GO7007_STD_NTSC ? 2 : 1, 3);
+ CODE_ADD(c, 0x105, 9);
+ CODE_ADD(c, 0x505, 16);
+ CODE_ADD(c, go->width, 14);
+ CODE_ADD(c, 1, 1);
+ CODE_ADD(c, go->height, 14);
+
+ /* Byte-align with zeros */
+ i = 8 - (CODE_LENGTH(c) % 8);
+ if (i != 8)
+ CODE_ADD(c, 0, i);
+ }
+ }
+
+ i = CODE_LENGTH(c) + 4 * 8;
+ buf[0] = i & 0xff;
+ buf[1] = i >> 8;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ buf[4] = 0x01;
+ buf[5] = 0xb3;
+ return i;
+}
+
+static int gen_mpeg1hdr_to_package(struct go7007 *go,
+ __le16 *code, int space, int *framelen)
+{
+ u8 *buf;
+ u16 mem = 0x3e00;
+ unsigned int addr = 0x19;
+ int i, off = 0, chunk;
+
+ buf = kzalloc(5120, GFP_KERNEL);
+ if (buf == NULL) {
+ printk(KERN_ERR "go7007: unable to allocate 5120 bytes for "
+ "firmware construction\n");
+ return -1;
+ }
+ framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME);
+ if (go->interlace_coding)
+ framelen[0] += mpeg1_frame_header(go, buf + framelen[0] / 8,
+ 0, 2, PFRAME);
+ buf[0] = framelen[0] & 0xff;
+ buf[1] = framelen[0] >> 8;
+ i = 368;
+ framelen[1] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_PRE);
+ if (go->interlace_coding)
+ framelen[1] += mpeg1_frame_header(go, buf + i + framelen[1] / 8,
+ 0, 2, BFRAME_PRE);
+ buf[i] = framelen[1] & 0xff;
+ buf[i + 1] = framelen[1] >> 8;
+ i += 1632;
+ framelen[2] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_POST);
+ if (go->interlace_coding)
+ framelen[2] += mpeg1_frame_header(go, buf + i + framelen[2] / 8,
+ 0, 2, BFRAME_POST);
+ buf[i] = framelen[2] & 0xff;
+ buf[i + 1] = framelen[2] >> 8;
+ i += 1432;
+ framelen[3] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_BIDIR);
+ if (go->interlace_coding)
+ framelen[3] += mpeg1_frame_header(go, buf + i + framelen[3] / 8,
+ 0, 2, BFRAME_BIDIR);
+ buf[i] = framelen[3] & 0xff;
+ buf[i + 1] = framelen[3] >> 8;
+ i += 1632 + 16;
+ mpeg1_sequence_header(go, buf + i, 0);
+ i += 40;
+ for (i = 0; i < 5120; i += chunk * 2) {
+ if (space - off < 32) {
+ off = -1;
+ goto done;
+ }
+
+ code[off + 1] = __cpu_to_le16(0x8000 | mem);
+
+ chunk = 28;
+ if (mem + chunk > 0x4000)
+ chunk = 0x4000 - mem;
+ if (i + 2 * chunk > 5120)
+ chunk = (5120 - i) / 2;
+
+ if (chunk < 28) {
+ code[off] = __cpu_to_le16(0x4000 | chunk);
+ code[off + 31] = __cpu_to_le16(addr);
+ if (mem + chunk == 0x4000) {
+ mem = 0x3e00;
+ ++addr;
+ }
+ } else {
+ code[off] = __cpu_to_le16(0x1000 | 28);
+ code[off + 31] = 0;
+ mem += 28;
+ }
+
+ memcpy(&code[off + 2], buf + i, chunk * 2);
+ off += 32;
+ }
+done:
+ kfree(buf);
+ return off;
+}
+
+static int vti_bitlen(struct go7007 *go)
+{
+ unsigned int i, max_time_incr = go->sensor_framerate / go->fps_scale;
+
+ for (i = 31; (max_time_incr & ((1 << i) - 1)) == max_time_incr; --i);
+ return i + 1;
+}
+
+static int mpeg4_frame_header(struct go7007 *go, unsigned char *buf,
+ int modulo, enum mpeg_frame_type frame)
+{
+ int i;
+ CODE_GEN(c, buf + 6);
+ int mb_count = (go->width >> 4) * (go->height >> 4);
+
+ CODE_ADD(c, frame == PFRAME ? 0x1 : 0x2, 2);
+ if (modulo)
+ CODE_ADD(c, 0x1, 1);
+ CODE_ADD(c, 0x1, 2);
+ CODE_ADD(c, 0, vti_bitlen(go));
+ CODE_ADD(c, 0x3, 2);
+ if (frame == PFRAME)
+ CODE_ADD(c, 0, 1);
+ CODE_ADD(c, 0xc, 11);
+ if (frame != PFRAME)
+ CODE_ADD(c, 0x4, 3);
+ if (frame != BFRAME_EMPTY) {
+ for (i = 0; i < mb_count; ++i) {
+ switch (frame) {
+ case PFRAME:
+ CODE_ADD(c, 0x1, 1);
+ break;
+ case BFRAME_PRE:
+ CODE_ADD(c, 0x47, 8);
+ break;
+ case BFRAME_POST:
+ CODE_ADD(c, 0x27, 7);
+ break;
+ case BFRAME_BIDIR:
+ CODE_ADD(c, 0x5f, 8);
+ break;
+ case BFRAME_EMPTY: /* keep compiler quiet */
+ break;
+ }
+ }
+ }
+
+ /* Byte-align with a zero followed by ones */
+ i = 8 - (CODE_LENGTH(c) % 8);
+ CODE_ADD(c, 0, 1);
+ CODE_ADD(c, (1 << (i - 1)) - 1, i - 1);
+
+ i = CODE_LENGTH(c) + 4 * 8;
+ buf[0] = i & 0xff;
+ buf[1] = i >> 8;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ buf[4] = 0x01;
+ buf[5] = 0xb6;
+ return i;
+}
+
+static int mpeg4_sequence_header(struct go7007 *go, unsigned char *buf, int ext)
+{
+ const unsigned char head[] = { 0x00, 0x00, 0x01, 0xb0, go->pali,
+ 0x00, 0x00, 0x01, 0xb5, 0x09,
+ 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x01, 0x20, };
+ int i, aspect_ratio;
+ int fps = go->sensor_framerate / go->fps_scale;
+ CODE_GEN(c, buf + 2 + sizeof(head));
+
+ switch (go->aspect_ratio) {
+ case GO7007_RATIO_4_3:
+ aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2;
+ break;
+ case GO7007_RATIO_16_9:
+ aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4;
+ break;
+ default:
+ aspect_ratio = 1;
+ break;
+ }
+
+ memcpy(buf + 2, head, sizeof(head));
+ CODE_ADD(c, 0x191, 17);
+ CODE_ADD(c, aspect_ratio, 4);
+ CODE_ADD(c, 0x1, 4);
+ CODE_ADD(c, fps, 16);
+ CODE_ADD(c, 0x3, 2);
+ CODE_ADD(c, 1001, vti_bitlen(go));
+ CODE_ADD(c, 1, 1);
+ CODE_ADD(c, go->width, 13);
+ CODE_ADD(c, 1, 1);
+ CODE_ADD(c, go->height, 13);
+ CODE_ADD(c, 0x2830, 14);
+
+ /* Byte-align */
+ i = 8 - (CODE_LENGTH(c) % 8);
+ CODE_ADD(c, 0, 1);
+ CODE_ADD(c, (1 << (i - 1)) - 1, i - 1);
+
+ i = CODE_LENGTH(c) + sizeof(head) * 8;
+ buf[0] = i & 0xff;
+ buf[1] = i >> 8;
+ return i;
+}
+
+static int gen_mpeg4hdr_to_package(struct go7007 *go,
+ __le16 *code, int space, int *framelen)
+{
+ u8 *buf;
+ u16 mem = 0x3e00;
+ unsigned int addr = 0x19;
+ int i, off = 0, chunk;
+
+ buf = kzalloc(5120, GFP_KERNEL);
+ if (buf == NULL) {
+ printk(KERN_ERR "go7007: unable to allocate 5120 bytes for "
+ "firmware construction\n");
+ return -1;
+ }
+ framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME);
+ i = 368;
+ framelen[1] = mpeg4_frame_header(go, buf + i, 0, BFRAME_PRE);
+ i += 1632;
+ framelen[2] = mpeg4_frame_header(go, buf + i, 0, BFRAME_POST);
+ i += 1432;
+ framelen[3] = mpeg4_frame_header(go, buf + i, 0, BFRAME_BIDIR);
+ i += 1632;
+ mpeg4_frame_header(go, buf + i, 0, BFRAME_EMPTY);
+ i += 16;
+ mpeg4_sequence_header(go, buf + i, 0);
+ i += 40;
+ for (i = 0; i < 5120; i += chunk * 2) {
+ if (space - off < 32) {
+ off = -1;
+ goto done;
+ }
+
+ code[off + 1] = __cpu_to_le16(0x8000 | mem);
+
+ chunk = 28;
+ if (mem + chunk > 0x4000)
+ chunk = 0x4000 - mem;
+ if (i + 2 * chunk > 5120)
+ chunk = (5120 - i) / 2;
+
+ if (chunk < 28) {
+ code[off] = __cpu_to_le16(0x4000 | chunk);
+ code[off + 31] = __cpu_to_le16(addr);
+ if (mem + chunk == 0x4000) {
+ mem = 0x3e00;
+ ++addr;
+ }
+ } else {
+ code[off] = __cpu_to_le16(0x1000 | 28);
+ code[off + 31] = 0;
+ mem += 28;
+ }
+
+ memcpy(&code[off + 2], buf + i, chunk * 2);
+ off += 32;
+ }
+ mem = 0x3e00;
+ addr = go->ipb ? 0x14f9 : 0x0af9;
+ memset(buf, 0, 5120);
+ framelen[4] = mpeg4_frame_header(go, buf, 1, PFRAME);
+ i = 368;
+ framelen[5] = mpeg4_frame_header(go, buf + i, 1, BFRAME_PRE);
+ i += 1632;
+ framelen[6] = mpeg4_frame_header(go, buf + i, 1, BFRAME_POST);
+ i += 1432;
+ framelen[7] = mpeg4_frame_header(go, buf + i, 1, BFRAME_BIDIR);
+ i += 1632;
+ mpeg4_frame_header(go, buf + i, 1, BFRAME_EMPTY);
+ i += 16;
+ for (i = 0; i < 5120; i += chunk * 2) {
+ if (space - off < 32) {
+ off = -1;
+ goto done;
+ }
+
+ code[off + 1] = __cpu_to_le16(0x8000 | mem);
+
+ chunk = 28;
+ if (mem + chunk > 0x4000)
+ chunk = 0x4000 - mem;
+ if (i + 2 * chunk > 5120)
+ chunk = (5120 - i) / 2;
+
+ if (chunk < 28) {
+ code[off] = __cpu_to_le16(0x4000 | chunk);
+ code[off + 31] = __cpu_to_le16(addr);
+ if (mem + chunk == 0x4000) {
+ mem = 0x3e00;
+ ++addr;
+ }
+ } else {
+ code[off] = __cpu_to_le16(0x1000 | 28);
+ code[off + 31] = 0;
+ mem += 28;
+ }
+
+ memcpy(&code[off + 2], buf + i, chunk * 2);
+ off += 32;
+ }
+done:
+ kfree(buf);
+ return off;
+}
+
+static int brctrl_to_package(struct go7007 *go,
+ __le16 *code, int space, int *framelen)
+{
+ int converge_speed = 0;
+ int lambda = (go->format == GO7007_FORMAT_MJPEG || go->dvd_mode) ?
+ 100 : 0;
+ int peak_rate = 6 * go->bitrate / 5;
+ int vbv_buffer = go->format == GO7007_FORMAT_MJPEG ?
+ go->bitrate :
+ (go->dvd_mode ? 900000 : peak_rate);
+ int fps = go->sensor_framerate / go->fps_scale;
+ int q = 0;
+ /* Bizarre math below depends on rounding errors in division */
+ u32 sgop_expt_addr = go->bitrate / 32 * (go->ipb ? 3 : 1) * 1001 / fps;
+ u32 sgop_peak_addr = peak_rate / 32 * 1001 / fps;
+ u32 total_expt_addr = go->bitrate / 32 * 1000 / fps * (fps / 1000);
+ u32 vbv_alert_addr = vbv_buffer * 3 / (4 * 32);
+ u32 cplx[] = {
+ q > 0 ? sgop_expt_addr * q :
+ 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
+ q > 0 ? sgop_expt_addr * q :
+ 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
+ q > 0 ? sgop_expt_addr * q :
+ 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
+ q > 0 ? sgop_expt_addr * q :
+ 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
+ };
+ u32 calc_q = q > 0 ? q : cplx[0] / sgop_expt_addr;
+ u16 pack[] = {
+ 0x200e, 0x0000,
+ 0xBF20, go->ipb ? converge_speed_ipb[converge_speed]
+ : converge_speed_ip[converge_speed],
+ 0xBF21, go->ipb ? 2 : 0,
+ 0xBF22, go->ipb ? LAMBDA_table[0][lambda / 2 + 50]
+ : 32767,
+ 0xBF23, go->ipb ? LAMBDA_table[1][lambda] : 32767,
+ 0xBF24, 32767,
+ 0xBF25, lambda > 99 ? 32767 : LAMBDA_table[3][lambda],
+ 0xBF26, sgop_expt_addr & 0x0000FFFF,
+ 0xBF27, sgop_expt_addr >> 16,
+ 0xBF28, sgop_peak_addr & 0x0000FFFF,
+ 0xBF29, sgop_peak_addr >> 16,
+ 0xBF2A, vbv_alert_addr & 0x0000FFFF,
+ 0xBF2B, vbv_alert_addr >> 16,
+ 0xBF2C, 0,
+ 0xBF2D, 0,
+ 0, 0,
+
+ 0x200e, 0x0000,
+ 0xBF2E, vbv_alert_addr & 0x0000FFFF,
+ 0xBF2F, vbv_alert_addr >> 16,
+ 0xBF30, cplx[0] & 0x0000FFFF,
+ 0xBF31, cplx[0] >> 16,
+ 0xBF32, cplx[1] & 0x0000FFFF,
+ 0xBF33, cplx[1] >> 16,
+ 0xBF34, cplx[2] & 0x0000FFFF,
+ 0xBF35, cplx[2] >> 16,
+ 0xBF36, cplx[3] & 0x0000FFFF,
+ 0xBF37, cplx[3] >> 16,
+ 0xBF38, 0,
+ 0xBF39, 0,
+ 0xBF3A, total_expt_addr & 0x0000FFFF,
+ 0xBF3B, total_expt_addr >> 16,
+ 0, 0,
+
+ 0x200e, 0x0000,
+ 0xBF3C, total_expt_addr & 0x0000FFFF,
+ 0xBF3D, total_expt_addr >> 16,
+ 0xBF3E, 0,
+ 0xBF3F, 0,
+ 0xBF48, 0,
+ 0xBF49, 0,
+ 0xBF4A, calc_q < 4 ? 4 : (calc_q > 124 ? 124 : calc_q),
+ 0xBF4B, 4,
+ 0xBF4C, 0,
+ 0xBF4D, 0,
+ 0xBF4E, 0,
+ 0xBF4F, 0,
+ 0xBF50, 0,
+ 0xBF51, 0,
+ 0, 0,
+
+ 0x200e, 0x0000,
+ 0xBF40, sgop_expt_addr & 0x0000FFFF,
+ 0xBF41, sgop_expt_addr >> 16,
+ 0xBF42, 0,
+ 0xBF43, 0,
+ 0xBF44, 0,
+ 0xBF45, 0,
+ 0xBF46, (go->width >> 4) * (go->height >> 4),
+ 0xBF47, 0,
+ 0xBF64, 0,
+ 0xBF65, 0,
+ 0xBF18, framelen[4],
+ 0xBF19, framelen[5],
+ 0xBF1A, framelen[6],
+ 0xBF1B, framelen[7],
+ 0, 0,
+
+#if 0
+ /* Remove once we don't care about matching */
+ 0x200e, 0x0000,
+ 0xBF56, 4,
+ 0xBF57, 0,
+ 0xBF58, 5,
+ 0xBF59, 0,
+ 0xBF5A, 6,
+ 0xBF5B, 0,
+ 0xBF5C, 8,
+ 0xBF5D, 0,
+ 0xBF5E, 1,
+ 0xBF5F, 0,
+ 0xBF60, 1,
+ 0xBF61, 0,
+ 0xBF62, 0,
+ 0xBF63, 0,
+ 0, 0,
+#else
+ 0x2008, 0x0000,
+ 0xBF56, 4,
+ 0xBF57, 0,
+ 0xBF58, 5,
+ 0xBF59, 0,
+ 0xBF5A, 6,
+ 0xBF5B, 0,
+ 0xBF5C, 8,
+ 0xBF5D, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+#endif
+
+ 0x200e, 0x0000,
+ 0xBF10, 0,
+ 0xBF11, 0,
+ 0xBF12, 0,
+ 0xBF13, 0,
+ 0xBF14, 0,
+ 0xBF15, 0,
+ 0xBF16, 0,
+ 0xBF17, 0,
+ 0xBF7E, 0,
+ 0xBF7F, 1,
+ 0xBF52, framelen[0],
+ 0xBF53, framelen[1],
+ 0xBF54, framelen[2],
+ 0xBF55, framelen[3],
+ 0, 0,
+ };
+
+ return copy_packages(code, pack, 6, space);
+}
+
+static int config_package(struct go7007 *go, __le16 *code, int space)
+{
+ int fps = go->sensor_framerate / go->fps_scale / 1000;
+ int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
+ int brc_window_size = fps;
+ int q_min = 2, q_max = 31;
+ int THACCoeffSet0 = 0;
+ u16 pack[] = {
+ 0x200e, 0x0000,
+ 0xc002, 0x14b4,
+ 0xc003, 0x28b4,
+ 0xc004, 0x3c5a,
+ 0xdc05, 0x2a77,
+ 0xc6c3, go->format == GO7007_FORMAT_MPEG4 ? 0 :
+ (go->format == GO7007_FORMAT_H263 ? 0 : 1),
+ 0xc680, go->format == GO7007_FORMAT_MPEG4 ? 0xf1 :
+ (go->format == GO7007_FORMAT_H263 ? 0x61 :
+ 0xd3),
+ 0xc780, 0x0140,
+ 0xe009, 0x0001,
+ 0xc60f, 0x0008,
+ 0xd4ff, 0x0002,
+ 0xe403, 2340,
+ 0xe406, 75,
+ 0xd411, 0x0001,
+ 0xd410, 0xa1d6,
+ 0x0001, 0x2801,
+
+ 0x200d, 0x0000,
+ 0xe402, 0x018b,
+ 0xe401, 0x8b01,
+ 0xd472, (go->board_info->sensor_flags &
+ GO7007_SENSOR_TV) &&
+ (!go->interlace_coding) ?
+ 0x01b0 : 0x0170,
+ 0xd475, (go->board_info->sensor_flags &
+ GO7007_SENSOR_TV) &&
+ (!go->interlace_coding) ?
+ 0x0008 : 0x0009,
+ 0xc404, go->interlace_coding ? 0x44 :
+ (go->format == GO7007_FORMAT_MPEG4 ? 0x11 :
+ (go->format == GO7007_FORMAT_MPEG1 ? 0x02 :
+ (go->format == GO7007_FORMAT_MPEG2 ? 0x04 :
+ (go->format == GO7007_FORMAT_H263 ? 0x08 :
+ 0x20)))),
+ 0xbf0a, (go->format == GO7007_FORMAT_MPEG4 ? 8 :
+ (go->format == GO7007_FORMAT_MPEG1 ? 1 :
+ (go->format == GO7007_FORMAT_MPEG2 ? 2 :
+ (go->format == GO7007_FORMAT_H263 ? 4 : 16)))) |
+ ((go->repeat_seqhead ? 1 : 0) << 6) |
+ ((go->dvd_mode ? 1 : 0) << 9) |
+ ((go->gop_header_enable ? 1 : 0) << 10),
+ 0xbf0b, 0,
+ 0xdd5a, go->ipb ? 0x14 : 0x0a,
+ 0xbf0c, 0,
+ 0xbf0d, 0,
+ 0xc683, THACCoeffSet0,
+ 0xc40a, (go->width << 4) | rows,
+ 0xe01a, go->board_info->hpi_buffer_cap,
+ 0, 0,
+ 0, 0,
+
+ 0x2008, 0,
+ 0xe402, 0x88,
+ 0xe401, 0x8f01,
+ 0xbf6a, 0,
+ 0xbf6b, 0,
+ 0xbf6c, 0,
+ 0xbf6d, 0,
+ 0xbf6e, 0,
+ 0xbf6f, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+
+ 0x200e, 0,
+ 0xbf66, brc_window_size,
+ 0xbf67, 0,
+ 0xbf68, q_min,
+ 0xbf69, q_max,
+ 0xbfe0, 0,
+ 0xbfe1, 0,
+ 0xbfe2, 0,
+ 0xbfe3, go->ipb ? 3 : 1,
+ 0xc031, go->board_info->sensor_flags &
+ GO7007_SENSOR_VBI ? 1 : 0,
+ 0xc01c, 0x1f,
+ 0xdd8c, 0x15,
+ 0xdd94, 0x15,
+ 0xdd88, go->ipb ? 0x1401 : 0x0a01,
+ 0xdd90, go->ipb ? 0x1401 : 0x0a01,
+ 0, 0,
+
+ 0x200e, 0,
+ 0xbfe4, 0,
+ 0xbfe5, 0,
+ 0xbfe6, 0,
+ 0xbfe7, fps << 8,
+ 0xbfe8, 0x3a00,
+ 0xbfe9, 0,
+ 0xbfea, 0,
+ 0xbfeb, 0,
+ 0xbfec, (go->interlace_coding ? 1 << 15 : 0) |
+ (go->modet_enable ? 0xa : 0) |
+ (go->board_info->sensor_flags &
+ GO7007_SENSOR_VBI ? 1 : 0),
+ 0xbfed, 0,
+ 0xbfee, 0,
+ 0xbfef, 0,
+ 0xbff0, go->board_info->sensor_flags &
+ GO7007_SENSOR_TV ? 0xf060 : 0xb060,
+ 0xbff1, 0,
+ 0, 0,
+ };
+
+ return copy_packages(code, pack, 5, space);
+}
+
+static int seqhead_to_package(struct go7007 *go, __le16 *code, int space,
+ int (*sequence_header_func)(struct go7007 *go,
+ unsigned char *buf, int ext))
+{
+ int vop_time_increment_bitlength = vti_bitlen(go);
+ int fps = go->sensor_framerate / go->fps_scale *
+ (go->interlace_coding ? 2 : 1);
+ unsigned char buf[40] = { };
+ int len = sequence_header_func(go, buf, 1);
+ u16 pack[] = {
+ 0x2006, 0,
+ 0xbf08, fps,
+ 0xbf09, 0,
+ 0xbff2, vop_time_increment_bitlength,
+ 0xbff3, (1 << vop_time_increment_bitlength) - 1,
+ 0xbfe6, 0,
+ 0xbfe7, (fps / 1000) << 8,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+
+ 0x2007, 0,
+ 0xc800, buf[2] << 8 | buf[3],
+ 0xc801, buf[4] << 8 | buf[5],
+ 0xc802, buf[6] << 8 | buf[7],
+ 0xc803, buf[8] << 8 | buf[9],
+ 0xc406, 64,
+ 0xc407, len - 64,
+ 0xc61b, 1,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+
+ 0x200e, 0,
+ 0xc808, buf[10] << 8 | buf[11],
+ 0xc809, buf[12] << 8 | buf[13],
+ 0xc80a, buf[14] << 8 | buf[15],
+ 0xc80b, buf[16] << 8 | buf[17],
+ 0xc80c, buf[18] << 8 | buf[19],
+ 0xc80d, buf[20] << 8 | buf[21],
+ 0xc80e, buf[22] << 8 | buf[23],
+ 0xc80f, buf[24] << 8 | buf[25],
+ 0xc810, buf[26] << 8 | buf[27],
+ 0xc811, buf[28] << 8 | buf[29],
+ 0xc812, buf[30] << 8 | buf[31],
+ 0xc813, buf[32] << 8 | buf[33],
+ 0xc814, buf[34] << 8 | buf[35],
+ 0xc815, buf[36] << 8 | buf[37],
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ };
+
+ return copy_packages(code, pack, 3, space);
+}
+
+static int relative_prime(int big, int little)
+{
+ int remainder;
+
+ while (little != 0) {
+ remainder = big % little;
+ big = little;
+ little = remainder;
+ }
+ return big;
+}
+
+static int avsync_to_package(struct go7007 *go, __le16 *code, int space)
+{
+ int arate = go->board_info->audio_rate * 1001 * go->fps_scale;
+ int ratio = arate / go->sensor_framerate;
+ int adjratio = ratio * 215 / 100;
+ int rprime = relative_prime(go->sensor_framerate,
+ arate % go->sensor_framerate);
+ int f1 = (arate % go->sensor_framerate) / rprime;
+ int f2 = (go->sensor_framerate - arate % go->sensor_framerate) / rprime;
+ u16 pack[] = {
+ 0x200e, 0,
+ 0xbf98, (u16)((-adjratio) & 0xffff),
+ 0xbf99, (u16)((-adjratio) >> 16),
+ 0xbf92, 0,
+ 0xbf93, 0,
+ 0xbff4, f1 > f2 ? f1 : f2,
+ 0xbff5, f1 < f2 ? f1 : f2,
+ 0xbff6, f1 < f2 ? ratio : ratio + 1,
+ 0xbff7, f1 > f2 ? ratio : ratio + 1,
+ 0xbff8, 0,
+ 0xbff9, 0,
+ 0xbffa, adjratio & 0xffff,
+ 0xbffb, adjratio >> 16,
+ 0xbf94, 0,
+ 0xbf95, 0,
+ 0, 0,
+ };
+
+ return copy_packages(code, pack, 1, space);
+}
+
+static int final_package(struct go7007 *go, __le16 *code, int space)
+{
+ int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
+ u16 pack[] = {
+ 0x8000,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2,
+ ((go->board_info->sensor_flags & GO7007_SENSOR_TV) &&
+ (!go->interlace_coding) ?
+ (1 << 14) | (1 << 9) : 0) |
+ ((go->encoder_subsample ? 1 : 0) << 8) |
+ (go->board_info->sensor_flags &
+ GO7007_SENSOR_CONFIG_MASK),
+ ((go->encoder_v_halve ? 1 : 0) << 14) |
+ (go->encoder_v_halve ? rows << 9 : rows << 8) |
+ (go->encoder_h_halve ? 1 << 6 : 0) |
+ (go->encoder_h_halve ? go->width >> 3 : go->width >> 4),
+ (1 << 15) | (go->encoder_v_offset << 6) |
+ (1 << 7) | (go->encoder_h_offset >> 2),
+ (1 << 6),
+ 0,
+ 0,
+ ((go->fps_scale - 1) << 8) |
+ (go->board_info->sensor_flags & GO7007_SENSOR_TV ?
+ (1 << 7) : 0) |
+ 0x41,
+ go->ipb ? 0xd4c : 0x36b,
+ (rows << 8) | (go->width >> 4),
+ go->format == GO7007_FORMAT_MPEG4 ? 0x0404 : 0,
+ (1 << 15) | ((go->interlace_coding ? 1 : 0) << 13) |
+ ((go->closed_gop ? 1 : 0) << 12) |
+ ((go->format == GO7007_FORMAT_MPEG4 ? 1 : 0) << 11) |
+ /* (1 << 9) | */
+ ((go->ipb ? 3 : 0) << 7) |
+ ((go->modet_enable ? 1 : 0) << 2) |
+ ((go->dvd_mode ? 1 : 0) << 1) | 1,
+ (go->format == GO7007_FORMAT_MPEG1 ? 0x89a0 :
+ (go->format == GO7007_FORMAT_MPEG2 ? 0x89a0 :
+ (go->format == GO7007_FORMAT_MJPEG ? 0x89a0 :
+ (go->format == GO7007_FORMAT_MPEG4 ? 0x8920 :
+ (go->format == GO7007_FORMAT_H263 ? 0x8920 : 0))))),
+ go->ipb ? 0x1f15 : 0x1f0b,
+ go->ipb ? 0x0015 : 0x000b,
+ go->ipb ? 0xa800 : 0x5800,
+ 0xffff,
+ 0x0020 + 0x034b * 0,
+ 0x0020 + 0x034b * 1,
+ 0x0020 + 0x034b * 2,
+ 0x0020 + 0x034b * 3,
+ 0x0020 + 0x034b * 4,
+ 0x0020 + 0x034b * 5,
+ go->ipb ? (go->gop_size / 3) : go->gop_size,
+ (go->height >> 4) * (go->width >> 4) * 110 / 100,
+ };
+
+ return copy_packages(code, pack, 1, space);
+}
+
+static int audio_to_package(struct go7007 *go, __le16 *code, int space)
+{
+ int clock_config = ((go->board_info->audio_flags &
+ GO7007_AUDIO_I2S_MASTER ? 1 : 0) << 11) |
+ ((go->board_info->audio_flags &
+ GO7007_AUDIO_OKI_MODE ? 1 : 0) << 8) |
+ (((go->board_info->audio_bclk_div / 4) - 1) << 4) |
+ (go->board_info->audio_main_div - 1);
+ u16 pack[] = {
+ 0x200d, 0,
+ 0x9002, 0,
+ 0x9002, 0,
+ 0x9031, 0,
+ 0x9032, 0,
+ 0x9033, 0,
+ 0x9034, 0,
+ 0x9035, 0,
+ 0x9036, 0,
+ 0x9037, 0,
+ 0x9040, 0,
+ 0x9000, clock_config,
+ 0x9001, (go->board_info->audio_flags & 0xffff) |
+ (1 << 9),
+ 0x9000, ((go->board_info->audio_flags &
+ GO7007_AUDIO_I2S_MASTER ?
+ 1 : 0) << 10) |
+ clock_config,
+ 0, 0,
+ 0, 0,
+ 0x2005, 0,
+ 0x9041, 0,
+ 0x9042, 256,
+ 0x9043, 0,
+ 0x9044, 16,
+ 0x9045, 16,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ };
+
+ return copy_packages(code, pack, 2, space);
+}
+
+static int modet_to_package(struct go7007 *go, __le16 *code, int space)
+{
+ int ret, mb, i, addr, cnt = 0;
+ u16 pack[32];
+ u16 thresholds[] = {
+ 0x200e, 0,
+ 0xbf82, go->modet[0].pixel_threshold,
+ 0xbf83, go->modet[1].pixel_threshold,
+ 0xbf84, go->modet[2].pixel_threshold,
+ 0xbf85, go->modet[3].pixel_threshold,
+ 0xbf86, go->modet[0].motion_threshold,
+ 0xbf87, go->modet[1].motion_threshold,
+ 0xbf88, go->modet[2].motion_threshold,
+ 0xbf89, go->modet[3].motion_threshold,
+ 0xbf8a, go->modet[0].mb_threshold,
+ 0xbf8b, go->modet[1].mb_threshold,
+ 0xbf8c, go->modet[2].mb_threshold,
+ 0xbf8d, go->modet[3].mb_threshold,
+ 0xbf8e, 0,
+ 0xbf8f, 0,
+ 0, 0,
+ };
+
+ ret = copy_packages(code, thresholds, 1, space);
+ if (ret < 0)
+ return -1;
+ cnt += ret;
+
+ addr = 0xbac0;
+ memset(pack, 0, 64);
+ i = 0;
+ for (mb = 0; mb < 1624; ++mb) {
+ pack[i * 2 + 3] <<= 2;
+ pack[i * 2 + 3] |= go->modet_map[mb];
+ if (mb % 8 != 7)
+ continue;
+ pack[i * 2 + 2] = addr++;
+ ++i;
+ if (i == 10 || mb == 1623) {
+ pack[0] = 0x2000 | i;
+ ret = copy_packages(code + cnt, pack, 1, space - cnt);
+ if (ret < 0)
+ return -1;
+ cnt += ret;
+ i = 0;
+ memset(pack, 0, 64);
+ }
+ pack[i * 2 + 3] = 0;
+ }
+
+ memset(pack, 0, 64);
+ i = 0;
+ for (addr = 0xbb90; addr < 0xbbfa; ++addr) {
+ pack[i * 2 + 2] = addr;
+ pack[i * 2 + 3] = 0;
+ ++i;
+ if (i == 10 || addr == 0xbbf9) {
+ pack[0] = 0x2000 | i;
+ ret = copy_packages(code + cnt, pack, 1, space - cnt);
+ if (ret < 0)
+ return -1;
+ cnt += ret;
+ i = 0;
+ memset(pack, 0, 64);
+ }
+ }
+ return cnt;
+}
+
+static int do_special(struct go7007 *go, u16 type, __le16 *code, int space,
+ int *framelen)
+{
+ switch (type) {
+ case SPECIAL_FRM_HEAD:
+ switch (go->format) {
+ case GO7007_FORMAT_MJPEG:
+ return gen_mjpeghdr_to_package(go, code, space);
+ case GO7007_FORMAT_MPEG1:
+ case GO7007_FORMAT_MPEG2:
+ return gen_mpeg1hdr_to_package(go, code, space,
+ framelen);
+ case GO7007_FORMAT_MPEG4:
+ return gen_mpeg4hdr_to_package(go, code, space,
+ framelen);
+ }
+ case SPECIAL_BRC_CTRL:
+ return brctrl_to_package(go, code, space, framelen);
+ case SPECIAL_CONFIG:
+ return config_package(go, code, space);
+ case SPECIAL_SEQHEAD:
+ switch (go->format) {
+ case GO7007_FORMAT_MPEG1:
+ case GO7007_FORMAT_MPEG2:
+ return seqhead_to_package(go, code, space,
+ mpeg1_sequence_header);
+ case GO7007_FORMAT_MPEG4:
+ return seqhead_to_package(go, code, space,
+ mpeg4_sequence_header);
+ default:
+ return 0;
+ }
+ case SPECIAL_AV_SYNC:
+ return avsync_to_package(go, code, space);
+ case SPECIAL_FINAL:
+ return final_package(go, code, space);
+ case SPECIAL_AUDIO:
+ return audio_to_package(go, code, space);
+ case SPECIAL_MODET:
+ return modet_to_package(go, code, space);
+ }
+ printk(KERN_ERR
+ "go7007: firmware file contains unsupported feature %04x\n",
+ type);
+ return -1;
+}
+
+int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen)
+{
+ const struct firmware *fw_entry;
+ __le16 *code, *src;
+ int framelen[8] = { }; /* holds the lengths of empty frame templates */
+ int codespace = 64 * 1024, i = 0, srclen, chunk_len, chunk_flags;
+ int mode_flag;
+ int ret;
+
+ switch (go->format) {
+ case GO7007_FORMAT_MJPEG:
+ mode_flag = FLAG_MODE_MJPEG;
+ break;
+ case GO7007_FORMAT_MPEG1:
+ mode_flag = FLAG_MODE_MPEG1;
+ break;
+ case GO7007_FORMAT_MPEG2:
+ mode_flag = FLAG_MODE_MPEG2;
+ break;
+ case GO7007_FORMAT_MPEG4:
+ mode_flag = FLAG_MODE_MPEG4;
+ break;
+ default:
+ return -1;
+ }
+ if (request_firmware(&fw_entry, go->board_info->firmware, go->dev)) {
+ printk(KERN_ERR
+ "go7007: unable to load firmware from file \"%s\"\n",
+ go->board_info->firmware);
+ return -1;
+ }
+ code = kzalloc(codespace * 2, GFP_KERNEL);
+ if (code == NULL) {
+ printk(KERN_ERR "go7007: unable to allocate %d bytes for "
+ "firmware construction\n", codespace * 2);
+ goto fw_failed;
+ }
+ src = (__le16 *)fw_entry->data;
+ srclen = fw_entry->size / 2;
+ while (srclen >= 2) {
+ chunk_flags = __le16_to_cpu(src[0]);
+ chunk_len = __le16_to_cpu(src[1]);
+ if (chunk_len + 2 > srclen) {
+ printk(KERN_ERR "go7007: firmware file \"%s\" "
+ "appears to be corrupted\n",
+ go->board_info->firmware);
+ goto fw_failed;
+ }
+ if (chunk_flags & mode_flag) {
+ if (chunk_flags & FLAG_SPECIAL) {
+ ret = do_special(go, __le16_to_cpu(src[2]),
+ &code[i], codespace - i, framelen);
+ if (ret < 0) {
+ printk(KERN_ERR "go7007: insufficient "
+ "memory for firmware "
+ "construction\n");
+ goto fw_failed;
+ }
+ i += ret;
+ } else {
+ if (codespace - i < chunk_len) {
+ printk(KERN_ERR "go7007: insufficient "
+ "memory for firmware "
+ "construction\n");
+ goto fw_failed;
+ }
+ memcpy(&code[i], &src[2], chunk_len * 2);
+ i += chunk_len;
+ }
+ }
+ srclen -= chunk_len + 2;
+ src += chunk_len + 2;
+ }
+ release_firmware(fw_entry);
+ *fw = (u8 *)code;
+ *fwlen = i * 2;
+ return 0;
+
+fw_failed:
+ kfree(code);
+ release_firmware(fw_entry);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#include "go7007-priv.h"
+#include "wis-i2c.h"
+
+/********************* Driver for on-board I2C adapter *********************/
+
+/* #define GO7007_I2C_DEBUG */
+
+#define SPI_I2C_ADDR_BASE 0x1400
+#define STATUS_REG_ADDR (SPI_I2C_ADDR_BASE + 0x2)
+#define I2C_CTRL_REG_ADDR (SPI_I2C_ADDR_BASE + 0x6)
+#define I2C_DEV_UP_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x7)
+#define I2C_LO_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x8)
+#define I2C_DATA_REG_ADDR (SPI_I2C_ADDR_BASE + 0x9)
+#define I2C_CLKFREQ_REG_ADDR (SPI_I2C_ADDR_BASE + 0xa)
+
+#define I2C_STATE_MASK 0x0007
+#define I2C_READ_READY_MASK 0x0008
+
+/* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs
+ * on the Adlink PCI-MPG24, so access is shared between all of them. */
+static DEFINE_MUTEX(adlink_mpg24_i2c_lock);
+
+static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read,
+ u16 command, int flags, u8 *data)
+{
+ int i, ret = -1;
+ u16 val;
+
+ if (go->status == STATUS_SHUTDOWN)
+ return -1;
+
+#ifdef GO7007_I2C_DEBUG
+ if (read)
+ printk(KERN_DEBUG "go7007-i2c: reading 0x%02x on 0x%02x\n",
+ command, addr);
+ else
+ printk(KERN_DEBUG
+ "go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n",
+ *data, command, addr);
+#endif
+
+ mutex_lock(&go->hw_lock);
+
+ if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) {
+ /* Bridge the I2C port on this GO7007 to the shared bus */
+ mutex_lock(&adlink_mpg24_i2c_lock);
+ go7007_write_addr(go, 0x3c82, 0x0020);
+ }
+
+ /* Wait for I2C adapter to be ready */
+ for (i = 0; i < 10; ++i) {
+ if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0)
+ goto i2c_done;
+ if (!(val & I2C_STATE_MASK))
+ break;
+ msleep(100);
+ }
+ if (i == 10) {
+ printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n");
+ goto i2c_done;
+ }
+
+ /* Set target register (command) */
+ go7007_write_addr(go, I2C_CTRL_REG_ADDR, flags);
+ go7007_write_addr(go, I2C_LO_ADDR_REG_ADDR, command);
+
+ /* If we're writing, send the data and target address and we're done */
+ if (!read) {
+ go7007_write_addr(go, I2C_DATA_REG_ADDR, *data);
+ go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR,
+ (addr << 9) | (command >> 8));
+ ret = 0;
+ goto i2c_done;
+ }
+
+ /* Otherwise, we're reading. First clear i2c_rx_data_rdy. */
+ if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0)
+ goto i2c_done;
+
+ /* Send the target address plus read flag */
+ go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR,
+ (addr << 9) | 0x0100 | (command >> 8));
+
+ /* Wait for i2c_rx_data_rdy */
+ for (i = 0; i < 10; ++i) {
+ if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0)
+ goto i2c_done;
+ if (val & I2C_READ_READY_MASK)
+ break;
+ msleep(100);
+ }
+ if (i == 10) {
+ printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n");
+ goto i2c_done;
+ }
+
+ /* Retrieve the read byte */
+ if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0)
+ goto i2c_done;
+ *data = val;
+ ret = 0;
+
+i2c_done:
+ if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) {
+ /* Isolate the I2C port on this GO7007 from the shared bus */
+ go7007_write_addr(go, 0x3c82, 0x0000);
+ mutex_unlock(&adlink_mpg24_i2c_lock);
+ }
+ mutex_unlock(&go->hw_lock);
+ return ret;
+}
+
+static int go7007_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data *data)
+{
+ struct go7007 *go = i2c_get_adapdata(adapter);
+
+ if (size != I2C_SMBUS_BYTE_DATA)
+ return -1;
+ return go7007_i2c_xfer(go, addr, read_write == I2C_SMBUS_READ, command,
+ flags & I2C_CLIENT_SCCB ? 0x10 : 0x00, &data->byte);
+}
+
+/* VERY LIMITED I2C master xfer function -- only needed because the
+ * SMBus functions only support 8-bit commands and the SAA7135 uses
+ * 16-bit commands. The I2C interface on the GO7007, as limited as
+ * it is, does support this mode. */
+
+static int go7007_i2c_master_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg msgs[], int num)
+{
+ struct go7007 *go = i2c_get_adapdata(adapter);
+ int i;
+
+ for (i = 0; i < num; ++i) {
+ /* We can only do two things here -- write three bytes, or
+ * write two bytes and read one byte. */
+ if (msgs[i].len == 2) {
+ if (i + 1 == num || msgs[i].addr != msgs[i + 1].addr ||
+ (msgs[i].flags & I2C_M_RD) ||
+ !(msgs[i + 1].flags & I2C_M_RD) ||
+ msgs[i + 1].len != 1)
+ return -1;
+ if (go7007_i2c_xfer(go, msgs[i].addr, 1,
+ (msgs[i].buf[0] << 8) | msgs[i].buf[1],
+ 0x01, &msgs[i + 1].buf[0]) < 0)
+ return -1;
+ ++i;
+ } else if (msgs[i].len == 3) {
+ if (msgs[i].flags & I2C_M_RD)
+ return -1;
+ if (msgs[i].len != 3)
+ return -1;
+ if (go7007_i2c_xfer(go, msgs[i].addr, 0,
+ (msgs[i].buf[0] << 8) | msgs[i].buf[1],
+ 0x01, &msgs[i].buf[2]) < 0)
+ return -1;
+ } else
+ return -1;
+ }
+
+ return 0;
+}
+
+static u32 go7007_functionality(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_SMBUS_BYTE_DATA;
+}
+
+static struct i2c_algorithm go7007_algo = {
+ .smbus_xfer = go7007_smbus_xfer,
+ .master_xfer = go7007_i2c_master_xfer,
+ .functionality = go7007_functionality,
+};
+
+static struct i2c_adapter go7007_adap_templ = {
+ .owner = THIS_MODULE,
+ .name = "WIS GO7007SB",
+ .algo = &go7007_algo,
+};
+
+int go7007_i2c_init(struct go7007 *go)
+{
+ memcpy(&go->i2c_adapter, &go7007_adap_templ,
+ sizeof(go7007_adap_templ));
+ go->i2c_adapter.dev.parent = go->dev;
+ i2c_set_adapdata(&go->i2c_adapter, go);
+ if (i2c_add_adapter(&go->i2c_adapter) < 0) {
+ printk(KERN_ERR
+ "go7007-i2c: error: i2c_add_adapter failed\n");
+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * This is the private include file for the go7007 driver. It should not
+ * be included by anybody but the driver itself, and especially not by
+ * user-space applications.
+ */
+
+#include <media/v4l2-device.h>
+
+struct go7007;
+
+/* IDs to activate board-specific support code */
+#define GO7007_BOARDID_MATRIX_II 0
+#define GO7007_BOARDID_MATRIX_RELOAD 1
+#define GO7007_BOARDID_STAR_TREK 2
+#define GO7007_BOARDID_PCI_VOYAGER 3
+#define GO7007_BOARDID_XMEN 4
+#define GO7007_BOARDID_XMEN_II 5
+#define GO7007_BOARDID_XMEN_III 6
+#define GO7007_BOARDID_MATRIX_REV 7
+#define GO7007_BOARDID_PX_M402U 16
+#define GO7007_BOARDID_PX_TV402U_ANY 17 /* need to check tuner model */
+#define GO7007_BOARDID_PX_TV402U_NA 18 /* detected NTSC tuner */
+#define GO7007_BOARDID_PX_TV402U_EU 19 /* detected PAL tuner */
+#define GO7007_BOARDID_PX_TV402U_JP 20 /* detected NTSC-J tuner */
+#define GO7007_BOARDID_LIFEVIEW_LR192 21 /* TV Walker Ultra */
+#define GO7007_BOARDID_ENDURA 22
+#define GO7007_BOARDID_ADLINK_MPG24 23
+#define GO7007_BOARDID_SENSORAY_2250 24 /* Sensoray 2250/2251 */
+
+/* Various characteristics of each board */
+#define GO7007_BOARD_HAS_AUDIO (1<<0)
+#define GO7007_BOARD_USE_ONBOARD_I2C (1<<1)
+#define GO7007_BOARD_HAS_TUNER (1<<2)
+
+/* Characteristics of sensor devices */
+#define GO7007_SENSOR_VALID_POLAR (1<<0)
+#define GO7007_SENSOR_HREF_POLAR (1<<1)
+#define GO7007_SENSOR_VREF_POLAR (1<<2)
+#define GO7007_SENSOR_FIELD_ID_POLAR (1<<3)
+#define GO7007_SENSOR_BIT_WIDTH (1<<4)
+#define GO7007_SENSOR_VALID_ENABLE (1<<5)
+#define GO7007_SENSOR_656 (1<<6)
+#define GO7007_SENSOR_CONFIG_MASK 0x7f
+#define GO7007_SENSOR_TV (1<<7)
+#define GO7007_SENSOR_VBI (1<<8)
+#define GO7007_SENSOR_SCALING (1<<9)
+
+/* Characteristics of audio sensor devices */
+#define GO7007_AUDIO_I2S_MODE_1 (1)
+#define GO7007_AUDIO_I2S_MODE_2 (2)
+#define GO7007_AUDIO_I2S_MODE_3 (3)
+#define GO7007_AUDIO_BCLK_POLAR (1<<2)
+#define GO7007_AUDIO_WORD_14 (14<<4)
+#define GO7007_AUDIO_WORD_16 (16<<4)
+#define GO7007_AUDIO_ONE_CHANNEL (1<<11)
+#define GO7007_AUDIO_I2S_MASTER (1<<16)
+#define GO7007_AUDIO_OKI_MODE (1<<17)
+
+struct go7007_board_info {
+ char *firmware;
+ unsigned int flags;
+ int hpi_buffer_cap;
+ unsigned int sensor_flags;
+ int sensor_width;
+ int sensor_height;
+ int sensor_framerate;
+ int sensor_h_offset;
+ int sensor_v_offset;
+ unsigned int audio_flags;
+ int audio_rate;
+ int audio_bclk_div;
+ int audio_main_div;
+ int num_i2c_devs;
+ struct {
+ const char *type;
+ int id;
+ int addr;
+ } i2c_devs[4];
+ int num_inputs;
+ struct {
+ int video_input;
+ int audio_input;
+ char *name;
+ } inputs[4];
+};
+
+struct go7007_hpi_ops {
+ int (*interface_reset)(struct go7007 *go);
+ int (*write_interrupt)(struct go7007 *go, int addr, int data);
+ int (*read_interrupt)(struct go7007 *go);
+ int (*stream_start)(struct go7007 *go);
+ int (*stream_stop)(struct go7007 *go);
+ int (*send_firmware)(struct go7007 *go, u8 *data, int len);
+ int (*send_command)(struct go7007 *go, unsigned int cmd, void *arg);
+};
+
+/* The video buffer size must be a multiple of PAGE_SIZE */
+#define GO7007_BUF_PAGES (128 * 1024 / PAGE_SIZE)
+#define GO7007_BUF_SIZE (GO7007_BUF_PAGES << PAGE_SHIFT)
+
+struct go7007_buffer {
+ struct go7007 *go; /* Reverse reference for VMA ops */
+ int index; /* Reverse reference for DQBUF */
+ enum { BUF_STATE_IDLE, BUF_STATE_QUEUED, BUF_STATE_DONE } state;
+ u32 seq;
+ struct timeval timestamp;
+ struct list_head stream;
+ struct page *pages[GO7007_BUF_PAGES + 1]; /* extra for straddling */
+ unsigned long user_addr;
+ unsigned int page_count;
+ unsigned int offset;
+ unsigned int bytesused;
+ unsigned int frame_offset;
+ u32 modet_active;
+ int mapped;
+};
+
+struct go7007_file {
+ struct go7007 *go;
+ struct mutex lock;
+ int buf_count;
+ struct go7007_buffer *bufs;
+};
+
+#define GO7007_FORMAT_MJPEG 0
+#define GO7007_FORMAT_MPEG4 1
+#define GO7007_FORMAT_MPEG1 2
+#define GO7007_FORMAT_MPEG2 3
+#define GO7007_FORMAT_H263 4
+
+#define GO7007_RATIO_1_1 0
+#define GO7007_RATIO_4_3 1
+#define GO7007_RATIO_16_9 2
+
+enum go7007_parser_state {
+ STATE_DATA,
+ STATE_00,
+ STATE_00_00,
+ STATE_00_00_01,
+ STATE_FF,
+ STATE_VBI_LEN_A,
+ STATE_VBI_LEN_B,
+ STATE_MODET_MAP,
+ STATE_UNPARSED,
+};
+
+struct go7007 {
+ struct device *dev;
+ struct go7007_board_info *board_info;
+ unsigned int board_id;
+ int tuner_type;
+ int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */
+ char name[64];
+ struct video_device *video_dev;
+ struct v4l2_device v4l2_dev;
+ int ref_count;
+ enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status;
+ spinlock_t spinlock;
+ struct mutex hw_lock;
+ int streaming;
+ int in_use;
+ int audio_enabled;
+
+ /* Video input */
+ int input;
+ enum { GO7007_STD_NTSC, GO7007_STD_PAL, GO7007_STD_OTHER } standard;
+ int sensor_framerate;
+ int width;
+ int height;
+ int encoder_h_offset;
+ int encoder_v_offset;
+ unsigned int encoder_h_halve:1;
+ unsigned int encoder_v_halve:1;
+ unsigned int encoder_subsample:1;
+
+ /* Encoder config */
+ int format;
+ int bitrate;
+ int fps_scale;
+ int pali;
+ int aspect_ratio;
+ int gop_size;
+ unsigned int ipb:1;
+ unsigned int closed_gop:1;
+ unsigned int repeat_seqhead:1;
+ unsigned int seq_header_enable:1;
+ unsigned int gop_header_enable:1;
+ unsigned int dvd_mode:1;
+ unsigned int interlace_coding:1;
+
+ /* Motion detection */
+ unsigned int modet_enable:1;
+ struct {
+ unsigned int enable:1;
+ int pixel_threshold;
+ int motion_threshold;
+ int mb_threshold;
+ } modet[4];
+ unsigned char modet_map[1624];
+ unsigned char active_map[216];
+
+ /* Video streaming */
+ struct go7007_buffer *active_buf;
+ enum go7007_parser_state state;
+ int parse_length;
+ u16 modet_word;
+ int seen_frame;
+ u32 next_seq;
+ struct list_head stream;
+ wait_queue_head_t frame_waitq;
+
+ /* Audio streaming */
+ void (*audio_deliver)(struct go7007 *go, u8 *buf, int length);
+ void *snd_context;
+
+ /* I2C */
+ int i2c_adapter_online;
+ struct i2c_adapter i2c_adapter;
+
+ /* HPI driver */
+ struct go7007_hpi_ops *hpi_ops;
+ void *hpi_context;
+ int interrupt_available;
+ wait_queue_head_t interrupt_waitq;
+ unsigned short interrupt_value;
+ unsigned short interrupt_data;
+};
+
+static inline struct go7007 *to_go7007(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct go7007, v4l2_dev);
+}
+
+/* All of these must be called with the hpi_lock mutex held! */
+#define go7007_interface_reset(go) \
+ ((go)->hpi_ops->interface_reset(go))
+#define go7007_write_interrupt(go, x, y) \
+ ((go)->hpi_ops->write_interrupt)((go), (x), (y))
+#define go7007_stream_start(go) \
+ ((go)->hpi_ops->stream_start(go))
+#define go7007_stream_stop(go) \
+ ((go)->hpi_ops->stream_stop(go))
+#define go7007_send_firmware(go, x, y) \
+ ((go)->hpi_ops->send_firmware)((go), (x), (y))
+#define go7007_write_addr(go, x, y) \
+ ((go)->hpi_ops->write_interrupt)((go), (x)|0x8000, (y))
+
+/* go7007-driver.c */
+int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data);
+int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data);
+int go7007_boot_encoder(struct go7007 *go, int init_i2c);
+int go7007_reset_encoder(struct go7007 *go);
+int go7007_register_encoder(struct go7007 *go);
+int go7007_start_encoder(struct go7007 *go);
+void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length);
+struct go7007 *go7007_alloc(struct go7007_board_info *board,
+ struct device *dev);
+void go7007_remove(struct go7007 *go);
+
+/* go7007-fw.c */
+int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen);
+
+/* go7007-i2c.c */
+int go7007_i2c_init(struct go7007 *go);
+int go7007_i2c_remove(struct go7007 *go);
+
+/* go7007-v4l2.c */
+int go7007_v4l2_init(struct go7007 *go);
+void go7007_v4l2_remove(struct go7007 *go);
+
+/* snd-go7007.c */
+int go7007_snd_init(struct go7007 *go);
+int go7007_snd_remove(struct go7007 *go);
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <asm/byteorder.h>
+#include <media/tvaudio.h>
+
+#include "go7007-priv.h"
+#include "wis-i2c.h"
+
+static unsigned int assume_endura;
+module_param(assume_endura, int, 0644);
+MODULE_PARM_DESC(assume_endura, "when probing fails, "
+ "hardware is a Pelco Endura");
+
+/* #define GO7007_USB_DEBUG */
+/* #define GO7007_I2C_DEBUG */ /* for debugging the EZ-USB I2C adapter */
+
+#define HPI_STATUS_ADDR 0xFFF4
+#define INT_PARAM_ADDR 0xFFF6
+#define INT_INDEX_ADDR 0xFFF8
+
+/*
+ * Pipes on EZ-USB interface:
+ * 0 snd - Control
+ * 0 rcv - Control
+ * 2 snd - Download firmware (control)
+ * 4 rcv - Read Interrupt (interrupt)
+ * 6 rcv - Read Video (bulk)
+ * 8 rcv - Read Audio (bulk)
+ */
+
+#define GO7007_USB_EZUSB (1<<0)
+#define GO7007_USB_EZUSB_I2C (1<<1)
+
+struct go7007_usb_board {
+ unsigned int flags;
+ struct go7007_board_info main_info;
+};
+
+struct go7007_usb {
+ struct go7007_usb_board *board;
+ struct mutex i2c_lock;
+ struct usb_device *usbdev;
+ struct urb *video_urbs[8];
+ struct urb *audio_urbs[8];
+ struct urb *intr_urb;
+};
+
+/*********************** Product specification data ***********************/
+
+static struct go7007_usb_board board_matrix_ii = {
+ .flags = GO7007_USB_EZUSB,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .flags = GO7007_BOARD_HAS_AUDIO |
+ GO7007_BOARD_USE_ONBOARD_I2C,
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_WORD_16,
+ .audio_rate = 48000,
+ .audio_bclk_div = 8,
+ .audio_main_div = 2,
+ .hpi_buffer_cap = 7,
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_VALID_ENABLE |
+ GO7007_SENSOR_TV |
+ GO7007_SENSOR_VBI |
+ GO7007_SENSOR_SCALING,
+ .num_i2c_devs = 1,
+ .i2c_devs = {
+ {
+ .type = "wis_saa7115",
+ .id = I2C_DRIVERID_WIS_SAA7115,
+ .addr = 0x20,
+ },
+ },
+ .num_inputs = 2,
+ .inputs = {
+ {
+ .video_input = 0,
+ .name = "Composite",
+ },
+ {
+ .video_input = 9,
+ .name = "S-Video",
+ },
+ },
+ },
+};
+
+static struct go7007_usb_board board_matrix_reload = {
+ .flags = GO7007_USB_EZUSB,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .flags = GO7007_BOARD_HAS_AUDIO |
+ GO7007_BOARD_USE_ONBOARD_I2C,
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_I2S_MASTER |
+ GO7007_AUDIO_WORD_16,
+ .audio_rate = 48000,
+ .audio_bclk_div = 8,
+ .audio_main_div = 2,
+ .hpi_buffer_cap = 7,
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_TV,
+ .num_i2c_devs = 1,
+ .i2c_devs = {
+ {
+ .type = "wis_saa7113",
+ .id = I2C_DRIVERID_WIS_SAA7113,
+ .addr = 0x25,
+ },
+ },
+ .num_inputs = 2,
+ .inputs = {
+ {
+ .video_input = 0,
+ .name = "Composite",
+ },
+ {
+ .video_input = 9,
+ .name = "S-Video",
+ },
+ },
+ },
+};
+
+static struct go7007_usb_board board_star_trek = {
+ .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .flags = GO7007_BOARD_HAS_AUDIO, /* |
+ GO7007_BOARD_HAS_TUNER, */
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_VALID_ENABLE |
+ GO7007_SENSOR_TV |
+ GO7007_SENSOR_VBI |
+ GO7007_SENSOR_SCALING,
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_WORD_16,
+ .audio_bclk_div = 8,
+ .audio_main_div = 2,
+ .hpi_buffer_cap = 7,
+ .num_i2c_devs = 1,
+ .i2c_devs = {
+ {
+ .type = "wis_saa7115",
+ .id = I2C_DRIVERID_WIS_SAA7115,
+ .addr = 0x20,
+ },
+ },
+ .num_inputs = 2,
+ .inputs = {
+ {
+ .video_input = 1,
+ /* .audio_input = AUDIO_EXTERN, */
+ .name = "Composite",
+ },
+ {
+ .video_input = 8,
+ /* .audio_input = AUDIO_EXTERN, */
+ .name = "S-Video",
+ },
+ /* {
+ * .video_input = 3,
+ * .audio_input = AUDIO_TUNER,
+ * .name = "Tuner",
+ * },
+ */
+ },
+ },
+};
+
+static struct go7007_usb_board board_px_tv402u = {
+ .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .flags = GO7007_BOARD_HAS_AUDIO |
+ GO7007_BOARD_HAS_TUNER,
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_VALID_ENABLE |
+ GO7007_SENSOR_TV |
+ GO7007_SENSOR_VBI |
+ GO7007_SENSOR_SCALING,
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_WORD_16,
+ .audio_bclk_div = 8,
+ .audio_main_div = 2,
+ .hpi_buffer_cap = 7,
+ .num_i2c_devs = 3,
+ .i2c_devs = {
+ {
+ .type = "wis_saa7115",
+ .id = I2C_DRIVERID_WIS_SAA7115,
+ .addr = 0x20,
+ },
+ {
+ .type = "wis_uda1342",
+ .id = I2C_DRIVERID_WIS_UDA1342,
+ .addr = 0x1a,
+ },
+ {
+ .type = "wis_sony_tuner",
+ .id = I2C_DRIVERID_WIS_SONY_TUNER,
+ .addr = 0x60,
+ },
+ },
+ .num_inputs = 3,
+ .inputs = {
+ {
+ .video_input = 1,
+ .audio_input = TVAUDIO_INPUT_EXTERN,
+ .name = "Composite",
+ },
+ {
+ .video_input = 8,
+ .audio_input = TVAUDIO_INPUT_EXTERN,
+ .name = "S-Video",
+ },
+ {
+ .video_input = 3,
+ .audio_input = TVAUDIO_INPUT_TUNER,
+ .name = "Tuner",
+ },
+ },
+ },
+};
+
+static struct go7007_usb_board board_xmen = {
+ .flags = 0,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .flags = GO7007_BOARD_USE_ONBOARD_I2C,
+ .hpi_buffer_cap = 0,
+ .sensor_flags = GO7007_SENSOR_VREF_POLAR,
+ .sensor_width = 320,
+ .sensor_height = 240,
+ .sensor_framerate = 30030,
+ .audio_flags = GO7007_AUDIO_ONE_CHANNEL |
+ GO7007_AUDIO_I2S_MODE_3 |
+ GO7007_AUDIO_WORD_14 |
+ GO7007_AUDIO_I2S_MASTER |
+ GO7007_AUDIO_BCLK_POLAR |
+ GO7007_AUDIO_OKI_MODE,
+ .audio_rate = 8000,
+ .audio_bclk_div = 48,
+ .audio_main_div = 1,
+ .num_i2c_devs = 1,
+ .i2c_devs = {
+ {
+ .type = "wis_ov7640",
+ .id = I2C_DRIVERID_WIS_OV7640,
+ .addr = 0x21,
+ },
+ },
+ .num_inputs = 1,
+ .inputs = {
+ {
+ .name = "Camera",
+ },
+ },
+ },
+};
+
+static struct go7007_usb_board board_matrix_revolution = {
+ .flags = GO7007_USB_EZUSB,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .flags = GO7007_BOARD_HAS_AUDIO |
+ GO7007_BOARD_USE_ONBOARD_I2C,
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_I2S_MASTER |
+ GO7007_AUDIO_WORD_16,
+ .audio_rate = 48000,
+ .audio_bclk_div = 8,
+ .audio_main_div = 2,
+ .hpi_buffer_cap = 7,
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_TV |
+ GO7007_SENSOR_VBI,
+ .num_i2c_devs = 1,
+ .i2c_devs = {
+ {
+ .type = "wis_tw9903",
+ .id = I2C_DRIVERID_WIS_TW9903,
+ .addr = 0x44,
+ },
+ },
+ .num_inputs = 2,
+ .inputs = {
+ {
+ .video_input = 2,
+ .name = "Composite",
+ },
+ {
+ .video_input = 8,
+ .name = "S-Video",
+ },
+ },
+ },
+};
+
+static struct go7007_usb_board board_lifeview_lr192 = {
+ .flags = GO7007_USB_EZUSB,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .flags = GO7007_BOARD_HAS_AUDIO |
+ GO7007_BOARD_USE_ONBOARD_I2C,
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_WORD_16,
+ .audio_rate = 48000,
+ .audio_bclk_div = 8,
+ .audio_main_div = 2,
+ .hpi_buffer_cap = 7,
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_VALID_ENABLE |
+ GO7007_SENSOR_TV |
+ GO7007_SENSOR_VBI |
+ GO7007_SENSOR_SCALING,
+ .num_i2c_devs = 0,
+ .num_inputs = 1,
+ .inputs = {
+ {
+ .video_input = 0,
+ .name = "Composite",
+ },
+ },
+ },
+};
+
+static struct go7007_usb_board board_endura = {
+ .flags = 0,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .flags = 0,
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_I2S_MASTER |
+ GO7007_AUDIO_WORD_16,
+ .audio_rate = 8000,
+ .audio_bclk_div = 48,
+ .audio_main_div = 8,
+ .hpi_buffer_cap = 0,
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_TV,
+ .sensor_h_offset = 8,
+ .num_i2c_devs = 0,
+ .num_inputs = 1,
+ .inputs = {
+ {
+ .name = "Camera",
+ },
+ },
+ },
+};
+
+static struct go7007_usb_board board_adlink_mpg24 = {
+ .flags = 0,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .flags = GO7007_BOARD_USE_ONBOARD_I2C,
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_I2S_MASTER |
+ GO7007_AUDIO_WORD_16,
+ .audio_rate = 48000,
+ .audio_bclk_div = 8,
+ .audio_main_div = 2,
+ .hpi_buffer_cap = 0,
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_TV |
+ GO7007_SENSOR_VBI,
+ .num_i2c_devs = 1,
+ .i2c_devs = {
+ {
+ .type = "wis_tw2804",
+ .id = I2C_DRIVERID_WIS_TW2804,
+ .addr = 0x00, /* yes, really */
+ },
+ },
+ .num_inputs = 1,
+ .inputs = {
+ {
+ .name = "Composite",
+ },
+ },
+ },
+};
+
+static struct go7007_usb_board board_sensoray_2250 = {
+ .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_I2S_MASTER |
+ GO7007_AUDIO_WORD_16,
+ .flags = GO7007_BOARD_HAS_AUDIO,
+ .audio_rate = 48000,
+ .audio_bclk_div = 8,
+ .audio_main_div = 2,
+ .hpi_buffer_cap = 7,
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_TV,
+ .num_i2c_devs = 1,
+ .i2c_devs = {
+ {
+ .type = "s2250",
+ .id = I2C_DRIVERID_S2250,
+ .addr = 0x43,
+ },
+ },
+ .num_inputs = 2,
+ .inputs = {
+ {
+ .video_input = 0,
+ .name = "Composite",
+ },
+ {
+ .video_input = 1,
+ .name = "S-Video",
+ },
+ },
+ },
+};
+
+MODULE_FIRMWARE("go7007tv.bin");
+
+static const struct usb_device_id go7007_usb_id_table[] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
+ .idProduct = 0x7007, /* Product ID of GO7007SB chip */
+ .bcdDevice_lo = 0x200, /* Revision number of XMen */
+ .bcdDevice_hi = 0x200,
+ .bInterfaceClass = 255,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 255,
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN,
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+ .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
+ .idProduct = 0x7007, /* Product ID of GO7007SB chip */
+ .bcdDevice_lo = 0x202, /* Revision number of Matrix II */
+ .bcdDevice_hi = 0x202,
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_II,
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+ .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
+ .idProduct = 0x7007, /* Product ID of GO7007SB chip */
+ .bcdDevice_lo = 0x204, /* Revision number of Matrix */
+ .bcdDevice_hi = 0x204, /* Reloaded */
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_RELOAD,
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
+ .idProduct = 0x7007, /* Product ID of GO7007SB chip */
+ .bcdDevice_lo = 0x205, /* Revision number of XMen-II */
+ .bcdDevice_hi = 0x205,
+ .bInterfaceClass = 255,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 255,
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_II,
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+ .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
+ .idProduct = 0x7007, /* Product ID of GO7007SB chip */
+ .bcdDevice_lo = 0x208, /* Revision number of Star Trek */
+ .bcdDevice_hi = 0x208,
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_STAR_TREK,
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
+ .idProduct = 0x7007, /* Product ID of GO7007SB chip */
+ .bcdDevice_lo = 0x209, /* Revision number of XMen-III */
+ .bcdDevice_hi = 0x209,
+ .bInterfaceClass = 255,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 255,
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_III,
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+ .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
+ .idProduct = 0x7007, /* Product ID of GO7007SB chip */
+ .bcdDevice_lo = 0x210, /* Revision number of Matrix */
+ .bcdDevice_hi = 0x210, /* Revolution */
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_REV,
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+ .idVendor = 0x093b, /* Vendor ID of Plextor */
+ .idProduct = 0xa102, /* Product ID of M402U */
+ .bcdDevice_lo = 0x1, /* revision number of Blueberry */
+ .bcdDevice_hi = 0x1,
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_M402U,
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+ .idVendor = 0x093b, /* Vendor ID of Plextor */
+ .idProduct = 0xa104, /* Product ID of TV402U */
+ .bcdDevice_lo = 0x1,
+ .bcdDevice_hi = 0x1,
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_TV402U_ANY,
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+ .idVendor = 0x10fd, /* Vendor ID of Anubis Electronics */
+ .idProduct = 0xde00, /* Product ID of Lifeview LR192 */
+ .bcdDevice_lo = 0x1,
+ .bcdDevice_hi = 0x1,
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192,
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+ .idVendor = 0x1943, /* Vendor ID Sensoray */
+ .idProduct = 0x2250, /* Product ID of 2250/2251 */
+ .bcdDevice_lo = 0x1,
+ .bcdDevice_hi = 0x1,
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250,
+ },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, go7007_usb_id_table);
+
+/********************* Driver for EZ-USB HPI interface *********************/
+
+static int go7007_usb_vendor_request(struct go7007 *go, int request,
+ int value, int index, void *transfer_buffer, int length, int in)
+{
+ struct go7007_usb *usb = go->hpi_context;
+ int timeout = 5000;
+
+ if (in) {
+ return usb_control_msg(usb->usbdev,
+ usb_rcvctrlpipe(usb->usbdev, 0), request,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ value, index, transfer_buffer, length, timeout);
+ } else {
+ return usb_control_msg(usb->usbdev,
+ usb_sndctrlpipe(usb->usbdev, 0), request,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, transfer_buffer, length, timeout);
+ }
+}
+
+static int go7007_usb_interface_reset(struct go7007 *go)
+{
+ struct go7007_usb *usb = go->hpi_context;
+ u16 intr_val, intr_data;
+
+ /* Reset encoder */
+ if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0)
+ return -1;
+ msleep(100);
+
+ if (usb->board->flags & GO7007_USB_EZUSB) {
+ /* Reset buffer in EZ-USB */
+#ifdef GO7007_USB_DEBUG
+ printk(KERN_DEBUG "go7007-usb: resetting EZ-USB buffers\n");
+#endif
+ if (go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0 ||
+ go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0)
+ return -1;
+
+ /* Reset encoder again */
+ if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0)
+ return -1;
+ msleep(100);
+ }
+
+ /* Wait for an interrupt to indicate successful hardware reset */
+ if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
+ (intr_val & ~0x1) != 0x55aa) {
+ printk(KERN_ERR
+ "go7007-usb: unable to reset the USB interface\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int go7007_usb_ezusb_write_interrupt(struct go7007 *go,
+ int addr, int data)
+{
+ struct go7007_usb *usb = go->hpi_context;
+ int i, r;
+ u16 status_reg;
+ int timeout = 500;
+
+#ifdef GO7007_USB_DEBUG
+ printk(KERN_DEBUG
+ "go7007-usb: WriteInterrupt: %04x %04x\n", addr, data);
+#endif
+
+ for (i = 0; i < 100; ++i) {
+ r = usb_control_msg(usb->usbdev,
+ usb_rcvctrlpipe(usb->usbdev, 0), 0x14,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0, HPI_STATUS_ADDR, &status_reg,
+ sizeof(status_reg), timeout);
+ if (r < 0)
+ goto write_int_error;
+ __le16_to_cpus(&status_reg);
+ if (!(status_reg & 0x0010))
+ break;
+ msleep(10);
+ }
+ if (i == 100) {
+ printk(KERN_ERR
+ "go7007-usb: device is hung, status reg = 0x%04x\n",
+ status_reg);
+ return -1;
+ }
+ r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), 0x12,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE, data,
+ INT_PARAM_ADDR, NULL, 0, timeout);
+ if (r < 0)
+ goto write_int_error;
+ r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0),
+ 0x12, USB_TYPE_VENDOR | USB_RECIP_DEVICE, addr,
+ INT_INDEX_ADDR, NULL, 0, timeout);
+ if (r < 0)
+ goto write_int_error;
+ return 0;
+
+write_int_error:
+ printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r);
+ return r;
+}
+
+static int go7007_usb_onboard_write_interrupt(struct go7007 *go,
+ int addr, int data)
+{
+ struct go7007_usb *usb = go->hpi_context;
+ u8 *tbuf;
+ int r;
+ int timeout = 500;
+
+#ifdef GO7007_USB_DEBUG
+ printk(KERN_DEBUG
+ "go7007-usb: WriteInterrupt: %04x %04x\n", addr, data);
+#endif
+
+ tbuf = kzalloc(8, GFP_KERNEL);
+ if (tbuf == NULL)
+ return -ENOMEM;
+ tbuf[0] = data & 0xff;
+ tbuf[1] = data >> 8;
+ tbuf[2] = addr & 0xff;
+ tbuf[3] = addr >> 8;
+ r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 2), 0x00,
+ USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x55aa,
+ 0xf0f0, tbuf, 8, timeout);
+ kfree(tbuf);
+ if (r < 0) {
+ printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r);
+ return r;
+ }
+ return 0;
+}
+
+static void go7007_usb_readinterrupt_complete(struct urb *urb)
+{
+ struct go7007 *go = (struct go7007 *)urb->context;
+ u16 *regs = (u16 *)urb->transfer_buffer;
+ int status = urb->status;
+
+ if (status) {
+ if (status != -ESHUTDOWN &&
+ go->status != STATUS_SHUTDOWN) {
+ printk(KERN_ERR
+ "go7007-usb: error in read interrupt: %d\n",
+ urb->status);
+ } else {
+ wake_up(&go->interrupt_waitq);
+ return;
+ }
+ } else if (urb->actual_length != urb->transfer_buffer_length) {
+ printk(KERN_ERR "go7007-usb: short read in interrupt pipe!\n");
+ } else {
+ go->interrupt_available = 1;
+ go->interrupt_data = __le16_to_cpu(regs[0]);
+ go->interrupt_value = __le16_to_cpu(regs[1]);
+#ifdef GO7007_USB_DEBUG
+ printk(KERN_DEBUG "go7007-usb: ReadInterrupt: %04x %04x\n",
+ go->interrupt_value, go->interrupt_data);
+#endif
+ }
+
+ wake_up(&go->interrupt_waitq);
+}
+
+static int go7007_usb_read_interrupt(struct go7007 *go)
+{
+ struct go7007_usb *usb = go->hpi_context;
+ int r;
+
+ r = usb_submit_urb(usb->intr_urb, GFP_KERNEL);
+ if (r < 0) {
+ printk(KERN_ERR
+ "go7007-usb: unable to submit interrupt urb: %d\n", r);
+ return r;
+ }
+ return 0;
+}
+
+static void go7007_usb_read_video_pipe_complete(struct urb *urb)
+{
+ struct go7007 *go = (struct go7007 *)urb->context;
+ int r, status = urb->status;
+
+ if (!go->streaming) {
+ wake_up_interruptible(&go->frame_waitq);
+ return;
+ }
+ if (status) {
+ printk(KERN_ERR "go7007-usb: error in video pipe: %d\n",
+ status);
+ return;
+ }
+ if (urb->actual_length != urb->transfer_buffer_length) {
+ printk(KERN_ERR "go7007-usb: short read in video pipe!\n");
+ return;
+ }
+ go7007_parse_video_stream(go, urb->transfer_buffer, urb->actual_length);
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r < 0)
+ printk(KERN_ERR "go7007-usb: error in video pipe: %d\n", r);
+}
+
+static void go7007_usb_read_audio_pipe_complete(struct urb *urb)
+{
+ struct go7007 *go = (struct go7007 *)urb->context;
+ int r, status = urb->status;
+
+ if (!go->streaming)
+ return;
+ if (status) {
+ printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n",
+ status);
+ return;
+ }
+ if (urb->actual_length != urb->transfer_buffer_length) {
+ printk(KERN_ERR "go7007-usb: short read in audio pipe!\n");
+ return;
+ }
+ if (go->audio_deliver != NULL)
+ go->audio_deliver(go, urb->transfer_buffer, urb->actual_length);
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r < 0)
+ printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", r);
+}
+
+static int go7007_usb_stream_start(struct go7007 *go)
+{
+ struct go7007_usb *usb = go->hpi_context;
+ int i, r;
+
+ for (i = 0; i < 8; ++i) {
+ r = usb_submit_urb(usb->video_urbs[i], GFP_KERNEL);
+ if (r < 0) {
+ printk(KERN_ERR "go7007-usb: error submitting video "
+ "urb %d: %d\n", i, r);
+ goto video_submit_failed;
+ }
+ }
+ if (!go->audio_enabled)
+ return 0;
+
+ for (i = 0; i < 8; ++i) {
+ r = usb_submit_urb(usb->audio_urbs[i], GFP_KERNEL);
+ if (r < 0) {
+ printk(KERN_ERR "go7007-usb: error submitting audio "
+ "urb %d: %d\n", i, r);
+ goto audio_submit_failed;
+ }
+ }
+ return 0;
+
+audio_submit_failed:
+ for (i = 0; i < 7; ++i)
+ usb_kill_urb(usb->audio_urbs[i]);
+video_submit_failed:
+ for (i = 0; i < 8; ++i)
+ usb_kill_urb(usb->video_urbs[i]);
+ return -1;
+}
+
+static int go7007_usb_stream_stop(struct go7007 *go)
+{
+ struct go7007_usb *usb = go->hpi_context;
+ int i;
+
+ if (go->status == STATUS_SHUTDOWN)
+ return 0;
+ for (i = 0; i < 8; ++i)
+ usb_kill_urb(usb->video_urbs[i]);
+ if (go->audio_enabled)
+ for (i = 0; i < 8; ++i)
+ usb_kill_urb(usb->audio_urbs[i]);
+ return 0;
+}
+
+static int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len)
+{
+ struct go7007_usb *usb = go->hpi_context;
+ int transferred, pipe;
+ int timeout = 500;
+
+#ifdef GO7007_USB_DEBUG
+ printk(KERN_DEBUG "go7007-usb: DownloadBuffer sending %d bytes\n", len);
+#endif
+
+ if (usb->board->flags & GO7007_USB_EZUSB)
+ pipe = usb_sndbulkpipe(usb->usbdev, 2);
+ else
+ pipe = usb_sndbulkpipe(usb->usbdev, 3);
+
+ return usb_bulk_msg(usb->usbdev, pipe, data, len,
+ &transferred, timeout);
+}
+
+static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = {
+ .interface_reset = go7007_usb_interface_reset,
+ .write_interrupt = go7007_usb_ezusb_write_interrupt,
+ .read_interrupt = go7007_usb_read_interrupt,
+ .stream_start = go7007_usb_stream_start,
+ .stream_stop = go7007_usb_stream_stop,
+ .send_firmware = go7007_usb_send_firmware,
+};
+
+static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = {
+ .interface_reset = go7007_usb_interface_reset,
+ .write_interrupt = go7007_usb_onboard_write_interrupt,
+ .read_interrupt = go7007_usb_read_interrupt,
+ .stream_start = go7007_usb_stream_start,
+ .stream_stop = go7007_usb_stream_stop,
+ .send_firmware = go7007_usb_send_firmware,
+};
+
+/********************* Driver for EZ-USB I2C adapter *********************/
+
+static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg msgs[], int num)
+{
+ struct go7007 *go = i2c_get_adapdata(adapter);
+ struct go7007_usb *usb = go->hpi_context;
+ u8 buf[16];
+ int buf_len, i;
+ int ret = -1;
+
+ if (go->status == STATUS_SHUTDOWN)
+ return -1;
+
+ mutex_lock(&usb->i2c_lock);
+
+ for (i = 0; i < num; ++i) {
+ /* The hardware command is "write some bytes then read some
+ * bytes", so we try to coalesce a write followed by a read
+ * into a single USB transaction */
+ if (i + 1 < num && msgs[i].addr == msgs[i + 1].addr &&
+ !(msgs[i].flags & I2C_M_RD) &&
+ (msgs[i + 1].flags & I2C_M_RD)) {
+#ifdef GO7007_I2C_DEBUG
+ printk(KERN_DEBUG "go7007-usb: i2c write/read %d/%d "
+ "bytes on %02x\n", msgs[i].len,
+ msgs[i + 1].len, msgs[i].addr);
+#endif
+ buf[0] = 0x01;
+ buf[1] = msgs[i].len + 1;
+ buf[2] = msgs[i].addr << 1;
+ memcpy(&buf[3], msgs[i].buf, msgs[i].len);
+ buf_len = msgs[i].len + 3;
+ buf[buf_len++] = msgs[++i].len;
+ } else if (msgs[i].flags & I2C_M_RD) {
+#ifdef GO7007_I2C_DEBUG
+ printk(KERN_DEBUG "go7007-usb: i2c read %d "
+ "bytes on %02x\n", msgs[i].len,
+ msgs[i].addr);
+#endif
+ buf[0] = 0x01;
+ buf[1] = 1;
+ buf[2] = msgs[i].addr << 1;
+ buf[3] = msgs[i].len;
+ buf_len = 4;
+ } else {
+#ifdef GO7007_I2C_DEBUG
+ printk(KERN_DEBUG "go7007-usb: i2c write %d "
+ "bytes on %02x\n", msgs[i].len,
+ msgs[i].addr);
+#endif
+ buf[0] = 0x00;
+ buf[1] = msgs[i].len + 1;
+ buf[2] = msgs[i].addr << 1;
+ memcpy(&buf[3], msgs[i].buf, msgs[i].len);
+ buf_len = msgs[i].len + 3;
+ buf[buf_len++] = 0;
+ }
+ if (go7007_usb_vendor_request(go, 0x24, 0, 0,
+ buf, buf_len, 0) < 0)
+ goto i2c_done;
+ if (msgs[i].flags & I2C_M_RD) {
+ memset(buf, 0, sizeof(buf));
+ if (go7007_usb_vendor_request(go, 0x25, 0, 0, buf,
+ msgs[i].len + 1, 1) < 0)
+ goto i2c_done;
+ memcpy(msgs[i].buf, buf + 1, msgs[i].len);
+ }
+ }
+ ret = 0;
+
+i2c_done:
+ mutex_unlock(&usb->i2c_lock);
+ return ret;
+}
+
+static u32 go7007_usb_functionality(struct i2c_adapter *adapter)
+{
+ /* No errors are reported by the hardware, so we don't bother
+ * supporting quick writes to avoid confusing probing */
+ return (I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK;
+}
+
+static struct i2c_algorithm go7007_usb_algo = {
+ .master_xfer = go7007_usb_i2c_master_xfer,
+ .functionality = go7007_usb_functionality,
+};
+
+static struct i2c_adapter go7007_usb_adap_templ = {
+ .owner = THIS_MODULE,
+ .name = "WIS GO7007SB EZ-USB",
+ .algo = &go7007_usb_algo,
+};
+
+/********************* USB add/remove functions *********************/
+
+static int go7007_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct go7007 *go;
+ struct go7007_usb *usb;
+ struct go7007_usb_board *board;
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ char *name;
+ int video_pipe, i, v_urb_len;
+
+ printk(KERN_DEBUG "go7007-usb: probing new GO7007 USB board\n");
+
+ switch (id->driver_info) {
+ case GO7007_BOARDID_MATRIX_II:
+ name = "WIS Matrix II or compatible";
+ board = &board_matrix_ii;
+ break;
+ case GO7007_BOARDID_MATRIX_RELOAD:
+ name = "WIS Matrix Reloaded or compatible";
+ board = &board_matrix_reload;
+ break;
+ case GO7007_BOARDID_MATRIX_REV:
+ name = "WIS Matrix Revolution or compatible";
+ board = &board_matrix_revolution;
+ break;
+ case GO7007_BOARDID_STAR_TREK:
+ name = "WIS Star Trek or compatible";
+ board = &board_star_trek;
+ break;
+ case GO7007_BOARDID_XMEN:
+ name = "WIS XMen or compatible";
+ board = &board_xmen;
+ break;
+ case GO7007_BOARDID_XMEN_II:
+ name = "WIS XMen II or compatible";
+ board = &board_xmen;
+ break;
+ case GO7007_BOARDID_XMEN_III:
+ name = "WIS XMen III or compatible";
+ board = &board_xmen;
+ break;
+ case GO7007_BOARDID_PX_M402U:
+ name = "Plextor PX-M402U";
+ board = &board_matrix_ii;
+ break;
+ case GO7007_BOARDID_PX_TV402U_ANY:
+ name = "Plextor PX-TV402U (unknown tuner)";
+ board = &board_px_tv402u;
+ break;
+ case GO7007_BOARDID_LIFEVIEW_LR192:
+ printk(KERN_ERR "go7007-usb: The Lifeview TV Walker Ultra "
+ "is not supported. Sorry!\n");
+ return 0;
+ name = "Lifeview TV Walker Ultra";
+ board = &board_lifeview_lr192;
+ break;
+ case GO7007_BOARDID_SENSORAY_2250:
+ printk(KERN_INFO "Sensoray 2250 found\n");
+ name = "Sensoray 2250/2251";
+ board = &board_sensoray_2250;
+ break;
+ default:
+ printk(KERN_ERR "go7007-usb: unknown board ID %d!\n",
+ (unsigned int)id->driver_info);
+ return 0;
+ }
+
+ usb = kzalloc(sizeof(struct go7007_usb), GFP_KERNEL);
+ if (usb == NULL)
+ return -ENOMEM;
+
+ /* Allocate the URB and buffer for receiving incoming interrupts */
+ usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (usb->intr_urb == NULL)
+ goto allocfail;
+ usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL);
+ if (usb->intr_urb->transfer_buffer == NULL)
+ goto allocfail;
+
+ go = go7007_alloc(&board->main_info, &intf->dev);
+ if (go == NULL)
+ goto allocfail;
+ usb->board = board;
+ usb->usbdev = usbdev;
+ go->board_id = id->driver_info;
+ strncpy(go->name, name, sizeof(go->name));
+ if (board->flags & GO7007_USB_EZUSB)
+ go->hpi_ops = &go7007_usb_ezusb_hpi_ops;
+ else
+ go->hpi_ops = &go7007_usb_onboard_hpi_ops;
+ go->hpi_context = usb;
+ usb_fill_int_urb(usb->intr_urb, usb->usbdev,
+ usb_rcvintpipe(usb->usbdev, 4),
+ usb->intr_urb->transfer_buffer, 2*sizeof(u16),
+ go7007_usb_readinterrupt_complete, go, 8);
+ usb_set_intfdata(intf, &go->v4l2_dev);
+
+ /* Boot the GO7007 */
+ if (go7007_boot_encoder(go, go->board_info->flags &
+ GO7007_BOARD_USE_ONBOARD_I2C) < 0)
+ goto initfail;
+
+ /* Register the EZ-USB I2C adapter, if we're using it */
+ if (board->flags & GO7007_USB_EZUSB_I2C) {
+ memcpy(&go->i2c_adapter, &go7007_usb_adap_templ,
+ sizeof(go7007_usb_adap_templ));
+ mutex_init(&usb->i2c_lock);
+ go->i2c_adapter.dev.parent = go->dev;
+ i2c_set_adapdata(&go->i2c_adapter, go);
+ if (i2c_add_adapter(&go->i2c_adapter) < 0) {
+ printk(KERN_ERR
+ "go7007-usb: error: i2c_add_adapter failed\n");
+ goto initfail;
+ }
+ go->i2c_adapter_online = 1;
+ }
+
+ /* Pelco and Adlink reused the XMen and XMen-III vendor and product
+ * IDs for their own incompatible designs. We can detect XMen boards
+ * by probing the sensor, but there is no way to probe the sensors on
+ * the Pelco and Adlink designs so we default to the Adlink. If it
+ * is actually a Pelco, the user must set the assume_endura module
+ * parameter. */
+ if ((go->board_id == GO7007_BOARDID_XMEN ||
+ go->board_id == GO7007_BOARDID_XMEN_III) &&
+ go->i2c_adapter_online) {
+ union i2c_smbus_data data;
+
+ /* Check to see if register 0x0A is 0x76 */
+ i2c_smbus_xfer(&go->i2c_adapter, 0x21, I2C_CLIENT_SCCB,
+ I2C_SMBUS_READ, 0x0A, I2C_SMBUS_BYTE_DATA, &data);
+ if (data.byte != 0x76) {
+ if (assume_endura) {
+ go->board_id = GO7007_BOARDID_ENDURA;
+ usb->board = board = &board_endura;
+ go->board_info = &board->main_info;
+ strncpy(go->name, "Pelco Endura",
+ sizeof(go->name));
+ } else {
+ u16 channel;
+
+ /* set GPIO5 to be an output, currently low */
+ go7007_write_addr(go, 0x3c82, 0x0000);
+ go7007_write_addr(go, 0x3c80, 0x00df);
+ /* read channel number from GPIO[1:0] */
+ go7007_read_addr(go, 0x3c81, &channel);
+ channel &= 0x3;
+ go->board_id = GO7007_BOARDID_ADLINK_MPG24;
+ usb->board = board = &board_adlink_mpg24;
+ go->board_info = &board->main_info;
+ go->channel_number = channel;
+ snprintf(go->name, sizeof(go->name),
+ "Adlink PCI-MPG24, channel #%d",
+ channel);
+ }
+ }
+ }
+
+ /* Probe the tuner model on the TV402U */
+ if (go->board_id == GO7007_BOARDID_PX_TV402U_ANY) {
+ u8 data[3];
+
+ /* Board strapping indicates tuner model */
+ if (go7007_usb_vendor_request(go, 0x41, 0, 0, data, 3, 1) < 0) {
+ printk(KERN_ERR "go7007-usb: GPIO read failed!\n");
+ goto initfail;
+ }
+ switch (data[0] >> 6) {
+ case 1:
+ go->board_id = GO7007_BOARDID_PX_TV402U_EU;
+ go->tuner_type = TUNER_SONY_BTF_PG472Z;
+ strncpy(go->name, "Plextor PX-TV402U-EU",
+ sizeof(go->name));
+ break;
+ case 2:
+ go->board_id = GO7007_BOARDID_PX_TV402U_JP;
+ go->tuner_type = TUNER_SONY_BTF_PK467Z;
+ strncpy(go->name, "Plextor PX-TV402U-JP",
+ sizeof(go->name));
+ break;
+ case 3:
+ go->board_id = GO7007_BOARDID_PX_TV402U_NA;
+ go->tuner_type = TUNER_SONY_BTF_PB463Z;
+ strncpy(go->name, "Plextor PX-TV402U-NA",
+ sizeof(go->name));
+ break;
+ default:
+ printk(KERN_DEBUG "go7007-usb: unable to detect "
+ "tuner type!\n");
+ break;
+ }
+ /* Configure tuner mode selection inputs connected
+ * to the EZ-USB GPIO output pins */
+ if (go7007_usb_vendor_request(go, 0x40, 0x7f02, 0,
+ NULL, 0, 0) < 0) {
+ printk(KERN_ERR "go7007-usb: GPIO write failed!\n");
+ goto initfail;
+ }
+ }
+
+ /* Print a nasty message if the user attempts to use a USB2.0 device in
+ * a USB1.1 port. There will be silent corruption of the stream. */
+ if ((board->flags & GO7007_USB_EZUSB) &&
+ usbdev->speed != USB_SPEED_HIGH)
+ printk(KERN_ERR "go7007-usb: *** WARNING *** This device "
+ "must be connected to a USB 2.0 port! "
+ "Attempting to capture video through a USB 1.1 "
+ "port will result in stream corruption, even "
+ "at low bitrates!\n");
+
+ /* Do any final GO7007 initialization, then register the
+ * V4L2 and ALSA interfaces */
+ if (go7007_register_encoder(go) < 0)
+ goto initfail;
+
+ /* Allocate the URBs and buffers for receiving the video stream */
+ if (board->flags & GO7007_USB_EZUSB) {
+ v_urb_len = 1024;
+ video_pipe = usb_rcvbulkpipe(usb->usbdev, 6);
+ } else {
+ v_urb_len = 512;
+ video_pipe = usb_rcvbulkpipe(usb->usbdev, 1);
+ }
+ for (i = 0; i < 8; ++i) {
+ usb->video_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (usb->video_urbs[i] == NULL)
+ goto initfail;
+ usb->video_urbs[i]->transfer_buffer =
+ kmalloc(v_urb_len, GFP_KERNEL);
+ if (usb->video_urbs[i]->transfer_buffer == NULL)
+ goto initfail;
+ usb_fill_bulk_urb(usb->video_urbs[i], usb->usbdev, video_pipe,
+ usb->video_urbs[i]->transfer_buffer, v_urb_len,
+ go7007_usb_read_video_pipe_complete, go);
+ }
+
+ /* Allocate the URBs and buffers for receiving the audio stream */
+ if ((board->flags & GO7007_USB_EZUSB) && go->audio_enabled)
+ for (i = 0; i < 8; ++i) {
+ usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (usb->audio_urbs[i] == NULL)
+ goto initfail;
+ usb->audio_urbs[i]->transfer_buffer = kmalloc(4096,
+ GFP_KERNEL);
+ if (usb->audio_urbs[i]->transfer_buffer == NULL)
+ goto initfail;
+ usb_fill_bulk_urb(usb->audio_urbs[i], usb->usbdev,
+ usb_rcvbulkpipe(usb->usbdev, 8),
+ usb->audio_urbs[i]->transfer_buffer, 4096,
+ go7007_usb_read_audio_pipe_complete, go);
+ }
+
+
+ go->status = STATUS_ONLINE;
+ return 0;
+
+initfail:
+ go->status = STATUS_SHUTDOWN;
+ return 0;
+
+allocfail:
+ if (usb->intr_urb) {
+ kfree(usb->intr_urb->transfer_buffer);
+ usb_free_urb(usb->intr_urb);
+ }
+ kfree(usb);
+ return -ENOMEM;
+}
+
+static void go7007_usb_disconnect(struct usb_interface *intf)
+{
+ struct go7007 *go = to_go7007(usb_get_intfdata(intf));
+ struct go7007_usb *usb = go->hpi_context;
+ struct urb *vurb, *aurb;
+ int i;
+
+ go->status = STATUS_SHUTDOWN;
+ usb_kill_urb(usb->intr_urb);
+
+ /* Free USB-related structs */
+ for (i = 0; i < 8; ++i) {
+ vurb = usb->video_urbs[i];
+ if (vurb) {
+ usb_kill_urb(vurb);
+ kfree(vurb->transfer_buffer);
+ usb_free_urb(vurb);
+ }
+ aurb = usb->audio_urbs[i];
+ if (aurb) {
+ usb_kill_urb(aurb);
+ kfree(aurb->transfer_buffer);
+ usb_free_urb(aurb);
+ }
+ }
+ kfree(usb->intr_urb->transfer_buffer);
+ usb_free_urb(usb->intr_urb);
+
+ kfree(go->hpi_context);
+
+ go7007_remove(go);
+}
+
+static struct usb_driver go7007_usb_driver = {
+ .name = "go7007",
+ .probe = go7007_usb_probe,
+ .disconnect = go7007_usb_disconnect,
+ .id_table = go7007_usb_id_table,
+};
+
+static int __init go7007_usb_init(void)
+{
+ return usb_register(&go7007_usb_driver);
+}
+
+static void __exit go7007_usb_cleanup(void)
+{
+ usb_deregister(&go7007_usb_driver);
+}
+
+module_init(go7007_usb_init);
+module_exit(go7007_usb_cleanup);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#include "go7007.h"
+#include "go7007-priv.h"
+#include "wis-i2c.h"
+
+/* Temporary defines until accepted in v4l-dvb */
+#ifndef V4L2_MPEG_STREAM_TYPE_MPEG_ELEM
+#define V4L2_MPEG_STREAM_TYPE_MPEG_ELEM 6 /* MPEG elementary stream */
+#endif
+#ifndef V4L2_MPEG_VIDEO_ENCODING_MPEG_4
+#define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 3
+#endif
+
+#define call_all(dev, o, f, args...) \
+ v4l2_device_call_until_err(dev, 0, o, f, ##args)
+
+static void deactivate_buffer(struct go7007_buffer *gobuf)
+{
+ int i;
+
+ if (gobuf->state != BUF_STATE_IDLE) {
+ list_del(&gobuf->stream);
+ gobuf->state = BUF_STATE_IDLE;
+ }
+ if (gobuf->page_count > 0) {
+ for (i = 0; i < gobuf->page_count; ++i)
+ page_cache_release(gobuf->pages[i]);
+ gobuf->page_count = 0;
+ }
+}
+
+static void abort_queued(struct go7007 *go)
+{
+ struct go7007_buffer *gobuf, *next;
+
+ list_for_each_entry_safe(gobuf, next, &go->stream, stream) {
+ deactivate_buffer(gobuf);
+ }
+}
+
+static int go7007_streamoff(struct go7007 *go)
+{
+ int retval = -EINVAL;
+ unsigned long flags;
+
+ mutex_lock(&go->hw_lock);
+ if (go->streaming) {
+ go->streaming = 0;
+ go7007_stream_stop(go);
+ spin_lock_irqsave(&go->spinlock, flags);
+ abort_queued(go);
+ spin_unlock_irqrestore(&go->spinlock, flags);
+ go7007_reset_encoder(go);
+ retval = 0;
+ }
+ mutex_unlock(&go->hw_lock);
+ return 0;
+}
+
+static int go7007_open(struct file *file)
+{
+ struct go7007 *go = video_get_drvdata(video_devdata(file));
+ struct go7007_file *gofh;
+
+ if (go->status != STATUS_ONLINE)
+ return -EBUSY;
+ gofh = kmalloc(sizeof(struct go7007_file), GFP_KERNEL);
+ if (gofh == NULL)
+ return -ENOMEM;
+ ++go->ref_count;
+ gofh->go = go;
+ mutex_init(&gofh->lock);
+ gofh->buf_count = 0;
+ file->private_data = gofh;
+ return 0;
+}
+
+static int go7007_release(struct file *file)
+{
+ struct go7007_file *gofh = file->private_data;
+ struct go7007 *go = gofh->go;
+
+ if (gofh->buf_count > 0) {
+ go7007_streamoff(go);
+ go->in_use = 0;
+ kfree(gofh->bufs);
+ gofh->buf_count = 0;
+ }
+ kfree(gofh);
+ if (--go->ref_count == 0)
+ kfree(go);
+ file->private_data = NULL;
+ return 0;
+}
+
+static u32 get_frame_type_flag(struct go7007_buffer *gobuf, int format)
+{
+ u8 *f = page_address(gobuf->pages[0]);
+
+ switch (format) {
+ case GO7007_FORMAT_MJPEG:
+ return V4L2_BUF_FLAG_KEYFRAME;
+ case GO7007_FORMAT_MPEG4:
+ switch ((f[gobuf->frame_offset + 4] >> 6) & 0x3) {
+ case 0:
+ return V4L2_BUF_FLAG_KEYFRAME;
+ case 1:
+ return V4L2_BUF_FLAG_PFRAME;
+ case 2:
+ return V4L2_BUF_FLAG_BFRAME;
+ default:
+ return 0;
+ }
+ case GO7007_FORMAT_MPEG1:
+ case GO7007_FORMAT_MPEG2:
+ switch ((f[gobuf->frame_offset + 5] >> 3) & 0x7) {
+ case 1:
+ return V4L2_BUF_FLAG_KEYFRAME;
+ case 2:
+ return V4L2_BUF_FLAG_PFRAME;
+ case 3:
+ return V4L2_BUF_FLAG_BFRAME;
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try)
+{
+ int sensor_height = 0, sensor_width = 0;
+ int width, height, i;
+
+ if (fmt != NULL && fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG &&
+ fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG &&
+ fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG4)
+ return -EINVAL;
+
+ switch (go->standard) {
+ case GO7007_STD_NTSC:
+ sensor_width = 720;
+ sensor_height = 480;
+ break;
+ case GO7007_STD_PAL:
+ sensor_width = 720;
+ sensor_height = 576;
+ break;
+ case GO7007_STD_OTHER:
+ sensor_width = go->board_info->sensor_width;
+ sensor_height = go->board_info->sensor_height;
+ break;
+ }
+
+ if (fmt == NULL) {
+ width = sensor_width;
+ height = sensor_height;
+ } else if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
+ if (fmt->fmt.pix.width > sensor_width)
+ width = sensor_width;
+ else if (fmt->fmt.pix.width < 144)
+ width = 144;
+ else
+ width = fmt->fmt.pix.width & ~0x0f;
+
+ if (fmt->fmt.pix.height > sensor_height)
+ height = sensor_height;
+ else if (fmt->fmt.pix.height < 96)
+ height = 96;
+ else
+ height = fmt->fmt.pix.height & ~0x0f;
+ } else {
+ int requested_size = fmt->fmt.pix.width * fmt->fmt.pix.height;
+ int sensor_size = sensor_width * sensor_height;
+
+ if (64 * requested_size < 9 * sensor_size) {
+ width = sensor_width / 4;
+ height = sensor_height / 4;
+ } else if (64 * requested_size < 36 * sensor_size) {
+ width = sensor_width / 2;
+ height = sensor_height / 2;
+ } else {
+ width = sensor_width;
+ height = sensor_height;
+ }
+ width &= ~0xf;
+ height &= ~0xf;
+ }
+
+ if (fmt != NULL) {
+ u32 pixelformat = fmt->fmt.pix.pixelformat;
+
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt->fmt.pix.width = width;
+ fmt->fmt.pix.height = height;
+ fmt->fmt.pix.pixelformat = pixelformat;
+ fmt->fmt.pix.field = V4L2_FIELD_NONE;
+ fmt->fmt.pix.bytesperline = 0;
+ fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* ?? */
+ }
+
+ if (try)
+ return 0;
+
+ go->width = width;
+ go->height = height;
+ go->encoder_h_offset = go->board_info->sensor_h_offset;
+ go->encoder_v_offset = go->board_info->sensor_v_offset;
+ for (i = 0; i < 4; ++i)
+ go->modet[i].enable = 0;
+ for (i = 0; i < 1624; ++i)
+ go->modet_map[i] = 0;
+
+ if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+ if (fmt != NULL)
+ mbus_fmt.width = fmt->fmt.pix.width;
+ else
+ mbus_fmt.width = width;
+
+ if (height > sensor_height / 2) {
+ mbus_fmt.height = height / 2;
+ go->encoder_v_halve = 0;
+ } else {
+ mbus_fmt.height = height;
+ go->encoder_v_halve = 1;
+ }
+ call_all(&go->v4l2_dev, video, s_mbus_fmt, &mbus_fmt);
+ } else {
+ if (width <= sensor_width / 4) {
+ go->encoder_h_halve = 1;
+ go->encoder_v_halve = 1;
+ go->encoder_subsample = 1;
+ } else if (width <= sensor_width / 2) {
+ go->encoder_h_halve = 1;
+ go->encoder_v_halve = 1;
+ go->encoder_subsample = 0;
+ } else {
+ go->encoder_h_halve = 0;
+ go->encoder_v_halve = 0;
+ go->encoder_subsample = 0;
+ }
+ }
+
+ if (fmt == NULL)
+ return 0;
+
+ switch (fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_MPEG:
+ if (go->format == GO7007_FORMAT_MPEG1 ||
+ go->format == GO7007_FORMAT_MPEG2 ||
+ go->format == GO7007_FORMAT_MPEG4)
+ break;
+ go->format = GO7007_FORMAT_MPEG1;
+ go->pali = 0;
+ go->aspect_ratio = GO7007_RATIO_1_1;
+ go->gop_size = go->sensor_framerate / 1000;
+ go->ipb = 0;
+ go->closed_gop = 1;
+ go->repeat_seqhead = 1;
+ go->seq_header_enable = 1;
+ go->gop_header_enable = 1;
+ go->dvd_mode = 0;
+ break;
+ /* Backwards compatibility only! */
+ case V4L2_PIX_FMT_MPEG4:
+ if (go->format == GO7007_FORMAT_MPEG4)
+ break;
+ go->format = GO7007_FORMAT_MPEG4;
+ go->pali = 0xf5;
+ go->aspect_ratio = GO7007_RATIO_1_1;
+ go->gop_size = go->sensor_framerate / 1000;
+ go->ipb = 0;
+ go->closed_gop = 1;
+ go->repeat_seqhead = 1;
+ go->seq_header_enable = 1;
+ go->gop_header_enable = 1;
+ go->dvd_mode = 0;
+ break;
+ case V4L2_PIX_FMT_MJPEG:
+ go->format = GO7007_FORMAT_MJPEG;
+ go->pali = 0;
+ go->aspect_ratio = GO7007_RATIO_1_1;
+ go->gop_size = 0;
+ go->ipb = 0;
+ go->closed_gop = 0;
+ go->repeat_seqhead = 0;
+ go->seq_header_enable = 0;
+ go->gop_header_enable = 0;
+ go->dvd_mode = 0;
+ break;
+ }
+ return 0;
+}
+
+#if 0
+static int clip_to_modet_map(struct go7007 *go, int region,
+ struct v4l2_clip *clip_list)
+{
+ struct v4l2_clip clip, *clip_ptr;
+ int x, y, mbnum;
+
+ /* Check if coordinates are OK and if any macroblocks are already
+ * used by other regions (besides 0) */
+ clip_ptr = clip_list;
+ while (clip_ptr) {
+ if (copy_from_user(&clip, clip_ptr, sizeof(clip)))
+ return -EFAULT;
+ if (clip.c.left < 0 || (clip.c.left & 0xF) ||
+ clip.c.width <= 0 || (clip.c.width & 0xF))
+ return -EINVAL;
+ if (clip.c.left + clip.c.width > go->width)
+ return -EINVAL;
+ if (clip.c.top < 0 || (clip.c.top & 0xF) ||
+ clip.c.height <= 0 || (clip.c.height & 0xF))
+ return -EINVAL;
+ if (clip.c.top + clip.c.height > go->height)
+ return -EINVAL;
+ for (y = 0; y < clip.c.height; y += 16)
+ for (x = 0; x < clip.c.width; x += 16) {
+ mbnum = (go->width >> 4) *
+ ((clip.c.top + y) >> 4) +
+ ((clip.c.left + x) >> 4);
+ if (go->modet_map[mbnum] != 0 &&
+ go->modet_map[mbnum] != region)
+ return -EBUSY;
+ }
+ clip_ptr = clip.next;
+ }
+
+ /* Clear old region macroblocks */
+ for (mbnum = 0; mbnum < 1624; ++mbnum)
+ if (go->modet_map[mbnum] == region)
+ go->modet_map[mbnum] = 0;
+
+ /* Claim macroblocks in this list */
+ clip_ptr = clip_list;
+ while (clip_ptr) {
+ if (copy_from_user(&clip, clip_ptr, sizeof(clip)))
+ return -EFAULT;
+ for (y = 0; y < clip.c.height; y += 16)
+ for (x = 0; x < clip.c.width; x += 16) {
+ mbnum = (go->width >> 4) *
+ ((clip.c.top + y) >> 4) +
+ ((clip.c.left + x) >> 4);
+ go->modet_map[mbnum] = region;
+ }
+ clip_ptr = clip.next;
+ }
+ return 0;
+}
+#endif
+
+static int mpeg_query_ctrl(struct v4l2_queryctrl *ctrl)
+{
+ static const u32 mpeg_ctrls[] = {
+ V4L2_CID_MPEG_CLASS,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ 0
+ };
+ static const u32 *ctrl_classes[] = {
+ mpeg_ctrls,
+ NULL
+ };
+
+ ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_CLASS:
+ return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0);
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return v4l2_ctrl_query_fill(ctrl,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_DVD,
+ V4L2_MPEG_STREAM_TYPE_MPEG_ELEM, 1,
+ V4L2_MPEG_STREAM_TYPE_MPEG_ELEM);
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return v4l2_ctrl_query_fill(ctrl,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4, 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return v4l2_ctrl_query_fill(ctrl,
+ V4L2_MPEG_VIDEO_ASPECT_1x1,
+ V4L2_MPEG_VIDEO_ASPECT_16x9, 1,
+ V4L2_MPEG_VIDEO_ASPECT_1x1);
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ return v4l2_ctrl_query_fill(ctrl, 0, 34, 1, 15);
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ return v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ return v4l2_ctrl_query_fill(ctrl,
+ 64000,
+ 10000000, 1,
+ 1500000);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mpeg_s_ctrl(struct v4l2_control *ctrl, struct go7007 *go)
+{
+ /* pretty sure we can't change any of these while streaming */
+ if (go->streaming)
+ return -EBUSY;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ switch (ctrl->value) {
+ case V4L2_MPEG_STREAM_TYPE_MPEG2_DVD:
+ go->format = GO7007_FORMAT_MPEG2;
+ go->bitrate = 9800000;
+ go->gop_size = 15;
+ go->pali = 0x48;
+ go->closed_gop = 1;
+ go->repeat_seqhead = 0;
+ go->seq_header_enable = 1;
+ go->gop_header_enable = 1;
+ go->dvd_mode = 1;
+ break;
+ case V4L2_MPEG_STREAM_TYPE_MPEG_ELEM:
+ /* todo: */
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ switch (ctrl->value) {
+ case V4L2_MPEG_VIDEO_ENCODING_MPEG_1:
+ go->format = GO7007_FORMAT_MPEG1;
+ go->pali = 0;
+ break;
+ case V4L2_MPEG_VIDEO_ENCODING_MPEG_2:
+ go->format = GO7007_FORMAT_MPEG2;
+ /*if (mpeg->pali >> 24 == 2)
+ go->pali = mpeg->pali & 0xff;
+ else*/
+ go->pali = 0x48;
+ break;
+ case V4L2_MPEG_VIDEO_ENCODING_MPEG_4:
+ go->format = GO7007_FORMAT_MPEG4;
+ /*if (mpeg->pali >> 24 == 4)
+ go->pali = mpeg->pali & 0xff;
+ else*/
+ go->pali = 0xf5;
+ break;
+ default:
+ return -EINVAL;
+ }
+ go->gop_header_enable =
+ /*mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER
+ ? 0 :*/ 1;
+ /*if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER)
+ go->repeat_seqhead = 1;
+ else*/
+ go->repeat_seqhead = 0;
+ go->dvd_mode = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ if (go->format == GO7007_FORMAT_MJPEG)
+ return -EINVAL;
+ switch (ctrl->value) {
+ case V4L2_MPEG_VIDEO_ASPECT_1x1:
+ go->aspect_ratio = GO7007_RATIO_1_1;
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_4x3:
+ go->aspect_ratio = GO7007_RATIO_4_3;
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_16x9:
+ go->aspect_ratio = GO7007_RATIO_16_9;
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_221x100:
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ if (ctrl->value < 0 || ctrl->value > 34)
+ return -EINVAL;
+ go->gop_size = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ if (ctrl->value != 0 && ctrl->value != 1)
+ return -EINVAL;
+ go->closed_gop = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ /* Upper bound is kind of arbitrary here */
+ if (ctrl->value < 64000 || ctrl->value > 10000000)
+ return -EINVAL;
+ go->bitrate = ctrl->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mpeg_g_ctrl(struct v4l2_control *ctrl, struct go7007 *go)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ if (go->dvd_mode)
+ ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_DVD;
+ else
+ ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG_ELEM;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ switch (go->format) {
+ case GO7007_FORMAT_MPEG1:
+ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+ break;
+ case GO7007_FORMAT_MPEG2:
+ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ break;
+ case GO7007_FORMAT_MPEG4:
+ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ switch (go->aspect_ratio) {
+ case GO7007_RATIO_1_1:
+ ctrl->value = V4L2_MPEG_VIDEO_ASPECT_1x1;
+ break;
+ case GO7007_RATIO_4_3:
+ ctrl->value = V4L2_MPEG_VIDEO_ASPECT_4x3;
+ break;
+ case GO7007_RATIO_16_9:
+ ctrl->value = V4L2_MPEG_VIDEO_ASPECT_16x9;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctrl->value = go->gop_size;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ ctrl->value = go->closed_gop;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctrl->value = go->bitrate;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ strlcpy(cap->driver, "go7007", sizeof(cap->driver));
+ strlcpy(cap->card, go->name, sizeof(cap->card));
+#if 0
+ strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
+#endif
+
+ cap->version = KERNEL_VERSION(0, 9, 8);
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING; /* | V4L2_CAP_AUDIO; */
+
+ if (go->board_info->flags & GO7007_BOARD_HAS_TUNER)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ char *desc = NULL;
+
+ switch (fmt->index) {
+ case 0:
+ fmt->pixelformat = V4L2_PIX_FMT_MJPEG;
+ desc = "Motion-JPEG";
+ break;
+ case 1:
+ fmt->pixelformat = V4L2_PIX_FMT_MPEG;
+ desc = "MPEG1/MPEG2/MPEG4";
+ break;
+ default:
+ return -EINVAL;
+ }
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt->flags = V4L2_FMT_FLAG_COMPRESSED;
+
+ strncpy(fmt->description, desc, sizeof(fmt->description));
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt->fmt.pix.width = go->width;
+ fmt->fmt.pix.height = go->height;
+ fmt->fmt.pix.pixelformat = (go->format == GO7007_FORMAT_MJPEG) ?
+ V4L2_PIX_FMT_MJPEG : V4L2_PIX_FMT_MPEG;
+ fmt->fmt.pix.field = V4L2_FIELD_NONE;
+ fmt->fmt.pix.bytesperline = 0;
+ fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ return set_capture_size(go, fmt, 1);
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (go->streaming)
+ return -EBUSY;
+
+ return set_capture_size(go, fmt, 0);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req)
+{
+ struct go7007_file *gofh = priv;
+ struct go7007 *go = gofh->go;
+ int retval = -EBUSY;
+ unsigned int count, i;
+
+ if (go->streaming)
+ return retval;
+
+ if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ req->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ mutex_lock(&gofh->lock);
+ for (i = 0; i < gofh->buf_count; ++i)
+ if (gofh->bufs[i].mapped > 0)
+ goto unlock_and_return;
+
+ mutex_lock(&go->hw_lock);
+ if (go->in_use > 0 && gofh->buf_count == 0) {
+ mutex_unlock(&go->hw_lock);
+ goto unlock_and_return;
+ }
+
+ if (gofh->buf_count > 0)
+ kfree(gofh->bufs);
+
+ retval = -ENOMEM;
+ count = req->count;
+ if (count > 0) {
+ if (count < 2)
+ count = 2;
+ if (count > 32)
+ count = 32;
+
+ gofh->bufs = kcalloc(count, sizeof(struct go7007_buffer),
+ GFP_KERNEL);
+
+ if (!gofh->bufs) {
+ mutex_unlock(&go->hw_lock);
+ goto unlock_and_return;
+ }
+
+ for (i = 0; i < count; ++i) {
+ gofh->bufs[i].go = go;
+ gofh->bufs[i].index = i;
+ gofh->bufs[i].state = BUF_STATE_IDLE;
+ gofh->bufs[i].mapped = 0;
+ }
+
+ go->in_use = 1;
+ } else {
+ go->in_use = 0;
+ }
+
+ gofh->buf_count = count;
+ mutex_unlock(&go->hw_lock);
+ mutex_unlock(&gofh->lock);
+
+ memset(req, 0, sizeof(*req));
+
+ req->count = count;
+ req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req->memory = V4L2_MEMORY_MMAP;
+
+ return 0;
+
+unlock_and_return:
+ mutex_unlock(&gofh->lock);
+ return retval;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct go7007_file *gofh = priv;
+ int retval = -EINVAL;
+ unsigned int index;
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return retval;
+
+ index = buf->index;
+
+ mutex_lock(&gofh->lock);
+ if (index >= gofh->buf_count)
+ goto unlock_and_return;
+
+ memset(buf, 0, sizeof(*buf));
+ buf->index = index;
+ buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ switch (gofh->bufs[index].state) {
+ case BUF_STATE_QUEUED:
+ buf->flags = V4L2_BUF_FLAG_QUEUED;
+ break;
+ case BUF_STATE_DONE:
+ buf->flags = V4L2_BUF_FLAG_DONE;
+ break;
+ default:
+ buf->flags = 0;
+ }
+
+ if (gofh->bufs[index].mapped)
+ buf->flags |= V4L2_BUF_FLAG_MAPPED;
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->m.offset = index * GO7007_BUF_SIZE;
+ buf->length = GO7007_BUF_SIZE;
+ mutex_unlock(&gofh->lock);
+
+ return 0;
+
+unlock_and_return:
+ mutex_unlock(&gofh->lock);
+ return retval;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct go7007_file *gofh = priv;
+ struct go7007 *go = gofh->go;
+ struct go7007_buffer *gobuf;
+ unsigned long flags;
+ int retval = -EINVAL;
+ int ret;
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ buf->memory != V4L2_MEMORY_MMAP)
+ return retval;
+
+ mutex_lock(&gofh->lock);
+ if (buf->index < 0 || buf->index >= gofh->buf_count)
+ goto unlock_and_return;
+
+ gobuf = &gofh->bufs[buf->index];
+ if (!gobuf->mapped)
+ goto unlock_and_return;
+
+ retval = -EBUSY;
+ if (gobuf->state != BUF_STATE_IDLE)
+ goto unlock_and_return;
+
+ /* offset will be 0 until we really support USERPTR streaming */
+ gobuf->offset = gobuf->user_addr & ~PAGE_MASK;
+ gobuf->bytesused = 0;
+ gobuf->frame_offset = 0;
+ gobuf->modet_active = 0;
+ if (gobuf->offset > 0)
+ gobuf->page_count = GO7007_BUF_PAGES + 1;
+ else
+ gobuf->page_count = GO7007_BUF_PAGES;
+
+ retval = -ENOMEM;
+ down_read(¤t->mm->mmap_sem);
+ ret = get_user_pages(current, current->mm,
+ gobuf->user_addr & PAGE_MASK, gobuf->page_count,
+ 1, 1, gobuf->pages, NULL);
+ up_read(¤t->mm->mmap_sem);
+
+ if (ret != gobuf->page_count) {
+ int i;
+ for (i = 0; i < ret; ++i)
+ page_cache_release(gobuf->pages[i]);
+ gobuf->page_count = 0;
+ goto unlock_and_return;
+ }
+
+ gobuf->state = BUF_STATE_QUEUED;
+ spin_lock_irqsave(&go->spinlock, flags);
+ list_add_tail(&gobuf->stream, &go->stream);
+ spin_unlock_irqrestore(&go->spinlock, flags);
+ mutex_unlock(&gofh->lock);
+
+ return 0;
+
+unlock_and_return:
+ mutex_unlock(&gofh->lock);
+ return retval;
+}
+
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct go7007_file *gofh = priv;
+ struct go7007 *go = gofh->go;
+ struct go7007_buffer *gobuf;
+ int retval = -EINVAL;
+ unsigned long flags;
+ u32 frame_type_flag;
+ DEFINE_WAIT(wait);
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return retval;
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return retval;
+
+ mutex_lock(&gofh->lock);
+ if (list_empty(&go->stream))
+ goto unlock_and_return;
+ gobuf = list_entry(go->stream.next,
+ struct go7007_buffer, stream);
+
+ retval = -EAGAIN;
+ if (gobuf->state != BUF_STATE_DONE &&
+ !(file->f_flags & O_NONBLOCK)) {
+ for (;;) {
+ prepare_to_wait(&go->frame_waitq, &wait,
+ TASK_INTERRUPTIBLE);
+ if (gobuf->state == BUF_STATE_DONE)
+ break;
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+ finish_wait(&go->frame_waitq, &wait);
+ }
+ if (gobuf->state != BUF_STATE_DONE)
+ goto unlock_and_return;
+
+ spin_lock_irqsave(&go->spinlock, flags);
+ deactivate_buffer(gobuf);
+ spin_unlock_irqrestore(&go->spinlock, flags);
+ frame_type_flag = get_frame_type_flag(gobuf, go->format);
+ gobuf->state = BUF_STATE_IDLE;
+
+ memset(buf, 0, sizeof(*buf));
+ buf->index = gobuf->index;
+ buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf->bytesused = gobuf->bytesused;
+ buf->flags = V4L2_BUF_FLAG_MAPPED | frame_type_flag;
+ buf->field = V4L2_FIELD_NONE;
+ buf->timestamp = gobuf->timestamp;
+ buf->sequence = gobuf->seq;
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->m.offset = gobuf->index * GO7007_BUF_SIZE;
+ buf->length = GO7007_BUF_SIZE;
+ buf->reserved = gobuf->modet_active;
+
+ mutex_unlock(&gofh->lock);
+ return 0;
+
+unlock_and_return:
+ mutex_unlock(&gofh->lock);
+ return retval;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct go7007_file *gofh = priv;
+ struct go7007 *go = gofh->go;
+ int retval = 0;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&gofh->lock);
+ mutex_lock(&go->hw_lock);
+
+ if (!go->streaming) {
+ go->streaming = 1;
+ go->next_seq = 0;
+ go->active_buf = NULL;
+ if (go7007_start_encoder(go) < 0)
+ retval = -EIO;
+ else
+ retval = 0;
+ }
+ mutex_unlock(&go->hw_lock);
+ mutex_unlock(&gofh->lock);
+
+ return retval;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct go7007_file *gofh = priv;
+ struct go7007 *go = gofh->go;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ mutex_lock(&gofh->lock);
+ go7007_streamoff(go);
+ mutex_unlock(&gofh->lock);
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *query)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+ int id = query->id;
+
+ if (0 == call_all(&go->v4l2_dev, core, queryctrl, query))
+ return 0;
+
+ query->id = id;
+ return mpeg_query_ctrl(query);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (0 == call_all(&go->v4l2_dev, core, g_ctrl, ctrl))
+ return 0;
+
+ return mpeg_g_ctrl(ctrl, go);
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (0 == call_all(&go->v4l2_dev, core, s_ctrl, ctrl))
+ return 0;
+
+ return mpeg_s_ctrl(ctrl, go);
+}
+
+static int vidioc_g_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+ struct v4l2_fract timeperframe = {
+ .numerator = 1001 * go->fps_scale,
+ .denominator = go->sensor_framerate,
+ };
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME;
+ parm->parm.capture.timeperframe = timeperframe;
+
+ return 0;
+}
+
+static int vidioc_s_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+ unsigned int n, d;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (parm->parm.capture.capturemode != 0)
+ return -EINVAL;
+
+ n = go->sensor_framerate *
+ parm->parm.capture.timeperframe.numerator;
+ d = 1001 * parm->parm.capture.timeperframe.denominator;
+ if (n != 0 && d != 0 && n > d)
+ go->fps_scale = (n + d/2) / d;
+ else
+ go->fps_scale = 1;
+
+ return 0;
+}
+
+/* VIDIOC_ENUMSTD on go7007 were used for enumberating the supported fps and
+ its resolution, when the device is not connected to TV.
+ This were an API abuse, probably used by the lack of specific IOCTL's to
+ enumberate it, by the time the driver were written.
+
+ However, since kernel 2.6.19, two new ioctls (VIDIOC_ENUM_FRAMEINTERVALS
+ and VIDIOC_ENUM_FRAMESIZES) were added for this purpose.
+
+ The two functions bellow implements the newer ioctls
+*/
+static int vidioc_enum_framesizes(struct file *filp, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ /* Return -EINVAL, if it is a TV board */
+ if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) ||
+ (go->board_info->sensor_flags & GO7007_SENSOR_TV))
+ return -EINVAL;
+
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = go->board_info->sensor_width;
+ fsize->discrete.height = go->board_info->sensor_height;
+
+ return 0;
+}
+
+static int vidioc_enum_frameintervals(struct file *filp, void *priv,
+ struct v4l2_frmivalenum *fival)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ /* Return -EINVAL, if it is a TV board */
+ if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) ||
+ (go->board_info->sensor_flags & GO7007_SENSOR_TV))
+ return -EINVAL;
+
+ if (fival->index > 0)
+ return -EINVAL;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.numerator = 1001;
+ fival->discrete.denominator = go->board_info->sensor_framerate;
+
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ switch (go->standard) {
+ case GO7007_STD_NTSC:
+ *std = V4L2_STD_NTSC;
+ break;
+ case GO7007_STD_PAL:
+ *std = V4L2_STD_PAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (go->streaming)
+ return -EBUSY;
+
+ if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && *std != 0)
+ return -EINVAL;
+
+ if (*std == 0)
+ return -EINVAL;
+
+ if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
+ go->input == go->board_info->num_inputs - 1) {
+ if (!go->i2c_adapter_online)
+ return -EIO;
+ if (call_all(&go->v4l2_dev, core, s_std, *std) < 0)
+ return -EINVAL;
+ }
+
+ if (*std & V4L2_STD_NTSC) {
+ go->standard = GO7007_STD_NTSC;
+ go->sensor_framerate = 30000;
+ } else if (*std & V4L2_STD_PAL) {
+ go->standard = GO7007_STD_PAL;
+ go->sensor_framerate = 25025;
+ } else if (*std & V4L2_STD_SECAM) {
+ go->standard = GO7007_STD_PAL;
+ go->sensor_framerate = 25025;
+ } else
+ return -EINVAL;
+
+ call_all(&go->v4l2_dev, core, s_std, *std);
+ set_capture_size(go, NULL, 0);
+
+ return 0;
+}
+
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
+ go->input == go->board_info->num_inputs - 1) {
+ if (!go->i2c_adapter_online)
+ return -EIO;
+ return call_all(&go->v4l2_dev, video, querystd, std);
+ } else if (go->board_info->sensor_flags & GO7007_SENSOR_TV)
+ *std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
+ else
+ *std = 0;
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (inp->index >= go->board_info->num_inputs)
+ return -EINVAL;
+
+ strncpy(inp->name, go->board_info->inputs[inp->index].name,
+ sizeof(inp->name));
+
+ /* If this board has a tuner, it will be the last input */
+ if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
+ inp->index == go->board_info->num_inputs - 1)
+ inp->type = V4L2_INPUT_TYPE_TUNER;
+ else
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ inp->audioset = 0;
+ inp->tuner = 0;
+ if (go->board_info->sensor_flags & GO7007_SENSOR_TV)
+ inp->std = V4L2_STD_NTSC | V4L2_STD_PAL |
+ V4L2_STD_SECAM;
+ else
+ inp->std = 0;
+
+ return 0;
+}
+
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *input)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ *input = go->input;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int input)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (input >= go->board_info->num_inputs)
+ return -EINVAL;
+ if (go->streaming)
+ return -EBUSY;
+
+ go->input = input;
+
+ return call_all(&go->v4l2_dev, video, s_routing, input, 0, 0);
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
+ return -EINVAL;
+ if (t->index != 0)
+ return -EINVAL;
+ if (!go->i2c_adapter_online)
+ return -EIO;
+
+ return call_all(&go->v4l2_dev, tuner, g_tuner, t);
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
+ return -EINVAL;
+ if (t->index != 0)
+ return -EINVAL;
+ if (!go->i2c_adapter_online)
+ return -EIO;
+
+ switch (go->board_id) {
+ case GO7007_BOARDID_PX_TV402U_NA:
+ case GO7007_BOARDID_PX_TV402U_JP:
+ /* No selectable options currently */
+ if (t->audmode != V4L2_TUNER_MODE_STEREO)
+ return -EINVAL;
+ break;
+ }
+
+ return call_all(&go->v4l2_dev, tuner, s_tuner, t);
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
+ return -EINVAL;
+ if (!go->i2c_adapter_online)
+ return -EIO;
+
+ f->type = V4L2_TUNER_ANALOG_TV;
+
+ return call_all(&go->v4l2_dev, tuner, g_frequency, f);
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
+ return -EINVAL;
+ if (!go->i2c_adapter_online)
+ return -EIO;
+
+ return call_all(&go->v4l2_dev, tuner, s_frequency, f);
+}
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cropcap)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* These specify the raw input of the sensor */
+ switch (go->standard) {
+ case GO7007_STD_NTSC:
+ cropcap->bounds.top = 0;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height = 480;
+ cropcap->defrect.top = 0;
+ cropcap->defrect.left = 0;
+ cropcap->defrect.width = 720;
+ cropcap->defrect.height = 480;
+ break;
+ case GO7007_STD_PAL:
+ cropcap->bounds.top = 0;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height = 576;
+ cropcap->defrect.top = 0;
+ cropcap->defrect.left = 0;
+ cropcap->defrect.width = 720;
+ cropcap->defrect.height = 576;
+ break;
+ case GO7007_STD_OTHER:
+ cropcap->bounds.top = 0;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.width = go->board_info->sensor_width;
+ cropcap->bounds.height = go->board_info->sensor_height;
+ cropcap->defrect.top = 0;
+ cropcap->defrect.left = 0;
+ cropcap->defrect.width = go->board_info->sensor_width;
+ cropcap->defrect.height = go->board_info->sensor_height;
+ break;
+ }
+
+ return 0;
+}
+
+static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
+{
+ struct go7007 *go = ((struct go7007_file *) priv)->go;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* These specify the raw input of the sensor */
+ switch (go->standard) {
+ case GO7007_STD_NTSC:
+ crop->c.top = 0;
+ crop->c.left = 0;
+ crop->c.width = 720;
+ crop->c.height = 480;
+ break;
+ case GO7007_STD_PAL:
+ crop->c.top = 0;
+ crop->c.left = 0;
+ crop->c.width = 720;
+ crop->c.height = 576;
+ break;
+ case GO7007_STD_OTHER:
+ crop->c.top = 0;
+ crop->c.left = 0;
+ crop->c.width = go->board_info->sensor_width;
+ crop->c.height = go->board_info->sensor_height;
+ break;
+ }
+
+ return 0;
+}
+
+/* FIXME: vidioc_s_crop is not really implemented!!!
+ */
+static int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop)
+{
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_g_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *params)
+{
+ memset(params, 0, sizeof(*params));
+ params->quality = 50; /* ?? */
+ params->jpeg_markers = V4L2_JPEG_MARKER_DHT |
+ V4L2_JPEG_MARKER_DQT;
+
+ return 0;
+}
+
+static int vidioc_s_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *params)
+{
+ if (params->quality != 50 ||
+ params->jpeg_markers != (V4L2_JPEG_MARKER_DHT |
+ V4L2_JPEG_MARKER_DQT))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* FIXME:
+ Those ioctls are private, and not needed, since several standard
+ extended controls already provide streaming control.
+ So, those ioctls should be converted into vidioc_g_ext_ctrls()
+ and vidioc_s_ext_ctrls()
+ */
+
+#if 0
+ /* Temporary ioctls for controlling compression characteristics */
+ case GO7007IOC_S_BITRATE:
+ {
+ int *bitrate = arg;
+
+ if (go->streaming)
+ return -EINVAL;
+ /* Upper bound is kind of arbitrary here */
+ if (*bitrate < 64000 || *bitrate > 10000000)
+ return -EINVAL;
+ go->bitrate = *bitrate;
+ return 0;
+ }
+ case GO7007IOC_G_BITRATE:
+ {
+ int *bitrate = arg;
+
+ *bitrate = go->bitrate;
+ return 0;
+ }
+ case GO7007IOC_S_COMP_PARAMS:
+ {
+ struct go7007_comp_params *comp = arg;
+
+ if (go->format == GO7007_FORMAT_MJPEG)
+ return -EINVAL;
+ if (comp->gop_size > 0)
+ go->gop_size = comp->gop_size;
+ else
+ go->gop_size = go->sensor_framerate / 1000;
+ if (go->gop_size != 15)
+ go->dvd_mode = 0;
+ /*go->ipb = comp->max_b_frames > 0;*/ /* completely untested */
+ if (go->board_info->sensor_flags & GO7007_SENSOR_TV) {
+ switch (comp->aspect_ratio) {
+ case GO7007_ASPECT_RATIO_4_3_NTSC:
+ case GO7007_ASPECT_RATIO_4_3_PAL:
+ go->aspect_ratio = GO7007_RATIO_4_3;
+ break;
+ case GO7007_ASPECT_RATIO_16_9_NTSC:
+ case GO7007_ASPECT_RATIO_16_9_PAL:
+ go->aspect_ratio = GO7007_RATIO_16_9;
+ break;
+ default:
+ go->aspect_ratio = GO7007_RATIO_1_1;
+ break;
+ }
+ }
+ if (comp->flags & GO7007_COMP_OMIT_SEQ_HEADER) {
+ go->dvd_mode = 0;
+ go->seq_header_enable = 0;
+ } else {
+ go->seq_header_enable = 1;
+ }
+ /* fall-through */
+ }
+ case GO7007IOC_G_COMP_PARAMS:
+ {
+ struct go7007_comp_params *comp = arg;
+
+ if (go->format == GO7007_FORMAT_MJPEG)
+ return -EINVAL;
+ memset(comp, 0, sizeof(*comp));
+ comp->gop_size = go->gop_size;
+ comp->max_b_frames = go->ipb ? 2 : 0;
+ switch (go->aspect_ratio) {
+ case GO7007_RATIO_4_3:
+ if (go->standard == GO7007_STD_NTSC)
+ comp->aspect_ratio =
+ GO7007_ASPECT_RATIO_4_3_NTSC;
+ else
+ comp->aspect_ratio =
+ GO7007_ASPECT_RATIO_4_3_PAL;
+ break;
+ case GO7007_RATIO_16_9:
+ if (go->standard == GO7007_STD_NTSC)
+ comp->aspect_ratio =
+ GO7007_ASPECT_RATIO_16_9_NTSC;
+ else
+ comp->aspect_ratio =
+ GO7007_ASPECT_RATIO_16_9_PAL;
+ break;
+ default:
+ comp->aspect_ratio = GO7007_ASPECT_RATIO_1_1;
+ break;
+ }
+ if (go->closed_gop)
+ comp->flags |= GO7007_COMP_CLOSED_GOP;
+ if (!go->seq_header_enable)
+ comp->flags |= GO7007_COMP_OMIT_SEQ_HEADER;
+ return 0;
+ }
+ case GO7007IOC_S_MPEG_PARAMS:
+ {
+ struct go7007_mpeg_params *mpeg = arg;
+
+ if (go->format != GO7007_FORMAT_MPEG1 &&
+ go->format != GO7007_FORMAT_MPEG2 &&
+ go->format != GO7007_FORMAT_MPEG4)
+ return -EINVAL;
+
+ if (mpeg->flags & GO7007_MPEG_FORCE_DVD_MODE) {
+ go->format = GO7007_FORMAT_MPEG2;
+ go->bitrate = 9800000;
+ go->gop_size = 15;
+ go->pali = 0x48;
+ go->closed_gop = 1;
+ go->repeat_seqhead = 0;
+ go->seq_header_enable = 1;
+ go->gop_header_enable = 1;
+ go->dvd_mode = 1;
+ } else {
+ switch (mpeg->mpeg_video_standard) {
+ case GO7007_MPEG_VIDEO_MPEG1:
+ go->format = GO7007_FORMAT_MPEG1;
+ go->pali = 0;
+ break;
+ case GO7007_MPEG_VIDEO_MPEG2:
+ go->format = GO7007_FORMAT_MPEG2;
+ if (mpeg->pali >> 24 == 2)
+ go->pali = mpeg->pali & 0xff;
+ else
+ go->pali = 0x48;
+ break;
+ case GO7007_MPEG_VIDEO_MPEG4:
+ go->format = GO7007_FORMAT_MPEG4;
+ if (mpeg->pali >> 24 == 4)
+ go->pali = mpeg->pali & 0xff;
+ else
+ go->pali = 0xf5;
+ break;
+ default:
+ return -EINVAL;
+ }
+ go->gop_header_enable =
+ mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER
+ ? 0 : 1;
+ if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER)
+ go->repeat_seqhead = 1;
+ else
+ go->repeat_seqhead = 0;
+ go->dvd_mode = 0;
+ }
+ /* fall-through */
+ }
+ case GO7007IOC_G_MPEG_PARAMS:
+ {
+ struct go7007_mpeg_params *mpeg = arg;
+
+ memset(mpeg, 0, sizeof(*mpeg));
+ switch (go->format) {
+ case GO7007_FORMAT_MPEG1:
+ mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG1;
+ mpeg->pali = 0;
+ break;
+ case GO7007_FORMAT_MPEG2:
+ mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG2;
+ mpeg->pali = GO7007_MPEG_PROFILE(2, go->pali);
+ break;
+ case GO7007_FORMAT_MPEG4:
+ mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG4;
+ mpeg->pali = GO7007_MPEG_PROFILE(4, go->pali);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (!go->gop_header_enable)
+ mpeg->flags |= GO7007_MPEG_OMIT_GOP_HEADER;
+ if (go->repeat_seqhead)
+ mpeg->flags |= GO7007_MPEG_REPEAT_SEQHEADER;
+ if (go->dvd_mode)
+ mpeg->flags |= GO7007_MPEG_FORCE_DVD_MODE;
+ return 0;
+ }
+ case GO7007IOC_S_MD_PARAMS:
+ {
+ struct go7007_md_params *mdp = arg;
+
+ if (mdp->region > 3)
+ return -EINVAL;
+ if (mdp->trigger > 0) {
+ go->modet[mdp->region].pixel_threshold =
+ mdp->pixel_threshold >> 1;
+ go->modet[mdp->region].motion_threshold =
+ mdp->motion_threshold >> 1;
+ go->modet[mdp->region].mb_threshold =
+ mdp->trigger >> 1;
+ go->modet[mdp->region].enable = 1;
+ } else
+ go->modet[mdp->region].enable = 0;
+ /* fall-through */
+ }
+ case GO7007IOC_G_MD_PARAMS:
+ {
+ struct go7007_md_params *mdp = arg;
+ int region = mdp->region;
+
+ if (mdp->region > 3)
+ return -EINVAL;
+ memset(mdp, 0, sizeof(struct go7007_md_params));
+ mdp->region = region;
+ if (!go->modet[region].enable)
+ return 0;
+ mdp->pixel_threshold =
+ (go->modet[region].pixel_threshold << 1) + 1;
+ mdp->motion_threshold =
+ (go->modet[region].motion_threshold << 1) + 1;
+ mdp->trigger =
+ (go->modet[region].mb_threshold << 1) + 1;
+ return 0;
+ }
+ case GO7007IOC_S_MD_REGION:
+ {
+ struct go7007_md_region *region = arg;
+
+ if (region->region < 1 || region->region > 3)
+ return -EINVAL;
+ return clip_to_modet_map(go, region->region, region->clips);
+ }
+#endif
+
+static ssize_t go7007_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static void go7007_vm_open(struct vm_area_struct *vma)
+{
+ struct go7007_buffer *gobuf = vma->vm_private_data;
+
+ ++gobuf->mapped;
+}
+
+static void go7007_vm_close(struct vm_area_struct *vma)
+{
+ struct go7007_buffer *gobuf = vma->vm_private_data;
+ unsigned long flags;
+
+ if (--gobuf->mapped == 0) {
+ spin_lock_irqsave(&gobuf->go->spinlock, flags);
+ deactivate_buffer(gobuf);
+ spin_unlock_irqrestore(&gobuf->go->spinlock, flags);
+ }
+}
+
+/* Copied from videobuf-dma-sg.c */
+static int go7007_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct page *page;
+
+ page = alloc_page(GFP_USER | __GFP_DMA32);
+ if (!page)
+ return VM_FAULT_OOM;
+ clear_user_highpage(page, (unsigned long)vmf->virtual_address);
+ vmf->page = page;
+ return 0;
+}
+
+static struct vm_operations_struct go7007_vm_ops = {
+ .open = go7007_vm_open,
+ .close = go7007_vm_close,
+ .fault = go7007_vm_fault,
+};
+
+static int go7007_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct go7007_file *gofh = file->private_data;
+ unsigned int index;
+
+ if (gofh->go->status != STATUS_ONLINE)
+ return -EIO;
+ if (!(vma->vm_flags & VM_SHARED))
+ return -EINVAL; /* only support VM_SHARED mapping */
+ if (vma->vm_end - vma->vm_start != GO7007_BUF_SIZE)
+ return -EINVAL; /* must map exactly one full buffer */
+ mutex_lock(&gofh->lock);
+ index = vma->vm_pgoff / GO7007_BUF_PAGES;
+ if (index >= gofh->buf_count) {
+ mutex_unlock(&gofh->lock);
+ return -EINVAL; /* trying to map beyond requested buffers */
+ }
+ if (index * GO7007_BUF_PAGES != vma->vm_pgoff) {
+ mutex_unlock(&gofh->lock);
+ return -EINVAL; /* offset is not aligned on buffer boundary */
+ }
+ if (gofh->bufs[index].mapped > 0) {
+ mutex_unlock(&gofh->lock);
+ return -EBUSY;
+ }
+ gofh->bufs[index].mapped = 1;
+ gofh->bufs[index].user_addr = vma->vm_start;
+ vma->vm_ops = &go7007_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_flags &= ~VM_IO;
+ vma->vm_private_data = &gofh->bufs[index];
+ mutex_unlock(&gofh->lock);
+ return 0;
+}
+
+static unsigned int go7007_poll(struct file *file, poll_table *wait)
+{
+ struct go7007_file *gofh = file->private_data;
+ struct go7007_buffer *gobuf;
+
+ if (list_empty(&gofh->go->stream))
+ return POLLERR;
+ gobuf = list_entry(gofh->go->stream.next, struct go7007_buffer, stream);
+ poll_wait(file, &gofh->go->frame_waitq, wait);
+ if (gobuf->state == BUF_STATE_DONE)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static void go7007_vfl_release(struct video_device *vfd)
+{
+ struct go7007 *go = video_get_drvdata(vfd);
+
+ video_device_release(vfd);
+ if (--go->ref_count == 0)
+ kfree(go);
+}
+
+static struct v4l2_file_operations go7007_fops = {
+ .owner = THIS_MODULE,
+ .open = go7007_open,
+ .release = go7007_release,
+ .ioctl = video_ioctl2,
+ .read = go7007_read,
+ .mmap = go7007_mmap,
+ .poll = go7007_poll,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_querystd = vidioc_querystd,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_g_crop = vidioc_g_crop,
+ .vidioc_s_crop = vidioc_s_crop,
+ .vidioc_g_jpegcomp = vidioc_g_jpegcomp,
+ .vidioc_s_jpegcomp = vidioc_s_jpegcomp,
+};
+
+static struct video_device go7007_template = {
+ .name = "go7007",
+ .fops = &go7007_fops,
+ .release = go7007_vfl_release,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = V4L2_STD_ALL,
+ .current_norm = V4L2_STD_NTSC,
+};
+
+int go7007_v4l2_init(struct go7007 *go)
+{
+ int rv;
+
+ go->video_dev = video_device_alloc();
+ if (go->video_dev == NULL)
+ return -ENOMEM;
+ *go->video_dev = go7007_template;
+ go->video_dev->parent = go->dev;
+ rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1);
+ if (rv < 0) {
+ video_device_release(go->video_dev);
+ go->video_dev = NULL;
+ return rv;
+ }
+ rv = v4l2_device_register(go->dev, &go->v4l2_dev);
+ if (rv < 0) {
+ video_device_release(go->video_dev);
+ go->video_dev = NULL;
+ return rv;
+ }
+ video_set_drvdata(go->video_dev, go);
+ ++go->ref_count;
+ printk(KERN_INFO "%s: registered device %s [v4l2]\n",
+ go->video_dev->name, video_device_node_name(go->video_dev));
+
+ return 0;
+}
+
+void go7007_v4l2_remove(struct go7007 *go)
+{
+ unsigned long flags;
+
+ mutex_lock(&go->hw_lock);
+ if (go->streaming) {
+ go->streaming = 0;
+ go7007_stream_stop(go);
+ spin_lock_irqsave(&go->spinlock, flags);
+ abort_queued(go);
+ spin_unlock_irqrestore(&go->spinlock, flags);
+ }
+ mutex_unlock(&go->hw_lock);
+ if (go->video_dev)
+ video_unregister_device(go->video_dev);
+ v4l2_device_unregister(&go->v4l2_dev);
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and the associated README documentation file (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* DEPRECATED -- use V4L2_PIX_FMT_MPEG and then call GO7007IOC_S_MPEG_PARAMS
+ * to select between MPEG1, MPEG2, and MPEG4 */
+#define V4L2_PIX_FMT_MPEG4 v4l2_fourcc('M', 'P', 'G', '4') /* MPEG4 */
+
+/* These will be replaced with a better interface
+ * soon, so don't get too attached to them */
+#define GO7007IOC_S_BITRATE _IOW('V', BASE_VIDIOC_PRIVATE + 0, int)
+#define GO7007IOC_G_BITRATE _IOR('V', BASE_VIDIOC_PRIVATE + 1, int)
+
+enum go7007_aspect_ratio {
+ GO7007_ASPECT_RATIO_1_1 = 0,
+ GO7007_ASPECT_RATIO_4_3_NTSC = 1,
+ GO7007_ASPECT_RATIO_4_3_PAL = 2,
+ GO7007_ASPECT_RATIO_16_9_NTSC = 3,
+ GO7007_ASPECT_RATIO_16_9_PAL = 4,
+};
+
+/* Used to set generic compression parameters */
+struct go7007_comp_params {
+ __u32 gop_size;
+ __u32 max_b_frames;
+ enum go7007_aspect_ratio aspect_ratio;
+ __u32 flags;
+ __u32 reserved[8];
+};
+
+#define GO7007_COMP_CLOSED_GOP 0x00000001
+#define GO7007_COMP_OMIT_SEQ_HEADER 0x00000002
+
+enum go7007_mpeg_video_standard {
+ GO7007_MPEG_VIDEO_MPEG1 = 0,
+ GO7007_MPEG_VIDEO_MPEG2 = 1,
+ GO7007_MPEG_VIDEO_MPEG4 = 2,
+};
+
+/* Used to set parameters for V4L2_PIX_FMT_MPEG format */
+struct go7007_mpeg_params {
+ enum go7007_mpeg_video_standard mpeg_video_standard;
+ __u32 flags;
+ __u32 pali;
+ __u32 reserved[8];
+};
+
+#define GO7007_MPEG_FORCE_DVD_MODE 0x00000001
+#define GO7007_MPEG_OMIT_GOP_HEADER 0x00000002
+#define GO7007_MPEG_REPEAT_SEQHEADER 0x00000004
+
+#define GO7007_MPEG_PROFILE(format, pali) (((format)<<24)|(pali))
+
+#define GO7007_MPEG2_PROFILE_MAIN_MAIN GO7007_MPEG_PROFILE(2, 0x48)
+
+#define GO7007_MPEG4_PROFILE_S_L0 GO7007_MPEG_PROFILE(4, 0x08)
+#define GO7007_MPEG4_PROFILE_S_L1 GO7007_MPEG_PROFILE(4, 0x01)
+#define GO7007_MPEG4_PROFILE_S_L2 GO7007_MPEG_PROFILE(4, 0x02)
+#define GO7007_MPEG4_PROFILE_S_L3 GO7007_MPEG_PROFILE(4, 0x03)
+#define GO7007_MPEG4_PROFILE_ARTS_L1 GO7007_MPEG_PROFILE(4, 0x91)
+#define GO7007_MPEG4_PROFILE_ARTS_L2 GO7007_MPEG_PROFILE(4, 0x92)
+#define GO7007_MPEG4_PROFILE_ARTS_L3 GO7007_MPEG_PROFILE(4, 0x93)
+#define GO7007_MPEG4_PROFILE_ARTS_L4 GO7007_MPEG_PROFILE(4, 0x94)
+#define GO7007_MPEG4_PROFILE_AS_L0 GO7007_MPEG_PROFILE(4, 0xf0)
+#define GO7007_MPEG4_PROFILE_AS_L1 GO7007_MPEG_PROFILE(4, 0xf1)
+#define GO7007_MPEG4_PROFILE_AS_L2 GO7007_MPEG_PROFILE(4, 0xf2)
+#define GO7007_MPEG4_PROFILE_AS_L3 GO7007_MPEG_PROFILE(4, 0xf3)
+#define GO7007_MPEG4_PROFILE_AS_L4 GO7007_MPEG_PROFILE(4, 0xf4)
+#define GO7007_MPEG4_PROFILE_AS_L5 GO7007_MPEG_PROFILE(4, 0xf5)
+
+struct go7007_md_params {
+ __u16 region;
+ __u16 trigger;
+ __u16 pixel_threshold;
+ __u16 motion_threshold;
+ __u32 reserved[8];
+};
+
+struct go7007_md_region {
+ __u16 region;
+ __u16 flags;
+ struct v4l2_clip *clips;
+ __u32 reserved[8];
+};
+
+#define GO7007IOC_S_MPEG_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 2, \
+ struct go7007_mpeg_params)
+#define GO7007IOC_G_MPEG_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 3, \
+ struct go7007_mpeg_params)
+#define GO7007IOC_S_COMP_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 4, \
+ struct go7007_comp_params)
+#define GO7007IOC_G_COMP_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 5, \
+ struct go7007_comp_params)
+#define GO7007IOC_S_MD_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 6, \
+ struct go7007_md_params)
+#define GO7007IOC_G_MD_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 7, \
+ struct go7007_md_params)
+#define GO7007IOC_S_MD_REGION _IOW('V', BASE_VIDIOC_PRIVATE + 8, \
+ struct go7007_md_region)
--- /dev/null
+This is a driver for the WIS GO7007SB multi-format video encoder.
+
+Pete Eberlein <pete@sensoray.com>
+
+The driver was originally released under the GPL and is currently hosted at:
+http://nikosapi.org/wiki/index.php/WIS_Go7007_Linux_driver
+The go7007 firmware can be acquired from the package on the site above.
+
+I've modified the driver to support the following Video4Linux2 MPEG
+controls, with acceptable values:
+
+V4L2_CID_MPEG_STREAM_TYPE V4L2_MPEG_STREAM_TYPE_MPEG2_DVD
+ V4L2_MPEG_STREAM_TYPE_MPEG_ELEM
+V4L2_CID_MPEG_VIDEO_ENCODING V4L2_MPEG_VIDEO_ENCODING_MPEG_1
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4
+V4L2_CID_MPEG_VIDEO_ASPECT V4L2_MPEG_VIDEO_ASPECT_1x1
+ V4L2_MPEG_VIDEO_ASPECT_4x3
+ V4L2_MPEG_VIDEO_ASPECT_16x9
+V4L2_CID_MPEG_VIDEO_GOP_SIZE integer
+V4L2_CID_MPEG_VIDEO_BITRATE 64000 .. 10000000
+
+These should be used instead of the non-standard GO7007 ioctls described
+below.
+
+
+The README files from the orignal package appear below:
+
+---------------------------------------------------------------------------
+ WIS GO7007SB Public Linux Driver
+---------------------------------------------------------------------------
+
+
+*** Please see the file RELEASE-NOTES for important last-minute updates ***
+
+
+ 0. OVERVIEW AND LICENSING/DISCLAIMER
+
+
+This driver kit contains Linux drivers for the WIS GO7007SB multi-format
+video encoder. Only kernel version 2.6.x is supported. The video stream
+is available through the Video4Linux2 API and the audio stream is available
+through the ALSA API (or the OSS emulation layer of the ALSA system).
+
+The files in kernel/ and hotplug/ are licensed under the GNU General Public
+License Version 2 from the Free Software Foundation. A copy of the license
+is included in the file COPYING.
+
+The example applications in apps/ and C header files in include/ are
+licensed under a permissive license included in the source files which
+allows copying, modification and redistribution for any purpose without
+attribution.
+
+The firmware files included in the firmware/ directory may be freely
+redistributed only in conjunction with this document; but modification,
+tampering and reverse engineering are prohibited.
+
+MICRONAS USA, INC., MAKES NO WARRANTIES TO ANY PERSON OR ENTITY WITH
+RESPECT TO THE SOFTWARE OR ANY DERIVATIVES THEREOF OR ANY SERVICES OR
+LICENSES AND DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATION
+WARRANTIES OF MERCHANTABILITY, SUPPORT, AND FITNESS FOR A PARTICULAR
+PURPOSE AND NON-INFRINGEMENT.
+
+
+ 1. SYSTEM REQUIREMENTS
+
+
+This driver requires Linux kernel 2.6. Kernel 2.4 is not supported. Using
+kernel 2.6.10 or later is recommended, as earlier kernels are known to have
+unstable USB 2.0 support.
+
+A fully built kernel source tree must be available. Typically this will be
+linked from "/lib/modules/<KERNEL VERSION>/build" for convenience. If this
+link does not exist, an extra parameter will need to be passed to the
+`make` command.
+
+All vendor-built kernels should already be configured properly. However,
+for custom-built kernels, the following options need to be enabled in the
+kernel as built-in or modules:
+
+ CONFIG_HOTPLUG - Support for hot-pluggable devices
+ CONFIG_MODULES - Enable loadable module support
+ CONFIG_KMOD - Automatic kernel module loading
+ CONFIG_FW_LOADER - Hotplug firmware loading support
+ CONFIG_I2C - I2C support
+ CONFIG_VIDEO_DEV - Video For Linux
+ CONFIG_SOUND - Sound card support
+ CONFIG_SND - Advanced Linux Sound Architecture
+ CONFIG_USB - Support for Host-side USB
+ CONFIG_USB_DEVICEFS - USB device filesystem
+ CONFIG_USB_EHCI_HCD - EHCI HCD (USB 2.0) support
+
+Additionally, to use the example application, the following options need to
+be enabled in the ALSA section:
+
+ CONFIG_SND_MIXER_OSS - OSS Mixer API
+ CONFIG_SND_PCM_OSS - OSS PCM (digital audio) API
+
+The hotplug scripts, along with the fxload utility, must also be installed.
+These scripts can be obtained from <http://linux-hotplug.sourceforge.net/>.
+Hotplugging is used for loading firmware into the Cypruss EZ-USB chip using
+fxload and for loading firmware into the driver using the firmware agent.
+
+
+ 2. COMPILING AND INSTALLING THE DRIVER
+
+
+Most users should be able to compile the driver by simply running:
+
+ $ make
+
+in the top-level directory of the driver kit. First the kernel modules
+will be built, followed by the example applications.
+
+If the build system is unable to locate the kernel source tree for the
+currently-running kernel, or if the module should be built for a kernel
+other than the currently-running kernel, an additional parameter will need
+to be passed to make to specify the appropriate kernel source directory:
+
+ $ make KERNELSRC=/usr/src/linux-2.6.10-custom3
+
+Once the compile completes, the driver and firmware files should be
+installed by running:
+
+ $ make install
+
+The kernel modules will be placed in "/lib/modules/<KERNEL VERSION>/extra"
+and the firmware files will be placed in the appropriate hotplug firmware
+directory, usually /lib/firmware. In addition, USB maps and scripts will
+be placed in /etc/hotplug/usb to enable fxload to initialize the EZ-USB
+control chip when the device is connected.
+
+
+ 3. PAL/SECAM TUNER CONFIGURATION (TV402U-EU only)
+
+
+The PAL model of the Plextor ConvertX TV402U may require additional
+configuration to correctly select the appropriate TV frequency band and
+audio subchannel.
+
+Users with a device other than the Plextor ConvertX TV402U-EU should skip
+this section.
+
+The wide variety of PAL TV systems used in Europe requires that additional
+information about the local TV standards be passed to the driver in order
+to properly tune TV channels. The two necessary parameters are (a) the PAL
+TV band, and (b) the audio subchannel format in use.
+
+In many cases, the appropriate TV band selection is passed to the driver
+from applications. However, in some cases, the application only specifies
+that the driver should use PAL but not the specific information about the
+appropriate TV band. To work around this issue, the correct TV band may be
+specified in the "force_band" parameter to the wis-sony-tuner module:
+
+ TV band force_band
+ ------- ----------
+ PAL B/G B
+ PAL I I
+ PAL D/K D
+ SECAM L L
+
+If the "force_band" parameter is specified, the driver will ignore any TV
+band specified by applications and will always use the band provided in the
+module parameter.
+
+The other parameter that can be specified is the audio subchannel format.
+There are several stereo audio carrier systems in use, including NICAM and
+three varieties of A2. To receive audio broadcast on one of these stereo
+carriers, the "force_mpx_mode" parameter must be specified to the
+wis-sony-tuner module.
+
+ TV band Audio subcarrier force_mpx_mode
+ ------- ---------------- --------------
+ PAL B/G Mono (default) 1
+ PAL B/G A2 2
+ PAL B/G NICAM 3
+ PAL I Mono (default) 4
+ PAL I NICAM 5
+ PAL D/K Mono (default) 6
+ PAL D/K A2 (1) 7
+ PAL D/K A2 (2) 8
+ PAL D/K A2 (3) 9
+ PAL D/K NICAM 10
+ SECAM L Mono (default) 11
+ SECAM L NICAM 12
+
+If the "force_mpx_mode" parameter is not specified, the correct mono-only
+mode will be chosen based on the TV band. However, the tuner will not
+receive stereo audio or bilingual broadcasts correctly.
+
+To pass the "force_band" or "force_mpx_mode" parameters to the
+wis-sony-tuner module, the following line must be added to the modprobe
+configuration file, which varies from one Linux distribution to another.
+
+ options wis-sony-tuner force_band=B force_mpx_mode=2
+
+The above example would force the tuner to the PAL B/G TV band and receive
+stereo audio broadcasts on the A2 carrier.
+
+To verify that the configuration has been placed in the correct location,
+execute:
+
+ $ modprobe -c | grep wis-sony-tuner
+
+If the configuration line appears, then modprobe will pass the parameters
+correctly the next time the wis-sony-tuner module is loaded into the
+kernel.
+
+
+ 4. TESTING THE DRIVER
+
+
+Because few Linux applications are able to correctly capture from
+Video4Linux2 devices with only compressed formats supported, the new driver
+should be tested with the "gorecord" application in the apps/ directory.
+
+First connect a video source to the device, such as a DVD player or VCR.
+This will be captured to a file for testing the driver. If an input source
+is unavailable, a test file can still be captured, but the video will be
+black and the audio will be silent.
+
+This application will auto-detect the V4L2 and ALSA/OSS device names of the
+hardware and will record video and audio to an AVI file for a specified
+number of seconds. For example:
+
+ $ apps/gorecord -duration 60 capture.avi
+
+If this application does not successfully record an AVI file, the error
+messages produced by gorecord and recorded in the system log (usually in
+/var/log/messages) should provide information to help resolve the problem.
+
+Supplying no parameters to gorecord will cause it to probe the available
+devices and exit. Use the -help flag for usage information.
+
+
+ 5. USING THE DRIVER
+
+
+The V4L2 device implemented by the driver provides a standard compressed
+format API, within the following criteria:
+
+ * Applications that only support the original Video4Linux1 API will not
+ be able to communicate with this driver at all.
+
+ * No raw video modes are supported, so applications like xawtv that
+ expect only uncompressed video will not function.
+
+ * Supported compression formats are: Motion-JPEG, MPEG1, MPEG2 and MPEG4.
+
+ * MPEG video formats are delivered as Video Elementary Streams only.
+ Program Stream (PS), Transport Stream (TS) and Packetized Elementary
+ Stream (PES) formats are not supported.
+
+ * Video parameters such as format and input port may not be changed while
+ the encoder is active.
+
+ * The audio capture device only functions when the video encoder is
+ actively capturing video. Attempts to read from the audio device when
+ the encoder is inactive will result in an I/O error.
+
+ * The native format of the audio device is 48Khz 2-channel 16-bit
+ little-endian PCM, delivered through the ALSA system. No audio
+ compression is implemented in the hardware. ALSA may convert to other
+ uncompressed formats on the fly.
+
+The include/ directory contains a C header file describing non-standard
+features of the GO7007SB encoder, which are described below:
+
+
+ GO7007IOC_S_COMP_PARAMS, GO7007IOC_G_COMP_PARAMS
+
+ These ioctls are used to negotiate general compression parameters.
+
+ To query the current parameters, call the GO7007IOC_G_COMP_PARAMS ioctl
+ with a pointer to a struct go7007_comp_params. If the driver is not
+ set to MPEG format, the EINVAL error code will be returned.
+
+ To change the current parameters, initialize all fields of a struct
+ go7007_comp_params and call the GO7007_IOC_S_COMP_PARAMS ioctl with a
+ pointer to this structure. The driver will return the current
+ parameters with any necessary changes to conform to the limitations of
+ the hardware or current compression mode. Any or all fields can be set
+ to zero to request a reasonable default value. If the driver is not
+ set to MPEG format, the EINVAL error code will be returned. When I/O
+ is in progress, the EBUSY error code will be returned.
+
+ Fields in struct go7007_comp_params:
+
+ __u32 The maximum number of frames in each
+ gop_size Group Of Pictures; i.e. the maximum
+ number of frames minus one between
+ each key frame.
+
+ __u32 The maximum number of sequential
+ max_b_frames bidirectionally-predicted frames.
+ (B-frames are not yet supported.)
+
+ enum go7007_aspect_ratio The aspect ratio to be encoded in the
+ aspect_ratio meta-data of the compressed format.
+
+ Choices are:
+ GO7007_ASPECT_RATIO_1_1
+ GO7007_ASPECT_RATIO_4_3_NTSC
+ GO7007_ASPECT_RATIO_4_3_PAL
+ GO7007_ASPECT_RATIO_16_9_NTSC
+ GO7007_ASPECT_RATIO_16_9_PAL
+
+ __u32 Bit-wise OR of control flags (below)
+ flags
+
+ Flags in struct go7007_comp_params:
+
+ GO7007_COMP_CLOSED_GOP Only produce self-contained GOPs, used
+ to produce streams appropriate for
+ random seeking.
+
+ GO7007_COMP_OMIT_SEQ_HEADER Omit the stream sequence header.
+
+
+ GO7007IOC_S_MPEG_PARAMS, GO7007IOC_G_MPEG_PARAMS
+
+ These ioctls are used to negotiate MPEG-specific stream parameters when
+ the pixelformat has been set to V4L2_PIX_FMT_MPEG.
+
+ To query the current parameters, call the GO7007IOC_G_MPEG_PARAMS ioctl
+ with a pointer to a struct go7007_mpeg_params. If the driver is not
+ set to MPEG format, the EINVAL error code will be returned.
+
+ To change the current parameters, initialize all fields of a struct
+ go7007_mpeg_params and call the GO7007_IOC_S_MPEG_PARAMS ioctl with a
+ pointer to this structure. The driver will return the current
+ parameters with any necessary changes to conform to the limitations of
+ the hardware or selected MPEG mode. Any or all fields can be set to
+ zero to request a reasonable default value. If the driver is not set
+ to MPEG format, the EINVAL error code will be returned. When I/O is in
+ progress, the EBUSY error code will be returned.
+
+ Fields in struct go7007_mpeg_params:
+
+ enum go7007_mpeg_video_standard
+ mpeg_video_standard The MPEG video standard in which to
+ compress the video.
+
+ Choices are:
+ GO7007_MPEG_VIDEO_MPEG1
+ GO7007_MPEG_VIDEO_MPEG2
+ GO7007_MPEG_VIDEO_MPEG4
+
+ __u32 Bit-wise OR of control flags (below)
+ flags
+
+ __u32 The profile and level indication to be
+ pali stored in the sequence header. This
+ is only used as an indicator to the
+ decoder, and does not affect the MPEG
+ features used in the video stream.
+ Not valid for MPEG1.
+
+ Choices for MPEG2 are:
+ GO7007_MPEG2_PROFILE_MAIN_MAIN
+
+ Choices for MPEG4 are:
+ GO7007_MPEG4_PROFILE_S_L0
+ GO7007_MPEG4_PROFILE_S_L1
+ GO7007_MPEG4_PROFILE_S_L2
+ GO7007_MPEG4_PROFILE_S_L3
+ GO7007_MPEG4_PROFILE_ARTS_L1
+ GO7007_MPEG4_PROFILE_ARTS_L2
+ GO7007_MPEG4_PROFILE_ARTS_L3
+ GO7007_MPEG4_PROFILE_ARTS_L4
+ GO7007_MPEG4_PROFILE_AS_L0
+ GO7007_MPEG4_PROFILE_AS_L1
+ GO7007_MPEG4_PROFILE_AS_L2
+ GO7007_MPEG4_PROFILE_AS_L3
+ GO7007_MPEG4_PROFILE_AS_L4
+ GO7007_MPEG4_PROFILE_AS_L5
+
+ Flags in struct go7007_mpeg_params:
+
+ GO7007_MPEG_FORCE_DVD_MODE Force all compression parameters and
+ bitrate control settings to comply
+ with DVD MPEG2 stream requirements.
+ This overrides most compression and
+ bitrate settings!
+
+ GO7007_MPEG_OMIT_GOP_HEADER Omit the GOP header.
+
+ GO7007_MPEG_REPEAT_SEQHEADER Repeat the MPEG sequence header at
+ the start of each GOP.
+
+
+ GO7007IOC_S_BITRATE, GO7007IOC_G_BITRATE
+
+ These ioctls are used to set and query the target bitrate value for the
+ compressed video stream. The bitrate may be selected by storing the
+ target bits per second in an int and calling GO7007IOC_S_BITRATE with a
+ pointer to the int. The bitrate may be queried by calling
+ GO7007IOC_G_BITRATE with a pointer to an int where the current bitrate
+ will be stored.
+
+ Note that this is the primary means of controlling the video quality
+ for all compression modes, including V4L2_PIX_FMT_MJPEG. The
+ VIDIOC_S_JPEGCOMP ioctl is not supported.
+
+
+----------------------------------------------------------------------------
+ Installing the WIS PCI Voyager Driver
+---------------------------------------------------------------------------
+
+The WIS PCI Voyager driver requires several patches to the Linux 2.6.11.x
+kernel source tree before compiling the driver. These patches update the
+in-kernel SAA7134 driver to the newest development version and patch bugs
+in the TDA8290/TDA8275 tuner driver.
+
+The following patches must be downloaded from Gerd Knorr's website and
+applied in the order listed:
+
+ http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner
+ http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner2
+ http://dl.bytesex.org/patches/2.6.11-2/v4l2-api-mpeg
+ http://dl.bytesex.org/patches/2.6.11-2/saa7134-update
+
+The following patches are included with this SDK and can be applied in any
+order:
+
+ patches/2.6.11/saa7134-voyager.diff
+ patches/2.6.11/tda8275-newaddr.diff
+ patches/2.6.11/tda8290-ntsc.diff
+
+Check to make sure the CONFIG_VIDEO_SAA7134 option is enabled in the kernel
+configuration, and build and install the kernel.
+
+After rebooting into the new kernel, the GO7007 driver can be compiled and
+installed:
+
+ $ make SAA7134_BUILD=y
+ $ make install
+ $ modprobe saa7134-go7007
+
+There will be two V4L video devices associated with the PCI Voyager. The
+first device (most likely /dev/video0) provides access to the raw video
+capture mode of the SAA7133 device and is used to configure the source
+video parameters and tune the TV tuner. This device can be used with xawtv
+or other V4L(2) video software as a standard uncompressed device.
+
+The second device (most likely /dev/video1) provides access to the
+compression functions of the GO7007. It can be tested using the gorecord
+application in the apps/ directory of this SDK:
+
+ $ apps/gorecord -vdevice /dev/video1 -noaudio test.avi
+
+Currently the frame resolution is fixed at 720x480 (NTSC) or 720x576 (PAL),
+and the video standard must be specified to both the raw and the compressed
+video devices (xawtv and gorecord, for example).
+
+
+--------------------------------------------------------------------------
+RELEASE NOTES FOR WIS GO7007SB LINUX DRIVER
+---------------------------------------------------------------------------
+
+Last updated: 5 November 2005
+
+ - Release 0.9.7 includes new support for using udev to run fxload. The
+ install script should automatically detect whether the old hotplug
+ scripts or the new udev rules should be used. To force the use of
+ hotplug, run "make install USE_UDEV=n". To force the use of udev, run
+ "make install USE_UDEV=y".
+
+ - Motion detection is supported but undocumented. Try the `modet` app
+ for a demonstration of how to use the facility.
+
+ - Using USB2.0 devices such as the TV402U with USB1.1 HCDs or hubs can
+ cause buffer overruns and frame drops, even at low framerates, due to
+ inconsistency in the bitrate control mechanism.
+
+ - On devices with an SAA7115, including the Plextor ConvertX, video height
+ values of 96, 128, 160, 192, 256, 320, and 384 do not work in NTSC mode.
+ All valid heights up to 512 work correctly in PAL mode.
+
+ - The WIS Star Trek and PCI Voyager boards have no support yet for audio
+ or the TV tuner.
--- /dev/null
+/*
+ * Copyright (C) 2008 Sensoray Company Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
+#include "go7007-priv.h"
+
+MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver");
+MODULE_LICENSE("GPL v2");
+
+#define TLV320_ADDRESS 0x34
+#define VPX322_ADDR_ANALOGCONTROL1 0x02
+#define VPX322_ADDR_BRIGHTNESS0 0x0127
+#define VPX322_ADDR_BRIGHTNESS1 0x0131
+#define VPX322_ADDR_CONTRAST0 0x0128
+#define VPX322_ADDR_CONTRAST1 0x0132
+#define VPX322_ADDR_HUE 0x00dc
+#define VPX322_ADDR_SAT 0x0030
+
+struct go7007_usb_board {
+ unsigned int flags;
+ struct go7007_board_info main_info;
+};
+
+struct go7007_usb {
+ struct go7007_usb_board *board;
+ struct mutex i2c_lock;
+ struct usb_device *usbdev;
+ struct urb *video_urbs[8];
+ struct urb *audio_urbs[8];
+ struct urb *intr_urb;
+};
+
+static unsigned char aud_regs[] = {
+ 0x1e, 0x00,
+ 0x00, 0x17,
+ 0x02, 0x17,
+ 0x04, 0xf9,
+ 0x06, 0xf9,
+ 0x08, 0x02,
+ 0x0a, 0x00,
+ 0x0c, 0x00,
+ 0x0a, 0x00,
+ 0x0c, 0x00,
+ 0x0e, 0x02,
+ 0x10, 0x00,
+ 0x12, 0x01,
+ 0x00, 0x00,
+};
+
+
+static unsigned char vid_regs[] = {
+ 0xF2, 0x0f,
+ 0xAA, 0x00,
+ 0xF8, 0xff,
+ 0x00, 0x00,
+};
+
+static u16 vid_regs_fp[] = {
+ 0x028, 0x067,
+ 0x120, 0x016,
+ 0x121, 0xcF2,
+ 0x122, 0x0F2,
+ 0x123, 0x00c,
+ 0x124, 0x2d0,
+ 0x125, 0x2e0,
+ 0x126, 0x004,
+ 0x128, 0x1E0,
+ 0x12A, 0x016,
+ 0x12B, 0x0F2,
+ 0x12C, 0x0F2,
+ 0x12D, 0x00c,
+ 0x12E, 0x2d0,
+ 0x12F, 0x2e0,
+ 0x130, 0x004,
+ 0x132, 0x1E0,
+ 0x140, 0x060,
+ 0x153, 0x00C,
+ 0x154, 0x200,
+ 0x150, 0x801,
+ 0x000, 0x000
+};
+
+/* PAL specific values */
+static u16 vid_regs_fp_pal[] =
+{
+ 0x120, 0x017,
+ 0x121, 0xd22,
+ 0x122, 0x122,
+ 0x12A, 0x017,
+ 0x12B, 0x122,
+ 0x12C, 0x122,
+ 0x140, 0x060,
+ 0x000, 0x000,
+};
+
+struct s2250 {
+ struct v4l2_subdev sd;
+ v4l2_std_id std;
+ int input;
+ int brightness;
+ int contrast;
+ int saturation;
+ int hue;
+ int reg12b_val;
+ int audio_input;
+ struct i2c_client *audio;
+};
+
+static inline struct s2250 *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s2250, sd);
+}
+
+/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
+static int go7007_usb_vendor_request(struct go7007 *go, u16 request,
+ u16 value, u16 index, void *transfer_buffer, int length, int in)
+{
+ struct go7007_usb *usb = go->hpi_context;
+ int timeout = 5000;
+
+ if (in) {
+ return usb_control_msg(usb->usbdev,
+ usb_rcvctrlpipe(usb->usbdev, 0), request,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ value, index, transfer_buffer, length, timeout);
+ } else {
+ return usb_control_msg(usb->usbdev,
+ usb_sndctrlpipe(usb->usbdev, 0), request,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, transfer_buffer, length, timeout);
+ }
+}
+/* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+ struct go7007 *go = i2c_get_adapdata(client->adapter);
+ struct go7007_usb *usb;
+ int rc;
+ int dev_addr = client->addr << 1; /* firmware wants 8-bit address */
+ u8 *buf;
+
+ if (go == NULL)
+ return -ENODEV;
+
+ if (go->status == STATUS_SHUTDOWN)
+ return -EBUSY;
+
+ buf = kzalloc(16, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ usb = go->hpi_context;
+ if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
+ printk(KERN_INFO "i2c lock failed\n");
+ kfree(buf);
+ return -EINTR;
+ }
+ rc = go7007_usb_vendor_request(go, 0x55, dev_addr,
+ (reg<<8 | value),
+ buf,
+ 16, 1);
+
+ mutex_unlock(&usb->i2c_lock);
+ kfree(buf);
+ return rc;
+}
+
+static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val)
+{
+ struct go7007 *go = i2c_get_adapdata(client->adapter);
+ struct go7007_usb *usb;
+ u8 *buf;
+ struct s2250 *dec = i2c_get_clientdata(client);
+
+ if (go == NULL)
+ return -ENODEV;
+
+ if (go->status == STATUS_SHUTDOWN)
+ return -EBUSY;
+
+ buf = kzalloc(16, GFP_KERNEL);
+
+ if (buf == NULL)
+ return -ENOMEM;
+
+
+
+ memset(buf, 0xcd, 6);
+
+ usb = go->hpi_context;
+ if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
+ printk(KERN_INFO "i2c lock failed\n");
+ kfree(buf);
+ return -EINTR;
+ }
+ if (go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1) < 0) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ mutex_unlock(&usb->i2c_lock);
+ if (buf[0] == 0) {
+ unsigned int subaddr, val_read;
+
+ subaddr = (buf[4] << 8) + buf[5];
+ val_read = (buf[2] << 8) + buf[3];
+ kfree(buf);
+ if (val_read != val) {
+ printk(KERN_INFO "invalid fp write %x %x\n",
+ val_read, val);
+ return -EFAULT;
+ }
+ if (subaddr != addr) {
+ printk(KERN_INFO "invalid fp write addr %x %x\n",
+ subaddr, addr);
+ return -EFAULT;
+ }
+ } else {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ /* save last 12b value */
+ if (addr == 0x12b)
+ dec->reg12b_val = val;
+
+ return 0;
+}
+
+static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val)
+{
+ struct go7007 *go = i2c_get_adapdata(client->adapter);
+ struct go7007_usb *usb;
+ u8 *buf;
+
+ if (go == NULL)
+ return -ENODEV;
+
+ if (go->status == STATUS_SHUTDOWN)
+ return -EBUSY;
+
+ buf = kzalloc(16, GFP_KERNEL);
+
+ if (buf == NULL)
+ return -ENOMEM;
+
+
+
+ memset(buf, 0xcd, 6);
+ usb = go->hpi_context;
+ if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
+ printk(KERN_INFO "i2c lock failed\n");
+ kfree(buf);
+ return -EINTR;
+ }
+ if (go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1) < 0) {
+ kfree(buf);
+ return -EFAULT;
+ }
+ mutex_unlock(&usb->i2c_lock);
+
+ *val = (buf[0] << 8) | buf[1];
+ kfree(buf);
+
+ return 0;
+}
+
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+ int i;
+
+ for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) {
+ if (write_reg(client, regs[i], regs[i+1]) < 0) {
+ printk(KERN_INFO "s2250: failed\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int write_regs_fp(struct i2c_client *client, u16 *regs)
+{
+ int i;
+
+ for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) {
+ if (write_reg_fp(client, regs[i], regs[i+1]) < 0) {
+ printk(KERN_INFO "s2250: failed fp\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
+ u32 config)
+{
+ struct s2250 *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int vidsys;
+
+ vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00;
+ if (input == 0) {
+ /* composite */
+ write_reg_fp(client, 0x20, 0x020 | vidsys);
+ write_reg_fp(client, 0x21, 0x662);
+ write_reg_fp(client, 0x140, 0x060);
+ } else if (input == 1) {
+ /* S-Video */
+ write_reg_fp(client, 0x20, 0x040 | vidsys);
+ write_reg_fp(client, 0x21, 0x666);
+ write_reg_fp(client, 0x140, 0x060);
+ } else {
+ return -EINVAL;
+ }
+ state->input = input;
+ return 0;
+}
+
+static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct s2250 *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 vidsource;
+
+ vidsource = (state->input == 1) ? 0x040 : 0x020;
+ switch (norm) {
+ case V4L2_STD_NTSC:
+ write_regs_fp(client, vid_regs_fp);
+ write_reg_fp(client, 0x20, vidsource | 1);
+ break;
+ case V4L2_STD_PAL:
+ write_regs_fp(client, vid_regs_fp);
+ write_regs_fp(client, vid_regs_fp_pal);
+ write_reg_fp(client, 0x20, vidsource);
+ break;
+ default:
+ return -EINVAL;
+ }
+ state->std = norm;
+ return 0;
+}
+
+static int s2250_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query)
+{
+ switch (query->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(query, -50, 50, 1, 0);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s2250_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct s2250 *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int value1;
+ u16 oldvalue;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value > 100)
+ state->brightness = 100;
+ else if (ctrl->value < 0)
+ state->brightness = 0;
+ else
+ state->brightness = ctrl->value;
+ value1 = (state->brightness - 50) * 255 / 100;
+ read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue);
+ write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0,
+ value1 | (oldvalue & ~0xff));
+ read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue);
+ write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1,
+ value1 | (oldvalue & ~0xff));
+ write_reg_fp(client, 0x140, 0x60);
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value > 100)
+ state->contrast = 100;
+ else if (ctrl->value < 0)
+ state->contrast = 0;
+ else
+ state->contrast = ctrl->value;
+ value1 = state->contrast * 0x40 / 100;
+ if (value1 > 0x3f)
+ value1 = 0x3f; /* max */
+ read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue);
+ write_reg_fp(client, VPX322_ADDR_CONTRAST0,
+ value1 | (oldvalue & ~0x3f));
+ read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue);
+ write_reg_fp(client, VPX322_ADDR_CONTRAST1,
+ value1 | (oldvalue & ~0x3f));
+ write_reg_fp(client, 0x140, 0x60);
+ break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value > 100)
+ state->saturation = 100;
+ else if (ctrl->value < 0)
+ state->saturation = 0;
+ else
+ state->saturation = ctrl->value;
+ value1 = state->saturation * 4140 / 100;
+ if (value1 > 4094)
+ value1 = 4094;
+ write_reg_fp(client, VPX322_ADDR_SAT, value1);
+ break;
+ case V4L2_CID_HUE:
+ if (ctrl->value > 50)
+ state->hue = 50;
+ else if (ctrl->value < -50)
+ state->hue = -50;
+ else
+ state->hue = ctrl->value;
+ /* clamp the hue range */
+ value1 = state->hue * 280 / 50;
+ write_reg_fp(client, VPX322_ADDR_HUE, value1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s2250_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct s2250 *state = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = state->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = state->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = state->saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = state->hue;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s2250_s_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct s2250 *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (fmt->height < 640) {
+ write_reg_fp(client, 0x12b, state->reg12b_val | 0x400);
+ write_reg_fp(client, 0x140, 0x060);
+ } else {
+ write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400);
+ write_reg_fp(client, 0x140, 0x060);
+ }
+ return 0;
+}
+
+static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output,
+ u32 config)
+{
+ struct s2250 *state = to_state(sd);
+
+ switch (input) {
+ case 0:
+ write_reg(state->audio, 0x08, 0x02); /* Line In */
+ break;
+ case 1:
+ write_reg(state->audio, 0x08, 0x04); /* Mic */
+ break;
+ case 2:
+ write_reg(state->audio, 0x08, 0x05); /* Mic Boost */
+ break;
+ default:
+ return -EINVAL;
+ }
+ state->audio_input = input;
+ return 0;
+}
+
+
+static int s2250_log_status(struct v4l2_subdev *sd)
+{
+ struct s2250 *state = to_state(sd);
+
+ v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" :
+ state->std == V4L2_STD_PAL ? "PAL" :
+ state->std == V4L2_STD_SECAM ? "SECAM" :
+ "unknown");
+ v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" :
+ state->input == 1 ? "S-video" :
+ "error");
+ v4l2_info(sd, "Brightness: %d\n", state->brightness);
+ v4l2_info(sd, "Contrast: %d\n", state->contrast);
+ v4l2_info(sd, "Saturation: %d\n", state->saturation);
+ v4l2_info(sd, "Hue: %d\n", state->hue); return 0;
+ v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" :
+ state->audio_input == 1 ? "Mic" :
+ state->audio_input == 2 ? "Mic Boost" :
+ "error");
+ return 0;
+}
+
+/* --------------------------------------------------------------------------*/
+
+static const struct v4l2_subdev_core_ops s2250_core_ops = {
+ .log_status = s2250_log_status,
+ .g_ctrl = s2250_g_ctrl,
+ .s_ctrl = s2250_s_ctrl,
+ .queryctrl = s2250_queryctrl,
+ .s_std = s2250_s_std,
+};
+
+static const struct v4l2_subdev_audio_ops s2250_audio_ops = {
+ .s_routing = s2250_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops s2250_video_ops = {
+ .s_routing = s2250_s_video_routing,
+ .s_mbus_fmt = s2250_s_mbus_fmt,
+};
+
+static const struct v4l2_subdev_ops s2250_ops = {
+ .core = &s2250_core_ops,
+ .audio = &s2250_audio_ops,
+ .video = &s2250_video_ops,
+};
+
+/* --------------------------------------------------------------------------*/
+
+static int s2250_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_client *audio;
+ struct i2c_adapter *adapter = client->adapter;
+ struct s2250 *state;
+ struct v4l2_subdev *sd;
+ u8 *data;
+ struct go7007 *go = i2c_get_adapdata(adapter);
+ struct go7007_usb *usb = go->hpi_context;
+
+ audio = i2c_new_dummy(adapter, TLV320_ADDRESS >> 1);
+ if (audio == NULL)
+ return -ENOMEM;
+
+ state = kmalloc(sizeof(struct s2250), GFP_KERNEL);
+ if (state == NULL) {
+ i2c_unregister_device(audio);
+ return -ENOMEM;
+ }
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &s2250_ops);
+
+ v4l2_info(sd, "initializing %s at address 0x%x on %s\n",
+ "Sensoray 2250/2251", client->addr, client->adapter->name);
+
+ state->std = V4L2_STD_NTSC;
+ state->brightness = 50;
+ state->contrast = 50;
+ state->saturation = 50;
+ state->hue = 0;
+ state->audio = audio;
+
+ /* initialize the audio */
+ if (write_regs(audio, aud_regs) < 0) {
+ printk(KERN_ERR
+ "s2250: error initializing audio\n");
+ i2c_unregister_device(audio);
+ kfree(state);
+ return 0;
+ }
+
+ if (write_regs(client, vid_regs) < 0) {
+ printk(KERN_ERR
+ "s2250: error initializing decoder\n");
+ i2c_unregister_device(audio);
+ kfree(state);
+ return 0;
+ }
+ if (write_regs_fp(client, vid_regs_fp) < 0) {
+ printk(KERN_ERR
+ "s2250: error initializing decoder\n");
+ i2c_unregister_device(audio);
+ kfree(state);
+ return 0;
+ }
+ /* set default channel */
+ /* composite */
+ write_reg_fp(client, 0x20, 0x020 | 1);
+ write_reg_fp(client, 0x21, 0x662);
+ write_reg_fp(client, 0x140, 0x060);
+
+ /* set default audio input */
+ state->audio_input = 0;
+ write_reg(client, 0x08, 0x02); /* Line In */
+
+ if (mutex_lock_interruptible(&usb->i2c_lock) == 0) {
+ data = kzalloc(16, GFP_KERNEL);
+ if (data != NULL) {
+ int rc;
+ rc = go7007_usb_vendor_request(go, 0x41, 0, 0,
+ data, 16, 1);
+ if (rc > 0) {
+ u8 mask;
+ data[0] = 0;
+ mask = 1<<5;
+ data[0] &= ~mask;
+ data[1] |= mask;
+ go7007_usb_vendor_request(go, 0x40, 0,
+ (data[1]<<8)
+ + data[1],
+ data, 16, 0);
+ }
+ kfree(data);
+ }
+ mutex_unlock(&usb->i2c_lock);
+ }
+
+ v4l2_info(sd, "initialized successfully\n");
+ return 0;
+}
+
+static int s2250_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id s2250_id[] = {
+ { "s2250", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, s2250_id);
+
+static struct i2c_driver s2250_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "s2250",
+ },
+ .probe = s2250_probe,
+ .remove = s2250_remove,
+ .id_table = s2250_id,
+};
+
+static __init int init_s2250(void)
+{
+ return i2c_add_driver(&s2250_driver);
+}
+
+static __exit void exit_s2250(void)
+{
+ i2c_del_driver(&s2250_driver);
+}
+
+module_init(init_s2250);
+module_exit(exit_s2250);
--- /dev/null
+/*
+ * Copyright (C) 2008 Sensoray Company Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <dvb-usb.h>
+
+#define S2250_LOADER_FIRMWARE "s2250_loader.fw"
+#define S2250_FIRMWARE "s2250.fw"
+
+typedef struct device_extension_s {
+ struct kref kref;
+ int minor;
+ struct usb_device *usbdev;
+} device_extension_t, *pdevice_extension_t;
+
+#define USB_s2250loader_MAJOR 240
+#define USB_s2250loader_MINOR_BASE 0
+#define MAX_DEVICES 256
+
+static pdevice_extension_t s2250_dev_table[MAX_DEVICES];
+static DEFINE_MUTEX(s2250_dev_table_mutex);
+
+#define to_s2250loader_dev_common(d) container_of(d, device_extension_t, kref)
+static void s2250loader_delete(struct kref *kref)
+{
+ pdevice_extension_t s = to_s2250loader_dev_common(kref);
+ s2250_dev_table[s->minor] = NULL;
+ kfree(s);
+}
+
+static int s2250loader_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev;
+ int minor, ret;
+ pdevice_extension_t s = NULL;
+ const struct firmware *fw;
+
+ usbdev = usb_get_dev(interface_to_usbdev(interface));
+ if (!usbdev) {
+ printk(KERN_ERR "Enter s2250loader_probe failed\n");
+ return -1;
+ }
+ printk(KERN_INFO "Enter s2250loader_probe 2.6 kernel\n");
+ printk(KERN_INFO "vendor id 0x%x, device id 0x%x devnum:%d\n",
+ usbdev->descriptor.idVendor, usbdev->descriptor.idProduct,
+ usbdev->devnum);
+
+ if (usbdev->descriptor.bNumConfigurations != 1) {
+ printk(KERN_ERR "can't handle multiple config\n");
+ return -1;
+ }
+ mutex_lock(&s2250_dev_table_mutex);
+
+ for (minor = 0; minor < MAX_DEVICES; minor++) {
+ if (s2250_dev_table[minor] == NULL)
+ break;
+ }
+
+ if (minor < 0 || minor >= MAX_DEVICES) {
+ printk(KERN_ERR "Invalid minor: %d\n", minor);
+ goto failed;
+ }
+
+ /* Allocate dev data structure */
+ s = kmalloc(sizeof(device_extension_t), GFP_KERNEL);
+ if (s == NULL) {
+ printk(KERN_ERR "Out of memory\n");
+ goto failed;
+ }
+ s2250_dev_table[minor] = s;
+
+ printk(KERN_INFO "s2250loader_probe: Device %d on Bus %d Minor %d\n",
+ usbdev->devnum, usbdev->bus->busnum, minor);
+
+ memset(s, 0, sizeof(device_extension_t));
+ s->usbdev = usbdev;
+ printk(KERN_INFO "loading 2250 loader\n");
+
+ kref_init(&(s->kref));
+
+ mutex_unlock(&s2250_dev_table_mutex);
+
+ if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) {
+ printk(KERN_ERR
+ "s2250: unable to load firmware from file \"%s\"\n",
+ S2250_LOADER_FIRMWARE);
+ goto failed2;
+ }
+ ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2);
+ release_firmware(fw);
+ if (0 != ret) {
+ printk(KERN_ERR "loader download failed\n");
+ goto failed2;
+ }
+
+ if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) {
+ printk(KERN_ERR
+ "s2250: unable to load firmware from file \"%s\"\n",
+ S2250_FIRMWARE);
+ goto failed2;
+ }
+ ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2);
+ release_firmware(fw);
+ if (0 != ret) {
+ printk(KERN_ERR "firmware_s2250 download failed\n");
+ goto failed2;
+ }
+
+ usb_set_intfdata(interface, s);
+ return 0;
+
+failed:
+ mutex_unlock(&s2250_dev_table_mutex);
+failed2:
+ if (s)
+ kref_put(&(s->kref), s2250loader_delete);
+
+ printk(KERN_ERR "probe failed\n");
+ return -1;
+}
+
+static void s2250loader_disconnect(struct usb_interface *interface)
+{
+ pdevice_extension_t s;
+ printk(KERN_INFO "s2250: disconnect\n");
+ s = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+ kref_put(&(s->kref), s2250loader_delete);
+}
+
+static const struct usb_device_id s2250loader_ids[] = {
+ {USB_DEVICE(0x1943, 0xa250)},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, s2250loader_ids);
+
+static struct usb_driver s2250loader_driver = {
+ .name = "s2250-loader",
+ .probe = s2250loader_probe,
+ .disconnect = s2250loader_disconnect,
+ .id_table = s2250loader_ids,
+};
+
+static int __init s2250loader_init(void)
+{
+ int r;
+ unsigned i = 0;
+
+ for (i = 0; i < MAX_DEVICES; i++)
+ s2250_dev_table[i] = NULL;
+
+ r = usb_register(&s2250loader_driver);
+ if (r) {
+ printk(KERN_ERR "usb_register failed. Error number %d\n", r);
+ return -1;
+ }
+
+ printk(KERN_INFO "s2250loader_init: driver registered\n");
+ return 0;
+}
+module_init(s2250loader_init);
+
+static void __exit s2250loader_cleanup(void)
+{
+ printk(KERN_INFO "s2250loader_cleanup\n");
+ usb_deregister(&s2250loader_driver);
+}
+module_exit(s2250loader_cleanup);
+
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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 _S2250_LOADER_H_
+#define _S2250_LOADER_H_
+
+extern int s2250loader_init(void);
+extern void s2250loader_cleanup(void);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <asm/byteorder.h>
+#include <media/v4l2-common.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include "go7007-priv.h"
+
+#define GO7007_HPI_DEBUG
+
+enum hpi_address {
+ HPI_ADDR_VIDEO_BUFFER = 0xe4,
+ HPI_ADDR_INIT_BUFFER = 0xea,
+ HPI_ADDR_INTR_RET_VALUE = 0xee,
+ HPI_ADDR_INTR_RET_DATA = 0xec,
+ HPI_ADDR_INTR_STATUS = 0xf4,
+ HPI_ADDR_INTR_WR_PARAM = 0xf6,
+ HPI_ADDR_INTR_WR_INDEX = 0xf8,
+};
+
+enum gpio_command {
+ GPIO_COMMAND_RESET = 0x00, /* 000b */
+ GPIO_COMMAND_REQ1 = 0x04, /* 001b */
+ GPIO_COMMAND_WRITE = 0x20, /* 010b */
+ GPIO_COMMAND_REQ2 = 0x24, /* 011b */
+ GPIO_COMMAND_READ = 0x80, /* 100b */
+ GPIO_COMMAND_VIDEO = 0x84, /* 101b */
+ GPIO_COMMAND_IDLE = 0xA0, /* 110b */
+ GPIO_COMMAND_ADDR = 0xA4, /* 111b */
+};
+
+struct saa7134_go7007 {
+ struct saa7134_dev *dev;
+ u8 *top;
+ u8 *bottom;
+ dma_addr_t top_dma;
+ dma_addr_t bottom_dma;
+};
+
+static struct go7007_board_info board_voyager = {
+ .firmware = "go7007tv.bin",
+ .flags = 0,
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_VALID_ENABLE |
+ GO7007_SENSOR_TV |
+ GO7007_SENSOR_VBI,
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_WORD_16,
+ .audio_rate = 48000,
+ .audio_bclk_div = 8,
+ .audio_main_div = 2,
+ .hpi_buffer_cap = 7,
+ .num_inputs = 1,
+ .inputs = {
+ {
+ .name = "SAA7134",
+ },
+ },
+};
+MODULE_FIRMWARE("go7007tv.bin");
+
+/********************* Driver for GPIO HPI interface *********************/
+
+static int gpio_write(struct saa7134_dev *dev, u8 addr, u16 data)
+{
+ saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+
+ /* Write HPI address */
+ saa_writeb(SAA7134_GPIO_GPSTATUS0, addr);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+ /* Write low byte */
+ saa_writeb(SAA7134_GPIO_GPSTATUS0, data & 0xff);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+ /* Write high byte */
+ saa_writeb(SAA7134_GPIO_GPSTATUS0, data >> 8);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+ return 0;
+}
+
+static int gpio_read(struct saa7134_dev *dev, u8 addr, u16 *data)
+{
+ saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+
+ /* Write HPI address */
+ saa_writeb(SAA7134_GPIO_GPSTATUS0, addr);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+ saa_writeb(SAA7134_GPIO_GPMODE0, 0x00);
+
+ /* Read low byte */
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ);
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ *data = saa_readb(SAA7134_GPIO_GPSTATUS0);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+ /* Read high byte */
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ);
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ *data |= saa_readb(SAA7134_GPIO_GPSTATUS0) << 8;
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+ return 0;
+}
+
+static int saa7134_go7007_interface_reset(struct go7007 *go)
+{
+ struct saa7134_go7007 *saa = go->hpi_context;
+ struct saa7134_dev *dev = saa->dev;
+ u32 status;
+ u16 intr_val, intr_data;
+ int count = 20;
+
+ saa_clearb(SAA7134_TS_PARALLEL, 0x80); /* Disable TS interface */
+ saa_writeb(SAA7134_GPIO_GPMODE2, 0xa4);
+ saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_RESET);
+ msleep(1);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2);
+ msleep(10);
+
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ status = saa_readb(SAA7134_GPIO_GPSTATUS2);
+ /*printk(KERN_DEBUG "status is %s\n", status & 0x40 ? "OK" : "not OK"); */
+
+ /* enter command mode...(?) */
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2);
+
+ do {
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ status = saa_readb(SAA7134_GPIO_GPSTATUS2);
+ /*printk(KERN_INFO "gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */
+ } while (--count > 0);
+
+ /* Wait for an interrupt to indicate successful hardware reset */
+ if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
+ (intr_val & ~0x1) != 0x55aa) {
+ printk(KERN_ERR
+ "saa7134-go7007: unable to reset the GO7007\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data)
+{
+ struct saa7134_go7007 *saa = go->hpi_context;
+ struct saa7134_dev *dev = saa->dev;
+ int i;
+ u16 status_reg;
+
+#ifdef GO7007_HPI_DEBUG
+ printk(KERN_DEBUG
+ "saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data);
+#endif
+
+ for (i = 0; i < 100; ++i) {
+ gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg);
+ if (!(status_reg & 0x0010))
+ break;
+ msleep(10);
+ }
+ if (i == 100) {
+ printk(KERN_ERR
+ "saa7134-go7007: device is hung, status reg = 0x%04x\n",
+ status_reg);
+ return -1;
+ }
+ gpio_write(dev, HPI_ADDR_INTR_WR_PARAM, data);
+ gpio_write(dev, HPI_ADDR_INTR_WR_INDEX, addr);
+
+ return 0;
+}
+
+static int saa7134_go7007_read_interrupt(struct go7007 *go)
+{
+ struct saa7134_go7007 *saa = go->hpi_context;
+ struct saa7134_dev *dev = saa->dev;
+
+ /* XXX we need to wait if there is no interrupt available */
+ go->interrupt_available = 1;
+ gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value);
+ gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data);
+#ifdef GO7007_HPI_DEBUG
+ printk(KERN_DEBUG "saa7134-go7007: ReadInterrupt: %04x %04x\n",
+ go->interrupt_value, go->interrupt_data);
+#endif
+ return 0;
+}
+
+static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev,
+ unsigned long status)
+{
+ struct go7007 *go = video_get_drvdata(dev->empress_dev);
+ struct saa7134_go7007 *saa = go->hpi_context;
+
+ if (!go->streaming)
+ return;
+ if (0 != (status & 0x000f0000))
+ printk(KERN_DEBUG "saa7134-go7007: irq: lost %ld\n",
+ (status >> 16) & 0x0f);
+ if (status & 0x100000) {
+ dma_sync_single_for_cpu(&dev->pci->dev,
+ saa->bottom_dma, PAGE_SIZE, DMA_FROM_DEVICE);
+ go7007_parse_video_stream(go, saa->bottom, PAGE_SIZE);
+ saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma));
+ } else {
+ dma_sync_single_for_cpu(&dev->pci->dev,
+ saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE);
+ go7007_parse_video_stream(go, saa->top, PAGE_SIZE);
+ saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma));
+ }
+}
+
+static int saa7134_go7007_stream_start(struct go7007 *go)
+{
+ struct saa7134_go7007 *saa = go->hpi_context;
+ struct saa7134_dev *dev = saa->dev;
+
+ saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top),
+ 0, PAGE_SIZE, DMA_FROM_DEVICE);
+ if (!saa->top_dma)
+ return -ENOMEM;
+ saa->bottom_dma = dma_map_page(&dev->pci->dev,
+ virt_to_page(saa->bottom),
+ 0, PAGE_SIZE, DMA_FROM_DEVICE);
+ if (!saa->bottom_dma) {
+ dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ return -ENOMEM;
+ }
+
+ saa_writel(SAA7134_VIDEO_PORT_CTRL0 >> 2, 0xA300B000);
+ saa_writel(SAA7134_VIDEO_PORT_CTRL4 >> 2, 0x40000200);
+
+ /* Set HPI interface for video */
+ saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+ saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_VIDEO_BUFFER);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+ saa_writeb(SAA7134_GPIO_GPMODE0, 0x00);
+
+ /* Enable TS interface */
+ saa_writeb(SAA7134_TS_PARALLEL, 0xe6);
+
+ /* Reset TS interface */
+ saa_setb(SAA7134_TS_SERIAL1, 0x01);
+ saa_clearb(SAA7134_TS_SERIAL1, 0x01);
+
+ /* Set up transfer block size */
+ saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1);
+ saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1);
+ saa_writeb(SAA7134_TS_DMA1, 0);
+ saa_writeb(SAA7134_TS_DMA2, 0);
+
+ /* Enable video streaming mode */
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO);
+
+ saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma));
+ saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma));
+ saa_writel(SAA7134_RS_PITCH(5), 128);
+ saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_MAX);
+
+ /* Enable TS FIFO */
+ saa_setl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5);
+
+ /* Enable DMA IRQ */
+ saa_setl(SAA7134_IRQ1,
+ SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0);
+
+ return 0;
+}
+
+static int saa7134_go7007_stream_stop(struct go7007 *go)
+{
+ struct saa7134_go7007 *saa = go->hpi_context;
+ struct saa7134_dev *dev;
+
+ if (!saa)
+ return -EINVAL;
+ dev = saa->dev;
+ if (!dev)
+ return -EINVAL;
+
+ /* Shut down TS FIFO */
+ saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5);
+
+ /* Disable DMA IRQ */
+ saa_clearl(SAA7134_IRQ1,
+ SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0);
+
+ /* Disable TS interface */
+ saa_clearb(SAA7134_TS_PARALLEL, 0x80);
+
+ dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ dma_unmap_page(&dev->pci->dev, saa->bottom_dma, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+
+ return 0;
+}
+
+static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len)
+{
+ struct saa7134_go7007 *saa = go->hpi_context;
+ struct saa7134_dev *dev = saa->dev;
+ u16 status_reg;
+ int i;
+
+#ifdef GO7007_HPI_DEBUG
+ printk(KERN_DEBUG "saa7134-go7007: DownloadBuffer "
+ "sending %d bytes\n", len);
+#endif
+
+ while (len > 0) {
+ i = len > 64 ? 64 : len;
+ saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+ saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_INIT_BUFFER);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+ while (i-- > 0) {
+ saa_writeb(SAA7134_GPIO_GPSTATUS0, *data);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+ ++data;
+ --len;
+ }
+ for (i = 0; i < 100; ++i) {
+ gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg);
+ if (!(status_reg & 0x0002))
+ break;
+ }
+ if (i == 100) {
+ printk(KERN_ERR "saa7134-go7007: device is hung, "
+ "status reg = 0x%04x\n", status_reg);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int saa7134_go7007_send_command(struct go7007 *go, unsigned int cmd,
+ void *arg)
+{
+ struct saa7134_go7007 *saa = go->hpi_context;
+ struct saa7134_dev *dev = saa->dev;
+
+ switch (cmd) {
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *std = arg;
+ return saa7134_s_std_internal(dev, NULL, std);
+ }
+ case VIDIOC_G_STD:
+ {
+ v4l2_std_id *std = arg;
+ *std = dev->tvnorm->id;
+ return 0;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *ctrl = arg;
+ if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER)
+ return saa7134_queryctrl(NULL, NULL, ctrl);
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+ if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER)
+ return saa7134_g_ctrl_internal(dev, NULL, ctrl);
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+ if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER)
+ return saa7134_s_ctrl_internal(dev, NULL, ctrl);
+ }
+ }
+ return -EINVAL;
+
+}
+
+static struct go7007_hpi_ops saa7134_go7007_hpi_ops = {
+ .interface_reset = saa7134_go7007_interface_reset,
+ .write_interrupt = saa7134_go7007_write_interrupt,
+ .read_interrupt = saa7134_go7007_read_interrupt,
+ .stream_start = saa7134_go7007_stream_start,
+ .stream_stop = saa7134_go7007_stream_stop,
+ .send_firmware = saa7134_go7007_send_firmware,
+ .send_command = saa7134_go7007_send_command,
+};
+
+/********************* Add/remove functions *********************/
+
+static int saa7134_go7007_init(struct saa7134_dev *dev)
+{
+ struct go7007 *go;
+ struct saa7134_go7007 *saa;
+
+ printk(KERN_DEBUG "saa7134-go7007: probing new SAA713X board\n");
+
+ saa = kzalloc(sizeof(struct saa7134_go7007), GFP_KERNEL);
+ if (saa == NULL)
+ return -ENOMEM;
+
+ /* Allocate a couple pages for receiving the compressed stream */
+ saa->top = (u8 *)get_zeroed_page(GFP_KERNEL);
+ if (!saa->top)
+ goto allocfail;
+ saa->bottom = (u8 *)get_zeroed_page(GFP_KERNEL);
+ if (!saa->bottom)
+ goto allocfail;
+
+ go = go7007_alloc(&board_voyager, &dev->pci->dev);
+ if (go == NULL)
+ goto allocfail;
+ go->board_id = GO7007_BOARDID_PCI_VOYAGER;
+ strncpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name));
+ go->hpi_ops = &saa7134_go7007_hpi_ops;
+ go->hpi_context = saa;
+ saa->dev = dev;
+
+ /* Boot the GO7007 */
+ if (go7007_boot_encoder(go, go->board_info->flags &
+ GO7007_BOARD_USE_ONBOARD_I2C) < 0)
+ goto initfail;
+
+ /* Do any final GO7007 initialization, then register the
+ * V4L2 and ALSA interfaces */
+ if (go7007_register_encoder(go) < 0)
+ goto initfail;
+ dev->empress_dev = go->video_dev;
+ video_set_drvdata(dev->empress_dev, go);
+
+ go->status = STATUS_ONLINE;
+ return 0;
+
+initfail:
+ go->status = STATUS_SHUTDOWN;
+ return 0;
+
+allocfail:
+ if (saa->top)
+ free_page((unsigned long)saa->top);
+ if (saa->bottom)
+ free_page((unsigned long)saa->bottom);
+ kfree(saa);
+ return -ENOMEM;
+}
+
+static int saa7134_go7007_fini(struct saa7134_dev *dev)
+{
+ struct go7007 *go;
+ struct saa7134_go7007 *saa;
+
+ if (NULL == dev->empress_dev)
+ return 0;
+
+ go = video_get_drvdata(dev->empress_dev);
+ saa = go->hpi_context;
+ go->status = STATUS_SHUTDOWN;
+ free_page((unsigned long)saa->top);
+ free_page((unsigned long)saa->bottom);
+ kfree(saa);
+ go7007_remove(go);
+ dev->empress_dev = NULL;
+
+ return 0;
+}
+
+static struct saa7134_mpeg_ops saa7134_go7007_ops = {
+ .type = SAA7134_MPEG_GO7007,
+ .init = saa7134_go7007_init,
+ .fini = saa7134_go7007_fini,
+ .irq_ts_done = saa7134_go7007_irq_ts_done,
+};
+
+static int __init saa7134_go7007_mod_init(void)
+{
+ return saa7134_ts_register(&saa7134_go7007_ops);
+}
+
+static void __exit saa7134_go7007_mod_cleanup(void)
+{
+ saa7134_ts_unregister(&saa7134_go7007_ops);
+}
+
+module_init(saa7134_go7007_mod_init);
+module_exit(saa7134_go7007_mod_cleanup);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <asm/system.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+#include "go7007-priv.h"
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+module_param_array(id, charp, NULL, 0444);
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the go7007 audio driver");
+MODULE_PARM_DESC(id, "ID string for the go7007 audio driver");
+MODULE_PARM_DESC(enable, "Enable for the go7007 audio driver");
+
+struct go7007_snd {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *substream;
+ spinlock_t lock;
+ int w_idx;
+ int hw_ptr;
+ int avail;
+ int capturing;
+};
+
+static struct snd_pcm_hardware go7007_snd_capture_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 4096,
+ .period_bytes_max = (128*1024),
+ .periods_min = 1,
+ .periods_max = 32,
+};
+
+static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length)
+{
+ struct go7007_snd *gosnd = go->snd_context;
+ struct snd_pcm_runtime *runtime = gosnd->substream->runtime;
+ int frames = bytes_to_frames(runtime, length);
+
+ spin_lock(&gosnd->lock);
+ gosnd->hw_ptr += frames;
+ if (gosnd->hw_ptr >= runtime->buffer_size)
+ gosnd->hw_ptr -= runtime->buffer_size;
+ gosnd->avail += frames;
+ spin_unlock(&gosnd->lock);
+ if (gosnd->w_idx + length > runtime->dma_bytes) {
+ int cpy = runtime->dma_bytes - gosnd->w_idx;
+
+ memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy);
+ length -= cpy;
+ buf += cpy;
+ gosnd->w_idx = 0;
+ }
+ memcpy(runtime->dma_area + gosnd->w_idx, buf, length);
+ gosnd->w_idx += length;
+ spin_lock(&gosnd->lock);
+ if (gosnd->avail < runtime->period_size) {
+ spin_unlock(&gosnd->lock);
+ return;
+ }
+ gosnd->avail -= runtime->period_size;
+ spin_unlock(&gosnd->lock);
+ if (gosnd->capturing)
+ snd_pcm_period_elapsed(gosnd->substream);
+}
+
+static int go7007_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct go7007 *go = snd_pcm_substream_chip(substream);
+ unsigned int bytes;
+
+ bytes = params_buffer_bytes(hw_params);
+ if (substream->runtime->dma_bytes > 0)
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_bytes = 0;
+ substream->runtime->dma_area = vmalloc(bytes);
+ if (substream->runtime->dma_area == NULL)
+ return -ENOMEM;
+ substream->runtime->dma_bytes = bytes;
+ go->audio_deliver = parse_audio_stream_data;
+ return 0;
+}
+
+static int go7007_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct go7007 *go = snd_pcm_substream_chip(substream);
+
+ go->audio_deliver = NULL;
+ if (substream->runtime->dma_bytes > 0)
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_bytes = 0;
+ return 0;
+}
+
+static int go7007_snd_capture_open(struct snd_pcm_substream *substream)
+{
+ struct go7007 *go = snd_pcm_substream_chip(substream);
+ struct go7007_snd *gosnd = go->snd_context;
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&gosnd->lock, flags);
+ if (gosnd->substream == NULL) {
+ gosnd->substream = substream;
+ substream->runtime->hw = go7007_snd_capture_hw;
+ r = 0;
+ } else
+ r = -EBUSY;
+ spin_unlock_irqrestore(&gosnd->lock, flags);
+ return r;
+}
+
+static int go7007_snd_capture_close(struct snd_pcm_substream *substream)
+{
+ struct go7007 *go = snd_pcm_substream_chip(substream);
+ struct go7007_snd *gosnd = go->snd_context;
+
+ gosnd->substream = NULL;
+ return 0;
+}
+
+static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct go7007 *go = snd_pcm_substream_chip(substream);
+ struct go7007_snd *gosnd = go->snd_context;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* Just set a flag to indicate we should signal ALSA when
+ * sound comes in */
+ gosnd->capturing = 1;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
+ gosnd->capturing = 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct go7007 *go = snd_pcm_substream_chip(substream);
+ struct go7007_snd *gosnd = go->snd_context;
+
+ return gosnd->hw_ptr;
+}
+
+static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ return vmalloc_to_page(substream->runtime->dma_area + offset);
+}
+
+static struct snd_pcm_ops go7007_snd_capture_ops = {
+ .open = go7007_snd_capture_open,
+ .close = go7007_snd_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = go7007_snd_hw_params,
+ .hw_free = go7007_snd_hw_free,
+ .prepare = go7007_snd_pcm_prepare,
+ .trigger = go7007_snd_pcm_trigger,
+ .pointer = go7007_snd_pcm_pointer,
+ .page = go7007_snd_pcm_page,
+};
+
+static int go7007_snd_free(struct snd_device *device)
+{
+ struct go7007 *go = device->device_data;
+
+ kfree(go->snd_context);
+ go->snd_context = NULL;
+ if (--go->ref_count == 0)
+ kfree(go);
+ return 0;
+}
+
+static struct snd_device_ops go7007_snd_device_ops = {
+ .dev_free = go7007_snd_free,
+};
+
+int go7007_snd_init(struct go7007 *go)
+{
+ static int dev;
+ struct go7007_snd *gosnd;
+ int ret = 0;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+ gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL);
+ if (gosnd == NULL)
+ return -ENOMEM;
+ spin_lock_init(&gosnd->lock);
+ gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
+ gosnd->capturing = 0;
+ ret = snd_card_create(index[dev], id[dev], THIS_MODULE, 0,
+ &gosnd->card);
+ if (ret < 0) {
+ kfree(gosnd);
+ return ret;
+ }
+ ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go,
+ &go7007_snd_device_ops);
+ if (ret < 0) {
+ kfree(gosnd);
+ return ret;
+ }
+ snd_card_set_dev(gosnd->card, go->dev);
+ ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm);
+ if (ret < 0) {
+ snd_card_free(gosnd->card);
+ kfree(gosnd);
+ return ret;
+ }
+ strncpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver));
+ strncpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver));
+ strncpy(gosnd->card->longname, gosnd->card->shortname,
+ sizeof(gosnd->card->longname));
+
+ gosnd->pcm->private_data = go;
+ snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &go7007_snd_capture_ops);
+
+ ret = snd_card_register(gosnd->card);
+ if (ret < 0) {
+ snd_card_free(gosnd->card);
+ kfree(gosnd);
+ return ret;
+ }
+
+ gosnd->substream = NULL;
+ go->snd_context = gosnd;
+ ++dev;
+ ++go->ref_count;
+
+ return 0;
+}
+EXPORT_SYMBOL(go7007_snd_init);
+
+int go7007_snd_remove(struct go7007 *go)
+{
+ struct go7007_snd *gosnd = go->snd_context;
+
+ snd_card_disconnect(gosnd->card);
+ snd_card_free_when_closed(gosnd->card);
+ return 0;
+}
+EXPORT_SYMBOL(go7007_snd_remove);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/* Temporary I2C IDs -- these need to be replaced with real registered IDs */
+#define I2C_DRIVERID_WIS_SAA7115 0xf0f0
+#define I2C_DRIVERID_WIS_UDA1342 0xf0f1
+#define I2C_DRIVERID_WIS_SONY_TUNER 0xf0f2
+#define I2C_DRIVERID_WIS_TW9903 0xf0f3
+#define I2C_DRIVERID_WIS_SAA7113 0xf0f4
+#define I2C_DRIVERID_WIS_OV7640 0xf0f5
+#define I2C_DRIVERID_WIS_TW2804 0xf0f6
+#define I2C_DRIVERID_S2250 0xf0f7
+
+/* Flag to indicate that the client needs to be accessed with SCCB semantics */
+/* We re-use the I2C_M_TEN value so the flag passes through the masks in the
+ * core I2C code. Major kludge, but the I2C layer ain't exactly flexible. */
+#define I2C_CLIENT_SCCB 0x10
+
+/* Definitions for new video decoder commands */
+
+struct video_decoder_resolution {
+ unsigned int width;
+ unsigned int height;
+};
+
+#define DECODER_SET_RESOLUTION _IOW('d', 200, struct video_decoder_resolution)
+#define DECODER_SET_CHANNEL _IOW('d', 201, int)
+
+/* Sony tuner types */
+
+#define TUNER_SONY_BTF_PG472Z 200
+#define TUNER_SONY_BTF_PK467Z 201
+#define TUNER_SONY_BTF_PB463Z 202
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+
+#include "wis-i2c.h"
+
+struct wis_ov7640 {
+ int brightness;
+ int contrast;
+ int saturation;
+ int hue;
+};
+
+static u8 initial_registers[] =
+{
+ 0x12, 0x80,
+ 0x12, 0x54,
+ 0x14, 0x24,
+ 0x15, 0x01,
+ 0x28, 0x20,
+ 0x75, 0x82,
+ 0xFF, 0xFF, /* Terminator (reg 0xFF is unused) */
+};
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+ int i;
+
+ for (i = 0; regs[i] != 0xFF; i += 2)
+ if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
+ return -1;
+ return 0;
+}
+
+static int wis_ov7640_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ client->flags = I2C_CLIENT_SCCB;
+
+ printk(KERN_DEBUG
+ "wis-ov7640: initializing OV7640 at address %d on %s\n",
+ client->addr, adapter->name);
+
+ if (write_regs(client, initial_registers) < 0) {
+ printk(KERN_ERR "wis-ov7640: error initializing OV7640\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int wis_ov7640_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id wis_ov7640_id[] = {
+ { "wis_ov7640", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wis_ov7640_id);
+
+static struct i2c_driver wis_ov7640_driver = {
+ .driver = {
+ .name = "WIS OV7640 I2C driver",
+ },
+ .probe = wis_ov7640_probe,
+ .remove = wis_ov7640_remove,
+ .id_table = wis_ov7640_id,
+};
+
+static int __init wis_ov7640_init(void)
+{
+ return i2c_add_driver(&wis_ov7640_driver);
+}
+
+static void __exit wis_ov7640_cleanup(void)
+{
+ i2c_del_driver(&wis_ov7640_driver);
+}
+
+module_init(wis_ov7640_init);
+module_exit(wis_ov7640_cleanup);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+
+#include "wis-i2c.h"
+
+struct wis_saa7113 {
+ int norm;
+ int brightness;
+ int contrast;
+ int saturation;
+ int hue;
+};
+
+static u8 initial_registers[] =
+{
+ 0x01, 0x08,
+ 0x02, 0xc0,
+ 0x03, 0x33,
+ 0x04, 0x00,
+ 0x05, 0x00,
+ 0x06, 0xe9,
+ 0x07, 0x0d,
+ 0x08, 0xd8,
+ 0x09, 0x40,
+ 0x0a, 0x80,
+ 0x0b, 0x47,
+ 0x0c, 0x40,
+ 0x0d, 0x00,
+ 0x0e, 0x01,
+ 0x0f, 0x2a,
+ 0x10, 0x40,
+ 0x11, 0x0c,
+ 0x12, 0xfe,
+ 0x13, 0x00,
+ 0x14, 0x00,
+ 0x15, 0x04,
+ 0x16, 0x00,
+ 0x17, 0x00,
+ 0x18, 0x00,
+ 0x19, 0x00,
+ 0x1a, 0x00,
+ 0x1b, 0x00,
+ 0x1c, 0x00,
+ 0x1d, 0x00,
+ 0x1e, 0x00,
+ 0x1f, 0xc8,
+ 0x40, 0x00,
+ 0x41, 0xff,
+ 0x42, 0xff,
+ 0x43, 0xff,
+ 0x44, 0xff,
+ 0x45, 0xff,
+ 0x46, 0xff,
+ 0x47, 0xff,
+ 0x48, 0xff,
+ 0x49, 0xff,
+ 0x4a, 0xff,
+ 0x4b, 0xff,
+ 0x4c, 0xff,
+ 0x4d, 0xff,
+ 0x4e, 0xff,
+ 0x4f, 0xff,
+ 0x50, 0xff,
+ 0x51, 0xff,
+ 0x52, 0xff,
+ 0x53, 0xff,
+ 0x54, 0xff,
+ 0x55, 0xff,
+ 0x56, 0xff,
+ 0x57, 0xff,
+ 0x58, 0x00,
+ 0x59, 0x54,
+ 0x5a, 0x07,
+ 0x5b, 0x83,
+ 0x5c, 0x00,
+ 0x5d, 0x00,
+ 0x5e, 0x00,
+ 0x5f, 0x00,
+ 0x60, 0x00,
+ 0x61, 0x00,
+ 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
+};
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+ int i;
+
+ for (i = 0; regs[i] != 0x00; i += 2)
+ if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
+ return -1;
+ return 0;
+}
+
+static int wis_saa7113_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
+{
+ struct wis_saa7113 *dec = i2c_get_clientdata(client);
+
+ switch (cmd) {
+ case VIDIOC_S_INPUT:
+ {
+ int *input = arg;
+
+ i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input);
+ i2c_smbus_write_byte_data(client, 0x09,
+ *input < 6 ? 0x40 : 0x80);
+ break;
+ }
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *input = arg;
+ dec->norm = *input;
+ if (dec->norm & V4L2_STD_NTSC) {
+ write_reg(client, 0x0e, 0x01);
+ write_reg(client, 0x10, 0x40);
+ } else if (dec->norm & V4L2_STD_PAL) {
+ write_reg(client, 0x0e, 0x01);
+ write_reg(client, 0x10, 0x48);
+ } else if (dec->norm * V4L2_STD_SECAM) {
+ write_reg(client, 0x0e, 0x50);
+ write_reg(client, 0x10, 0x48);
+ }
+ break;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 255;
+ ctrl->step = 1;
+ ctrl->default_value = 128;
+ ctrl->flags = 0;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 127;
+ ctrl->step = 1;
+ ctrl->default_value = 71;
+ ctrl->flags = 0;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 127;
+ ctrl->step = 1;
+ ctrl->default_value = 64;
+ ctrl->flags = 0;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
+ ctrl->minimum = -128;
+ ctrl->maximum = 127;
+ ctrl->step = 1;
+ ctrl->default_value = 0;
+ ctrl->flags = 0;
+ break;
+ }
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value > 255)
+ dec->brightness = 255;
+ else if (ctrl->value < 0)
+ dec->brightness = 0;
+ else
+ dec->brightness = ctrl->value;
+ write_reg(client, 0x0a, dec->brightness);
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value > 127)
+ dec->contrast = 127;
+ else if (ctrl->value < 0)
+ dec->contrast = 0;
+ else
+ dec->contrast = ctrl->value;
+ write_reg(client, 0x0b, dec->contrast);
+ break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value > 127)
+ dec->saturation = 127;
+ else if (ctrl->value < 0)
+ dec->saturation = 0;
+ else
+ dec->saturation = ctrl->value;
+ write_reg(client, 0x0c, dec->saturation);
+ break;
+ case V4L2_CID_HUE:
+ if (ctrl->value > 127)
+ dec->hue = 127;
+ else if (ctrl->value < -128)
+ dec->hue = -128;
+ else
+ dec->hue = ctrl->value;
+ write_reg(client, 0x0d, dec->hue);
+ break;
+ }
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = dec->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = dec->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = dec->saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = dec->hue;
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wis_saa7113_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct wis_saa7113 *dec;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ dec = kmalloc(sizeof(struct wis_saa7113), GFP_KERNEL);
+ if (dec == NULL)
+ return -ENOMEM;
+
+ dec->norm = V4L2_STD_NTSC;
+ dec->brightness = 128;
+ dec->contrast = 71;
+ dec->saturation = 64;
+ dec->hue = 0;
+ i2c_set_clientdata(client, dec);
+
+ printk(KERN_DEBUG
+ "wis-saa7113: initializing SAA7113 at address %d on %s\n",
+ client->addr, adapter->name);
+
+ if (write_regs(client, initial_registers) < 0) {
+ printk(KERN_ERR
+ "wis-saa7113: error initializing SAA7113\n");
+ kfree(dec);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int wis_saa7113_remove(struct i2c_client *client)
+{
+ struct wis_saa7113 *dec = i2c_get_clientdata(client);
+
+ kfree(dec);
+ return 0;
+}
+
+static const struct i2c_device_id wis_saa7113_id[] = {
+ { "wis_saa7113", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wis_saa7113_id);
+
+static struct i2c_driver wis_saa7113_driver = {
+ .driver = {
+ .name = "WIS SAA7113 I2C driver",
+ },
+ .probe = wis_saa7113_probe,
+ .remove = wis_saa7113_remove,
+ .command = wis_saa7113_command,
+ .id_table = wis_saa7113_id,
+};
+
+static int __init wis_saa7113_init(void)
+{
+ return i2c_add_driver(&wis_saa7113_driver);
+}
+
+static void __exit wis_saa7113_cleanup(void)
+{
+ i2c_del_driver(&wis_saa7113_driver);
+}
+
+module_init(wis_saa7113_init);
+module_exit(wis_saa7113_cleanup);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+
+#include "wis-i2c.h"
+
+struct wis_saa7115 {
+ int norm;
+ int brightness;
+ int contrast;
+ int saturation;
+ int hue;
+};
+
+static u8 initial_registers[] =
+{
+ 0x01, 0x08,
+ 0x02, 0xc0,
+ 0x03, 0x20,
+ 0x04, 0x80,
+ 0x05, 0x80,
+ 0x06, 0xeb,
+ 0x07, 0xe0,
+ 0x08, 0xf0, /* always toggle FID */
+ 0x09, 0x40,
+ 0x0a, 0x80,
+ 0x0b, 0x40,
+ 0x0c, 0x40,
+ 0x0d, 0x00,
+ 0x0e, 0x03,
+ 0x0f, 0x2a,
+ 0x10, 0x0e,
+ 0x11, 0x00,
+ 0x12, 0x8d,
+ 0x13, 0x00,
+ 0x14, 0x00,
+ 0x15, 0x11,
+ 0x16, 0x01,
+ 0x17, 0xda,
+ 0x18, 0x40,
+ 0x19, 0x80,
+ 0x1a, 0x00,
+ 0x1b, 0x42,
+ 0x1c, 0xa9,
+ 0x30, 0x66,
+ 0x31, 0x90,
+ 0x32, 0x01,
+ 0x34, 0x00,
+ 0x35, 0x00,
+ 0x36, 0x20,
+ 0x38, 0x03,
+ 0x39, 0x20,
+ 0x3a, 0x88,
+ 0x40, 0x00,
+ 0x41, 0xff,
+ 0x42, 0xff,
+ 0x43, 0xff,
+ 0x44, 0xff,
+ 0x45, 0xff,
+ 0x46, 0xff,
+ 0x47, 0xff,
+ 0x48, 0xff,
+ 0x49, 0xff,
+ 0x4a, 0xff,
+ 0x4b, 0xff,
+ 0x4c, 0xff,
+ 0x4d, 0xff,
+ 0x4e, 0xff,
+ 0x4f, 0xff,
+ 0x50, 0xff,
+ 0x51, 0xff,
+ 0x52, 0xff,
+ 0x53, 0xff,
+ 0x54, 0xf4 /*0xff*/,
+ 0x55, 0xff,
+ 0x56, 0xff,
+ 0x57, 0xff,
+ 0x58, 0x40,
+ 0x59, 0x47,
+ 0x5a, 0x06 /*0x03*/,
+ 0x5b, 0x83,
+ 0x5d, 0x06,
+ 0x5e, 0x00,
+ 0x80, 0x30, /* window defined scaler operation, task A and B enabled */
+ 0x81, 0x03, /* use scaler datapath generated V */
+ 0x83, 0x00,
+ 0x84, 0x00,
+ 0x85, 0x00,
+ 0x86, 0x45,
+ 0x87, 0x31,
+ 0x88, 0xc0,
+ 0x90, 0x02, /* task A process top field */
+ 0x91, 0x08,
+ 0x92, 0x09,
+ 0x93, 0x80,
+ 0x94, 0x06,
+ 0x95, 0x00,
+ 0x96, 0xc0,
+ 0x97, 0x02,
+ 0x98, 0x12,
+ 0x99, 0x00,
+ 0x9a, 0xf2,
+ 0x9b, 0x00,
+ 0x9c, 0xd0,
+ 0x9d, 0x02,
+ 0x9e, 0xf2,
+ 0x9f, 0x00,
+ 0xa0, 0x01,
+ 0xa1, 0x01,
+ 0xa2, 0x01,
+ 0xa4, 0x80,
+ 0xa5, 0x40,
+ 0xa6, 0x40,
+ 0xa8, 0x00,
+ 0xa9, 0x04,
+ 0xaa, 0x00,
+ 0xac, 0x00,
+ 0xad, 0x02,
+ 0xae, 0x00,
+ 0xb0, 0x00,
+ 0xb1, 0x04,
+ 0xb2, 0x00,
+ 0xb3, 0x04,
+ 0xb4, 0x00,
+ 0xb8, 0x00,
+ 0xbc, 0x00,
+ 0xc0, 0x03, /* task B process bottom field */
+ 0xc1, 0x08,
+ 0xc2, 0x09,
+ 0xc3, 0x80,
+ 0xc4, 0x06,
+ 0xc5, 0x00,
+ 0xc6, 0xc0,
+ 0xc7, 0x02,
+ 0xc8, 0x12,
+ 0xc9, 0x00,
+ 0xca, 0xf2,
+ 0xcb, 0x00,
+ 0xcc, 0xd0,
+ 0xcd, 0x02,
+ 0xce, 0xf2,
+ 0xcf, 0x00,
+ 0xd0, 0x01,
+ 0xd1, 0x01,
+ 0xd2, 0x01,
+ 0xd4, 0x80,
+ 0xd5, 0x40,
+ 0xd6, 0x40,
+ 0xd8, 0x00,
+ 0xd9, 0x04,
+ 0xda, 0x00,
+ 0xdc, 0x00,
+ 0xdd, 0x02,
+ 0xde, 0x00,
+ 0xe0, 0x00,
+ 0xe1, 0x04,
+ 0xe2, 0x00,
+ 0xe3, 0x04,
+ 0xe4, 0x00,
+ 0xe8, 0x00,
+ 0x88, 0xf0, /* End of original static list */
+ 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
+};
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+ int i;
+
+ for (i = 0; regs[i] != 0x00; i += 2)
+ if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
+ return -1;
+ return 0;
+}
+
+static int wis_saa7115_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
+{
+ struct wis_saa7115 *dec = i2c_get_clientdata(client);
+
+ switch (cmd) {
+ case VIDIOC_S_INPUT:
+ {
+ int *input = arg;
+
+ i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input);
+ i2c_smbus_write_byte_data(client, 0x09,
+ *input < 6 ? 0x40 : 0xC0);
+ break;
+ }
+ case DECODER_SET_RESOLUTION:
+ {
+ struct video_decoder_resolution *res = arg;
+ /* Course-grained scaler */
+ int h_integer_scaler = res->width < 704 ? 704 / res->width : 1;
+ /* Fine-grained scaler to take care of remainder */
+ int h_scaling_increment = (704 / h_integer_scaler) *
+ 1024 / res->width;
+ /* Fine-grained scaler only */
+ int v_scaling_increment = (dec->norm & V4L2_STD_NTSC ?
+ 240 : 288) * 1024 / res->height;
+ u8 regs[] = {
+ 0x88, 0xc0,
+ 0x9c, res->width & 0xff,
+ 0x9d, res->width >> 8,
+ 0x9e, res->height & 0xff,
+ 0x9f, res->height >> 8,
+ 0xa0, h_integer_scaler,
+ 0xa1, 1,
+ 0xa2, 1,
+ 0xa8, h_scaling_increment & 0xff,
+ 0xa9, h_scaling_increment >> 8,
+ 0xac, (h_scaling_increment / 2) & 0xff,
+ 0xad, (h_scaling_increment / 2) >> 8,
+ 0xb0, v_scaling_increment & 0xff,
+ 0xb1, v_scaling_increment >> 8,
+ 0xb2, v_scaling_increment & 0xff,
+ 0xb3, v_scaling_increment >> 8,
+ 0xcc, res->width & 0xff,
+ 0xcd, res->width >> 8,
+ 0xce, res->height & 0xff,
+ 0xcf, res->height >> 8,
+ 0xd0, h_integer_scaler,
+ 0xd1, 1,
+ 0xd2, 1,
+ 0xd8, h_scaling_increment & 0xff,
+ 0xd9, h_scaling_increment >> 8,
+ 0xdc, (h_scaling_increment / 2) & 0xff,
+ 0xdd, (h_scaling_increment / 2) >> 8,
+ 0xe0, v_scaling_increment & 0xff,
+ 0xe1, v_scaling_increment >> 8,
+ 0xe2, v_scaling_increment & 0xff,
+ 0xe3, v_scaling_increment >> 8,
+ 0x88, 0xf0,
+ 0, 0,
+ };
+ write_regs(client, regs);
+ break;
+ }
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *input = arg;
+ u8 regs[] = {
+ 0x88, 0xc0,
+ 0x98, *input & V4L2_STD_NTSC ? 0x12 : 0x16,
+ 0x9a, *input & V4L2_STD_NTSC ? 0xf2 : 0x20,
+ 0x9b, *input & V4L2_STD_NTSC ? 0x00 : 0x01,
+ 0xc8, *input & V4L2_STD_NTSC ? 0x12 : 0x16,
+ 0xca, *input & V4L2_STD_NTSC ? 0xf2 : 0x20,
+ 0xcb, *input & V4L2_STD_NTSC ? 0x00 : 0x01,
+ 0x88, 0xf0,
+ 0x30, *input & V4L2_STD_NTSC ? 0x66 : 0x00,
+ 0x31, *input & V4L2_STD_NTSC ? 0x90 : 0xe0,
+ 0, 0,
+ };
+ write_regs(client, regs);
+ dec->norm = *input;
+ break;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 255;
+ ctrl->step = 1;
+ ctrl->default_value = 128;
+ ctrl->flags = 0;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 127;
+ ctrl->step = 1;
+ ctrl->default_value = 64;
+ ctrl->flags = 0;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 127;
+ ctrl->step = 1;
+ ctrl->default_value = 64;
+ ctrl->flags = 0;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
+ ctrl->minimum = -128;
+ ctrl->maximum = 127;
+ ctrl->step = 1;
+ ctrl->default_value = 0;
+ ctrl->flags = 0;
+ break;
+ }
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value > 255)
+ dec->brightness = 255;
+ else if (ctrl->value < 0)
+ dec->brightness = 0;
+ else
+ dec->brightness = ctrl->value;
+ write_reg(client, 0x0a, dec->brightness);
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value > 127)
+ dec->contrast = 127;
+ else if (ctrl->value < 0)
+ dec->contrast = 0;
+ else
+ dec->contrast = ctrl->value;
+ write_reg(client, 0x0b, dec->contrast);
+ break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value > 127)
+ dec->saturation = 127;
+ else if (ctrl->value < 0)
+ dec->saturation = 0;
+ else
+ dec->saturation = ctrl->value;
+ write_reg(client, 0x0c, dec->saturation);
+ break;
+ case V4L2_CID_HUE:
+ if (ctrl->value > 127)
+ dec->hue = 127;
+ else if (ctrl->value < -128)
+ dec->hue = -128;
+ else
+ dec->hue = ctrl->value;
+ write_reg(client, 0x0d, dec->hue);
+ break;
+ }
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = dec->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = dec->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = dec->saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = dec->hue;
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wis_saa7115_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct wis_saa7115 *dec;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ dec = kmalloc(sizeof(struct wis_saa7115), GFP_KERNEL);
+ if (dec == NULL)
+ return -ENOMEM;
+
+ dec->norm = V4L2_STD_NTSC;
+ dec->brightness = 128;
+ dec->contrast = 64;
+ dec->saturation = 64;
+ dec->hue = 0;
+ i2c_set_clientdata(client, dec);
+
+ printk(KERN_DEBUG
+ "wis-saa7115: initializing SAA7115 at address %d on %s\n",
+ client->addr, adapter->name);
+
+ if (write_regs(client, initial_registers) < 0) {
+ printk(KERN_ERR
+ "wis-saa7115: error initializing SAA7115\n");
+ kfree(dec);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int wis_saa7115_remove(struct i2c_client *client)
+{
+ struct wis_saa7115 *dec = i2c_get_clientdata(client);
+
+ kfree(dec);
+ return 0;
+}
+
+static const struct i2c_device_id wis_saa7115_id[] = {
+ { "wis_saa7115", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wis_saa7115_id);
+
+static struct i2c_driver wis_saa7115_driver = {
+ .driver = {
+ .name = "WIS SAA7115 I2C driver",
+ },
+ .probe = wis_saa7115_probe,
+ .remove = wis_saa7115_remove,
+ .command = wis_saa7115_command,
+ .id_table = wis_saa7115_id,
+};
+
+static int __init wis_saa7115_init(void)
+{
+ return i2c_add_driver(&wis_saa7115_driver);
+}
+
+static void __exit wis_saa7115_cleanup(void)
+{
+ i2c_del_driver(&wis_saa7115_driver);
+}
+
+module_init(wis_saa7115_init);
+module_exit(wis_saa7115_cleanup);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "wis-i2c.h"
+
+/* #define MPX_DEBUG */
+
+/* AS(IF/MPX) pin: LOW HIGH/OPEN
+ * IF/MPX address: 0x42/0x40 0x43/0x44
+ */
+#define IF_I2C_ADDR 0x43
+#define MPX_I2C_ADDR 0x44
+
+static v4l2_std_id force_band;
+static char force_band_str[] = "-";
+module_param_string(force_band, force_band_str, sizeof(force_band_str), 0644);
+static int force_mpx_mode = -1;
+module_param(force_mpx_mode, int, 0644);
+
+/* Store tuner info in the same format as tuner.c, so maybe we can put the
+ * Sony tuner support in there. */
+struct sony_tunertype {
+ char *name;
+ unsigned char Vendor; /* unused here */
+ unsigned char Type; /* unused here */
+
+ unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */
+ unsigned short thresh2; /* band switch VHF_HI <=> UHF */
+ unsigned char VHF_L;
+ unsigned char VHF_H;
+ unsigned char UHF;
+ unsigned char config;
+ unsigned short IFPCoff;
+};
+
+/* This array is indexed by (tuner_type - 200) */
+static struct sony_tunertype sony_tuners[] = {
+ { "Sony PAL+SECAM (BTF-PG472Z)", 0, 0,
+ 16*144.25, 16*427.25, 0x01, 0x02, 0x04, 0xc6, 623},
+ { "Sony NTSC_JP (BTF-PK467Z)", 0, 0,
+ 16*220.25, 16*467.25, 0x01, 0x02, 0x04, 0xc6, 940},
+ { "Sony NTSC (BTF-PB463Z)", 0, 0,
+ 16*130.25, 16*364.25, 0x01, 0x02, 0x04, 0xc6, 732},
+};
+
+struct wis_sony_tuner {
+ int type;
+ v4l2_std_id std;
+ unsigned int freq;
+ int mpxmode;
+ u32 audmode;
+};
+
+/* Basically the same as default_set_tv_freq() in tuner.c */
+static int set_freq(struct i2c_client *client, int freq)
+{
+ struct wis_sony_tuner *t = i2c_get_clientdata(client);
+ char *band_name;
+ int n;
+ int band_select;
+ struct sony_tunertype *tun;
+ u8 buffer[4];
+
+ tun = &sony_tuners[t->type - 200];
+ if (freq < tun->thresh1) {
+ band_name = "VHF_L";
+ band_select = tun->VHF_L;
+ } else if (freq < tun->thresh2) {
+ band_name = "VHF_H";
+ band_select = tun->VHF_H;
+ } else {
+ band_name = "UHF";
+ band_select = tun->UHF;
+ }
+ printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n",
+ freq / 16, (freq % 16) * 625, band_name);
+ n = freq + tun->IFPCoff;
+
+ buffer[0] = n >> 8;
+ buffer[1] = n & 0xff;
+ buffer[2] = tun->config;
+ buffer[3] = band_select;
+ i2c_master_send(client, buffer, 4);
+
+ return 0;
+}
+
+static int mpx_write(struct i2c_client *client, int dev, int addr, int val)
+{
+ u8 buffer[5];
+ struct i2c_msg msg;
+
+ buffer[0] = dev;
+ buffer[1] = addr >> 8;
+ buffer[2] = addr & 0xff;
+ buffer[3] = val >> 8;
+ buffer[4] = val & 0xff;
+ msg.addr = MPX_I2C_ADDR;
+ msg.flags = 0;
+ msg.len = 5;
+ msg.buf = buffer;
+ i2c_transfer(client->adapter, &msg, 1);
+ return 0;
+}
+
+/*
+ * MPX register values for the BTF-PG472Z:
+ *
+ * FM_ NICAM_ SCART_
+ * MODUS SOURCE ACB PRESCAL PRESCAL PRESCAL SYSTEM VOLUME
+ * 10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000
+ * ---------------------------------------------------------------
+ * Auto 1003 0020 0100 2603 5000 XXXX 0001 7500
+ *
+ * B/G
+ * Mono 1003 0020 0100 2603 5000 XXXX 0003 7500
+ * A2 1003 0020 0100 2601 5000 XXXX 0003 7500
+ * NICAM 1003 0120 0100 2603 5000 XXXX 0008 7500
+ *
+ * I
+ * Mono 1003 0020 0100 2603 7900 XXXX 000A 7500
+ * NICAM 1003 0120 0100 2603 7900 XXXX 000A 7500
+ *
+ * D/K
+ * Mono 1003 0020 0100 2603 5000 XXXX 0004 7500
+ * A2-1 1003 0020 0100 2601 5000 XXXX 0004 7500
+ * A2-2 1003 0020 0100 2601 5000 XXXX 0005 7500
+ * A2-3 1003 0020 0100 2601 5000 XXXX 0007 7500
+ * NICAM 1003 0120 0100 2603 5000 XXXX 000B 7500
+ *
+ * L/L'
+ * Mono 0003 0200 0100 7C03 5000 2200 0009 7500
+ * NICAM 0003 0120 0100 7C03 5000 XXXX 0009 7500
+ *
+ * M
+ * Mono 1003 0200 0100 2B03 5000 2B00 0002 7500
+ *
+ * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX.
+ *
+ * Bilingual selection in A2/NICAM:
+ *
+ * High byte of SOURCE Left chan Right chan
+ * 0x01 MAIN SUB
+ * 0x03 MAIN MAIN
+ * 0x04 SUB SUB
+ *
+ * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or
+ * 0x00 (all other bands). Force mono in A2 with FMONO_A2:
+ *
+ * FMONO_A2
+ * 10/0022
+ * --------
+ * Forced mono ON 07F0
+ * Forced mono OFF 0190
+ */
+
+static struct {
+ enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode;
+ u16 modus;
+ u16 source;
+ u16 acb;
+ u16 fm_prescale;
+ u16 nicam_prescale;
+ u16 scart_prescale;
+ u16 system;
+ u16 volume;
+} mpx_audio_modes[] = {
+ /* Auto */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
+ 0x5000, 0x0000, 0x0001, 0x7500 },
+ /* B/G Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
+ 0x5000, 0x0000, 0x0003, 0x7500 },
+ /* B/G A2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
+ 0x5000, 0x0000, 0x0003, 0x7500 },
+ /* B/G NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
+ 0x5000, 0x0000, 0x0008, 0x7500 },
+ /* I Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
+ 0x7900, 0x0000, 0x000A, 0x7500 },
+ /* I NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
+ 0x7900, 0x0000, 0x000A, 0x7500 },
+ /* D/K Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
+ 0x5000, 0x0000, 0x0004, 0x7500 },
+ /* D/K A2-1 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
+ 0x5000, 0x0000, 0x0004, 0x7500 },
+ /* D/K A2-2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
+ 0x5000, 0x0000, 0x0005, 0x7500 },
+ /* D/K A2-3 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
+ 0x5000, 0x0000, 0x0007, 0x7500 },
+ /* D/K NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
+ 0x5000, 0x0000, 0x000B, 0x7500 },
+ /* L/L' Mono */ { AUD_MONO, 0x0003, 0x0200, 0x0100, 0x7C03,
+ 0x5000, 0x2200, 0x0009, 0x7500 },
+ /* L/L' NICAM */{ AUD_NICAM_L, 0x0003, 0x0120, 0x0100, 0x7C03,
+ 0x5000, 0x0000, 0x0009, 0x7500 },
+};
+
+#define MPX_NUM_MODES ARRAY_SIZE(mpx_audio_modes)
+
+static int mpx_setup(struct i2c_client *client)
+{
+ struct wis_sony_tuner *t = i2c_get_clientdata(client);
+ u16 source = 0;
+ u8 buffer[3];
+ struct i2c_msg msg;
+
+ /* reset MPX */
+ buffer[0] = 0x00;
+ buffer[1] = 0x80;
+ buffer[2] = 0x00;
+ msg.addr = MPX_I2C_ADDR;
+ msg.flags = 0;
+ msg.len = 3;
+ msg.buf = buffer;
+ i2c_transfer(client->adapter, &msg, 1);
+ buffer[1] = 0x00;
+ i2c_transfer(client->adapter, &msg, 1);
+
+ if (mpx_audio_modes[t->mpxmode].audio_mode != AUD_MONO) {
+ switch (t->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ switch (mpx_audio_modes[t->mpxmode].audio_mode) {
+ case AUD_A2:
+ source = mpx_audio_modes[t->mpxmode].source;
+ break;
+ case AUD_NICAM:
+ source = 0x0000;
+ break;
+ case AUD_NICAM_L:
+ source = 0x0200;
+ break;
+ default:
+ break;
+ }
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ source = mpx_audio_modes[t->mpxmode].source;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ source = 0x0300;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ source = 0x0400;
+ break;
+ }
+ source |= mpx_audio_modes[t->mpxmode].source & 0x00ff;
+ } else
+ source = mpx_audio_modes[t->mpxmode].source;
+
+ mpx_write(client, 0x10, 0x0030, mpx_audio_modes[t->mpxmode].modus);
+ mpx_write(client, 0x12, 0x0008, source);
+ mpx_write(client, 0x12, 0x0013, mpx_audio_modes[t->mpxmode].acb);
+ mpx_write(client, 0x12, 0x000e,
+ mpx_audio_modes[t->mpxmode].fm_prescale);
+ mpx_write(client, 0x12, 0x0010,
+ mpx_audio_modes[t->mpxmode].nicam_prescale);
+ mpx_write(client, 0x12, 0x000d,
+ mpx_audio_modes[t->mpxmode].scart_prescale);
+ mpx_write(client, 0x10, 0x0020, mpx_audio_modes[t->mpxmode].system);
+ mpx_write(client, 0x12, 0x0000, mpx_audio_modes[t->mpxmode].volume);
+ if (mpx_audio_modes[t->mpxmode].audio_mode == AUD_A2)
+ mpx_write(client, 0x10, 0x0022,
+ t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190);
+
+#ifdef MPX_DEBUG
+ {
+ u8 buf1[3], buf2[2];
+ struct i2c_msg msgs[2];
+
+ printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x "
+ "%04x %04x %04x %04x %04x %04x\n",
+ mpx_audio_modes[t->mpxmode].modus,
+ source,
+ mpx_audio_modes[t->mpxmode].acb,
+ mpx_audio_modes[t->mpxmode].fm_prescale,
+ mpx_audio_modes[t->mpxmode].nicam_prescale,
+ mpx_audio_modes[t->mpxmode].scart_prescale,
+ mpx_audio_modes[t->mpxmode].system,
+ mpx_audio_modes[t->mpxmode].volume);
+ buf1[0] = 0x11;
+ buf1[1] = 0x00;
+ buf1[2] = 0x7e;
+ msgs[0].addr = MPX_I2C_ADDR;
+ msgs[0].flags = 0;
+ msgs[0].len = 3;
+ msgs[0].buf = buf1;
+ msgs[1].addr = MPX_I2C_ADDR;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 2;
+ msgs[1].buf = buf2;
+ i2c_transfer(client->adapter, msgs, 2);
+ printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n",
+ buf2[0], buf2[1]);
+ buf1[0] = 0x11;
+ buf1[1] = 0x02;
+ buf1[2] = 0x00;
+ i2c_transfer(client->adapter, msgs, 2);
+ printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n",
+ buf2[0], buf2[1]);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * IF configuration values for the BTF-PG472Z:
+ *
+ * B/G: 0x94 0x70 0x49
+ * I: 0x14 0x70 0x4a
+ * D/K: 0x14 0x70 0x4b
+ * L: 0x04 0x70 0x4b
+ * L': 0x44 0x70 0x53
+ * M: 0x50 0x30 0x4c
+ */
+
+static int set_if(struct i2c_client *client)
+{
+ struct wis_sony_tuner *t = i2c_get_clientdata(client);
+ u8 buffer[4];
+ struct i2c_msg msg;
+ int default_mpx_mode = 0;
+
+ /* configure IF */
+ buffer[0] = 0;
+ if (t->std & V4L2_STD_PAL_BG) {
+ buffer[1] = 0x94;
+ buffer[2] = 0x70;
+ buffer[3] = 0x49;
+ default_mpx_mode = 1;
+ } else if (t->std & V4L2_STD_PAL_I) {
+ buffer[1] = 0x14;
+ buffer[2] = 0x70;
+ buffer[3] = 0x4a;
+ default_mpx_mode = 4;
+ } else if (t->std & V4L2_STD_PAL_DK) {
+ buffer[1] = 0x14;
+ buffer[2] = 0x70;
+ buffer[3] = 0x4b;
+ default_mpx_mode = 6;
+ } else if (t->std & V4L2_STD_SECAM_L) {
+ buffer[1] = 0x04;
+ buffer[2] = 0x70;
+ buffer[3] = 0x4b;
+ default_mpx_mode = 11;
+ }
+ msg.addr = IF_I2C_ADDR;
+ msg.flags = 0;
+ msg.len = 4;
+ msg.buf = buffer;
+ i2c_transfer(client->adapter, &msg, 1);
+
+ /* Select MPX mode if not forced by the user */
+ if (force_mpx_mode >= 0 && force_mpx_mode < MPX_NUM_MODES)
+ t->mpxmode = force_mpx_mode;
+ else
+ t->mpxmode = default_mpx_mode;
+ printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n",
+ t->mpxmode);
+ mpx_setup(client);
+
+ return 0;
+}
+
+static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ struct wis_sony_tuner *t = i2c_get_clientdata(client);
+
+ switch (cmd) {
+#if 0
+#ifdef TUNER_SET_TYPE_ADDR
+ case TUNER_SET_TYPE_ADDR:
+ {
+ struct tuner_setup *tun_setup = arg;
+ int *type = &tun_setup->type;
+#else
+ case TUNER_SET_TYPE:
+ {
+ int *type = arg;
+#endif
+
+ if (t->type >= 0) {
+ if (t->type != *type)
+ printk(KERN_ERR "wis-sony-tuner: type already "
+ "set to %d, ignoring request for %d\n",
+ t->type, *type);
+ break;
+ }
+ t->type = *type;
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ switch (force_band_str[0]) {
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ printk(KERN_INFO "wis-sony-tuner: forcing "
+ "tuner to PAL-B/G bands\n");
+ force_band = V4L2_STD_PAL_BG;
+ break;
+ case 'i':
+ case 'I':
+ printk(KERN_INFO "wis-sony-tuner: forcing "
+ "tuner to PAL-I band\n");
+ force_band = V4L2_STD_PAL_I;
+ break;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ printk(KERN_INFO "wis-sony-tuner: forcing "
+ "tuner to PAL-D/K bands\n");
+ force_band = V4L2_STD_PAL_I;
+ break;
+ case 'l':
+ case 'L':
+ printk(KERN_INFO "wis-sony-tuner: forcing "
+ "tuner to SECAM-L band\n");
+ force_band = V4L2_STD_SECAM_L;
+ break;
+ default:
+ force_band = 0;
+ break;
+ }
+ if (force_band)
+ t->std = force_band;
+ else
+ t->std = V4L2_STD_PAL_BG;
+ set_if(client);
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ t->std = V4L2_STD_NTSC_M_JP;
+ break;
+ case TUNER_SONY_BTF_PB463Z:
+ t->std = V4L2_STD_NTSC_M;
+ break;
+ default:
+ printk(KERN_ERR "wis-sony-tuner: tuner type %d is not "
+ "supported by this module\n", *type);
+ break;
+ }
+ if (type >= 0)
+ printk(KERN_INFO
+ "wis-sony-tuner: type set to %d (%s)\n",
+ t->type, sony_tuners[t->type - 200].name);
+ break;
+ }
+#endif
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ f->frequency = t->freq;
+ break;
+ }
+ case VIDIOC_S_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ t->freq = f->frequency;
+ set_freq(client, t->freq);
+ break;
+ }
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *std = arg;
+
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ switch (std->index) {
+ case 0:
+ v4l2_video_std_construct(std,
+ V4L2_STD_PAL_BG, "PAL-B/G");
+ break;
+ case 1:
+ v4l2_video_std_construct(std,
+ V4L2_STD_PAL_I, "PAL-I");
+ break;
+ case 2:
+ v4l2_video_std_construct(std,
+ V4L2_STD_PAL_DK, "PAL-D/K");
+ break;
+ case 3:
+ v4l2_video_std_construct(std,
+ V4L2_STD_SECAM_L, "SECAM-L");
+ break;
+ default:
+ std->id = 0; /* hack to indicate EINVAL */
+ break;
+ }
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ if (std->index != 0) {
+ std->id = 0; /* hack to indicate EINVAL */
+ break;
+ }
+ v4l2_video_std_construct(std,
+ V4L2_STD_NTSC_M_JP, "NTSC-J");
+ break;
+ case TUNER_SONY_BTF_PB463Z:
+ if (std->index != 0) {
+ std->id = 0; /* hack to indicate EINVAL */
+ break;
+ }
+ v4l2_video_std_construct(std, V4L2_STD_NTSC_M, "NTSC");
+ break;
+ }
+ break;
+ }
+ case VIDIOC_G_STD:
+ {
+ v4l2_std_id *std = arg;
+
+ *std = t->std;
+ break;
+ }
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *std = arg;
+ v4l2_std_id old = t->std;
+
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ if (force_band && (*std & force_band) != *std &&
+ *std != V4L2_STD_PAL &&
+ *std != V4L2_STD_SECAM) {
+ printk(KERN_DEBUG "wis-sony-tuner: ignoring "
+ "requested TV standard in "
+ "favor of force_band value\n");
+ t->std = force_band;
+ } else if (*std & V4L2_STD_PAL_BG) { /* default */
+ t->std = V4L2_STD_PAL_BG;
+ } else if (*std & V4L2_STD_PAL_I) {
+ t->std = V4L2_STD_PAL_I;
+ } else if (*std & V4L2_STD_PAL_DK) {
+ t->std = V4L2_STD_PAL_DK;
+ } else if (*std & V4L2_STD_SECAM_L) {
+ t->std = V4L2_STD_SECAM_L;
+ } else {
+ printk(KERN_ERR "wis-sony-tuner: TV standard "
+ "not supported\n");
+ *std = 0; /* hack to indicate EINVAL */
+ break;
+ }
+ if (old != t->std)
+ set_if(client);
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ if (!(*std & V4L2_STD_NTSC_M_JP)) {
+ printk(KERN_ERR "wis-sony-tuner: TV standard "
+ "not supported\n");
+ *std = 0; /* hack to indicate EINVAL */
+ }
+ break;
+ case TUNER_SONY_BTF_PB463Z:
+ if (!(*std & V4L2_STD_NTSC_M)) {
+ printk(KERN_ERR "wis-sony-tuner: TV standard "
+ "not supported\n");
+ *std = 0; /* hack to indicate EINVAL */
+ }
+ break;
+ }
+ break;
+ }
+ case VIDIOC_QUERYSTD:
+ {
+ v4l2_std_id *std = arg;
+
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ if (force_band)
+ *std = force_band;
+ else
+ *std = V4L2_STD_PAL_BG | V4L2_STD_PAL_I |
+ V4L2_STD_PAL_DK | V4L2_STD_SECAM_L;
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ *std = V4L2_STD_NTSC_M_JP;
+ break;
+ case TUNER_SONY_BTF_PB463Z:
+ *std = V4L2_STD_NTSC_M;
+ break;
+ }
+ break;
+ }
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *tun = arg;
+
+ memset(tun, 0, sizeof(*tun));
+ strcpy(tun->name, "Television");
+ tun->type = V4L2_TUNER_ANALOG_TV;
+ tun->rangelow = 0UL; /* does anything use these? */
+ tun->rangehigh = 0xffffffffUL;
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ tun->capability = V4L2_TUNER_CAP_NORM |
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2;
+ tun->rxsubchans = V4L2_TUNER_SUB_MONO |
+ V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2;
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ case TUNER_SONY_BTF_PB463Z:
+ tun->capability = V4L2_TUNER_CAP_STEREO;
+ tun->rxsubchans = V4L2_TUNER_SUB_MONO |
+ V4L2_TUNER_SUB_STEREO;
+ break;
+ }
+ tun->audmode = t->audmode;
+ return 0;
+ }
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *tun = arg;
+
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ if (tun->audmode != t->audmode) {
+ t->audmode = tun->audmode;
+ mpx_setup(client);
+ }
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ case TUNER_SONY_BTF_PB463Z:
+ break;
+ }
+ return 0;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wis_sony_tuner_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct wis_sony_tuner *t;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -ENODEV;
+
+ t = kmalloc(sizeof(struct wis_sony_tuner), GFP_KERNEL);
+ if (t == NULL)
+ return -ENOMEM;
+
+ t->type = -1;
+ t->freq = 0;
+ t->mpxmode = 0;
+ t->audmode = V4L2_TUNER_MODE_STEREO;
+ i2c_set_clientdata(client, t);
+
+ printk(KERN_DEBUG
+ "wis-sony-tuner: initializing tuner at address %d on %s\n",
+ client->addr, adapter->name);
+
+ return 0;
+}
+
+static int wis_sony_tuner_remove(struct i2c_client *client)
+{
+ struct wis_sony_tuner *t = i2c_get_clientdata(client);
+
+ kfree(t);
+ return 0;
+}
+
+static const struct i2c_device_id wis_sony_tuner_id[] = {
+ { "wis_sony_tuner", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wis_sony_tuner_id);
+
+static struct i2c_driver wis_sony_tuner_driver = {
+ .driver = {
+ .name = "WIS Sony TV Tuner I2C driver",
+ },
+ .probe = wis_sony_tuner_probe,
+ .remove = wis_sony_tuner_remove,
+ .command = tuner_command,
+ .id_table = wis_sony_tuner_id,
+};
+
+static int __init wis_sony_tuner_init(void)
+{
+ return i2c_add_driver(&wis_sony_tuner_driver);
+}
+
+static void __exit wis_sony_tuner_cleanup(void)
+{
+ i2c_del_driver(&wis_sony_tuner_driver);
+}
+
+module_init(wis_sony_tuner_init);
+module_exit(wis_sony_tuner_cleanup);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+
+#include "wis-i2c.h"
+
+struct wis_tw2804 {
+ int channel;
+ int norm;
+ int brightness;
+ int contrast;
+ int saturation;
+ int hue;
+};
+
+static u8 global_registers[] = {
+ 0x39, 0x00,
+ 0x3a, 0xff,
+ 0x3b, 0x84,
+ 0x3c, 0x80,
+ 0x3d, 0x80,
+ 0x3e, 0x82,
+ 0x3f, 0x82,
+ 0xff, 0xff, /* Terminator (reg 0xff does not exist) */
+};
+
+static u8 channel_registers[] = {
+ 0x01, 0xc4,
+ 0x02, 0xa5,
+ 0x03, 0x20,
+ 0x04, 0xd0,
+ 0x05, 0x20,
+ 0x06, 0xd0,
+ 0x07, 0x88,
+ 0x08, 0x20,
+ 0x09, 0x07,
+ 0x0a, 0xf0,
+ 0x0b, 0x07,
+ 0x0c, 0xf0,
+ 0x0d, 0x40,
+ 0x0e, 0xd2,
+ 0x0f, 0x80,
+ 0x10, 0x80,
+ 0x11, 0x80,
+ 0x12, 0x80,
+ 0x13, 0x1f,
+ 0x14, 0x00,
+ 0x15, 0x00,
+ 0x16, 0x00,
+ 0x17, 0x00,
+ 0x18, 0xff,
+ 0x19, 0xff,
+ 0x1a, 0xff,
+ 0x1b, 0xff,
+ 0x1c, 0xff,
+ 0x1d, 0xff,
+ 0x1e, 0xff,
+ 0x1f, 0xff,
+ 0x20, 0x07,
+ 0x21, 0x07,
+ 0x22, 0x00,
+ 0x23, 0x91,
+ 0x24, 0x51,
+ 0x25, 0x03,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x00,
+ 0x29, 0x00,
+ 0x2a, 0x00,
+ 0x2b, 0x00,
+ 0x2c, 0x00,
+ 0x2d, 0x00,
+ 0x2e, 0x00,
+ 0x2f, 0x00,
+ 0x30, 0x00,
+ 0x31, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x00,
+ 0x34, 0x00,
+ 0x35, 0x00,
+ 0x36, 0x00,
+ 0x37, 0x00,
+ 0xff, 0xff, /* Terminator (reg 0xff does not exist) */
+};
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel)
+{
+ return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
+}
+
+static int write_regs(struct i2c_client *client, u8 *regs, int channel)
+{
+ int i;
+
+ for (i = 0; regs[i] != 0xff; i += 2)
+ if (i2c_smbus_write_byte_data(client,
+ regs[i] | (channel << 6), regs[i + 1]) < 0)
+ return -1;
+ return 0;
+}
+
+static int wis_tw2804_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
+{
+ struct wis_tw2804 *dec = i2c_get_clientdata(client);
+
+ if (cmd == DECODER_SET_CHANNEL) {
+ int *input = arg;
+
+ if (*input < 0 || *input > 3) {
+ printk(KERN_ERR "wis-tw2804: channel %d is not "
+ "between 0 and 3!\n", *input);
+ return 0;
+ }
+ dec->channel = *input;
+ printk(KERN_DEBUG "wis-tw2804: initializing TW2804 "
+ "channel %d\n", dec->channel);
+ if (dec->channel == 0 &&
+ write_regs(client, global_registers, 0) < 0) {
+ printk(KERN_ERR "wis-tw2804: error initializing "
+ "TW2804 global registers\n");
+ return 0;
+ }
+ if (write_regs(client, channel_registers, dec->channel) < 0) {
+ printk(KERN_ERR "wis-tw2804: error initializing "
+ "TW2804 channel %d\n", dec->channel);
+ return 0;
+ }
+ return 0;
+ }
+
+ if (dec->channel < 0) {
+ printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until "
+ "channel number is set\n", cmd);
+ return 0;
+ }
+
+ switch (cmd) {
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *input = arg;
+ u8 regs[] = {
+ 0x01, *input & V4L2_STD_NTSC ? 0xc4 : 0x84,
+ 0x09, *input & V4L2_STD_NTSC ? 0x07 : 0x04,
+ 0x0a, *input & V4L2_STD_NTSC ? 0xf0 : 0x20,
+ 0x0b, *input & V4L2_STD_NTSC ? 0x07 : 0x04,
+ 0x0c, *input & V4L2_STD_NTSC ? 0xf0 : 0x20,
+ 0x0d, *input & V4L2_STD_NTSC ? 0x40 : 0x4a,
+ 0x16, *input & V4L2_STD_NTSC ? 0x00 : 0x40,
+ 0x17, *input & V4L2_STD_NTSC ? 0x00 : 0x40,
+ 0x20, *input & V4L2_STD_NTSC ? 0x07 : 0x0f,
+ 0x21, *input & V4L2_STD_NTSC ? 0x07 : 0x0f,
+ 0xff, 0xff,
+ };
+ write_regs(client, regs, dec->channel);
+ dec->norm = *input;
+ break;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 255;
+ ctrl->step = 1;
+ ctrl->default_value = 128;
+ ctrl->flags = 0;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 255;
+ ctrl->step = 1;
+ ctrl->default_value = 128;
+ ctrl->flags = 0;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 255;
+ ctrl->step = 1;
+ ctrl->default_value = 128;
+ ctrl->flags = 0;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 255;
+ ctrl->step = 1;
+ ctrl->default_value = 128;
+ ctrl->flags = 0;
+ break;
+ }
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value > 255)
+ dec->brightness = 255;
+ else if (ctrl->value < 0)
+ dec->brightness = 0;
+ else
+ dec->brightness = ctrl->value;
+ write_reg(client, 0x12, dec->brightness, dec->channel);
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value > 255)
+ dec->contrast = 255;
+ else if (ctrl->value < 0)
+ dec->contrast = 0;
+ else
+ dec->contrast = ctrl->value;
+ write_reg(client, 0x11, dec->contrast, dec->channel);
+ break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value > 255)
+ dec->saturation = 255;
+ else if (ctrl->value < 0)
+ dec->saturation = 0;
+ else
+ dec->saturation = ctrl->value;
+ write_reg(client, 0x10, dec->saturation, dec->channel);
+ break;
+ case V4L2_CID_HUE:
+ if (ctrl->value > 255)
+ dec->hue = 255;
+ else if (ctrl->value < 0)
+ dec->hue = 0;
+ else
+ dec->hue = ctrl->value;
+ write_reg(client, 0x0f, dec->hue, dec->channel);
+ break;
+ }
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = dec->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = dec->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = dec->saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = dec->hue;
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wis_tw2804_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct wis_tw2804 *dec;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL);
+ if (dec == NULL)
+ return -ENOMEM;
+
+ dec->channel = -1;
+ dec->norm = V4L2_STD_NTSC;
+ dec->brightness = 128;
+ dec->contrast = 128;
+ dec->saturation = 128;
+ dec->hue = 128;
+ i2c_set_clientdata(client, dec);
+
+ printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n",
+ client->addr, adapter->name);
+
+ return 0;
+}
+
+static int wis_tw2804_remove(struct i2c_client *client)
+{
+ struct wis_tw2804 *dec = i2c_get_clientdata(client);
+
+ kfree(dec);
+ return 0;
+}
+
+static const struct i2c_device_id wis_tw2804_id[] = {
+ { "wis_tw2804", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wis_tw2804_id);
+
+static struct i2c_driver wis_tw2804_driver = {
+ .driver = {
+ .name = "WIS TW2804 I2C driver",
+ },
+ .probe = wis_tw2804_probe,
+ .remove = wis_tw2804_remove,
+ .command = wis_tw2804_command,
+ .id_table = wis_tw2804_id,
+};
+
+static int __init wis_tw2804_init(void)
+{
+ return i2c_add_driver(&wis_tw2804_driver);
+}
+
+static void __exit wis_tw2804_cleanup(void)
+{
+ i2c_del_driver(&wis_tw2804_driver);
+}
+
+module_init(wis_tw2804_init);
+module_exit(wis_tw2804_cleanup);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+
+#include "wis-i2c.h"
+
+struct wis_tw9903 {
+ int norm;
+ int brightness;
+ int contrast;
+ int hue;
+};
+
+static u8 initial_registers[] =
+{
+ 0x02, 0x44, /* input 1, composite */
+ 0x03, 0x92, /* correct digital format */
+ 0x04, 0x00,
+ 0x05, 0x80, /* or 0x00 for PAL */
+ 0x06, 0x40, /* second internal current reference */
+ 0x07, 0x02, /* window */
+ 0x08, 0x14, /* window */
+ 0x09, 0xf0, /* window */
+ 0x0a, 0x81, /* window */
+ 0x0b, 0xd0, /* window */
+ 0x0c, 0x8c,
+ 0x0d, 0x00, /* scaling */
+ 0x0e, 0x11, /* scaling */
+ 0x0f, 0x00, /* scaling */
+ 0x10, 0x00, /* brightness */
+ 0x11, 0x60, /* contrast */
+ 0x12, 0x01, /* sharpness */
+ 0x13, 0x7f, /* U gain */
+ 0x14, 0x5a, /* V gain */
+ 0x15, 0x00, /* hue */
+ 0x16, 0xc3, /* sharpness */
+ 0x18, 0x00,
+ 0x19, 0x58, /* vbi */
+ 0x1a, 0x80,
+ 0x1c, 0x0f, /* video norm */
+ 0x1d, 0x7f, /* video norm */
+ 0x20, 0xa0, /* clamping gain (working 0x50) */
+ 0x21, 0x22,
+ 0x22, 0xf0,
+ 0x23, 0xfe,
+ 0x24, 0x3c,
+ 0x25, 0x38,
+ 0x26, 0x44,
+ 0x27, 0x20,
+ 0x28, 0x00,
+ 0x29, 0x15,
+ 0x2a, 0xa0,
+ 0x2b, 0x44,
+ 0x2c, 0x37,
+ 0x2d, 0x00,
+ 0x2e, 0xa5, /* burst PLL control (working: a9) */
+ 0x2f, 0xe0, /* 0xea is blue test frame -- 0xe0 for normal */
+ 0x31, 0x00,
+ 0x33, 0x22,
+ 0x34, 0x11,
+ 0x35, 0x35,
+ 0x3b, 0x05,
+ 0x06, 0xc0, /* reset device */
+ 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
+};
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+ int i;
+
+ for (i = 0; regs[i] != 0x00; i += 2)
+ if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
+ return -1;
+ return 0;
+}
+
+static int wis_tw9903_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
+{
+ struct wis_tw9903 *dec = i2c_get_clientdata(client);
+
+ switch (cmd) {
+ case VIDIOC_S_INPUT:
+ {
+ int *input = arg;
+
+ i2c_smbus_write_byte_data(client, 0x02, 0x40 | (*input << 1));
+ break;
+ }
+#if 0
+ /* The scaler on this thing seems to be horribly broken */
+ case DECODER_SET_RESOLUTION:
+ {
+ struct video_decoder_resolution *res = arg;
+ /*int hscale = 256 * 720 / res->width;*/
+ int hscale = 256 * 720 / (res->width - (res->width > 704 ? 0 : 8));
+ int vscale = 256 * (dec->norm & V4L2_STD_NTSC ? 240 : 288)
+ / res->height;
+ u8 regs[] = {
+ 0x0d, vscale & 0xff,
+ 0x0f, hscale & 0xff,
+ 0x0e, ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8),
+ 0x06, 0xc0, /* reset device */
+ 0, 0,
+ };
+ printk(KERN_DEBUG "vscale is %04x, hscale is %04x\n",
+ vscale, hscale);
+ /*write_regs(client, regs);*/
+ break;
+ }
+#endif
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *input = arg;
+ u8 regs[] = {
+ 0x05, *input & V4L2_STD_NTSC ? 0x80 : 0x00,
+ 0x07, *input & V4L2_STD_NTSC ? 0x02 : 0x12,
+ 0x08, *input & V4L2_STD_NTSC ? 0x14 : 0x18,
+ 0x09, *input & V4L2_STD_NTSC ? 0xf0 : 0x20,
+ 0, 0,
+ };
+ write_regs(client, regs);
+ dec->norm = *input;
+ break;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
+ ctrl->minimum = -128;
+ ctrl->maximum = 127;
+ ctrl->step = 1;
+ ctrl->default_value = 0x00;
+ ctrl->flags = 0;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 255;
+ ctrl->step = 1;
+ ctrl->default_value = 0x60;
+ ctrl->flags = 0;
+ break;
+#if 0
+ /* I don't understand how the Chroma Gain registers work... */
+ case V4L2_CID_SATURATION:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
+ ctrl->minimum = 0;
+ ctrl->maximum = 127;
+ ctrl->step = 1;
+ ctrl->default_value = 64;
+ ctrl->flags = 0;
+ break;
+#endif
+ case V4L2_CID_HUE:
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
+ ctrl->minimum = -128;
+ ctrl->maximum = 127;
+ ctrl->step = 1;
+ ctrl->default_value = 0;
+ ctrl->flags = 0;
+ break;
+ }
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value > 127)
+ dec->brightness = 127;
+ else if (ctrl->value < -128)
+ dec->brightness = -128;
+ else
+ dec->brightness = ctrl->value;
+ write_reg(client, 0x10, dec->brightness);
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value > 255)
+ dec->contrast = 255;
+ else if (ctrl->value < 0)
+ dec->contrast = 0;
+ else
+ dec->contrast = ctrl->value;
+ write_reg(client, 0x11, dec->contrast);
+ break;
+#if 0
+ case V4L2_CID_SATURATION:
+ if (ctrl->value > 127)
+ dec->saturation = 127;
+ else if (ctrl->value < 0)
+ dec->saturation = 0;
+ else
+ dec->saturation = ctrl->value;
+ /*write_reg(client, 0x0c, dec->saturation);*/
+ break;
+#endif
+ case V4L2_CID_HUE:
+ if (ctrl->value > 127)
+ dec->hue = 127;
+ else if (ctrl->value < -128)
+ dec->hue = -128;
+ else
+ dec->hue = ctrl->value;
+ write_reg(client, 0x15, dec->hue);
+ break;
+ }
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = dec->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = dec->contrast;
+ break;
+#if 0
+ case V4L2_CID_SATURATION:
+ ctrl->value = dec->saturation;
+ break;
+#endif
+ case V4L2_CID_HUE:
+ ctrl->value = dec->hue;
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wis_tw9903_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct wis_tw9903 *dec;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ dec = kmalloc(sizeof(struct wis_tw9903), GFP_KERNEL);
+ if (dec == NULL)
+ return -ENOMEM;
+
+ dec->norm = V4L2_STD_NTSC;
+ dec->brightness = 0;
+ dec->contrast = 0x60;
+ dec->hue = 0;
+ i2c_set_clientdata(client, dec);
+
+ printk(KERN_DEBUG
+ "wis-tw9903: initializing TW9903 at address %d on %s\n",
+ client->addr, adapter->name);
+
+ if (write_regs(client, initial_registers) < 0) {
+ printk(KERN_ERR "wis-tw9903: error initializing TW9903\n");
+ kfree(dec);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int wis_tw9903_remove(struct i2c_client *client)
+{
+ struct wis_tw9903 *dec = i2c_get_clientdata(client);
+
+ kfree(dec);
+ return 0;
+}
+
+static const struct i2c_device_id wis_tw9903_id[] = {
+ { "wis_tw9903", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wis_tw9903_id);
+
+static struct i2c_driver wis_tw9903_driver = {
+ .driver = {
+ .name = "WIS TW9903 I2C driver",
+ },
+ .probe = wis_tw9903_probe,
+ .remove = wis_tw9903_remove,
+ .command = wis_tw9903_command,
+ .id_table = wis_tw9903_id,
+};
+
+static int __init wis_tw9903_init(void)
+{
+ return i2c_add_driver(&wis_tw9903_driver);
+}
+
+static void __exit wis_tw9903_cleanup(void)
+{
+ i2c_del_driver(&wis_tw9903_driver);
+}
+
+module_init(wis_tw9903_init);
+module_exit(wis_tw9903_cleanup);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/tvaudio.h>
+#include <media/v4l2-common.h>
+
+#include "wis-i2c.h"
+
+static int write_reg(struct i2c_client *client, int reg, int value)
+{
+ /* UDA1342 wants MSB first, but SMBus sends LSB first */
+ i2c_smbus_write_word_data(client, reg, swab16(value));
+ return 0;
+}
+
+static int wis_uda1342_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case VIDIOC_S_AUDIO:
+ {
+ int *inp = arg;
+
+ switch (*inp) {
+ case TVAUDIO_INPUT_TUNER:
+ write_reg(client, 0x00, 0x1441); /* select input 2 */
+ break;
+ case TVAUDIO_INPUT_EXTERN:
+ write_reg(client, 0x00, 0x1241); /* select input 1 */
+ break;
+ default:
+ printk(KERN_ERR "wis-uda1342: input %d not supported\n",
+ *inp);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wis_uda1342_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ printk(KERN_DEBUG
+ "wis-uda1342: initializing UDA1342 at address %d on %s\n",
+ client->addr, adapter->name);
+
+ write_reg(client, 0x00, 0x8000); /* reset registers */
+ write_reg(client, 0x00, 0x1241); /* select input 1 */
+
+ return 0;
+}
+
+static int wis_uda1342_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id wis_uda1342_id[] = {
+ { "wis_uda1342", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wis_uda1342_id);
+
+static struct i2c_driver wis_uda1342_driver = {
+ .driver = {
+ .name = "WIS UDA1342 I2C driver",
+ },
+ .probe = wis_uda1342_probe,
+ .remove = wis_uda1342_remove,
+ .command = wis_uda1342_command,
+ .id_table = wis_uda1342_id,
+};
+
+static int __init wis_uda1342_init(void)
+{
+ return i2c_add_driver(&wis_uda1342_driver);
+}
+
+static void __exit wis_uda1342_cleanup(void)
+{
+ i2c_del_driver(&wis_uda1342_driver);
+}
+
+module_init(wis_uda1342_init);
+module_exit(wis_uda1342_cleanup);
+
+MODULE_LICENSE("GPL v2");
--- /dev/null
+#
+# LIRC driver(s) configuration
+#
+menuconfig LIRC_STAGING
+ bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
+ depends on LIRC
+ help
+ Say Y here, and all supported Linux Infrared Remote Control IR and
+ RF receiver and transmitter drivers will be displayed. When paired
+ with a remote control and the lirc daemon, the receiver drivers
+ allow control of your Linux system via remote control.
+
+if LIRC_STAGING
+
+config LIRC_BT829
+ tristate "BT829 based hardware"
+ depends on LIRC && PCI
+ help
+ Driver for the IR interface on BT829-based hardware
+
+config LIRC_IGORPLUGUSB
+ tristate "Igor Cesko's USB IR Receiver"
+ depends on LIRC && USB
+ help
+ Driver for Igor Cesko's USB IR Receiver
+
+config LIRC_IMON
+ tristate "Legacy SoundGraph iMON Receiver and Display"
+ depends on LIRC && USB
+ help
+ Driver for the original SoundGraph iMON IR Receiver and Display
+
+ Current generation iMON devices use the input layer imon driver.
+
+config LIRC_PARALLEL
+ tristate "Homebrew Parallel Port Receiver"
+ depends on LIRC && PARPORT
+ help
+ Driver for Homebrew Parallel Port Receivers
+
+config LIRC_SASEM
+ tristate "Sasem USB IR Remote"
+ depends on LIRC && USB
+ help
+ Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
+
+config LIRC_SERIAL
+ tristate "Homebrew Serial Port Receiver"
+ depends on LIRC
+ help
+ Driver for Homebrew Serial Port Receivers
+
+config LIRC_SERIAL_TRANSMITTER
+ bool "Serial Port Transmitter"
+ default y
+ depends on LIRC_SERIAL
+ help
+ Serial Port Transmitter support
+
+config LIRC_SIR
+ tristate "Built-in SIR IrDA port"
+ depends on LIRC
+ help
+ Driver for the SIR IrDA port
+
+config LIRC_TTUSBIR
+ tristate "Technotrend USB IR Receiver"
+ depends on LIRC && USB
+ help
+ Driver for the Technotrend USB IR Receiver
+
+config LIRC_ZILOG
+ tristate "Zilog/Hauppauge IR Transmitter"
+ depends on LIRC && I2C
+ help
+ Driver for the Zilog/Hauppauge IR Transmitter, found on
+ PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
+endif
--- /dev/null
+# Makefile for the lirc drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
+obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
+obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
+obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
+obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
+obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
+obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
+obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o
+obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o
--- /dev/null
+- All drivers should either be ported to ir-core, or dropped entirely
+ (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an
+ example of a previously completed port).
+
+Please send patches to:
+Jarod Wilson <jarod@wilsonet.com>
+Greg Kroah-Hartman <greg@kroah.com>
+
--- /dev/null
+1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for
+the chips supported by lirc_zilog. Before moving lirc_zilog out of staging:
+
+a. ir-kbd-i2c needs a module parameter added to allow the user to tell
+ ir-kbd-i2c to ignore Z8 IR units.
+
+b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c
+ does.
+
+
+2. lirc_zilog module ref-counting need examination. It has not been
+verified that cdev and lirc_dev will take the proper module references on
+lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node
+is open.
+
+(The good news is ref-counting of lirc_zilog internal structures appears to be
+complete. Testing has shown the cx18 module can be unloaded out from under
+irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse
+effects. The cx18 module could then be reloaded and irw properly began
+receiving button presses again and ir_send worked without error.)
+
+
+3. Bridge drivers, if able, should provide a chip reset() callback
+to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines
+to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog
+to bring the chip back to normal when it hangs, in the same places the
+original lirc_pvr150 driver code does. This is not strictly needed, so it
+is not required to move lirc_zilog out of staging.
+
+Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed
+and installed on Hauppauge products. When working on either module, developers
+must consider at least the following bridge drivers which mention an IR Rx unit
+at address 0x71 (indicative of a Z8):
+
+ ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134
+
--- /dev/null
+/*
+ * Remote control driver for the TV-card based on bt829
+ *
+ * by Leonid Froenchenko <lfroen@galileo.co.il>
+ *
+ * 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/threads.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include <media/lirc_dev.h>
+
+static int poll_main(void);
+static int atir_init_start(void);
+
+static void write_index(unsigned char index, unsigned int value);
+static unsigned int read_index(unsigned char index);
+
+static void do_i2c_start(void);
+static void do_i2c_stop(void);
+
+static void seems_wr_byte(unsigned char al);
+static unsigned char seems_rd_byte(void);
+
+static unsigned int read_index(unsigned char al);
+static void write_index(unsigned char ah, unsigned int edx);
+
+static void cycle_delay(int cycle);
+
+static void do_set_bits(unsigned char bl);
+static unsigned char do_get_bits(void);
+
+#define DATA_PCI_OFF 0x7FFC00
+#define WAIT_CYCLE 20
+
+#define DRIVER_NAME "lirc_bt829"
+
+static int debug;
+#define dprintk(fmt, args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG DRIVER_NAME ": "fmt, ## args); \
+ } while (0)
+
+static int atir_minor;
+static unsigned long pci_addr_phys;
+static unsigned char *pci_addr_lin;
+
+static struct lirc_driver atir_driver;
+
+static struct pci_dev *do_pci_probe(void)
+{
+ struct pci_dev *my_dev;
+ my_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+ PCI_DEVICE_ID_ATI_264VT, NULL);
+ if (my_dev) {
+ printk(KERN_ERR DRIVER_NAME ": Using device: %s\n",
+ pci_name(my_dev));
+ pci_addr_phys = 0;
+ if (my_dev->resource[0].flags & IORESOURCE_MEM) {
+ pci_addr_phys = my_dev->resource[0].start;
+ printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X\n",
+ (unsigned int)pci_addr_phys);
+ }
+ if (pci_addr_phys == 0) {
+ printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n");
+ return NULL;
+ }
+ } else {
+ printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n");
+ return NULL;
+ }
+ return my_dev;
+}
+
+static int atir_add_to_buf(void *data, struct lirc_buffer *buf)
+{
+ unsigned char key;
+ int status;
+ status = poll_main();
+ key = (status >> 8) & 0xFF;
+ if (status & 0xFF) {
+ dprintk("reading key %02X\n", key);
+ lirc_buffer_write(buf, &key);
+ return 0;
+ }
+ return -ENODATA;
+}
+
+static int atir_set_use_inc(void *data)
+{
+ dprintk("driver is opened\n");
+ return 0;
+}
+
+static void atir_set_use_dec(void *data)
+{
+ dprintk("driver is closed\n");
+}
+
+int init_module(void)
+{
+ struct pci_dev *pdev;
+
+ pdev = do_pci_probe();
+ if (pdev == NULL)
+ return -ENODEV;
+
+ if (!atir_init_start())
+ return -ENODEV;
+
+ strcpy(atir_driver.name, "ATIR");
+ atir_driver.minor = -1;
+ atir_driver.code_length = 8;
+ atir_driver.sample_rate = 10;
+ atir_driver.data = 0;
+ atir_driver.add_to_buf = atir_add_to_buf;
+ atir_driver.set_use_inc = atir_set_use_inc;
+ atir_driver.set_use_dec = atir_set_use_dec;
+ atir_driver.dev = &pdev->dev;
+ atir_driver.owner = THIS_MODULE;
+
+ atir_minor = lirc_register_driver(&atir_driver);
+ if (atir_minor < 0) {
+ printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n");
+ return atir_minor;
+ }
+ dprintk("driver is registered on minor %d\n", atir_minor);
+
+ return 0;
+}
+
+
+void cleanup_module(void)
+{
+ lirc_unregister_driver(atir_minor);
+}
+
+
+static int atir_init_start(void)
+{
+ pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400);
+ if (pci_addr_lin == 0) {
+ printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n");
+ return 0;
+ }
+ return 1;
+}
+
+static void cycle_delay(int cycle)
+{
+ udelay(WAIT_CYCLE*cycle);
+}
+
+
+static int poll_main()
+{
+ unsigned char status_high, status_low;
+
+ do_i2c_start();
+
+ seems_wr_byte(0xAA);
+ seems_wr_byte(0x01);
+
+ do_i2c_start();
+
+ seems_wr_byte(0xAB);
+
+ status_low = seems_rd_byte();
+ status_high = seems_rd_byte();
+
+ do_i2c_stop();
+
+ return (status_high << 8) | status_low;
+}
+
+static void do_i2c_start(void)
+{
+ do_set_bits(3);
+ cycle_delay(4);
+
+ do_set_bits(1);
+ cycle_delay(7);
+
+ do_set_bits(0);
+ cycle_delay(2);
+}
+
+static void do_i2c_stop(void)
+{
+ unsigned char bits;
+ bits = do_get_bits() & 0xFD;
+ do_set_bits(bits);
+ cycle_delay(1);
+
+ bits |= 1;
+ do_set_bits(bits);
+ cycle_delay(2);
+
+ bits |= 2;
+ do_set_bits(bits);
+ bits = 3;
+ do_set_bits(bits);
+ cycle_delay(2);
+}
+
+static void seems_wr_byte(unsigned char value)
+{
+ int i;
+ unsigned char reg;
+
+ reg = do_get_bits();
+ for (i = 0; i < 8; i++) {
+ if (value & 0x80)
+ reg |= 0x02;
+ else
+ reg &= 0xFD;
+
+ do_set_bits(reg);
+ cycle_delay(1);
+
+ reg |= 1;
+ do_set_bits(reg);
+ cycle_delay(1);
+
+ reg &= 0xFE;
+ do_set_bits(reg);
+ cycle_delay(1);
+ value <<= 1;
+ }
+ cycle_delay(2);
+
+ reg |= 2;
+ do_set_bits(reg);
+
+ reg |= 1;
+ do_set_bits(reg);
+
+ cycle_delay(1);
+ do_get_bits();
+
+ reg &= 0xFE;
+ do_set_bits(reg);
+ cycle_delay(3);
+}
+
+static unsigned char seems_rd_byte(void)
+{
+ int i;
+ int rd_byte;
+ unsigned char bits_2, bits_1;
+
+ bits_1 = do_get_bits() | 2;
+ do_set_bits(bits_1);
+
+ rd_byte = 0;
+ for (i = 0; i < 8; i++) {
+ bits_1 &= 0xFE;
+ do_set_bits(bits_1);
+ cycle_delay(2);
+
+ bits_1 |= 1;
+ do_set_bits(bits_1);
+ cycle_delay(1);
+
+ bits_2 = do_get_bits();
+ if (bits_2 & 2)
+ rd_byte |= 1;
+
+ rd_byte <<= 1;
+ }
+
+ bits_1 = 0;
+ if (bits_2 == 0)
+ bits_1 |= 2;
+
+ do_set_bits(bits_1);
+ cycle_delay(2);
+
+ bits_1 |= 1;
+ do_set_bits(bits_1);
+ cycle_delay(3);
+
+ bits_1 &= 0xFE;
+ do_set_bits(bits_1);
+ cycle_delay(2);
+
+ rd_byte >>= 1;
+ rd_byte &= 0xFF;
+ return rd_byte;
+}
+
+static void do_set_bits(unsigned char new_bits)
+{
+ int reg_val;
+ reg_val = read_index(0x34);
+ if (new_bits & 2) {
+ reg_val &= 0xFFFFFFDF;
+ reg_val |= 1;
+ } else {
+ reg_val &= 0xFFFFFFFE;
+ reg_val |= 0x20;
+ }
+ reg_val |= 0x10;
+ write_index(0x34, reg_val);
+
+ reg_val = read_index(0x31);
+ if (new_bits & 1)
+ reg_val |= 0x1000000;
+ else
+ reg_val &= 0xFEFFFFFF;
+
+ reg_val |= 0x8000000;
+ write_index(0x31, reg_val);
+}
+
+static unsigned char do_get_bits(void)
+{
+ unsigned char bits;
+ int reg_val;
+
+ reg_val = read_index(0x34);
+ reg_val |= 0x10;
+ reg_val &= 0xFFFFFFDF;
+ write_index(0x34, reg_val);
+
+ reg_val = read_index(0x34);
+ bits = 0;
+ if (reg_val & 8)
+ bits |= 2;
+ else
+ bits &= 0xFD;
+
+ reg_val = read_index(0x31);
+ if (reg_val & 0x1000000)
+ bits |= 1;
+ else
+ bits &= 0xFE;
+
+ return bits;
+}
+
+static unsigned int read_index(unsigned char index)
+{
+ unsigned char *addr;
+ unsigned int value;
+ /* addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */
+ addr = pci_addr_lin + ((index & 0xFF) << 2);
+ value = readl(addr);
+ return value;
+}
+
+static void write_index(unsigned char index, unsigned int reg_val)
+{
+ unsigned char *addr;
+ addr = pci_addr_lin + ((index & 0xFF) << 2);
+ writel(reg_val, addr);
+}
+
+MODULE_AUTHOR("Froenchenko Leonid");
+MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
--- /dev/null
+/*
+ * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
+ *
+ * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
+ *
+ * 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 <media/lirc.h>
+#include <media/lirc_dev.h>
+
+/* hardware address */
+#define ENE_STATUS 0 /* hardware status - unused */
+#define ENE_ADDR_HI 1 /* hi byte of register address */
+#define ENE_ADDR_LO 2 /* low byte of register address */
+#define ENE_IO 3 /* read/write window */
+#define ENE_MAX_IO 4
+
+/* 8 bytes of samples, divided in 2 halfs*/
+#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */
+#define ENE_SAMPLE_SPC_MASK (1 << 7) /* sample is space */
+#define ENE_SAMPLE_VALUE_MASK 0x7F
+#define ENE_SAMPLE_OVERFLOW 0x7F
+#define ENE_SAMPLES_SIZE 4
+
+/* fan input sample buffer */
+#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */
+ /* each sample of normal buffer */
+
+#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */
+ /* if set, says that sample is pulse */
+#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */
+
+/* first firmware register */
+#define ENE_FW1 0xF8F8
+#define ENE_FW1_ENABLE (1 << 0) /* enable fw processing */
+#define ENE_FW1_TXIRQ (1 << 1) /* TX interrupt pending */
+#define ENE_FW1_WAKE (1 << 6) /* enable wake from S3 */
+#define ENE_FW1_IRQ (1 << 7) /* enable interrupt */
+
+/* second firmware register */
+#define ENE_FW2 0xF8F9
+#define ENE_FW2_BUF_HIGH (1 << 0) /* which half of the buffer to read */
+#define ENE_FW2_IRQ_CLR (1 << 2) /* clear this on IRQ */
+#define ENE_FW2_GP40_AS_LEARN (1 << 4) /* normal input is used as */
+ /* learning input */
+#define ENE_FW2_FAN_AS_NRML_IN (1 << 6) /* fan is used as normal input */
+#define ENE_FW2_LEARNING (1 << 7) /* hardware supports learning and TX */
+
+/* fan as input settings - only if learning capable */
+#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */
+#define ENE_FAN_AS_IN1_EN 0xCD
+#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */
+#define ENE_FAN_AS_IN2_EN 0x03
+#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */
+
+/* IRQ registers block (for revision B) */
+#define ENEB_IRQ 0xFD09 /* IRQ number */
+#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */
+#define ENEB_IRQ_STATUS 0xFD80 /* irq status */
+#define ENEB_IRQ_STATUS_IR (1 << 5) /* IR irq */
+
+/* IRQ registers block (for revision C,D) */
+#define ENEC_IRQ 0xFE9B /* new irq settings register */
+#define ENEC_IRQ_MASK 0x0F /* irq number mask */
+#define ENEC_IRQ_UNK_EN (1 << 4) /* always enabled */
+#define ENEC_IRQ_STATUS (1 << 5) /* irq status and ACK */
+
+/* CIR block settings */
+#define ENE_CIR_CONF1 0xFEC0
+#define ENE_CIR_CONF1_ADC_ON 0x7 /* receiver on gpio40 enabled */
+#define ENE_CIR_CONF1_LEARN1 (1 << 3) /* enabled on learning mode */
+#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */
+#define ENE_CIR_CONF1_TX_CARR (1 << 7) /* send TX carrier or not */
+
+#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */
+#define ENE_CIR_CONF2_LEARN2 (1 << 4) /* set on enable learning */
+#define ENE_CIR_CONF2_GPIO40DIS (1 << 5) /* disable normal input via gpio40 */
+
+#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */
+#define ENE_CIR_SAMPLE_OVERFLOW (1 << 7) /* interrupt on overflows if set */
+
+
+/* transmitter - not implemented yet */
+/* KB3926C and higher */
+/* transmission is very similar to receiving, a byte is written to */
+/* ENE_TX_INPUT, in same manner as it is read from sample buffer */
+/* sample period is fixed*/
+
+
+/* transmitter ports */
+#define ENE_TX_PORT1 0xFC01 /* this enables one or both */
+#define ENE_TX_PORT1_EN (1 << 5) /* TX ports */
+#define ENE_TX_PORT2 0xFC08
+#define ENE_TX_PORT2_EN (1 << 1)
+
+#define ENE_TX_INPUT 0xFEC9 /* next byte to transmit */
+#define ENE_TX_SPC_MASK (1 << 7) /* Transmitted sample is space */
+#define ENE_TX_UNK1 0xFECB /* set to 0x63 */
+#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period */
+
+
+#define ENE_TX_CARRIER 0xFECE /* TX carrier * 2 (khz) */
+#define ENE_TX_CARRIER_UNKBIT 0x80 /* This bit set on transmit */
+#define ENE_TX_CARRIER_LOW 0xFECF /* TX carrier / 2 */
+
+/* Hardware versions */
+#define ENE_HW_VERSION 0xFF00 /* hardware revision */
+#define ENE_HW_UNK 0xFF1D
+#define ENE_HW_UNK_CLR (1 << 2)
+#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */
+#define ENE_HW_VER_MINOR 0xFF1F
+#define ENE_HW_VER_OLD 0xFD00
+
+#define same_sign(a, b) ((((a) > 0) && (b) > 0) || ((a) < 0 && (b) < 0))
+
+#define ENE_DRIVER_NAME "enecir"
+#define ENE_MAXGAP 250000 /* this is amount of time we wait
+ before turning the sampler, chosen
+ arbitry */
+
+#define space(len) (-(len)) /* add a space */
+
+/* software defines */
+#define ENE_IRQ_RX 1
+#define ENE_IRQ_TX 2
+
+#define ENE_HW_B 1 /* 3926B */
+#define ENE_HW_C 2 /* 3926C */
+#define ENE_HW_D 3 /* 3926D */
+
+#define ene_printk(level, text, ...) \
+ printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
+
+struct ene_device {
+ struct pnp_dev *pnp_dev;
+ struct lirc_driver *lirc_driver;
+
+ /* hw settings */
+ unsigned long hw_io;
+ int irq;
+
+ int hw_revision; /* hardware revision */
+ int hw_learning_and_tx_capable; /* learning capable */
+ int hw_gpio40_learning; /* gpio40 is learning */
+ int hw_fan_as_normal_input; /* fan input is used as regular input */
+
+ /* device data */
+ int idle;
+ int fan_input_inuse;
+
+ int sample;
+ int in_use;
+
+ struct timeval gap_start;
+};
--- /dev/null
+/*
+ * lirc_igorplugusb - USB remote support for LIRC
+ *
+ * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware.
+ * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm
+ *
+ * The device can only record bursts of up to 36 pulses/spaces.
+ * Works fine with RC5. Longer commands lead to device buffer overrun.
+ * (Maybe a better firmware or a microcontroller with more ram can help?)
+ *
+ * Version 0.1 [beta status]
+ *
+ * Copyright (C) 2004 Jan M. Hochstein
+ * <hochstein@algo.informatik.tu-darmstadt.de>
+ *
+ * This driver was derived from:
+ * Paul Miller <pmiller9@users.sourceforge.net>
+ * "lirc_atiusb" module
+ * Vladimir Dergachev <volodya@minspring.com>'s 2002
+ * "USB ATI Remote support" (input device)
+ * Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
+ * "USB StreamZap remote driver" (LIRC)
+ * Artur Lipowski <alipowski@kki.net.pl>'s 2002
+ * "lirc_dev" and "lirc_gpio" LIRC modules
+ */
+
+/*
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/usb.h>
+#include <linux/time.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+
+/* module identification */
+#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR \
+ "Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
+#define DRIVER_DESC "Igorplug USB remote driver for LIRC"
+#define DRIVER_NAME "lirc_igorplugusb"
+
+/* debugging support */
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+#define dprintk(fmt, args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG fmt, ## args); \
+ } while (0)
+
+/* One mode2 pulse/space has 4 bytes. */
+#define CODE_LENGTH sizeof(int)
+
+/* Igor's firmware cannot record bursts longer than 36. */
+#define DEVICE_BUFLEN 36
+
+/*
+ * Header at the beginning of the device's buffer:
+ * unsigned char data_length
+ * unsigned char data_start (!=0 means ring-buffer overrun)
+ * unsigned char counter (incremented by each burst)
+ */
+#define DEVICE_HEADERLEN 3
+
+/* This is for the gap */
+#define ADDITIONAL_LIRC_BYTES 2
+
+/* times to poll per second */
+#define SAMPLE_RATE 100
+static int sample_rate = SAMPLE_RATE;
+
+
+/**** Igor's USB Request Codes */
+
+#define SET_INFRABUFFER_EMPTY 1
+/**
+ * Params: none
+ * Answer: empty
+ */
+
+#define GET_INFRACODE 2
+/**
+ * Params:
+ * wValue: offset to begin reading infra buffer
+ *
+ * Answer: infra data
+ */
+
+#define SET_DATAPORT_DIRECTION 3
+/**
+ * Params:
+ * wValue: (byte) 1 bit for each data port pin (0=in, 1=out)
+ *
+ * Answer: empty
+ */
+
+#define GET_DATAPORT_DIRECTION 4
+/**
+ * Params: none
+ *
+ * Answer: (byte) 1 bit for each data port pin (0=in, 1=out)
+ */
+
+#define SET_OUT_DATAPORT 5
+/**
+ * Params:
+ * wValue: byte to write to output data port
+ *
+ * Answer: empty
+ */
+
+#define GET_OUT_DATAPORT 6
+/**
+ * Params: none
+ *
+ * Answer: least significant 3 bits read from output data port
+ */
+
+#define GET_IN_DATAPORT 7
+/**
+ * Params: none
+ *
+ * Answer: least significant 3 bits read from input data port
+ */
+
+#define READ_EEPROM 8
+/**
+ * Params:
+ * wValue: offset to begin reading EEPROM
+ *
+ * Answer: EEPROM bytes
+ */
+
+#define WRITE_EEPROM 9
+/**
+ * Params:
+ * wValue: offset to EEPROM byte
+ * wIndex: byte to write
+ *
+ * Answer: empty
+ */
+
+#define SEND_RS232 10
+/**
+ * Params:
+ * wValue: byte to send
+ *
+ * Answer: empty
+ */
+
+#define RECV_RS232 11
+/**
+ * Params: none
+ *
+ * Answer: byte received
+ */
+
+#define SET_RS232_BAUD 12
+/**
+ * Params:
+ * wValue: byte to write to UART bit rate register (UBRR)
+ *
+ * Answer: empty
+ */
+
+#define GET_RS232_BAUD 13
+/**
+ * Params: none
+ *
+ * Answer: byte read from UART bit rate register (UBRR)
+ */
+
+
+/* data structure for each usb remote */
+struct igorplug {
+
+ /* usb */
+ struct usb_device *usbdev;
+ int devnum;
+
+ unsigned char *buf_in;
+ unsigned int len_in;
+ int in_space;
+ struct timeval last_time;
+
+ dma_addr_t dma_in;
+
+ /* lirc */
+ struct lirc_driver *d;
+
+ /* handle sending (init strings) */
+ int send_flags;
+};
+
+static int unregister_from_lirc(struct igorplug *ir)
+{
+ struct lirc_driver *d;
+ int devnum;
+
+ if (!ir) {
+ printk(KERN_ERR "%s: called with NULL device struct!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ devnum = ir->devnum;
+ d = ir->d;
+
+ if (!d) {
+ printk(KERN_ERR "%s: called with NULL lirc driver struct!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ dprintk(DRIVER_NAME "[%d]: calling lirc_unregister_driver\n", devnum);
+ lirc_unregister_driver(d->minor);
+
+ kfree(d);
+ ir->d = NULL;
+ kfree(ir);
+
+ return devnum;
+}
+
+static int set_use_inc(void *data)
+{
+ struct igorplug *ir = data;
+
+ if (!ir) {
+ printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
+ return -EIO;
+ }
+
+ dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
+
+ if (!ir->usbdev)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void set_use_dec(void *data)
+{
+ struct igorplug *ir = data;
+
+ if (!ir) {
+ printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
+ return;
+ }
+
+ dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
+}
+
+static void send_fragment(struct igorplug *ir, struct lirc_buffer *buf,
+ int i, int max)
+{
+ int code;
+
+ /* MODE2: pulse/space (PULSE_BIT) in 1us units */
+ while (i < max) {
+ /* 1 Igor-tick = 85.333333 us */
+ code = (unsigned int)ir->buf_in[i] * 85 +
+ (unsigned int)ir->buf_in[i] / 3;
+ ir->last_time.tv_usec += code;
+ if (ir->in_space)
+ code |= PULSE_BIT;
+ lirc_buffer_write(buf, (unsigned char *)&code);
+ /* 1 chunk = CODE_LENGTH bytes */
+ ir->in_space ^= 1;
+ ++i;
+ }
+}
+
+/**
+ * Called in user context.
+ * return 0 if data was added to the buffer and
+ * -ENODATA if none was available. This should add some number of bits
+ * evenly divisible by code_length to the buffer
+ */
+static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf)
+{
+ int ret;
+ struct igorplug *ir = (struct igorplug *)data;
+
+ if (!ir || !ir->usbdev) /* Has the device been removed? */
+ return -ENODEV;
+
+ memset(ir->buf_in, 0, ir->len_in);
+
+ ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+ GET_INFRACODE, USB_TYPE_VENDOR | USB_DIR_IN,
+ 0/* offset */, /*unused*/0,
+ ir->buf_in, ir->len_in,
+ /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
+ if (ret > 0) {
+ int code, timediff;
+ struct timeval now;
+
+ /* ACK packet has 1 byte --> ignore */
+ if (ret < DEVICE_HEADERLEN)
+ return -ENODATA;
+
+ dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n",
+ ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
+
+ do_gettimeofday(&now);
+ timediff = now.tv_sec - ir->last_time.tv_sec;
+ if (timediff + 1 > PULSE_MASK / 1000000)
+ timediff = PULSE_MASK;
+ else {
+ timediff *= 1000000;
+ timediff += now.tv_usec - ir->last_time.tv_usec;
+ }
+ ir->last_time.tv_sec = now.tv_sec;
+ ir->last_time.tv_usec = now.tv_usec;
+
+ /* create leading gap */
+ code = timediff;
+ lirc_buffer_write(buf, (unsigned char *)&code);
+ ir->in_space = 1; /* next comes a pulse */
+
+ if (ir->buf_in[2] == 0)
+ send_fragment(ir, buf, DEVICE_HEADERLEN, ret);
+ else {
+ printk(KERN_WARNING DRIVER_NAME
+ "[%d]: Device buffer overrun.\n", ir->devnum);
+ /* HHHNNNNNNNNNNNOOOOOOOO H = header
+ <---[2]---> N = newer
+ <---------ret--------> O = older */
+ ir->buf_in[2] %= ret - DEVICE_HEADERLEN; /* sanitize */
+ /* keep even-ness to not desync pulse/pause */
+ send_fragment(ir, buf, DEVICE_HEADERLEN +
+ ir->buf_in[2] - (ir->buf_in[2] & 1), ret);
+ send_fragment(ir, buf, DEVICE_HEADERLEN,
+ DEVICE_HEADERLEN + ir->buf_in[2]);
+ }
+
+ ret = usb_control_msg(
+ ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+ SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
+ /*unused*/0, /*unused*/0,
+ /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
+ /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
+ if (ret < 0)
+ printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: "
+ "error %d\n", ir->devnum, ret);
+ return 0;
+ } else if (ret < 0)
+ printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n",
+ ir->devnum, ret);
+
+ return -ENODATA;
+}
+
+
+
+static int igorplugusb_remote_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = NULL;
+ struct usb_host_interface *idesc = NULL;
+ struct usb_endpoint_descriptor *ep;
+ struct igorplug *ir = NULL;
+ struct lirc_driver *driver = NULL;
+ int devnum, pipe, maxp;
+ int minor = 0;
+ char buf[63], name[128] = "";
+ int mem_failure = 0;
+ int ret;
+
+ dprintk(DRIVER_NAME ": usb probe called.\n");
+
+ dev = interface_to_usbdev(intf);
+
+ idesc = intf->cur_altsetting;
+
+ if (idesc->desc.bNumEndpoints != 1)
+ return -ENODEV;
+
+ ep = &idesc->endpoint->desc;
+ if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ != USB_DIR_IN)
+ || (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ != USB_ENDPOINT_XFER_CONTROL)
+ return -ENODEV;
+
+ pipe = usb_rcvctrlpipe(dev, ep->bEndpointAddress);
+ devnum = dev->devnum;
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+ dprintk(DRIVER_NAME "[%d]: bytes_in_key=%zu maxp=%d\n",
+ devnum, CODE_LENGTH, maxp);
+
+ mem_failure = 0;
+ ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL);
+ if (!ir) {
+ mem_failure = 1;
+ goto mem_failure_switch;
+ }
+ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
+ if (!driver) {
+ mem_failure = 2;
+ goto mem_failure_switch;
+ }
+
+ ir->buf_in = usb_alloc_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN,
+ GFP_ATOMIC, &ir->dma_in);
+ if (!ir->buf_in) {
+ mem_failure = 3;
+ goto mem_failure_switch;
+ }
+
+ strcpy(driver->name, DRIVER_NAME " ");
+ driver->minor = -1;
+ driver->code_length = CODE_LENGTH * 8; /* in bits */
+ driver->features = LIRC_CAN_REC_MODE2;
+ driver->data = ir;
+ driver->chunk_size = CODE_LENGTH;
+ driver->buffer_size = DEVICE_BUFLEN + ADDITIONAL_LIRC_BYTES;
+ driver->set_use_inc = &set_use_inc;
+ driver->set_use_dec = &set_use_dec;
+ driver->sample_rate = sample_rate; /* per second */
+ driver->add_to_buf = &igorplugusb_remote_poll;
+ driver->dev = &intf->dev;
+ driver->owner = THIS_MODULE;
+
+ minor = lirc_register_driver(driver);
+ if (minor < 0)
+ mem_failure = 9;
+
+mem_failure_switch:
+
+ switch (mem_failure) {
+ case 9:
+ usb_free_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN,
+ ir->buf_in, ir->dma_in);
+ case 3:
+ kfree(driver);
+ case 2:
+ kfree(ir);
+ case 1:
+ printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
+ devnum, mem_failure);
+ return -ENOMEM;
+ }
+
+ driver->minor = minor;
+ ir->d = driver;
+ ir->devnum = devnum;
+ ir->usbdev = dev;
+ ir->len_in = DEVICE_BUFLEN + DEVICE_HEADERLEN;
+ ir->in_space = 1; /* First mode2 event is a space. */
+ do_gettimeofday(&ir->last_time);
+
+ if (dev->descriptor.iManufacturer
+ && usb_string(dev, dev->descriptor.iManufacturer,
+ buf, sizeof(buf)) > 0)
+ strlcpy(name, buf, sizeof(name));
+ if (dev->descriptor.iProduct
+ && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0)
+ snprintf(name + strlen(name), sizeof(name) - strlen(name),
+ " %s", buf);
+ printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
+ dev->bus->busnum, devnum);
+
+ /* clear device buffer */
+ ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+ SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
+ /*unused*/0, /*unused*/0,
+ /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
+ /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
+ if (ret < 0)
+ printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n",
+ devnum, ret);
+
+ usb_set_intfdata(intf, ir);
+ return 0;
+}
+
+
+static void igorplugusb_remote_disconnect(struct usb_interface *intf)
+{
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct igorplug *ir = usb_get_intfdata(intf);
+ struct device *dev = &intf->dev;
+ int devnum;
+
+ usb_set_intfdata(intf, NULL);
+
+ if (!ir || !ir->d)
+ return;
+
+ ir->usbdev = NULL;
+
+ usb_free_coherent(usbdev, ir->len_in, ir->buf_in, ir->dma_in);
+
+ devnum = unregister_from_lirc(ir);
+
+ dev_info(dev, DRIVER_NAME "[%d]: %s done\n", devnum, __func__);
+}
+
+static struct usb_device_id igorplugusb_remote_id_table[] = {
+ /* Igor Plug USB (Atmel's Manufact. ID) */
+ { USB_DEVICE(0x03eb, 0x0002) },
+ /* Fit PC2 Infrared Adapter */
+ { USB_DEVICE(0x03eb, 0x21fe) },
+
+ /* Terminating entry */
+ { }
+};
+
+static struct usb_driver igorplugusb_remote_driver = {
+ .name = DRIVER_NAME,
+ .probe = igorplugusb_remote_probe,
+ .disconnect = igorplugusb_remote_disconnect,
+ .id_table = igorplugusb_remote_id_table
+};
+
+static int __init igorplugusb_remote_init(void)
+{
+ int ret = 0;
+
+ dprintk(DRIVER_NAME ": loaded, debug mode enabled\n");
+
+ ret = usb_register(&igorplugusb_remote_driver);
+ if (ret)
+ printk(KERN_ERR DRIVER_NAME ": usb register failed!\n");
+
+ return ret;
+}
+
+static void __exit igorplugusb_remote_exit(void)
+{
+ usb_deregister(&igorplugusb_remote_driver);
+}
+
+module_init(igorplugusb_remote_init);
+module_exit(igorplugusb_remote_exit);
+
+#include <linux/vermagic.h>
+MODULE_INFO(vermagic, VERMAGIC_STRING);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, igorplugusb_remote_id_table);
+
+module_param(sample_rate, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
--- /dev/null
+/*
+ * lirc_imon.c: LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD
+ * including the iMON PAD model
+ *
+ * Copyright(C) 2004 Venky Raju(dev@venky.ws)
+ * Copyright(C) 2009 Jarod Wilson <jarod@wilsonet.com>
+ *
+ * lirc_imon 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+
+#define MOD_AUTHOR "Venky Raju <dev@venky.ws>"
+#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display"
+#define MOD_NAME "lirc_imon"
+#define MOD_VERSION "0.8"
+
+#define DISPLAY_MINOR_BASE 144
+#define DEVICE_NAME "lcd%d"
+
+#define BUF_CHUNK_SIZE 4
+#define BUF_SIZE 128
+
+#define BIT_DURATION 250 /* each bit received is 250us */
+
+/*** P R O T O T Y P E S ***/
+
+/* USB Callback prototypes */
+static int imon_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void imon_disconnect(struct usb_interface *interface);
+static void usb_rx_callback(struct urb *urb);
+static void usb_tx_callback(struct urb *urb);
+
+/* suspend/resume support */
+static int imon_resume(struct usb_interface *intf);
+static int imon_suspend(struct usb_interface *intf, pm_message_t message);
+
+/* Display file_operations function prototypes */
+static int display_open(struct inode *inode, struct file *file);
+static int display_close(struct inode *inode, struct file *file);
+
+/* VFD write operation */
+static ssize_t vfd_write(struct file *file, const char *buf,
+ size_t n_bytes, loff_t *pos);
+
+/* LIRC driver function prototypes */
+static int ir_open(void *data);
+static void ir_close(void *data);
+
+/* Driver init/exit prototypes */
+static int __init imon_init(void);
+static void __exit imon_exit(void);
+
+/*** G L O B A L S ***/
+#define IMON_DATA_BUF_SZ 35
+
+struct imon_context {
+ struct usb_device *usbdev;
+ /* Newer devices have two interfaces */
+ int display; /* not all controllers do */
+ int display_isopen; /* display port has been opened */
+ int ir_isopen; /* IR port open */
+ int dev_present; /* USB device presence */
+ struct mutex ctx_lock; /* to lock this object */
+ wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
+
+ int vfd_proto_6p; /* some VFD require a 6th packet */
+
+ struct lirc_driver *driver;
+ struct usb_endpoint_descriptor *rx_endpoint;
+ struct usb_endpoint_descriptor *tx_endpoint;
+ struct urb *rx_urb;
+ struct urb *tx_urb;
+ unsigned char usb_rx_buf[8];
+ unsigned char usb_tx_buf[8];
+
+ struct rx_data {
+ int count; /* length of 0 or 1 sequence */
+ int prev_bit; /* logic level of sequence */
+ int initial_space; /* initial space flag */
+ } rx;
+
+ struct tx_t {
+ unsigned char data_buf[IMON_DATA_BUF_SZ]; /* user data buffer */
+ struct completion finished; /* wait for write to finish */
+ atomic_t busy; /* write in progress */
+ int status; /* status of tx completion */
+ } tx;
+};
+
+static const struct file_operations display_fops = {
+ .owner = THIS_MODULE,
+ .open = &display_open,
+ .write = &vfd_write,
+ .release = &display_close,
+ .llseek = noop_llseek,
+};
+
+/*
+ * USB Device ID for iMON USB Control Boards
+ *
+ * The Windows drivers contain 6 different inf files, more or less one for
+ * each new device until the 0x0034-0x0046 devices, which all use the same
+ * driver. Some of the devices in the 34-46 range haven't been definitively
+ * identified yet. Early devices have either a TriGem Computer, Inc. or a
+ * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later
+ * devices use the SoundGraph vendor ID (0x15c2).
+ */
+static struct usb_device_id imon_usb_id_table[] = {
+ /* TriGem iMON (IR only) -- TG_iMON.inf */
+ { USB_DEVICE(0x0aa8, 0x8001) },
+
+ /* SoundGraph iMON (IR only) -- sg_imon.inf */
+ { USB_DEVICE(0x04e8, 0xff30) },
+
+ /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */
+ { USB_DEVICE(0x0aa8, 0xffda) },
+
+ /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */
+ { USB_DEVICE(0x15c2, 0xffda) },
+
+ {}
+};
+
+/* Some iMON VFD models requires a 6th packet for VFD writes */
+static struct usb_device_id vfd_proto_6p_list[] = {
+ { USB_DEVICE(0x15c2, 0xffda) },
+ {}
+};
+
+/* Some iMON devices have no lcd/vfd, don't set one up */
+static struct usb_device_id ir_only_list[] = {
+ { USB_DEVICE(0x0aa8, 0x8001) },
+ { USB_DEVICE(0x04e8, 0xff30) },
+ {}
+};
+
+/* USB Device data */
+static struct usb_driver imon_driver = {
+ .name = MOD_NAME,
+ .probe = imon_probe,
+ .disconnect = imon_disconnect,
+ .suspend = imon_suspend,
+ .resume = imon_resume,
+ .id_table = imon_usb_id_table,
+};
+
+static struct usb_class_driver imon_class = {
+ .name = DEVICE_NAME,
+ .fops = &display_fops,
+ .minor_base = DISPLAY_MINOR_BASE,
+};
+
+/* to prevent races between open() and disconnect(), probing, etc */
+static DEFINE_MUTEX(driver_lock);
+
+static int debug;
+
+/*** M O D U L E C O D E ***/
+
+MODULE_AUTHOR(MOD_AUTHOR);
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_VERSION(MOD_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
+
+static void free_imon_context(struct imon_context *context)
+{
+ struct device *dev = context->driver->dev;
+ usb_free_urb(context->tx_urb);
+ usb_free_urb(context->rx_urb);
+ lirc_buffer_free(context->driver->rbuf);
+ kfree(context->driver->rbuf);
+ kfree(context->driver);
+ kfree(context);
+
+ dev_dbg(dev, "%s: iMON context freed\n", __func__);
+}
+
+static void deregister_from_lirc(struct imon_context *context)
+{
+ int retval;
+ int minor = context->driver->minor;
+
+ retval = lirc_unregister_driver(minor);
+ if (retval)
+ err("%s: unable to deregister from lirc(%d)",
+ __func__, retval);
+ else
+ printk(KERN_INFO MOD_NAME ": Deregistered iMON driver "
+ "(minor:%d)\n", minor);
+
+}
+
+/**
+ * Called when the Display device (e.g. /dev/lcd0)
+ * is opened by the application.
+ */
+static int display_open(struct inode *inode, struct file *file)
+{
+ struct usb_interface *interface;
+ struct imon_context *context = NULL;
+ int subminor;
+ int retval = 0;
+
+ /* prevent races with disconnect */
+ mutex_lock(&driver_lock);
+
+ subminor = iminor(inode);
+ interface = usb_find_interface(&imon_driver, subminor);
+ if (!interface) {
+ err("%s: could not find interface for minor %d",
+ __func__, subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+ context = usb_get_intfdata(interface);
+
+ if (!context) {
+ err("%s: no context found for minor %d",
+ __func__, subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (!context->display) {
+ err("%s: display not supported by device", __func__);
+ retval = -ENODEV;
+ } else if (context->display_isopen) {
+ err("%s: display port is already open", __func__);
+ retval = -EBUSY;
+ } else {
+ context->display_isopen = 1;
+ file->private_data = context;
+ dev_info(context->driver->dev, "display port opened\n");
+ }
+
+ mutex_unlock(&context->ctx_lock);
+
+exit:
+ mutex_unlock(&driver_lock);
+ return retval;
+}
+
+/**
+ * Called when the display device (e.g. /dev/lcd0)
+ * is closed by the application.
+ */
+static int display_close(struct inode *inode, struct file *file)
+{
+ struct imon_context *context = NULL;
+ int retval = 0;
+
+ context = file->private_data;
+
+ if (!context) {
+ err("%s: no context for device", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (!context->display) {
+ err("%s: display not supported by device", __func__);
+ retval = -ENODEV;
+ } else if (!context->display_isopen) {
+ err("%s: display is not open", __func__);
+ retval = -EIO;
+ } else {
+ context->display_isopen = 0;
+ dev_info(context->driver->dev, "display port closed\n");
+ if (!context->dev_present && !context->ir_isopen) {
+ /*
+ * Device disconnected before close and IR port is not
+ * open. If IR port is open, context will be deleted by
+ * ir_close.
+ */
+ mutex_unlock(&context->ctx_lock);
+ free_imon_context(context);
+ return retval;
+ }
+ }
+
+ mutex_unlock(&context->ctx_lock);
+ return retval;
+}
+
+/**
+ * Sends a packet to the device -- this function must be called
+ * with context->ctx_lock held.
+ */
+static int send_packet(struct imon_context *context)
+{
+ unsigned int pipe;
+ int interval = 0;
+ int retval = 0;
+
+ /* Check if we need to use control or interrupt urb */
+ pipe = usb_sndintpipe(context->usbdev,
+ context->tx_endpoint->bEndpointAddress);
+ interval = context->tx_endpoint->bInterval;
+
+ usb_fill_int_urb(context->tx_urb, context->usbdev, pipe,
+ context->usb_tx_buf,
+ sizeof(context->usb_tx_buf),
+ usb_tx_callback, context, interval);
+
+ context->tx_urb->actual_length = 0;
+
+ init_completion(&context->tx.finished);
+ atomic_set(&(context->tx.busy), 1);
+
+ retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
+ if (retval) {
+ atomic_set(&(context->tx.busy), 0);
+ err("%s: error submitting urb(%d)", __func__, retval);
+ } else {
+ /* Wait for transmission to complete (or abort) */
+ mutex_unlock(&context->ctx_lock);
+ retval = wait_for_completion_interruptible(
+ &context->tx.finished);
+ if (retval)
+ err("%s: task interrupted", __func__);
+ mutex_lock(&context->ctx_lock);
+
+ retval = context->tx.status;
+ if (retval)
+ err("%s: packet tx failed (%d)", __func__, retval);
+ }
+
+ return retval;
+}
+
+/**
+ * Writes data to the VFD. The iMON VFD is 2x16 characters
+ * and requires data in 5 consecutive USB interrupt packets,
+ * each packet but the last carrying 7 bytes.
+ *
+ * I don't know if the VFD board supports features such as
+ * scrolling, clearing rows, blanking, etc. so at
+ * the caller must provide a full screen of data. If fewer
+ * than 32 bytes are provided spaces will be appended to
+ * generate a full screen.
+ */
+static ssize_t vfd_write(struct file *file, const char *buf,
+ size_t n_bytes, loff_t *pos)
+{
+ int i;
+ int offset;
+ int seq;
+ int retval = 0;
+ struct imon_context *context;
+ const unsigned char vfd_packet6[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
+ int *data_buf = NULL;
+
+ context = file->private_data;
+ if (!context) {
+ err("%s: no context for device", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (!context->dev_present) {
+ err("%s: no iMON device present", __func__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ if (n_bytes <= 0 || n_bytes > IMON_DATA_BUF_SZ - 3) {
+ err("%s: invalid payload size", __func__);
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ data_buf = memdup_user(buf, n_bytes);
+ if (IS_ERR(data_buf)) {
+ retval = PTR_ERR(data_buf);
+ goto exit;
+ }
+
+ memcpy(context->tx.data_buf, data_buf, n_bytes);
+
+ /* Pad with spaces */
+ for (i = n_bytes; i < IMON_DATA_BUF_SZ - 3; ++i)
+ context->tx.data_buf[i] = ' ';
+
+ for (i = IMON_DATA_BUF_SZ - 3; i < IMON_DATA_BUF_SZ; ++i)
+ context->tx.data_buf[i] = 0xFF;
+
+ offset = 0;
+ seq = 0;
+
+ do {
+ memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7);
+ context->usb_tx_buf[7] = (unsigned char) seq;
+
+ retval = send_packet(context);
+ if (retval) {
+ err("%s: send packet failed for packet #%d",
+ __func__, seq/2);
+ goto exit;
+ } else {
+ seq += 2;
+ offset += 7;
+ }
+
+ } while (offset < IMON_DATA_BUF_SZ);
+
+ if (context->vfd_proto_6p) {
+ /* Send packet #6 */
+ memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6));
+ context->usb_tx_buf[7] = (unsigned char) seq;
+ retval = send_packet(context);
+ if (retval)
+ err("%s: send packet failed for packet #%d",
+ __func__, seq/2);
+ }
+
+exit:
+ mutex_unlock(&context->ctx_lock);
+ kfree(data_buf);
+
+ return (!retval) ? n_bytes : retval;
+}
+
+/**
+ * Callback function for USB core API: transmit data
+ */
+static void usb_tx_callback(struct urb *urb)
+{
+ struct imon_context *context;
+
+ if (!urb)
+ return;
+ context = (struct imon_context *)urb->context;
+ if (!context)
+ return;
+
+ context->tx.status = urb->status;
+
+ /* notify waiters that write has finished */
+ atomic_set(&context->tx.busy, 0);
+ complete(&context->tx.finished);
+
+ return;
+}
+
+/**
+ * Called by lirc_dev when the application opens /dev/lirc
+ */
+static int ir_open(void *data)
+{
+ int retval = 0;
+ struct imon_context *context;
+
+ /* prevent races with disconnect */
+ mutex_lock(&driver_lock);
+
+ context = (struct imon_context *)data;
+
+ /* initial IR protocol decode variables */
+ context->rx.count = 0;
+ context->rx.initial_space = 1;
+ context->rx.prev_bit = 0;
+
+ context->ir_isopen = 1;
+ dev_info(context->driver->dev, "IR port opened\n");
+
+ mutex_unlock(&driver_lock);
+ return retval;
+}
+
+/**
+ * Called by lirc_dev when the application closes /dev/lirc
+ */
+static void ir_close(void *data)
+{
+ struct imon_context *context;
+
+ context = (struct imon_context *)data;
+ if (!context) {
+ err("%s: no context for device", __func__);
+ return;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ context->ir_isopen = 0;
+ dev_info(context->driver->dev, "IR port closed\n");
+
+ if (!context->dev_present) {
+ /*
+ * Device disconnected while IR port was still open. Driver
+ * was not deregistered at disconnect time, so do it now.
+ */
+ deregister_from_lirc(context);
+
+ if (!context->display_isopen) {
+ mutex_unlock(&context->ctx_lock);
+ free_imon_context(context);
+ return;
+ }
+ /*
+ * If display port is open, context will be deleted by
+ * display_close
+ */
+ }
+
+ mutex_unlock(&context->ctx_lock);
+ return;
+}
+
+/**
+ * Convert bit count to time duration (in us) and submit
+ * the value to lirc_dev.
+ */
+static void submit_data(struct imon_context *context)
+{
+ unsigned char buf[4];
+ int value = context->rx.count;
+ int i;
+
+ dev_dbg(context->driver->dev, "submitting data to LIRC\n");
+
+ value *= BIT_DURATION;
+ value &= PULSE_MASK;
+ if (context->rx.prev_bit)
+ value |= PULSE_BIT;
+
+ for (i = 0; i < 4; ++i)
+ buf[i] = value>>(i*8);
+
+ lirc_buffer_write(context->driver->rbuf, buf);
+ wake_up(&context->driver->rbuf->wait_poll);
+ return;
+}
+
+static inline int tv2int(const struct timeval *a, const struct timeval *b)
+{
+ int usecs = 0;
+ int sec = 0;
+
+ if (b->tv_usec > a->tv_usec) {
+ usecs = 1000000;
+ sec--;
+ }
+
+ usecs += a->tv_usec - b->tv_usec;
+
+ sec += a->tv_sec - b->tv_sec;
+ sec *= 1000;
+ usecs /= 1000;
+ sec += usecs;
+
+ if (sec < 0)
+ sec = 1000;
+
+ return sec;
+}
+
+/**
+ * Process the incoming packet
+ */
+static void imon_incoming_packet(struct imon_context *context,
+ struct urb *urb, int intf)
+{
+ int len = urb->actual_length;
+ unsigned char *buf = urb->transfer_buffer;
+ struct device *dev = context->driver->dev;
+ int octet, bit;
+ unsigned char mask;
+ int i;
+
+ /*
+ * just bail out if no listening IR client
+ */
+ if (!context->ir_isopen)
+ return;
+
+ if (len != 8) {
+ dev_warn(dev, "imon %s: invalid incoming packet "
+ "size (len = %d, intf%d)\n", __func__, len, intf);
+ return;
+ }
+
+ if (debug) {
+ printk(KERN_INFO "raw packet: ");
+ for (i = 0; i < len; ++i)
+ printk("%02x ", buf[i]);
+ printk("\n");
+ }
+
+ /*
+ * Translate received data to pulse and space lengths.
+ * Received data is active low, i.e. pulses are 0 and
+ * spaces are 1.
+ *
+ * My original algorithm was essentially similar to
+ * Changwoo Ryu's with the exception that he switched
+ * the incoming bits to active high and also fed an
+ * initial space to LIRC at the start of a new sequence
+ * if the previous bit was a pulse.
+ *
+ * I've decided to adopt his algorithm.
+ */
+
+ if (buf[7] == 1 && context->rx.initial_space) {
+ /* LIRC requires a leading space */
+ context->rx.prev_bit = 0;
+ context->rx.count = 4;
+ submit_data(context);
+ context->rx.count = 0;
+ }
+
+ for (octet = 0; octet < 5; ++octet) {
+ mask = 0x80;
+ for (bit = 0; bit < 8; ++bit) {
+ int curr_bit = !(buf[octet] & mask);
+ if (curr_bit != context->rx.prev_bit) {
+ if (context->rx.count) {
+ submit_data(context);
+ context->rx.count = 0;
+ }
+ context->rx.prev_bit = curr_bit;
+ }
+ ++context->rx.count;
+ mask >>= 1;
+ }
+ }
+
+ if (buf[7] == 10) {
+ if (context->rx.count) {
+ submit_data(context);
+ context->rx.count = 0;
+ }
+ context->rx.initial_space = context->rx.prev_bit;
+ }
+}
+
+/**
+ * Callback function for USB core API: receive data
+ */
+static void usb_rx_callback(struct urb *urb)
+{
+ struct imon_context *context;
+ int intfnum = 0;
+
+ if (!urb)
+ return;
+
+ context = (struct imon_context *)urb->context;
+ if (!context)
+ return;
+
+ switch (urb->status) {
+ case -ENOENT: /* usbcore unlink successful! */
+ return;
+
+ case 0:
+ imon_incoming_packet(context, urb, intfnum);
+ break;
+
+ default:
+ dev_warn(context->driver->dev, "imon %s: status(%d): ignored\n",
+ __func__, urb->status);
+ break;
+ }
+
+ usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+
+ return;
+}
+
+/**
+ * Callback function for USB core API: Probe
+ */
+static int imon_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev = NULL;
+ struct usb_host_interface *iface_desc = NULL;
+ struct usb_endpoint_descriptor *rx_endpoint = NULL;
+ struct usb_endpoint_descriptor *tx_endpoint = NULL;
+ struct urb *rx_urb = NULL;
+ struct urb *tx_urb = NULL;
+ struct lirc_driver *driver = NULL;
+ struct lirc_buffer *rbuf = NULL;
+ struct device *dev = &interface->dev;
+ int ifnum;
+ int lirc_minor = 0;
+ int num_endpts;
+ int retval = 0;
+ int display_ep_found = 0;
+ int ir_ep_found = 0;
+ int alloc_status = 0;
+ int vfd_proto_6p = 0;
+ struct imon_context *context = NULL;
+ int i;
+ u16 vendor, product;
+
+ /* prevent races probing devices w/multiple interfaces */
+ mutex_lock(&driver_lock);
+
+ context = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
+ if (!context) {
+ err("%s: kzalloc failed for context", __func__);
+ alloc_status = 1;
+ goto alloc_status_switch;
+ }
+
+ /*
+ * Try to auto-detect the type of display if the user hasn't set
+ * it by hand via the display_type modparam. Default is VFD.
+ */
+ if (usb_match_id(interface, ir_only_list))
+ context->display = 0;
+ else
+ context->display = 1;
+
+ usbdev = usb_get_dev(interface_to_usbdev(interface));
+ iface_desc = interface->cur_altsetting;
+ num_endpts = iface_desc->desc.bNumEndpoints;
+ ifnum = iface_desc->desc.bInterfaceNumber;
+ vendor = le16_to_cpu(usbdev->descriptor.idVendor);
+ product = le16_to_cpu(usbdev->descriptor.idProduct);
+
+ dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n",
+ __func__, vendor, product, ifnum);
+
+ /*
+ * Scan the endpoint list and set:
+ * first input endpoint = IR endpoint
+ * first output endpoint = display endpoint
+ */
+ for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) {
+ struct usb_endpoint_descriptor *ep;
+ int ep_dir;
+ int ep_type;
+ ep = &iface_desc->endpoint[i].desc;
+ ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+ ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ if (!ir_ep_found &&
+ ep_dir == USB_DIR_IN &&
+ ep_type == USB_ENDPOINT_XFER_INT) {
+
+ rx_endpoint = ep;
+ ir_ep_found = 1;
+ dev_dbg(dev, "%s: found IR endpoint\n", __func__);
+
+ } else if (!display_ep_found && ep_dir == USB_DIR_OUT &&
+ ep_type == USB_ENDPOINT_XFER_INT) {
+ tx_endpoint = ep;
+ display_ep_found = 1;
+ dev_dbg(dev, "%s: found display endpoint\n", __func__);
+ }
+ }
+
+ /*
+ * Some iMON receivers have no display. Unfortunately, it seems
+ * that SoundGraph recycles device IDs between devices both with
+ * and without... :\
+ */
+ if (context->display == 0) {
+ display_ep_found = 0;
+ dev_dbg(dev, "%s: device has no display\n", __func__);
+ }
+
+ /* Input endpoint is mandatory */
+ if (!ir_ep_found) {
+ err("%s: no valid input (IR) endpoint found.", __func__);
+ retval = -ENODEV;
+ alloc_status = 2;
+ goto alloc_status_switch;
+ }
+
+ /* Determine if display requires 6 packets */
+ if (display_ep_found) {
+ if (usb_match_id(interface, vfd_proto_6p_list))
+ vfd_proto_6p = 1;
+
+ dev_dbg(dev, "%s: vfd_proto_6p: %d\n",
+ __func__, vfd_proto_6p);
+ }
+
+ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
+ if (!driver) {
+ err("%s: kzalloc failed for lirc_driver", __func__);
+ alloc_status = 2;
+ goto alloc_status_switch;
+ }
+ rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+ if (!rbuf) {
+ err("%s: kmalloc failed for lirc_buffer", __func__);
+ alloc_status = 3;
+ goto alloc_status_switch;
+ }
+ if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
+ err("%s: lirc_buffer_init failed", __func__);
+ alloc_status = 4;
+ goto alloc_status_switch;
+ }
+ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rx_urb) {
+ err("%s: usb_alloc_urb failed for IR urb", __func__);
+ alloc_status = 5;
+ goto alloc_status_switch;
+ }
+ tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!tx_urb) {
+ err("%s: usb_alloc_urb failed for display urb",
+ __func__);
+ alloc_status = 6;
+ goto alloc_status_switch;
+ }
+
+ mutex_init(&context->ctx_lock);
+ context->vfd_proto_6p = vfd_proto_6p;
+
+ strcpy(driver->name, MOD_NAME);
+ driver->minor = -1;
+ driver->code_length = BUF_CHUNK_SIZE * 8;
+ driver->sample_rate = 0;
+ driver->features = LIRC_CAN_REC_MODE2;
+ driver->data = context;
+ driver->rbuf = rbuf;
+ driver->set_use_inc = ir_open;
+ driver->set_use_dec = ir_close;
+ driver->dev = &interface->dev;
+ driver->owner = THIS_MODULE;
+
+ mutex_lock(&context->ctx_lock);
+
+ context->driver = driver;
+ /* start out in keyboard mode */
+
+ lirc_minor = lirc_register_driver(driver);
+ if (lirc_minor < 0) {
+ err("%s: lirc_register_driver failed", __func__);
+ alloc_status = 7;
+ goto unlock;
+ } else
+ dev_info(dev, "Registered iMON driver "
+ "(lirc minor: %d)\n", lirc_minor);
+
+ /* Needed while unregistering! */
+ driver->minor = lirc_minor;
+
+ context->usbdev = usbdev;
+ context->dev_present = 1;
+ context->rx_endpoint = rx_endpoint;
+ context->rx_urb = rx_urb;
+
+ /*
+ * tx is used to send characters to lcd/vfd, associate RF
+ * remotes, set IR protocol, and maybe more...
+ */
+ context->tx_endpoint = tx_endpoint;
+ context->tx_urb = tx_urb;
+
+ if (display_ep_found)
+ context->display = 1;
+
+ usb_fill_int_urb(context->rx_urb, context->usbdev,
+ usb_rcvintpipe(context->usbdev,
+ context->rx_endpoint->bEndpointAddress),
+ context->usb_rx_buf, sizeof(context->usb_rx_buf),
+ usb_rx_callback, context,
+ context->rx_endpoint->bInterval);
+
+ retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
+
+ if (retval) {
+ err("%s: usb_submit_urb failed for intf0 (%d)",
+ __func__, retval);
+ mutex_unlock(&context->ctx_lock);
+ goto exit;
+ }
+
+ usb_set_intfdata(interface, context);
+
+ if (context->display && ifnum == 0) {
+ dev_dbg(dev, "%s: Registering iMON display with sysfs\n",
+ __func__);
+
+ if (usb_register_dev(interface, &imon_class)) {
+ /* Not a fatal error, so ignore */
+ dev_info(dev, "%s: could not get a minor number for "
+ "display\n", __func__);
+ }
+ }
+
+ dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
+ "usb<%d:%d> initialized\n", vendor, product, ifnum,
+ usbdev->bus->busnum, usbdev->devnum);
+
+unlock:
+ mutex_unlock(&context->ctx_lock);
+alloc_status_switch:
+
+ switch (alloc_status) {
+ case 7:
+ usb_free_urb(tx_urb);
+ case 6:
+ usb_free_urb(rx_urb);
+ case 5:
+ if (rbuf)
+ lirc_buffer_free(rbuf);
+ case 4:
+ kfree(rbuf);
+ case 3:
+ kfree(driver);
+ case 2:
+ kfree(context);
+ context = NULL;
+ case 1:
+ if (retval != -ENODEV)
+ retval = -ENOMEM;
+ break;
+ case 0:
+ retval = 0;
+ }
+
+exit:
+ mutex_unlock(&driver_lock);
+
+ return retval;
+}
+
+/**
+ * Callback function for USB core API: disconnect
+ */
+static void imon_disconnect(struct usb_interface *interface)
+{
+ struct imon_context *context;
+ int ifnum;
+
+ /* prevent races with ir_open()/display_open() */
+ mutex_lock(&driver_lock);
+
+ context = usb_get_intfdata(interface);
+ ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
+
+ mutex_lock(&context->ctx_lock);
+
+ usb_set_intfdata(interface, NULL);
+
+ /* Abort ongoing write */
+ if (atomic_read(&context->tx.busy)) {
+ usb_kill_urb(context->tx_urb);
+ complete_all(&context->tx.finished);
+ }
+
+ context->dev_present = 0;
+ usb_kill_urb(context->rx_urb);
+ if (context->display)
+ usb_deregister_dev(interface, &imon_class);
+
+ if (!context->ir_isopen && !context->dev_present) {
+ deregister_from_lirc(context);
+ mutex_unlock(&context->ctx_lock);
+ if (!context->display_isopen)
+ free_imon_context(context);
+ } else
+ mutex_unlock(&context->ctx_lock);
+
+ mutex_unlock(&driver_lock);
+
+ printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n",
+ __func__, ifnum);
+}
+
+static int imon_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct imon_context *context = usb_get_intfdata(intf);
+
+ usb_kill_urb(context->rx_urb);
+
+ return 0;
+}
+
+static int imon_resume(struct usb_interface *intf)
+{
+ int rc = 0;
+ struct imon_context *context = usb_get_intfdata(intf);
+
+ usb_fill_int_urb(context->rx_urb, context->usbdev,
+ usb_rcvintpipe(context->usbdev,
+ context->rx_endpoint->bEndpointAddress),
+ context->usb_rx_buf, sizeof(context->usb_rx_buf),
+ usb_rx_callback, context,
+ context->rx_endpoint->bInterval);
+
+ rc = usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+
+ return rc;
+}
+
+static int __init imon_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO MOD_NAME ": " MOD_DESC ", v" MOD_VERSION "\n");
+
+ rc = usb_register(&imon_driver);
+ if (rc) {
+ err("%s: usb register failed(%d)", __func__, rc);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit imon_exit(void)
+{
+ usb_deregister(&imon_driver);
+ printk(KERN_INFO MOD_NAME ": module removed. Goodbye!\n");
+}
+
+module_init(imon_init);
+module_exit(imon_exit);
--- /dev/null
+/*
+ * lirc_parallel.c
+ *
+ * lirc_parallel - device driver for infra-red signal receiving and
+ * transmitting unit built by the author
+ *
+ * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
+ *
+ * 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
+ *
+ */
+
+/*** Includes ***/
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/uaccess.h>
+#include <asm/div64.h>
+
+#include <linux/poll.h>
+#include <linux/parport.h>
+#include <linux/platform_device.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+#include "lirc_parallel.h"
+
+#define LIRC_DRIVER_NAME "lirc_parallel"
+
+#ifndef LIRC_IRQ
+#define LIRC_IRQ 7
+#endif
+#ifndef LIRC_PORT
+#define LIRC_PORT 0x378
+#endif
+#ifndef LIRC_TIMER
+#define LIRC_TIMER 65536
+#endif
+
+/*** Global Variables ***/
+
+static int debug;
+static int check_pselecd;
+
+unsigned int irq = LIRC_IRQ;
+unsigned int io = LIRC_PORT;
+#ifdef LIRC_TIMER
+unsigned int timer;
+unsigned int default_timer = LIRC_TIMER;
+#endif
+
+#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
+
+static int rbuf[RBUF_SIZE];
+
+DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
+
+unsigned int rptr;
+unsigned int wptr;
+unsigned int lost_irqs;
+int is_open;
+
+struct parport *pport;
+struct pardevice *ppdevice;
+int is_claimed;
+
+unsigned int tx_mask = 1;
+
+/*** Internal Functions ***/
+
+static unsigned int in(int offset)
+{
+ switch (offset) {
+ case LIRC_LP_BASE:
+ return parport_read_data(pport);
+ case LIRC_LP_STATUS:
+ return parport_read_status(pport);
+ case LIRC_LP_CONTROL:
+ return parport_read_control(pport);
+ }
+ return 0; /* make compiler happy */
+}
+
+static void out(int offset, int value)
+{
+ switch (offset) {
+ case LIRC_LP_BASE:
+ parport_write_data(pport, value);
+ break;
+ case LIRC_LP_CONTROL:
+ parport_write_control(pport, value);
+ break;
+ case LIRC_LP_STATUS:
+ printk(KERN_INFO "%s: attempt to write to status register\n",
+ LIRC_DRIVER_NAME);
+ break;
+ }
+}
+
+static unsigned int lirc_get_timer(void)
+{
+ return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
+}
+
+static unsigned int lirc_get_signal(void)
+{
+ return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
+}
+
+static void lirc_on(void)
+{
+ out(LIRC_PORT_DATA, tx_mask);
+}
+
+static void lirc_off(void)
+{
+ out(LIRC_PORT_DATA, 0);
+}
+
+static unsigned int init_lirc_timer(void)
+{
+ struct timeval tv, now;
+ unsigned int level, newlevel, timeelapsed, newtimer;
+ int count = 0;
+
+ do_gettimeofday(&tv);
+ tv.tv_sec++; /* wait max. 1 sec. */
+ level = lirc_get_timer();
+ do {
+ newlevel = lirc_get_timer();
+ if (level == 0 && newlevel != 0)
+ count++;
+ level = newlevel;
+ do_gettimeofday(&now);
+ } while (count < 1000 && (now.tv_sec < tv.tv_sec
+ || (now.tv_sec == tv.tv_sec
+ && now.tv_usec < tv.tv_usec)));
+
+ timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000
+ + (now.tv_usec - tv.tv_usec));
+ if (count >= 1000 && timeelapsed > 0) {
+ if (default_timer == 0) {
+ /* autodetect timer */
+ newtimer = (1000000*count)/timeelapsed;
+ printk(KERN_INFO "%s: %u Hz timer detected\n",
+ LIRC_DRIVER_NAME, newtimer);
+ return newtimer;
+ } else {
+ newtimer = (1000000*count)/timeelapsed;
+ if (abs(newtimer - default_timer) > default_timer/10) {
+ /* bad timer */
+ printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
+ LIRC_DRIVER_NAME, newtimer);
+ printk(KERN_NOTICE "%s: using default timer: "
+ "%u Hz\n",
+ LIRC_DRIVER_NAME, default_timer);
+ return default_timer;
+ } else {
+ printk(KERN_INFO "%s: %u Hz timer detected\n",
+ LIRC_DRIVER_NAME, newtimer);
+ return newtimer; /* use detected value */
+ }
+ }
+ } else {
+ printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME);
+ return 0;
+ }
+}
+
+static int lirc_claim(void)
+{
+ if (parport_claim(ppdevice) != 0) {
+ printk(KERN_WARNING "%s: could not claim port\n",
+ LIRC_DRIVER_NAME);
+ printk(KERN_WARNING "%s: waiting for port becoming available"
+ "\n", LIRC_DRIVER_NAME);
+ if (parport_claim_or_block(ppdevice) < 0) {
+ printk(KERN_NOTICE "%s: could not claim port, giving"
+ " up\n", LIRC_DRIVER_NAME);
+ return 0;
+ }
+ }
+ out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+ is_claimed = 1;
+ return 1;
+}
+
+/*** interrupt handler ***/
+
+static void rbuf_write(int signal)
+{
+ unsigned int nwptr;
+
+ nwptr = (wptr + 1) & (RBUF_SIZE - 1);
+ if (nwptr == rptr) {
+ /* no new signals will be accepted */
+ lost_irqs++;
+ printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME);
+ return;
+ }
+ rbuf[wptr] = signal;
+ wptr = nwptr;
+}
+
+static void irq_handler(void *blah)
+{
+ struct timeval tv;
+ static struct timeval lasttv;
+ static int init;
+ long signal;
+ int data;
+ unsigned int level, newlevel;
+ unsigned int timeout;
+
+ if (!is_open)
+ return;
+
+ if (!is_claimed)
+ return;
+
+#if 0
+ /* disable interrupt */
+ disable_irq(irq);
+ out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
+#endif
+ if (check_pselecd && (in(1) & LP_PSELECD))
+ return;
+
+#ifdef LIRC_TIMER
+ if (init) {
+ do_gettimeofday(&tv);
+
+ signal = tv.tv_sec - lasttv.tv_sec;
+ if (signal > 15)
+ /* really long time */
+ data = PULSE_MASK;
+ else
+ data = (int) (signal*1000000 +
+ tv.tv_usec - lasttv.tv_usec +
+ LIRC_SFH506_DELAY);
+
+ rbuf_write(data); /* space */
+ } else {
+ if (timer == 0) {
+ /*
+ * wake up; we'll lose this signal, but it will be
+ * garbage if the device is turned on anyway
+ */
+ timer = init_lirc_timer();
+ /* enable_irq(irq); */
+ return;
+ }
+ init = 1;
+ }
+
+ timeout = timer/10; /* timeout after 1/10 sec. */
+ signal = 1;
+ level = lirc_get_timer();
+ do {
+ newlevel = lirc_get_timer();
+ if (level == 0 && newlevel != 0)
+ signal++;
+ level = newlevel;
+
+ /* giving up */
+ if (signal > timeout
+ || (check_pselecd && (in(1) & LP_PSELECD))) {
+ signal = 0;
+ printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME);
+ break;
+ }
+ } while (lirc_get_signal());
+
+ if (signal != 0) {
+ /* adjust value to usecs */
+ __u64 helper;
+
+ helper = ((__u64) signal)*1000000;
+ do_div(helper, timer);
+ signal = (long) helper;
+
+ if (signal > LIRC_SFH506_DELAY)
+ data = signal - LIRC_SFH506_DELAY;
+ else
+ data = 1;
+ rbuf_write(PULSE_BIT|data); /* pulse */
+ }
+ do_gettimeofday(&lasttv);
+#else
+ /* add your code here */
+#endif
+
+ wake_up_interruptible(&lirc_wait);
+
+ /* enable interrupt */
+ /*
+ enable_irq(irq);
+ out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
+ */
+}
+
+/*** file operations ***/
+
+static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig)
+{
+ return -ESPIPE;
+}
+
+static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos)
+{
+ int result = 0;
+ int count = 0;
+ DECLARE_WAITQUEUE(wait, current);
+
+ if (n % sizeof(int))
+ return -EINVAL;
+
+ add_wait_queue(&lirc_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (count < n) {
+ if (rptr != wptr) {
+ if (copy_to_user(buf+count, (char *) &rbuf[rptr],
+ sizeof(int))) {
+ result = -EFAULT;
+ break;
+ }
+ rptr = (rptr + 1) & (RBUF_SIZE - 1);
+ count += sizeof(int);
+ } else {
+ if (filep->f_flags & O_NONBLOCK) {
+ result = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ result = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ }
+ remove_wait_queue(&lirc_wait, &wait);
+ set_current_state(TASK_RUNNING);
+ return count ? count : result;
+}
+
+static ssize_t lirc_write(struct file *filep, const char *buf, size_t n,
+ loff_t *ppos)
+{
+ int count;
+ unsigned int i;
+ unsigned int level, newlevel;
+ unsigned long flags;
+ int counttimer;
+ int *wbuf;
+ ssize_t ret;
+
+ if (!is_claimed)
+ return -EBUSY;
+
+ count = n / sizeof(int);
+
+ if (n % sizeof(int) || count % 2 == 0)
+ return -EINVAL;
+
+ wbuf = memdup_user(buf, n);
+ if (IS_ERR(wbuf))
+ return PTR_ERR(wbuf);
+
+#ifdef LIRC_TIMER
+ if (timer == 0) {
+ /* try again if device is ready */
+ timer = init_lirc_timer();
+ if (timer == 0) {
+ ret = -EIO;
+ goto out;
+ }
+ }
+
+ /* adjust values from usecs */
+ for (i = 0; i < count; i++) {
+ __u64 helper;
+
+ helper = ((__u64) wbuf[i])*timer;
+ do_div(helper, 1000000);
+ wbuf[i] = (int) helper;
+ }
+
+ local_irq_save(flags);
+ i = 0;
+ while (i < count) {
+ level = lirc_get_timer();
+ counttimer = 0;
+ lirc_on();
+ do {
+ newlevel = lirc_get_timer();
+ if (level == 0 && newlevel != 0)
+ counttimer++;
+ level = newlevel;
+ if (check_pselecd && (in(1) & LP_PSELECD)) {
+ lirc_off();
+ local_irq_restore(flags);
+ ret = -EIO;
+ goto out;
+ }
+ } while (counttimer < wbuf[i]);
+ i++;
+
+ lirc_off();
+ if (i == count)
+ break;
+ counttimer = 0;
+ do {
+ newlevel = lirc_get_timer();
+ if (level == 0 && newlevel != 0)
+ counttimer++;
+ level = newlevel;
+ if (check_pselecd && (in(1) & LP_PSELECD)) {
+ local_irq_restore(flags);
+ ret = -EIO;
+ goto out;
+ }
+ } while (counttimer < wbuf[i]);
+ i++;
+ }
+ local_irq_restore(flags);
+#else
+ /* place code that handles write without external timer here */
+#endif
+ ret = n;
+out:
+ kfree(wbuf);
+
+ return ret;
+}
+
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
+{
+ poll_wait(file, &lirc_wait, wait);
+ if (rptr != wptr)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ int result;
+ __u32 features = LIRC_CAN_SET_TRANSMITTER_MASK |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+ __u32 mode;
+ __u32 value;
+
+ switch (cmd) {
+ case LIRC_GET_FEATURES:
+ result = put_user(features, (__u32 *) arg);
+ if (result)
+ return result;
+ break;
+ case LIRC_GET_SEND_MODE:
+ result = put_user(LIRC_MODE_PULSE, (__u32 *) arg);
+ if (result)
+ return result;
+ break;
+ case LIRC_GET_REC_MODE:
+ result = put_user(LIRC_MODE_MODE2, (__u32 *) arg);
+ if (result)
+ return result;
+ break;
+ case LIRC_SET_SEND_MODE:
+ result = get_user(mode, (__u32 *) arg);
+ if (result)
+ return result;
+ if (mode != LIRC_MODE_PULSE)
+ return -EINVAL;
+ break;
+ case LIRC_SET_REC_MODE:
+ result = get_user(mode, (__u32 *) arg);
+ if (result)
+ return result;
+ if (mode != LIRC_MODE_MODE2)
+ return -ENOSYS;
+ break;
+ case LIRC_SET_TRANSMITTER_MASK:
+ result = get_user(value, (__u32 *) arg);
+ if (result)
+ return result;
+ if ((value & LIRC_PARALLEL_TRANSMITTER_MASK) != value)
+ return LIRC_PARALLEL_MAX_TRANSMITTERS;
+ tx_mask = value;
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int lirc_open(struct inode *node, struct file *filep)
+{
+ if (is_open || !lirc_claim())
+ return -EBUSY;
+
+ parport_enable_irq(pport);
+
+ /* init read ptr */
+ rptr = 0;
+ wptr = 0;
+ lost_irqs = 0;
+
+ is_open = 1;
+ return 0;
+}
+
+static int lirc_close(struct inode *node, struct file *filep)
+{
+ if (is_claimed) {
+ is_claimed = 0;
+ parport_release(ppdevice);
+ }
+ is_open = 0;
+ return 0;
+}
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .llseek = lirc_lseek,
+ .read = lirc_read,
+ .write = lirc_write,
+ .poll = lirc_poll,
+ .unlocked_ioctl = lirc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lirc_ioctl,
+#endif
+ .open = lirc_open,
+ .release = lirc_close
+};
+
+static int set_use_inc(void *data)
+{
+ return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+static struct lirc_driver driver = {
+ .name = LIRC_DRIVER_NAME,
+ .minor = -1,
+ .code_length = 1,
+ .sample_rate = 0,
+ .data = NULL,
+ .add_to_buf = NULL,
+ .set_use_inc = set_use_inc,
+ .set_use_dec = set_use_dec,
+ .fops = &lirc_fops,
+ .dev = NULL,
+ .owner = THIS_MODULE,
+};
+
+static struct platform_device *lirc_parallel_dev;
+
+static int __devinit lirc_parallel_probe(struct platform_device *dev)
+{
+ return 0;
+}
+
+static int __devexit lirc_parallel_remove(struct platform_device *dev)
+{
+ return 0;
+}
+
+static int lirc_parallel_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int lirc_parallel_resume(struct platform_device *dev)
+{
+ return 0;
+}
+
+static struct platform_driver lirc_parallel_driver = {
+ .probe = lirc_parallel_probe,
+ .remove = __devexit_p(lirc_parallel_remove),
+ .suspend = lirc_parallel_suspend,
+ .resume = lirc_parallel_resume,
+ .driver = {
+ .name = LIRC_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int pf(void *handle)
+{
+ parport_disable_irq(pport);
+ is_claimed = 0;
+ return 0;
+}
+
+static void kf(void *handle)
+{
+ if (!is_open)
+ return;
+ if (!lirc_claim())
+ return;
+ parport_enable_irq(pport);
+ lirc_off();
+ /* this is a bit annoying when you actually print...*/
+ /*
+ printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
+ */
+}
+
+/*** module initialization and cleanup ***/
+
+static int __init lirc_parallel_init(void)
+{
+ int result;
+
+ result = platform_driver_register(&lirc_parallel_driver);
+ if (result) {
+ printk(KERN_NOTICE "platform_driver_register"
+ " returned %d\n", result);
+ return result;
+ }
+
+ lirc_parallel_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
+ if (!lirc_parallel_dev) {
+ result = -ENOMEM;
+ goto exit_driver_unregister;
+ }
+
+ result = platform_device_add(lirc_parallel_dev);
+ if (result)
+ goto exit_device_put;
+
+ pport = parport_find_base(io);
+ if (pport == NULL) {
+ printk(KERN_NOTICE "%s: no port at %x found\n",
+ LIRC_DRIVER_NAME, io);
+ result = -ENXIO;
+ goto exit_device_put;
+ }
+ ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME,
+ pf, kf, irq_handler, 0, NULL);
+ parport_put_port(pport);
+ if (ppdevice == NULL) {
+ printk(KERN_NOTICE "%s: parport_register_device() failed\n",
+ LIRC_DRIVER_NAME);
+ result = -ENXIO;
+ goto exit_device_put;
+ }
+ if (parport_claim(ppdevice) != 0)
+ goto skip_init;
+ is_claimed = 1;
+ out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+
+#ifdef LIRC_TIMER
+ if (debug)
+ out(LIRC_PORT_DATA, tx_mask);
+
+ timer = init_lirc_timer();
+
+#if 0 /* continue even if device is offline */
+ if (timer == 0) {
+ is_claimed = 0;
+ parport_release(pport);
+ parport_unregister_device(ppdevice);
+ result = -EIO;
+ goto exit_device_put;
+ }
+
+#endif
+ if (debug)
+ out(LIRC_PORT_DATA, 0);
+#endif
+
+ is_claimed = 0;
+ parport_release(ppdevice);
+ skip_init:
+ driver.dev = &lirc_parallel_dev->dev;
+ driver.minor = lirc_register_driver(&driver);
+ if (driver.minor < 0) {
+ printk(KERN_NOTICE "%s: register_chrdev() failed\n",
+ LIRC_DRIVER_NAME);
+ parport_unregister_device(ppdevice);
+ result = -EIO;
+ goto exit_device_put;
+ }
+ printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n",
+ LIRC_DRIVER_NAME, io, irq);
+ return 0;
+
+exit_device_put:
+ platform_device_put(lirc_parallel_dev);
+exit_driver_unregister:
+ platform_driver_unregister(&lirc_parallel_driver);
+ return result;
+}
+
+static void __exit lirc_parallel_exit(void)
+{
+ parport_unregister_device(ppdevice);
+ lirc_unregister_driver(driver.minor);
+
+ platform_device_unregister(lirc_parallel_dev);
+ platform_driver_unregister(&lirc_parallel_driver);
+}
+
+module_init(lirc_parallel_init);
+module_exit(lirc_parallel_exit);
+
+MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
+MODULE_AUTHOR("Christoph Bartelmus");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, S_IRUGO);
+MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
+
+module_param(irq, int, S_IRUGO);
+MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
+
+module_param(tx_mask, int, S_IRUGO);
+MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+module_param(check_pselecd, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Check for printer (default: 0)");
--- /dev/null
+/* lirc_parallel.h */
+
+#ifndef _LIRC_PARALLEL_H
+#define _LIRC_PARALLEL_H
+
+#include <linux/lp.h>
+
+#define LIRC_PORT_LEN 3
+
+#define LIRC_LP_BASE 0
+#define LIRC_LP_STATUS 1
+#define LIRC_LP_CONTROL 2
+
+#define LIRC_PORT_DATA LIRC_LP_BASE /* base */
+#define LIRC_PORT_TIMER LIRC_LP_STATUS /* status port */
+#define LIRC_PORT_TIMER_BIT LP_PBUSY /* busy signal */
+#define LIRC_PORT_SIGNAL LIRC_LP_STATUS /* status port */
+#define LIRC_PORT_SIGNAL_BIT LP_PACK /* ack signal */
+#define LIRC_PORT_IRQ LIRC_LP_CONTROL /* control port */
+
+#define LIRC_SFH506_DELAY 0 /* delay t_phl in usecs */
+
+#define LIRC_PARALLEL_MAX_TRANSMITTERS 8
+#define LIRC_PARALLEL_TRANSMITTER_MASK ((1<<LIRC_PARALLEL_MAX_TRANSMITTERS) - 1)
+
+#endif
--- /dev/null
+/*
+ * lirc_sasem.c - USB remote support for LIRC
+ * Version 0.5
+ *
+ * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
+ * Tim Davies <tim@opensystems.net.au>
+ *
+ * This driver was derived from:
+ * Venky Raju <dev@venky.ws>
+ * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
+ * Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
+ * "lirc_atiusb - USB remote support for LIRC"
+ * Culver Consulting Services <henry@culcon.com>'s 2003
+ * "Sasem OnAir VFD/IR USB driver"
+ *
+ *
+ * NOTE - The LCDproc iMon driver should work with this module. More info at
+ * http://www.frogstorm.info/sasem
+ */
+
+/*
+ * 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/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+
+#define MOD_AUTHOR "Oliver Stabel <oliver.stabel@gmx.de>, " \
+ "Tim Davies <tim@opensystems.net.au>"
+#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1"
+#define MOD_NAME "lirc_sasem"
+#define MOD_VERSION "0.5"
+
+#define VFD_MINOR_BASE 144 /* Same as LCD */
+#define DEVICE_NAME "lcd%d"
+
+#define BUF_CHUNK_SIZE 8
+#define BUF_SIZE 128
+
+#define IOCTL_LCD_CONTRAST 1
+
+/*** P R O T O T Y P E S ***/
+
+/* USB Callback prototypes */
+static int sasem_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void sasem_disconnect(struct usb_interface *interface);
+static void usb_rx_callback(struct urb *urb);
+static void usb_tx_callback(struct urb *urb);
+
+/* VFD file_operations function prototypes */
+static int vfd_open(struct inode *inode, struct file *file);
+static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg);
+static int vfd_close(struct inode *inode, struct file *file);
+static ssize_t vfd_write(struct file *file, const char *buf,
+ size_t n_bytes, loff_t *pos);
+
+/* LIRC driver function prototypes */
+static int ir_open(void *data);
+static void ir_close(void *data);
+
+/* Driver init/exit prototypes */
+static int __init sasem_init(void);
+static void __exit sasem_exit(void);
+
+/*** G L O B A L S ***/
+#define SASEM_DATA_BUF_SZ 32
+
+struct sasem_context {
+
+ struct usb_device *dev;
+ int vfd_isopen; /* VFD port has been opened */
+ unsigned int vfd_contrast; /* VFD contrast */
+ int ir_isopen; /* IR port has been opened */
+ int dev_present; /* USB device presence */
+ struct mutex ctx_lock; /* to lock this object */
+ wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
+
+ struct lirc_driver *driver;
+ struct usb_endpoint_descriptor *rx_endpoint;
+ struct usb_endpoint_descriptor *tx_endpoint;
+ struct urb *rx_urb;
+ struct urb *tx_urb;
+ unsigned char usb_rx_buf[8];
+ unsigned char usb_tx_buf[8];
+
+ struct tx_t {
+ unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data buffer */
+ struct completion finished; /* wait for write to finish */
+ atomic_t busy; /* write in progress */
+ int status; /* status of tx completion */
+ } tx;
+
+ /* for dealing with repeat codes (wish there was a toggle bit!) */
+ struct timeval presstime;
+ char lastcode[8];
+ int codesaved;
+};
+
+/* VFD file operations */
+static const struct file_operations vfd_fops = {
+ .owner = THIS_MODULE,
+ .open = &vfd_open,
+ .write = &vfd_write,
+ .unlocked_ioctl = &vfd_ioctl,
+ .release = &vfd_close,
+ .llseek = noop_llseek,
+};
+
+/* USB Device ID for Sasem USB Control Board */
+static struct usb_device_id sasem_usb_id_table[] = {
+ /* Sasem USB Control Board */
+ { USB_DEVICE(0x11ba, 0x0101) },
+ /* Terminating entry */
+ {}
+};
+
+/* USB Device data */
+static struct usb_driver sasem_driver = {
+ .name = MOD_NAME,
+ .probe = sasem_probe,
+ .disconnect = sasem_disconnect,
+ .id_table = sasem_usb_id_table,
+};
+
+static struct usb_class_driver sasem_class = {
+ .name = DEVICE_NAME,
+ .fops = &vfd_fops,
+ .minor_base = VFD_MINOR_BASE,
+};
+
+/* to prevent races between open() and disconnect() */
+static DEFINE_MUTEX(disconnect_lock);
+
+static int debug;
+
+
+/*** M O D U L E C O D E ***/
+
+MODULE_AUTHOR(MOD_AUTHOR);
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
+
+static void delete_context(struct sasem_context *context)
+{
+ usb_free_urb(context->tx_urb); /* VFD */
+ usb_free_urb(context->rx_urb); /* IR */
+ lirc_buffer_free(context->driver->rbuf);
+ kfree(context->driver->rbuf);
+ kfree(context->driver);
+ kfree(context);
+
+ if (debug)
+ printk(KERN_INFO "%s: context deleted\n", __func__);
+}
+
+static void deregister_from_lirc(struct sasem_context *context)
+{
+ int retval;
+ int minor = context->driver->minor;
+
+ retval = lirc_unregister_driver(minor);
+ if (retval)
+ err("%s: unable to deregister from lirc (%d)",
+ __func__, retval);
+ else
+ printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n",
+ minor);
+
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is opened by the application.
+ */
+static int vfd_open(struct inode *inode, struct file *file)
+{
+ struct usb_interface *interface;
+ struct sasem_context *context = NULL;
+ int subminor;
+ int retval = 0;
+
+ /* prevent races with disconnect */
+ mutex_lock(&disconnect_lock);
+
+ subminor = iminor(inode);
+ interface = usb_find_interface(&sasem_driver, subminor);
+ if (!interface) {
+ err("%s: could not find interface for minor %d",
+ __func__, subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+ context = usb_get_intfdata(interface);
+
+ if (!context) {
+ err("%s: no context found for minor %d",
+ __func__, subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (context->vfd_isopen) {
+ err("%s: VFD port is already open", __func__);
+ retval = -EBUSY;
+ } else {
+ context->vfd_isopen = 1;
+ file->private_data = context;
+ printk(KERN_INFO "VFD port opened\n");
+ }
+
+ mutex_unlock(&context->ctx_lock);
+
+exit:
+ mutex_unlock(&disconnect_lock);
+ return retval;
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is closed by the application.
+ */
+static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg)
+{
+ struct sasem_context *context = NULL;
+
+ context = (struct sasem_context *) file->private_data;
+
+ if (!context) {
+ err("%s: no context for device", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ switch (cmd) {
+ case IOCTL_LCD_CONTRAST:
+ if (arg > 1000)
+ arg = 1000;
+ context->vfd_contrast = (unsigned int)arg;
+ break;
+ default:
+ printk(KERN_INFO "Unknown IOCTL command\n");
+ mutex_unlock(&context->ctx_lock);
+ return -ENOIOCTLCMD; /* not supported */
+ }
+
+ mutex_unlock(&context->ctx_lock);
+ return 0;
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is closed by the application.
+ */
+static int vfd_close(struct inode *inode, struct file *file)
+{
+ struct sasem_context *context = NULL;
+ int retval = 0;
+
+ context = (struct sasem_context *) file->private_data;
+
+ if (!context) {
+ err("%s: no context for device", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (!context->vfd_isopen) {
+ err("%s: VFD is not open", __func__);
+ retval = -EIO;
+ } else {
+ context->vfd_isopen = 0;
+ printk(KERN_INFO "VFD port closed\n");
+ if (!context->dev_present && !context->ir_isopen) {
+
+ /* Device disconnected before close and IR port is
+ * not open. If IR port is open, context will be
+ * deleted by ir_close. */
+ mutex_unlock(&context->ctx_lock);
+ delete_context(context);
+ return retval;
+ }
+ }
+
+ mutex_unlock(&context->ctx_lock);
+ return retval;
+}
+
+/**
+ * Sends a packet to the VFD.
+ */
+static int send_packet(struct sasem_context *context)
+{
+ unsigned int pipe;
+ int interval = 0;
+ int retval = 0;
+
+ pipe = usb_sndintpipe(context->dev,
+ context->tx_endpoint->bEndpointAddress);
+ interval = context->tx_endpoint->bInterval;
+
+ usb_fill_int_urb(context->tx_urb, context->dev, pipe,
+ context->usb_tx_buf, sizeof(context->usb_tx_buf),
+ usb_tx_callback, context, interval);
+
+ context->tx_urb->actual_length = 0;
+
+ init_completion(&context->tx.finished);
+ atomic_set(&(context->tx.busy), 1);
+
+ retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
+ if (retval) {
+ atomic_set(&(context->tx.busy), 0);
+ err("%s: error submitting urb (%d)", __func__, retval);
+ } else {
+ /* Wait for transmission to complete (or abort) */
+ mutex_unlock(&context->ctx_lock);
+ wait_for_completion(&context->tx.finished);
+ mutex_lock(&context->ctx_lock);
+
+ retval = context->tx.status;
+ if (retval)
+ err("%s: packet tx failed (%d)", __func__, retval);
+ }
+
+ return retval;
+}
+
+/**
+ * Writes data to the VFD. The Sasem VFD is 2x16 characters
+ * and requires data in 9 consecutive USB interrupt packets,
+ * each packet carrying 8 bytes.
+ */
+static ssize_t vfd_write(struct file *file, const char *buf,
+ size_t n_bytes, loff_t *pos)
+{
+ int i;
+ int retval = 0;
+ struct sasem_context *context;
+ int *data_buf = NULL;
+
+ context = (struct sasem_context *) file->private_data;
+ if (!context) {
+ err("%s: no context for device", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (!context->dev_present) {
+ err("%s: no Sasem device present", __func__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) {
+ err("%s: invalid payload size", __func__);
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ data_buf = memdup_user(buf, n_bytes);
+ if (IS_ERR(data_buf)) {
+ retval = PTR_ERR(data_buf);
+ goto exit;
+ }
+
+ memcpy(context->tx.data_buf, data_buf, n_bytes);
+
+ /* Pad with spaces */
+ for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i)
+ context->tx.data_buf[i] = ' ';
+
+ /* Nine 8 byte packets to be sent */
+ /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
+ * will clear the VFD */
+ for (i = 0; i < 9; i++) {
+ switch (i) {
+ case 0:
+ memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
+ context->usb_tx_buf[1] = (context->vfd_contrast) ?
+ (0x2B - (context->vfd_contrast - 1) / 250)
+ : 0x2B;
+ break;
+ case 1:
+ memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
+ break;
+ case 2:
+ memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
+ break;
+ case 3:
+ memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
+ break;
+ case 4:
+ memcpy(context->usb_tx_buf,
+ context->tx.data_buf + 8, 8);
+ break;
+ case 5:
+ memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
+ break;
+ case 6:
+ memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
+ break;
+ case 7:
+ memcpy(context->usb_tx_buf,
+ context->tx.data_buf + 16, 8);
+ break;
+ case 8:
+ memcpy(context->usb_tx_buf,
+ context->tx.data_buf + 24, 8);
+ break;
+ }
+ retval = send_packet(context);
+ if (retval) {
+
+ err("%s: send packet failed for packet #%d",
+ __func__, i);
+ goto exit;
+ }
+ }
+exit:
+
+ mutex_unlock(&context->ctx_lock);
+ kfree(data_buf);
+
+ return (!retval) ? n_bytes : retval;
+}
+
+/**
+ * Callback function for USB core API: transmit data
+ */
+static void usb_tx_callback(struct urb *urb)
+{
+ struct sasem_context *context;
+
+ if (!urb)
+ return;
+ context = (struct sasem_context *) urb->context;
+ if (!context)
+ return;
+
+ context->tx.status = urb->status;
+
+ /* notify waiters that write has finished */
+ atomic_set(&context->tx.busy, 0);
+ complete(&context->tx.finished);
+
+ return;
+}
+
+/**
+ * Called by lirc_dev when the application opens /dev/lirc
+ */
+static int ir_open(void *data)
+{
+ int retval = 0;
+ struct sasem_context *context;
+
+ /* prevent races with disconnect */
+ mutex_lock(&disconnect_lock);
+
+ context = (struct sasem_context *) data;
+
+ mutex_lock(&context->ctx_lock);
+
+ if (context->ir_isopen) {
+ err("%s: IR port is already open", __func__);
+ retval = -EBUSY;
+ goto exit;
+ }
+
+ usb_fill_int_urb(context->rx_urb, context->dev,
+ usb_rcvintpipe(context->dev,
+ context->rx_endpoint->bEndpointAddress),
+ context->usb_rx_buf, sizeof(context->usb_rx_buf),
+ usb_rx_callback, context, context->rx_endpoint->bInterval);
+
+ retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
+
+ if (retval)
+ err("%s: usb_submit_urb failed for ir_open (%d)",
+ __func__, retval);
+ else {
+ context->ir_isopen = 1;
+ printk(KERN_INFO "IR port opened\n");
+ }
+
+exit:
+ mutex_unlock(&context->ctx_lock);
+
+ mutex_unlock(&disconnect_lock);
+ return retval;
+}
+
+/**
+ * Called by lirc_dev when the application closes /dev/lirc
+ */
+static void ir_close(void *data)
+{
+ struct sasem_context *context;
+
+ context = (struct sasem_context *)data;
+ if (!context) {
+ err("%s: no context for device", __func__);
+ return;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ usb_kill_urb(context->rx_urb);
+ context->ir_isopen = 0;
+ printk(KERN_INFO "IR port closed\n");
+
+ if (!context->dev_present) {
+
+ /*
+ * Device disconnected while IR port was
+ * still open. Driver was not deregistered
+ * at disconnect time, so do it now.
+ */
+ deregister_from_lirc(context);
+
+ if (!context->vfd_isopen) {
+
+ mutex_unlock(&context->ctx_lock);
+ delete_context(context);
+ return;
+ }
+ /* If VFD port is open, context will be deleted by vfd_close */
+ }
+
+ mutex_unlock(&context->ctx_lock);
+ return;
+}
+
+/**
+ * Process the incoming packet
+ */
+static void incoming_packet(struct sasem_context *context,
+ struct urb *urb)
+{
+ int len = urb->actual_length;
+ unsigned char *buf = urb->transfer_buffer;
+ long ms;
+ struct timeval tv;
+ int i;
+
+ if (len != 8) {
+ printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n",
+ __func__, len);
+ return;
+ }
+
+ if (debug) {
+ printk(KERN_INFO "Incoming data: ");
+ for (i = 0; i < 8; ++i)
+ printk(KERN_CONT "%02x ", buf[i]);
+ printk(KERN_CONT "\n");
+ }
+
+ /*
+ * Lirc could deal with the repeat code, but we really need to block it
+ * if it arrives too late. Otherwise we could repeat the wrong code.
+ */
+
+ /* get the time since the last button press */
+ do_gettimeofday(&tv);
+ ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
+ (tv.tv_usec - context->presstime.tv_usec) / 1000;
+
+ if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
+ /*
+ * the repeat code is being sent, so we copy
+ * the old code to LIRC
+ */
+
+ /*
+ * NOTE: Only if the last code was less than 250ms ago
+ * - no one should be able to push another (undetected) button
+ * in that time and then get a false repeat of the previous
+ * press but it is long enough for a genuine repeat
+ */
+ if ((ms < 250) && (context->codesaved != 0)) {
+ memcpy(buf, &context->lastcode, 8);
+ context->presstime.tv_sec = tv.tv_sec;
+ context->presstime.tv_usec = tv.tv_usec;
+ }
+ } else {
+ /* save the current valid code for repeats */
+ memcpy(&context->lastcode, buf, 8);
+ /*
+ * set flag to signal a valid code was save;
+ * just for safety reasons
+ */
+ context->codesaved = 1;
+ context->presstime.tv_sec = tv.tv_sec;
+ context->presstime.tv_usec = tv.tv_usec;
+ }
+
+ lirc_buffer_write(context->driver->rbuf, buf);
+ wake_up(&context->driver->rbuf->wait_poll);
+}
+
+/**
+ * Callback function for USB core API: receive data
+ */
+static void usb_rx_callback(struct urb *urb)
+{
+ struct sasem_context *context;
+
+ if (!urb)
+ return;
+ context = (struct sasem_context *) urb->context;
+ if (!context)
+ return;
+
+ switch (urb->status) {
+
+ case -ENOENT: /* usbcore unlink successful! */
+ return;
+
+ case 0:
+ if (context->ir_isopen)
+ incoming_packet(context, urb);
+ break;
+
+ default:
+ printk(KERN_WARNING "%s: status (%d): ignored",
+ __func__, urb->status);
+ break;
+ }
+
+ usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+ return;
+}
+
+
+
+/**
+ * Callback function for USB core API: Probe
+ */
+static int sasem_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = NULL;
+ struct usb_host_interface *iface_desc = NULL;
+ struct usb_endpoint_descriptor *rx_endpoint = NULL;
+ struct usb_endpoint_descriptor *tx_endpoint = NULL;
+ struct urb *rx_urb = NULL;
+ struct urb *tx_urb = NULL;
+ struct lirc_driver *driver = NULL;
+ struct lirc_buffer *rbuf = NULL;
+ int lirc_minor = 0;
+ int num_endpoints;
+ int retval = 0;
+ int vfd_ep_found;
+ int ir_ep_found;
+ int alloc_status;
+ struct sasem_context *context = NULL;
+ int i;
+
+ printk(KERN_INFO "%s: found Sasem device\n", __func__);
+
+
+ dev = usb_get_dev(interface_to_usbdev(interface));
+ iface_desc = interface->cur_altsetting;
+ num_endpoints = iface_desc->desc.bNumEndpoints;
+
+ /*
+ * Scan the endpoint list and set:
+ * first input endpoint = IR endpoint
+ * first output endpoint = VFD endpoint
+ */
+
+ ir_ep_found = 0;
+ vfd_ep_found = 0;
+
+ for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
+
+ struct usb_endpoint_descriptor *ep;
+ int ep_dir;
+ int ep_type;
+ ep = &iface_desc->endpoint [i].desc;
+ ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+ ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ if (!ir_ep_found &&
+ ep_dir == USB_DIR_IN &&
+ ep_type == USB_ENDPOINT_XFER_INT) {
+
+ rx_endpoint = ep;
+ ir_ep_found = 1;
+ if (debug)
+ printk(KERN_INFO "%s: found IR endpoint\n",
+ __func__);
+
+ } else if (!vfd_ep_found &&
+ ep_dir == USB_DIR_OUT &&
+ ep_type == USB_ENDPOINT_XFER_INT) {
+
+ tx_endpoint = ep;
+ vfd_ep_found = 1;
+ if (debug)
+ printk(KERN_INFO "%s: found VFD endpoint\n",
+ __func__);
+ }
+ }
+
+ /* Input endpoint is mandatory */
+ if (!ir_ep_found) {
+
+ err("%s: no valid input (IR) endpoint found.", __func__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ if (!vfd_ep_found)
+ printk(KERN_INFO "%s: no valid output (VFD) endpoint found.\n",
+ __func__);
+
+
+ /* Allocate memory */
+ alloc_status = 0;
+
+ context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL);
+ if (!context) {
+ err("%s: kzalloc failed for context", __func__);
+ alloc_status = 1;
+ goto alloc_status_switch;
+ }
+ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
+ if (!driver) {
+ err("%s: kzalloc failed for lirc_driver", __func__);
+ alloc_status = 2;
+ goto alloc_status_switch;
+ }
+ rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+ if (!rbuf) {
+ err("%s: kmalloc failed for lirc_buffer", __func__);
+ alloc_status = 3;
+ goto alloc_status_switch;
+ }
+ if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
+ err("%s: lirc_buffer_init failed", __func__);
+ alloc_status = 4;
+ goto alloc_status_switch;
+ }
+ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rx_urb) {
+ err("%s: usb_alloc_urb failed for IR urb", __func__);
+ alloc_status = 5;
+ goto alloc_status_switch;
+ }
+ if (vfd_ep_found) {
+ tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!tx_urb) {
+ err("%s: usb_alloc_urb failed for VFD urb",
+ __func__);
+ alloc_status = 6;
+ goto alloc_status_switch;
+ }
+ }
+
+ mutex_init(&context->ctx_lock);
+
+ strcpy(driver->name, MOD_NAME);
+ driver->minor = -1;
+ driver->code_length = 64;
+ driver->sample_rate = 0;
+ driver->features = LIRC_CAN_REC_LIRCCODE;
+ driver->data = context;
+ driver->rbuf = rbuf;
+ driver->set_use_inc = ir_open;
+ driver->set_use_dec = ir_close;
+ driver->dev = &interface->dev;
+ driver->owner = THIS_MODULE;
+
+ mutex_lock(&context->ctx_lock);
+
+ lirc_minor = lirc_register_driver(driver);
+ if (lirc_minor < 0) {
+ err("%s: lirc_register_driver failed", __func__);
+ alloc_status = 7;
+ retval = lirc_minor;
+ goto unlock;
+ } else
+ printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n",
+ __func__, lirc_minor);
+
+ /* Needed while unregistering! */
+ driver->minor = lirc_minor;
+
+ context->dev = dev;
+ context->dev_present = 1;
+ context->rx_endpoint = rx_endpoint;
+ context->rx_urb = rx_urb;
+ if (vfd_ep_found) {
+ context->tx_endpoint = tx_endpoint;
+ context->tx_urb = tx_urb;
+ context->vfd_contrast = 1000; /* range 0 - 1000 */
+ }
+ context->driver = driver;
+
+ usb_set_intfdata(interface, context);
+
+ if (vfd_ep_found) {
+
+ if (debug)
+ printk(KERN_INFO "Registering VFD with sysfs\n");
+ if (usb_register_dev(interface, &sasem_class))
+ /* Not a fatal error, so ignore */
+ printk(KERN_INFO "%s: could not get a minor number "
+ "for VFD\n", __func__);
+ }
+
+ printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n",
+ __func__, dev->bus->busnum, dev->devnum);
+unlock:
+ mutex_unlock(&context->ctx_lock);
+
+alloc_status_switch:
+ switch (alloc_status) {
+
+ case 7:
+ if (vfd_ep_found)
+ usb_free_urb(tx_urb);
+ case 6:
+ usb_free_urb(rx_urb);
+ case 5:
+ lirc_buffer_free(rbuf);
+ case 4:
+ kfree(rbuf);
+ case 3:
+ kfree(driver);
+ case 2:
+ kfree(context);
+ context = NULL;
+ case 1:
+ if (retval == 0)
+ retval = -ENOMEM;
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * Callback function for USB core API: disonnect
+ */
+static void sasem_disconnect(struct usb_interface *interface)
+{
+ struct sasem_context *context;
+
+ /* prevent races with ir_open()/vfd_open() */
+ mutex_lock(&disconnect_lock);
+
+ context = usb_get_intfdata(interface);
+ mutex_lock(&context->ctx_lock);
+
+ printk(KERN_INFO "%s: Sasem device disconnected\n", __func__);
+
+ usb_set_intfdata(interface, NULL);
+ context->dev_present = 0;
+
+ /* Stop reception */
+ usb_kill_urb(context->rx_urb);
+
+ /* Abort ongoing write */
+ if (atomic_read(&context->tx.busy)) {
+
+ usb_kill_urb(context->tx_urb);
+ wait_for_completion(&context->tx.finished);
+ }
+
+ /* De-register from lirc_dev if IR port is not open */
+ if (!context->ir_isopen)
+ deregister_from_lirc(context);
+
+ usb_deregister_dev(interface, &sasem_class);
+
+ mutex_unlock(&context->ctx_lock);
+
+ if (!context->ir_isopen && !context->vfd_isopen)
+ delete_context(context);
+
+ mutex_unlock(&disconnect_lock);
+}
+
+static int __init sasem_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n");
+ printk(KERN_INFO MOD_AUTHOR "\n");
+
+ rc = usb_register(&sasem_driver);
+ if (rc < 0) {
+ err("%s: usb register failed (%d)", __func__, rc);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void __exit sasem_exit(void)
+{
+ usb_deregister(&sasem_driver);
+ printk(KERN_INFO "module removed. Goodbye!\n");
+}
+
+
+module_init(sasem_init);
+module_exit(sasem_exit);
--- /dev/null
+/*
+ * lirc_serial.c
+ *
+ * lirc_serial - Device driver that records pulse- and pause-lengths
+ * (space-lengths) between DDCD event on a serial port.
+ *
+ * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
+ * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
+ * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
+ * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
+ * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
+ * 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
+ *
+ */
+
+/*
+ * Steve's changes to improve transmission fidelity:
+ * - for systems with the rdtsc instruction and the clock counter, a
+ * send_pule that times the pulses directly using the counter.
+ * This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is
+ * not needed. Measurement shows very stable waveform, even where
+ * PCI activity slows the access to the UART, which trips up other
+ * versions.
+ * - For other system, non-integer-microsecond pulse/space lengths,
+ * done using fixed point binary. So, much more accurate carrier
+ * frequency.
+ * - fine tuned transmitter latency, taking advantage of fractional
+ * microseconds in previous change
+ * - Fixed bug in the way transmitter latency was accounted for by
+ * tuning the pulse lengths down - the send_pulse routine ignored
+ * this overhead as it timed the overall pulse length - so the
+ * pulse frequency was right but overall pulse length was too
+ * long. Fixed by accounting for latency on each pulse/space
+ * iteration.
+ *
+ * Steve Davies <steve@daviesfam.org> July 2001
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+
+#include <asm/system.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/fcntl.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
+#include <asm/hardware.h>
+#endif
+/* From Intel IXP42X Developer's Manual (#252480-005): */
+/* ftp://download.intel.com/design/network/manuals/25248005.pdf */
+#define UART_IE_IXP42X_UUE 0x40 /* IXP42X UART Unit enable */
+#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+#define LIRC_DRIVER_NAME "lirc_serial"
+
+struct lirc_serial {
+ int signal_pin;
+ int signal_pin_change;
+ u8 on;
+ u8 off;
+ long (*send_pulse)(unsigned long length);
+ void (*send_space)(long length);
+ int features;
+ spinlock_t lock;
+};
+
+#define LIRC_HOMEBREW 0
+#define LIRC_IRDEO 1
+#define LIRC_IRDEO_REMOTE 2
+#define LIRC_ANIMAX 3
+#define LIRC_IGOR 4
+#define LIRC_NSLU2 5
+
+/*** module parameters ***/
+static int type;
+static int io;
+static int irq;
+static int iommap;
+static int ioshift;
+static int softcarrier = 1;
+static int share_irq;
+static int debug;
+static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */
+static int txsense; /* 0 = active high, 1 = active low */
+
+#define dprintk(fmt, args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
+ fmt, ## args); \
+ } while (0)
+
+/* forward declarations */
+static long send_pulse_irdeo(unsigned long length);
+static long send_pulse_homebrew(unsigned long length);
+static void send_space_irdeo(long length);
+static void send_space_homebrew(long length);
+
+static struct lirc_serial hardware[] = {
+ [LIRC_HOMEBREW] = {
+ .signal_pin = UART_MSR_DCD,
+ .signal_pin_change = UART_MSR_DDCD,
+ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
+ .off = (UART_MCR_RTS | UART_MCR_OUT2),
+ .send_pulse = send_pulse_homebrew,
+ .send_space = send_space_homebrew,
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
+ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SET_SEND_CARRIER |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
+#else
+ .features = LIRC_CAN_REC_MODE2
+#endif
+ },
+
+ [LIRC_IRDEO] = {
+ .signal_pin = UART_MSR_DSR,
+ .signal_pin_change = UART_MSR_DDSR,
+ .on = UART_MCR_OUT2,
+ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
+ .send_pulse = send_pulse_irdeo,
+ .send_space = send_space_irdeo,
+ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
+ },
+
+ [LIRC_IRDEO_REMOTE] = {
+ .signal_pin = UART_MSR_DSR,
+ .signal_pin_change = UART_MSR_DDSR,
+ .on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
+ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
+ .send_pulse = send_pulse_irdeo,
+ .send_space = send_space_irdeo,
+ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
+ },
+
+ [LIRC_ANIMAX] = {
+ .signal_pin = UART_MSR_DCD,
+ .signal_pin_change = UART_MSR_DDCD,
+ .on = 0,
+ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
+ .send_pulse = NULL,
+ .send_space = NULL,
+ .features = LIRC_CAN_REC_MODE2
+ },
+
+ [LIRC_IGOR] = {
+ .signal_pin = UART_MSR_DSR,
+ .signal_pin_change = UART_MSR_DDSR,
+ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
+ .off = (UART_MCR_RTS | UART_MCR_OUT2),
+ .send_pulse = send_pulse_homebrew,
+ .send_space = send_space_homebrew,
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
+ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SET_SEND_CARRIER |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
+#else
+ .features = LIRC_CAN_REC_MODE2
+#endif
+ },
+
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
+ /*
+ * Modified Linksys Network Storage Link USB 2.0 (NSLU2):
+ * We receive on CTS of the 2nd serial port (R142,LHS), we
+ * transmit with a IR diode between GPIO[1] (green status LED),
+ * and ground (Matthias Goebl <matthias.goebl@goebl.net>).
+ * See also http://www.nslu2-linux.org for this device
+ */
+ [LIRC_NSLU2] = {
+ .signal_pin = UART_MSR_CTS,
+ .signal_pin_change = UART_MSR_DCTS,
+ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
+ .off = (UART_MCR_RTS | UART_MCR_OUT2),
+ .send_pulse = send_pulse_homebrew,
+ .send_space = send_space_homebrew,
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
+ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SET_SEND_CARRIER |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
+#else
+ .features = LIRC_CAN_REC_MODE2
+#endif
+ },
+#endif
+
+};
+
+#define RS_ISR_PASS_LIMIT 256
+
+/*
+ * A long pulse code from a remote might take up to 300 bytes. The
+ * daemon should read the bytes as soon as they are generated, so take
+ * the number of keys you think you can push before the daemon runs
+ * and multiply by 300. The driver will warn you if you overrun this
+ * buffer. If you have a slow computer or non-busmastering IDE disks,
+ * maybe you will need to increase this.
+ */
+
+/* This MUST be a power of two! It has to be larger than 1 as well. */
+
+#define RBUF_LEN 256
+
+static struct timeval lasttv = {0, 0};
+
+static struct lirc_buffer rbuf;
+
+static unsigned int freq = 38000;
+static unsigned int duty_cycle = 50;
+
+/* Initialized in init_timing_params() */
+static unsigned long period;
+static unsigned long pulse_width;
+static unsigned long space_width;
+
+#if defined(__i386__)
+/*
+ * From:
+ * Linux I/O port programming mini-HOWTO
+ * Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
+ * v, 28 December 1997
+ *
+ * [...]
+ * Actually, a port I/O instruction on most ports in the 0-0x3ff range
+ * takes almost exactly 1 microsecond, so if you're, for example, using
+ * the parallel port directly, just do additional inb()s from that port
+ * to delay.
+ * [...]
+ */
+/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
+ * comment above plus trimming to match actual measured frequency.
+ * This will be sensitive to cpu speed, though hopefully most of the 1.5us
+ * is spent in the uart access. Still - for reference test machine was a
+ * 1.13GHz Athlon system - Steve
+ */
+
+/*
+ * changed from 400 to 450 as this works better on slower machines;
+ * faster machines will use the rdtsc code anyway
+ */
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
+
+#else
+
+/* does anybody have information on other platforms ? */
+/* 256 = 1<<8 */
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
+
+#endif /* __i386__ */
+/*
+ * FIXME: should we be using hrtimers instead of this
+ * LIRC_SERIAL_TRANSMITTER_LATENCY nonsense?
+ */
+
+/* fetch serial input packet (1 byte) from register offset */
+static u8 sinp(int offset)
+{
+ if (iommap != 0)
+ /* the register is memory-mapped */
+ offset <<= ioshift;
+
+ return inb(io + offset);
+}
+
+/* write serial output packet (1 byte) of value to register offset */
+static void soutp(int offset, u8 value)
+{
+ if (iommap != 0)
+ /* the register is memory-mapped */
+ offset <<= ioshift;
+
+ outb(value, io + offset);
+}
+
+static void on(void)
+{
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
+ /*
+ * On NSLU2, we put the transmit diode between the output of the green
+ * status LED and ground
+ */
+ if (type == LIRC_NSLU2) {
+ gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_LOW);
+ return;
+ }
+#endif
+ if (txsense)
+ soutp(UART_MCR, hardware[type].off);
+ else
+ soutp(UART_MCR, hardware[type].on);
+}
+
+static void off(void)
+{
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
+ if (type == LIRC_NSLU2) {
+ gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_HIGH);
+ return;
+ }
+#endif
+ if (txsense)
+ soutp(UART_MCR, hardware[type].on);
+ else
+ soutp(UART_MCR, hardware[type].off);
+}
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static void safe_udelay(unsigned long usecs)
+{
+ while (usecs > MAX_UDELAY_US) {
+ udelay(MAX_UDELAY_US);
+ usecs -= MAX_UDELAY_US;
+ }
+ udelay(usecs);
+}
+
+#ifdef USE_RDTSC
+/*
+ * This is an overflow/precision juggle, complicated in that we can't
+ * do long long divide in the kernel
+ */
+
+/*
+ * When we use the rdtsc instruction to measure clocks, we keep the
+ * pulse and space widths as clock cycles. As this is CPU speed
+ * dependent, the widths must be calculated in init_port and ioctl
+ * time
+ */
+
+/* So send_pulse can quickly convert microseconds to clocks */
+static unsigned long conv_us_to_clocks;
+
+static int init_timing_params(unsigned int new_duty_cycle,
+ unsigned int new_freq)
+{
+ __u64 loops_per_sec, work;
+
+ duty_cycle = new_duty_cycle;
+ freq = new_freq;
+
+ loops_per_sec = __this_cpu_read(cpu.info.loops_per_jiffy);
+ loops_per_sec *= HZ;
+
+ /* How many clocks in a microsecond?, avoiding long long divide */
+ work = loops_per_sec;
+ work *= 4295; /* 4295 = 2^32 / 1e6 */
+ conv_us_to_clocks = (work >> 32);
+
+ /*
+ * Carrier period in clocks, approach good up to 32GHz clock,
+ * gets carrier frequency within 8Hz
+ */
+ period = loops_per_sec >> 3;
+ period /= (freq >> 3);
+
+ /* Derive pulse and space from the period */
+ pulse_width = period * duty_cycle / 100;
+ space_width = period - pulse_width;
+ dprintk("in init_timing_params, freq=%d, duty_cycle=%d, "
+ "clk/jiffy=%ld, pulse=%ld, space=%ld, "
+ "conv_us_to_clocks=%ld\n",
+ freq, duty_cycle, __this_cpu_read(cpu_info.loops_per_jiffy),
+ pulse_width, space_width, conv_us_to_clocks);
+ return 0;
+}
+#else /* ! USE_RDTSC */
+static int init_timing_params(unsigned int new_duty_cycle,
+ unsigned int new_freq)
+{
+/*
+ * period, pulse/space width are kept with 8 binary places -
+ * IE multiplied by 256.
+ */
+ if (256 * 1000000L / new_freq * new_duty_cycle / 100 <=
+ LIRC_SERIAL_TRANSMITTER_LATENCY)
+ return -EINVAL;
+ if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <=
+ LIRC_SERIAL_TRANSMITTER_LATENCY)
+ return -EINVAL;
+ duty_cycle = new_duty_cycle;
+ freq = new_freq;
+ period = 256 * 1000000L / freq;
+ pulse_width = period * duty_cycle / 100;
+ space_width = period - pulse_width;
+ dprintk("in init_timing_params, freq=%d pulse=%ld, "
+ "space=%ld\n", freq, pulse_width, space_width);
+ return 0;
+}
+#endif /* USE_RDTSC */
+
+
+/* return value: space length delta */
+
+static long send_pulse_irdeo(unsigned long length)
+{
+ long rawbits, ret;
+ int i;
+ unsigned char output;
+ unsigned char chunk, shifted;
+
+ /* how many bits have to be sent ? */
+ rawbits = length * 1152 / 10000;
+ if (duty_cycle > 50)
+ chunk = 3;
+ else
+ chunk = 1;
+ for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) {
+ shifted = chunk << (i * 3);
+ shifted >>= 1;
+ output &= (~shifted);
+ i++;
+ if (i == 3) {
+ soutp(UART_TX, output);
+ while (!(sinp(UART_LSR) & UART_LSR_THRE))
+ ;
+ output = 0x7f;
+ i = 0;
+ }
+ }
+ if (i != 0) {
+ soutp(UART_TX, output);
+ while (!(sinp(UART_LSR) & UART_LSR_TEMT))
+ ;
+ }
+
+ if (i == 0)
+ ret = (-rawbits) * 10000 / 1152;
+ else
+ ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152;
+
+ return ret;
+}
+
+#ifdef USE_RDTSC
+/* Version that uses Pentium rdtsc instruction to measure clocks */
+
+/*
+ * This version does sub-microsecond timing using rdtsc instruction,
+ * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
+ * Implicitly i586 architecture... - Steve
+ */
+
+static long send_pulse_homebrew_softcarrier(unsigned long length)
+{
+ int flag;
+ unsigned long target, start, now;
+
+ /* Get going quick as we can */
+ rdtscl(start);
+ on();
+ /* Convert length from microseconds to clocks */
+ length *= conv_us_to_clocks;
+ /* And loop till time is up - flipping at right intervals */
+ now = start;
+ target = pulse_width;
+ flag = 1;
+ /*
+ * FIXME: This looks like a hard busy wait, without even an occasional,
+ * polite, cpu_relax() call. There's got to be a better way?
+ *
+ * The i2c code has the result of a lot of bit-banging work, I wonder if
+ * there's something there which could be helpful here.
+ */
+ while ((now - start) < length) {
+ /* Delay till flip time */
+ do {
+ rdtscl(now);
+ } while ((now - start) < target);
+
+ /* flip */
+ if (flag) {
+ rdtscl(now);
+ off();
+ target += space_width;
+ } else {
+ rdtscl(now); on();
+ target += pulse_width;
+ }
+ flag = !flag;
+ }
+ rdtscl(now);
+ return ((now - start) - length) / conv_us_to_clocks;
+}
+#else /* ! USE_RDTSC */
+/* Version using udelay() */
+
+/*
+ * here we use fixed point arithmetic, with 8
+ * fractional bits. that gets us within 0.1% or so of the right average
+ * frequency, albeit with some jitter in pulse length - Steve
+ */
+
+/* To match 8 fractional bits used for pulse/space length */
+
+static long send_pulse_homebrew_softcarrier(unsigned long length)
+{
+ int flag;
+ unsigned long actual, target, d;
+ length <<= 8;
+
+ actual = 0; target = 0; flag = 0;
+ while (actual < length) {
+ if (flag) {
+ off();
+ target += space_width;
+ } else {
+ on();
+ target += pulse_width;
+ }
+ d = (target - actual -
+ LIRC_SERIAL_TRANSMITTER_LATENCY + 128) >> 8;
+ /*
+ * Note - we've checked in ioctl that the pulse/space
+ * widths are big enough so that d is > 0
+ */
+ udelay(d);
+ actual += (d << 8) + LIRC_SERIAL_TRANSMITTER_LATENCY;
+ flag = !flag;
+ }
+ return (actual-length) >> 8;
+}
+#endif /* USE_RDTSC */
+
+static long send_pulse_homebrew(unsigned long length)
+{
+ if (length <= 0)
+ return 0;
+
+ if (softcarrier)
+ return send_pulse_homebrew_softcarrier(length);
+ else {
+ on();
+ safe_udelay(length);
+ return 0;
+ }
+}
+
+static void send_space_irdeo(long length)
+{
+ if (length <= 0)
+ return;
+
+ safe_udelay(length);
+}
+
+static void send_space_homebrew(long length)
+{
+ off();
+ if (length <= 0)
+ return;
+ safe_udelay(length);
+}
+
+static void rbwrite(int l)
+{
+ if (lirc_buffer_full(&rbuf)) {
+ /* no new signals will be accepted */
+ dprintk("Buffer overrun\n");
+ return;
+ }
+ lirc_buffer_write(&rbuf, (void *)&l);
+}
+
+static void frbwrite(int l)
+{
+ /* simple noise filter */
+ static int pulse, space;
+ static unsigned int ptr;
+
+ if (ptr > 0 && (l & PULSE_BIT)) {
+ pulse += l & PULSE_MASK;
+ if (pulse > 250) {
+ rbwrite(space);
+ rbwrite(pulse | PULSE_BIT);
+ ptr = 0;
+ pulse = 0;
+ }
+ return;
+ }
+ if (!(l & PULSE_BIT)) {
+ if (ptr == 0) {
+ if (l > 20000) {
+ space = l;
+ ptr++;
+ return;
+ }
+ } else {
+ if (l > 20000) {
+ space += pulse;
+ if (space > PULSE_MASK)
+ space = PULSE_MASK;
+ space += l;
+ if (space > PULSE_MASK)
+ space = PULSE_MASK;
+ pulse = 0;
+ return;
+ }
+ rbwrite(space);
+ rbwrite(pulse | PULSE_BIT);
+ ptr = 0;
+ pulse = 0;
+ }
+ }
+ rbwrite(l);
+}
+
+static irqreturn_t irq_handler(int i, void *blah)
+{
+ struct timeval tv;
+ int counter, dcd;
+ u8 status;
+ long deltv;
+ int data;
+ static int last_dcd = -1;
+
+ if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
+ /* not our interrupt */
+ return IRQ_NONE;
+ }
+
+ counter = 0;
+ do {
+ counter++;
+ status = sinp(UART_MSR);
+ if (counter > RS_ISR_PASS_LIMIT) {
+ printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: "
+ "We're caught!\n");
+ break;
+ }
+ if ((status & hardware[type].signal_pin_change)
+ && sense != -1) {
+ /* get current time */
+ do_gettimeofday(&tv);
+
+ /* New mode, written by Trent Piepho
+ <xyzzy@u.washington.edu>. */
+
+ /*
+ * The old format was not very portable.
+ * We now use an int to pass pulses
+ * and spaces to user space.
+ *
+ * If PULSE_BIT is set a pulse has been
+ * received, otherwise a space has been
+ * received. The driver needs to know if your
+ * receiver is active high or active low, or
+ * the space/pulse sense could be
+ * inverted. The bits denoted by PULSE_MASK are
+ * the length in microseconds. Lengths greater
+ * than or equal to 16 seconds are clamped to
+ * PULSE_MASK. All other bits are unused.
+ * This is a much simpler interface for user
+ * programs, as well as eliminating "out of
+ * phase" errors with space/pulse
+ * autodetection.
+ */
+
+ /* calc time since last interrupt in microseconds */
+ dcd = (status & hardware[type].signal_pin) ? 1 : 0;
+
+ if (dcd == last_dcd) {
+ printk(KERN_WARNING LIRC_DRIVER_NAME
+ ": ignoring spike: %d %d %lx %lx %lx %lx\n",
+ dcd, sense,
+ tv.tv_sec, lasttv.tv_sec,
+ tv.tv_usec, lasttv.tv_usec);
+ continue;
+ }
+
+ deltv = tv.tv_sec-lasttv.tv_sec;
+ if (tv.tv_sec < lasttv.tv_sec ||
+ (tv.tv_sec == lasttv.tv_sec &&
+ tv.tv_usec < lasttv.tv_usec)) {
+ printk(KERN_WARNING LIRC_DRIVER_NAME
+ ": AIEEEE: your clock just jumped "
+ "backwards\n");
+ printk(KERN_WARNING LIRC_DRIVER_NAME
+ ": %d %d %lx %lx %lx %lx\n",
+ dcd, sense,
+ tv.tv_sec, lasttv.tv_sec,
+ tv.tv_usec, lasttv.tv_usec);
+ data = PULSE_MASK;
+ } else if (deltv > 15) {
+ data = PULSE_MASK; /* really long time */
+ if (!(dcd^sense)) {
+ /* sanity check */
+ printk(KERN_WARNING LIRC_DRIVER_NAME
+ ": AIEEEE: "
+ "%d %d %lx %lx %lx %lx\n",
+ dcd, sense,
+ tv.tv_sec, lasttv.tv_sec,
+ tv.tv_usec, lasttv.tv_usec);
+ /*
+ * detecting pulse while this
+ * MUST be a space!
+ */
+ sense = sense ? 0 : 1;
+ }
+ } else
+ data = (int) (deltv*1000000 +
+ tv.tv_usec -
+ lasttv.tv_usec);
+ frbwrite(dcd^sense ? data : (data|PULSE_BIT));
+ lasttv = tv;
+ last_dcd = dcd;
+ wake_up_interruptible(&rbuf.wait_poll);
+ }
+ } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
+ return IRQ_HANDLED;
+}
+
+
+static int hardware_init_port(void)
+{
+ u8 scratch, scratch2, scratch3;
+
+ /*
+ * This is a simple port existence test, borrowed from the autoconfig
+ * function in drivers/serial/8250.c
+ */
+ scratch = sinp(UART_IER);
+ soutp(UART_IER, 0);
+#ifdef __i386__
+ outb(0xff, 0x080);
+#endif
+ scratch2 = sinp(UART_IER) & 0x0f;
+ soutp(UART_IER, 0x0f);
+#ifdef __i386__
+ outb(0x00, 0x080);
+#endif
+ scratch3 = sinp(UART_IER) & 0x0f;
+ soutp(UART_IER, scratch);
+ if (scratch2 != 0 || scratch3 != 0x0f) {
+ /* we fail, there's nothing here */
+ printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test "
+ "failed, cannot continue\n");
+ return -EINVAL;
+ }
+
+
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* First of all, disable all interrupts */
+ soutp(UART_IER, sinp(UART_IER) &
+ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+ /* Clear registers. */
+ sinp(UART_LSR);
+ sinp(UART_RX);
+ sinp(UART_IIR);
+ sinp(UART_MSR);
+
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
+ if (type == LIRC_NSLU2) {
+ /* Setup NSLU2 UART */
+
+ /* Enable UART */
+ soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE);
+ /* Disable Receiver data Time out interrupt */
+ soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE);
+ /* set out2 = interrupt unmask; off() doesn't set MCR
+ on NSLU2 */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+ }
+#endif
+
+ /* Set line for power source */
+ off();
+
+ /* Clear registers again to be sure. */
+ sinp(UART_LSR);
+ sinp(UART_RX);
+ sinp(UART_IIR);
+ sinp(UART_MSR);
+
+ switch (type) {
+ case LIRC_IRDEO:
+ case LIRC_IRDEO_REMOTE:
+ /* setup port to 7N1 @ 115200 Baud */
+ /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+ /* Set divisor to 1 => 115200 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 1);
+ /* Set DLAB 0 + 7N1 */
+ soutp(UART_LCR, UART_LCR_WLEN7);
+ /* THR interrupt already disabled at this point */
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int init_port(void)
+{
+ int i, nlow, nhigh, result;
+
+ result = request_irq(irq, irq_handler,
+ (share_irq ? IRQF_SHARED : 0),
+ LIRC_DRIVER_NAME, (void *)&hardware);
+
+ switch (result) {
+ case -EBUSY:
+ printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
+ return -EBUSY;
+ case -EINVAL:
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": Bad irq number or handler\n");
+ return -EINVAL;
+ default:
+ break;
+ };
+
+ /* Reserve io region. */
+ /*
+ * Future MMAP-Developers: Attention!
+ * For memory mapped I/O you *might* need to use ioremap() first,
+ * for the NSLU2 it's done in boot code.
+ */
+ if (((iommap != 0)
+ && (request_mem_region(iommap, 8 << ioshift,
+ LIRC_DRIVER_NAME) == NULL))
+ || ((iommap == 0)
+ && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) {
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": port %04x already in use\n", io);
+ printk(KERN_WARNING LIRC_DRIVER_NAME
+ ": use 'setserial /dev/ttySX uart none'\n");
+ printk(KERN_WARNING LIRC_DRIVER_NAME
+ ": or compile the serial port driver as module and\n");
+ printk(KERN_WARNING LIRC_DRIVER_NAME
+ ": make sure this module is loaded first\n");
+ return -EBUSY;
+ }
+
+ if (hardware_init_port() < 0)
+ return -EINVAL;
+
+ /* Initialize pulse/space widths */
+ init_timing_params(duty_cycle, freq);
+
+ /* If pin is high, then this must be an active low receiver. */
+ if (sense == -1) {
+ /* wait 1/2 sec for the power supply */
+ msleep(500);
+
+ /*
+ * probe 9 times every 0.04s, collect "votes" for
+ * active high/low
+ */
+ nlow = 0;
+ nhigh = 0;
+ for (i = 0; i < 9; i++) {
+ if (sinp(UART_MSR) & hardware[type].signal_pin)
+ nlow++;
+ else
+ nhigh++;
+ msleep(40);
+ }
+ sense = (nlow >= nhigh ? 1 : 0);
+ printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active "
+ "%s receiver\n", sense ? "low" : "high");
+ } else
+ printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active "
+ "%s receiver\n", sense ? "low" : "high");
+
+ dprintk("Interrupt %d, port %04x obtained\n", irq, io);
+ return 0;
+}
+
+static int set_use_inc(void *data)
+{
+ unsigned long flags;
+
+ /* initialize timestamp */
+ do_gettimeofday(&lasttv);
+
+ spin_lock_irqsave(&hardware[type].lock, flags);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
+
+ spin_unlock_irqrestore(&hardware[type].lock, flags);
+
+ return 0;
+}
+
+static void set_use_dec(void *data)
+{ unsigned long flags;
+
+ spin_lock_irqsave(&hardware[type].lock, flags);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* First of all, disable all interrupts */
+ soutp(UART_IER, sinp(UART_IER) &
+ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+ spin_unlock_irqrestore(&hardware[type].lock, flags);
+}
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+ size_t n, loff_t *ppos)
+{
+ int i, count;
+ unsigned long flags;
+ long delta = 0;
+ int *wbuf;
+
+ if (!(hardware[type].features & LIRC_CAN_SEND_PULSE))
+ return -EBADF;
+
+ count = n / sizeof(int);
+ if (n % sizeof(int) || count % 2 == 0)
+ return -EINVAL;
+ wbuf = memdup_user(buf, n);
+ if (IS_ERR(wbuf))
+ return PTR_ERR(wbuf);
+ spin_lock_irqsave(&hardware[type].lock, flags);
+ if (type == LIRC_IRDEO) {
+ /* DTR, RTS down */
+ on();
+ }
+ for (i = 0; i < count; i++) {
+ if (i%2)
+ hardware[type].send_space(wbuf[i] - delta);
+ else
+ delta = hardware[type].send_pulse(wbuf[i]);
+ }
+ off();
+ spin_unlock_irqrestore(&hardware[type].lock, flags);
+ kfree(wbuf);
+ return n;
+}
+
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ int result;
+ __u32 value;
+
+ switch (cmd) {
+ case LIRC_GET_SEND_MODE:
+ if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
+ return -ENOIOCTLCMD;
+
+ result = put_user(LIRC_SEND2MODE
+ (hardware[type].features&LIRC_CAN_SEND_MASK),
+ (__u32 *) arg);
+ if (result)
+ return result;
+ break;
+
+ case LIRC_SET_SEND_MODE:
+ if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
+ return -ENOIOCTLCMD;
+
+ result = get_user(value, (__u32 *) arg);
+ if (result)
+ return result;
+ /* only LIRC_MODE_PULSE supported */
+ if (value != LIRC_MODE_PULSE)
+ return -ENOSYS;
+ break;
+
+ case LIRC_GET_LENGTH:
+ return -ENOSYS;
+ break;
+
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ dprintk("SET_SEND_DUTY_CYCLE\n");
+ if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
+ return -ENOIOCTLCMD;
+
+ result = get_user(value, (__u32 *) arg);
+ if (result)
+ return result;
+ if (value <= 0 || value > 100)
+ return -EINVAL;
+ return init_timing_params(value, freq);
+ break;
+
+ case LIRC_SET_SEND_CARRIER:
+ dprintk("SET_SEND_CARRIER\n");
+ if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
+ return -ENOIOCTLCMD;
+
+ result = get_user(value, (__u32 *) arg);
+ if (result)
+ return result;
+ if (value > 500000 || value < 20000)
+ return -EINVAL;
+ return init_timing_params(duty_cycle, value);
+ break;
+
+ default:
+ return lirc_dev_fop_ioctl(filep, cmd, arg);
+ }
+ return 0;
+}
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .write = lirc_write,
+ .unlocked_ioctl = lirc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lirc_ioctl,
+#endif
+ .read = lirc_dev_fop_read,
+ .poll = lirc_dev_fop_poll,
+ .open = lirc_dev_fop_open,
+ .release = lirc_dev_fop_close,
+ .llseek = no_llseek,
+};
+
+static struct lirc_driver driver = {
+ .name = LIRC_DRIVER_NAME,
+ .minor = -1,
+ .code_length = 1,
+ .sample_rate = 0,
+ .data = NULL,
+ .add_to_buf = NULL,
+ .rbuf = &rbuf,
+ .set_use_inc = set_use_inc,
+ .set_use_dec = set_use_dec,
+ .fops = &lirc_fops,
+ .dev = NULL,
+ .owner = THIS_MODULE,
+};
+
+static struct platform_device *lirc_serial_dev;
+
+static int __devinit lirc_serial_probe(struct platform_device *dev)
+{
+ return 0;
+}
+
+static int __devexit lirc_serial_remove(struct platform_device *dev)
+{
+ return 0;
+}
+
+static int lirc_serial_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* Disable all interrupts */
+ soutp(UART_IER, sinp(UART_IER) &
+ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+ /* Clear registers. */
+ sinp(UART_LSR);
+ sinp(UART_RX);
+ sinp(UART_IIR);
+ sinp(UART_MSR);
+
+ return 0;
+}
+
+/* twisty maze... need a forward-declaration here... */
+static void lirc_serial_exit(void);
+
+static int lirc_serial_resume(struct platform_device *dev)
+{
+ unsigned long flags;
+
+ if (hardware_init_port() < 0) {
+ lirc_serial_exit();
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&hardware[type].lock, flags);
+ /* Enable Interrupt */
+ do_gettimeofday(&lasttv);
+ soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
+ off();
+
+ lirc_buffer_clear(&rbuf);
+
+ spin_unlock_irqrestore(&hardware[type].lock, flags);
+
+ return 0;
+}
+
+static struct platform_driver lirc_serial_driver = {
+ .probe = lirc_serial_probe,
+ .remove = __devexit_p(lirc_serial_remove),
+ .suspend = lirc_serial_suspend,
+ .resume = lirc_serial_resume,
+ .driver = {
+ .name = "lirc_serial",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init lirc_serial_init(void)
+{
+ int result;
+
+ /* Init read buffer. */
+ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
+ if (result < 0)
+ return -ENOMEM;
+
+ result = platform_driver_register(&lirc_serial_driver);
+ if (result) {
+ printk("lirc register returned %d\n", result);
+ goto exit_buffer_free;
+ }
+
+ lirc_serial_dev = platform_device_alloc("lirc_serial", 0);
+ if (!lirc_serial_dev) {
+ result = -ENOMEM;
+ goto exit_driver_unregister;
+ }
+
+ result = platform_device_add(lirc_serial_dev);
+ if (result)
+ goto exit_device_put;
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(lirc_serial_dev);
+exit_driver_unregister:
+ platform_driver_unregister(&lirc_serial_driver);
+exit_buffer_free:
+ lirc_buffer_free(&rbuf);
+ return result;
+}
+
+static void lirc_serial_exit(void)
+{
+ platform_device_unregister(lirc_serial_dev);
+ platform_driver_unregister(&lirc_serial_driver);
+ lirc_buffer_free(&rbuf);
+}
+
+static int __init lirc_serial_init_module(void)
+{
+ int result;
+
+ result = lirc_serial_init();
+ if (result)
+ return result;
+
+ switch (type) {
+ case LIRC_HOMEBREW:
+ case LIRC_IRDEO:
+ case LIRC_IRDEO_REMOTE:
+ case LIRC_ANIMAX:
+ case LIRC_IGOR:
+ /* if nothing specified, use ttyS0/com1 and irq 4 */
+ io = io ? io : 0x3f8;
+ irq = irq ? irq : 4;
+ break;
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
+ case LIRC_NSLU2:
+ io = io ? io : IRQ_IXP4XX_UART2;
+ irq = irq ? irq : (IXP4XX_UART2_BASE_VIRT + REG_OFFSET);
+ iommap = iommap ? iommap : IXP4XX_UART2_BASE_PHYS;
+ ioshift = ioshift ? ioshift : 2;
+ break;
+#endif
+ default:
+ result = -EINVAL;
+ goto exit_serial_exit;
+ }
+ if (!softcarrier) {
+ switch (type) {
+ case LIRC_HOMEBREW:
+ case LIRC_IGOR:
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
+ case LIRC_NSLU2:
+#endif
+ hardware[type].features &=
+ ~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+ LIRC_CAN_SET_SEND_CARRIER);
+ break;
+ }
+ }
+
+ result = init_port();
+ if (result < 0)
+ goto exit_serial_exit;
+ driver.features = hardware[type].features;
+ driver.dev = &lirc_serial_dev->dev;
+ driver.minor = lirc_register_driver(&driver);
+ if (driver.minor < 0) {
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": register_chrdev failed!\n");
+ result = -EIO;
+ goto exit_release;
+ }
+ return 0;
+exit_release:
+ release_region(io, 8);
+exit_serial_exit:
+ lirc_serial_exit();
+ return result;
+}
+
+static void __exit lirc_serial_exit_module(void)
+{
+ lirc_serial_exit();
+
+ free_irq(irq, (void *)&hardware);
+
+ if (iommap != 0)
+ release_mem_region(iommap, 8 << ioshift);
+ else
+ release_region(io, 8);
+ lirc_unregister_driver(driver.minor);
+ dprintk("cleaned up module\n");
+}
+
+
+module_init(lirc_serial_init_module);
+module_exit(lirc_serial_exit_module);
+
+MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
+MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, "
+ "Christoph Bartelmus, Andrei Tanas");
+MODULE_LICENSE("GPL");
+
+module_param(type, int, S_IRUGO);
+MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
+ " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug,"
+ " 5 = NSLU2 RX:CTS2/TX:GreenLED)");
+
+module_param(io, int, S_IRUGO);
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
+
+/* some architectures (e.g. intel xscale) have memory mapped registers */
+module_param(iommap, bool, S_IRUGO);
+MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O"
+ " (0 = no memory mapped io)");
+
+/*
+ * some architectures (e.g. intel xscale) align the 8bit serial registers
+ * on 32bit word boundaries.
+ * See linux-kernel/serial/8250.c serial_in()/out()
+ */
+module_param(ioshift, int, S_IRUGO);
+MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
+
+module_param(irq, int, S_IRUGO);
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
+
+module_param(share_irq, bool, S_IRUGO);
+MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
+
+module_param(sense, bool, S_IRUGO);
+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
+ " (0 = active high, 1 = active low )");
+
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
+module_param(txsense, bool, S_IRUGO);
+MODULE_PARM_DESC(txsense, "Sense of transmitter circuit"
+ " (0 = active high, 1 = active low )");
+#endif
+
+module_param(softcarrier, bool, S_IRUGO);
+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
--- /dev/null
+/*
+ * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
+ *
+ * lirc_sir - Device driver for use with SIR (serial infra red)
+ * mode of IrDA on many notebooks.
+ *
+ * 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
+ *
+ *
+ * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> :
+ * added timeout and relaxed pulse detection, removed gap bug
+ *
+ * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> :
+ * added support for Tekram Irmate 210 (sending does not work yet,
+ * kind of disappointing that nobody was able to implement that
+ * before),
+ * major clean-up
+ *
+ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
+ * added support for StrongARM SA1100 embedded microprocessor
+ * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <asm/system.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <linux/fcntl.h>
+#ifdef LIRC_ON_SA1100
+#include <asm/hardware.h>
+#ifdef CONFIG_SA1100_COLLIE
+#include <asm/arch/tc35143.h>
+#include <asm/ucb1200.h>
+#endif
+#endif
+
+#include <linux/timer.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+/* SECTION: Definitions */
+
+/*** Tekram dongle ***/
+#ifdef LIRC_SIR_TEKRAM
+/* stolen from kernel source */
+/* definitions for Tekram dongle */
+#define TEKRAM_115200 0x00
+#define TEKRAM_57600 0x01
+#define TEKRAM_38400 0x02
+#define TEKRAM_19200 0x03
+#define TEKRAM_9600 0x04
+#define TEKRAM_2400 0x08
+
+#define TEKRAM_PW 0x10 /* Pulse select bit */
+
+/* 10bit * 1s/115200bit in milliseconds = 87ms*/
+#define TIME_CONST (10000000ul/115200ul)
+
+#endif
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+static void init_act200(void);
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+static void init_act220(void);
+#endif
+
+/*** SA1100 ***/
+#ifdef LIRC_ON_SA1100
+struct sa1100_ser2_registers {
+ /* HSSP control register */
+ unsigned char hscr0;
+ /* UART registers */
+ unsigned char utcr0;
+ unsigned char utcr1;
+ unsigned char utcr2;
+ unsigned char utcr3;
+ unsigned char utcr4;
+ unsigned char utdr;
+ unsigned char utsr0;
+ unsigned char utsr1;
+} sr;
+
+static int irq = IRQ_Ser2ICP;
+
+#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0
+
+/* pulse/space ratio of 50/50 */
+static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
+/* 1000000/freq-pulse_width */
+static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
+static unsigned int freq = 38000; /* modulation frequency */
+static unsigned int duty_cycle = 50; /* duty cycle of 50% */
+
+#endif
+
+#define RBUF_LEN 1024
+#define WBUF_LEN 1024
+
+#define LIRC_DRIVER_NAME "lirc_sir"
+
+#define PULSE '['
+
+#ifndef LIRC_SIR_TEKRAM
+/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/
+#define TIME_CONST (9000000ul/115200ul)
+#endif
+
+
+/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */
+#define SIR_TIMEOUT (HZ*5/100)
+
+#ifndef LIRC_ON_SA1100
+#ifndef LIRC_IRQ
+#define LIRC_IRQ 4
+#endif
+#ifndef LIRC_PORT
+/* for external dongles, default to com1 */
+#if defined(LIRC_SIR_ACTISYS_ACT200L) || \
+ defined(LIRC_SIR_ACTISYS_ACT220L) || \
+ defined(LIRC_SIR_TEKRAM)
+#define LIRC_PORT 0x3f8
+#else
+/* onboard sir ports are typically com3 */
+#define LIRC_PORT 0x3e8
+#endif
+#endif
+
+static int io = LIRC_PORT;
+static int irq = LIRC_IRQ;
+static int threshold = 3;
+#endif
+
+static DEFINE_SPINLOCK(timer_lock);
+static struct timer_list timerlist;
+/* time of last signal change detected */
+static struct timeval last_tv = {0, 0};
+/* time of last UART data ready interrupt */
+static struct timeval last_intr_tv = {0, 0};
+static int last_value;
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
+
+static DEFINE_SPINLOCK(hardware_lock);
+
+static int rx_buf[RBUF_LEN];
+static unsigned int rx_tail, rx_head;
+
+static int debug;
+#define dprintk(fmt, args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
+ fmt, ## args); \
+ } while (0)
+
+/* SECTION: Prototypes */
+
+/* Communication with user-space */
+static unsigned int lirc_poll(struct file *file, poll_table *wait);
+static ssize_t lirc_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos);
+static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
+ loff_t *pos);
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
+static void add_read_queue(int flag, unsigned long val);
+static int init_chrdev(void);
+static void drop_chrdev(void);
+/* Hardware */
+static irqreturn_t sir_interrupt(int irq, void *dev_id);
+static void send_space(unsigned long len);
+static void send_pulse(unsigned long len);
+static int init_hardware(void);
+static void drop_hardware(void);
+/* Initialisation */
+static int init_port(void);
+static void drop_port(void);
+
+#ifdef LIRC_ON_SA1100
+static void on(void)
+{
+ PPSR |= PPC_TXD2;
+}
+
+static void off(void)
+{
+ PPSR &= ~PPC_TXD2;
+}
+#else
+static inline unsigned int sinp(int offset)
+{
+ return inb(io + offset);
+}
+
+static inline void soutp(int offset, int value)
+{
+ outb(value, io + offset);
+}
+#endif
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static void safe_udelay(unsigned long usecs)
+{
+ while (usecs > MAX_UDELAY_US) {
+ udelay(MAX_UDELAY_US);
+ usecs -= MAX_UDELAY_US;
+ }
+ udelay(usecs);
+}
+
+/* SECTION: Communication with user-space */
+
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
+{
+ poll_wait(file, &lirc_read_queue, wait);
+ if (rx_head != rx_tail)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static ssize_t lirc_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos)
+{
+ int n = 0;
+ int retval = 0;
+ DECLARE_WAITQUEUE(wait, current);
+
+ if (count % sizeof(int))
+ return -EINVAL;
+
+ add_wait_queue(&lirc_read_queue, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (n < count) {
+ if (rx_head != rx_tail) {
+ if (copy_to_user((void *) buf + n,
+ (void *) (rx_buf + rx_head),
+ sizeof(int))) {
+ retval = -EFAULT;
+ break;
+ }
+ rx_head = (rx_head + 1) & (RBUF_LEN - 1);
+ n += sizeof(int);
+ } else {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ }
+ remove_wait_queue(&lirc_read_queue, &wait);
+ set_current_state(TASK_RUNNING);
+ return n ? n : retval;
+}
+static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
+ loff_t *pos)
+{
+ unsigned long flags;
+ int i, count;
+ int *tx_buf;
+
+ count = n / sizeof(int);
+ if (n % sizeof(int) || count % 2 == 0)
+ return -EINVAL;
+ tx_buf = memdup_user(buf, n);
+ if (IS_ERR(tx_buf))
+ return PTR_ERR(tx_buf);
+ i = 0;
+#ifdef LIRC_ON_SA1100
+ /* disable receiver */
+ Ser2UTCR3 = 0;
+#endif
+ local_irq_save(flags);
+ while (1) {
+ if (i >= count)
+ break;
+ if (tx_buf[i])
+ send_pulse(tx_buf[i]);
+ i++;
+ if (i >= count)
+ break;
+ if (tx_buf[i])
+ send_space(tx_buf[i]);
+ i++;
+ }
+ local_irq_restore(flags);
+#ifdef LIRC_ON_SA1100
+ off();
+ udelay(1000); /* wait 1ms for IR diode to recover */
+ Ser2UTCR3 = 0;
+ /* clear status register to prevent unwanted interrupts */
+ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+ /* enable receiver */
+ Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
+#endif
+ kfree(tx_buf);
+ return count;
+}
+
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ __u32 value = 0;
+#ifdef LIRC_ON_SA1100
+
+ if (cmd == LIRC_GET_FEATURES)
+ value = LIRC_CAN_SEND_PULSE |
+ LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SET_SEND_CARRIER |
+ LIRC_CAN_REC_MODE2;
+ else if (cmd == LIRC_GET_SEND_MODE)
+ value = LIRC_MODE_PULSE;
+ else if (cmd == LIRC_GET_REC_MODE)
+ value = LIRC_MODE_MODE2;
+#else
+ if (cmd == LIRC_GET_FEATURES)
+ value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+ else if (cmd == LIRC_GET_SEND_MODE)
+ value = LIRC_MODE_PULSE;
+ else if (cmd == LIRC_GET_REC_MODE)
+ value = LIRC_MODE_MODE2;
+#endif
+
+ switch (cmd) {
+ case LIRC_GET_FEATURES:
+ case LIRC_GET_SEND_MODE:
+ case LIRC_GET_REC_MODE:
+ retval = put_user(value, (__u32 *) arg);
+ break;
+
+ case LIRC_SET_SEND_MODE:
+ case LIRC_SET_REC_MODE:
+ retval = get_user(value, (__u32 *) arg);
+ break;
+#ifdef LIRC_ON_SA1100
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ retval = get_user(value, (__u32 *) arg);
+ if (retval)
+ return retval;
+ if (value <= 0 || value > 100)
+ return -EINVAL;
+ /* (value/100)*(1000000/freq) */
+ duty_cycle = value;
+ pulse_width = (unsigned long) duty_cycle*10000/freq;
+ space_width = (unsigned long) 1000000L/freq-pulse_width;
+ if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+ pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+ if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+ space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+ break;
+ case LIRC_SET_SEND_CARRIER:
+ retval = get_user(value, (__u32 *) arg);
+ if (retval)
+ return retval;
+ if (value > 500000 || value < 20000)
+ return -EINVAL;
+ freq = value;
+ pulse_width = (unsigned long) duty_cycle*10000/freq;
+ space_width = (unsigned long) 1000000L/freq-pulse_width;
+ if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+ pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+ if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+ space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+ break;
+#endif
+ default:
+ retval = -ENOIOCTLCMD;
+
+ }
+
+ if (retval)
+ return retval;
+ if (cmd == LIRC_SET_REC_MODE) {
+ if (value != LIRC_MODE_MODE2)
+ retval = -ENOSYS;
+ } else if (cmd == LIRC_SET_SEND_MODE) {
+ if (value != LIRC_MODE_PULSE)
+ retval = -ENOSYS;
+ }
+
+ return retval;
+}
+
+static void add_read_queue(int flag, unsigned long val)
+{
+ unsigned int new_rx_tail;
+ int newval;
+
+ dprintk("add flag %d with val %lu\n", flag, val);
+
+ newval = val & PULSE_MASK;
+
+ /*
+ * statistically, pulses are ~TIME_CONST/2 too long. we could
+ * maybe make this more exact, but this is good enough
+ */
+ if (flag) {
+ /* pulse */
+ if (newval > TIME_CONST/2)
+ newval -= TIME_CONST/2;
+ else /* should not ever happen */
+ newval = 1;
+ newval |= PULSE_BIT;
+ } else {
+ newval += TIME_CONST/2;
+ }
+ new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
+ if (new_rx_tail == rx_head) {
+ dprintk("Buffer overrun.\n");
+ return;
+ }
+ rx_buf[rx_tail] = newval;
+ rx_tail = new_rx_tail;
+ wake_up_interruptible(&lirc_read_queue);
+}
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .read = lirc_read,
+ .write = lirc_write,
+ .poll = lirc_poll,
+ .unlocked_ioctl = lirc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lirc_ioctl,
+#endif
+ .open = lirc_dev_fop_open,
+ .release = lirc_dev_fop_close,
+ .llseek = no_llseek,
+};
+
+static int set_use_inc(void *data)
+{
+ return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+static struct lirc_driver driver = {
+ .name = LIRC_DRIVER_NAME,
+ .minor = -1,
+ .code_length = 1,
+ .sample_rate = 0,
+ .data = NULL,
+ .add_to_buf = NULL,
+ .set_use_inc = set_use_inc,
+ .set_use_dec = set_use_dec,
+ .fops = &lirc_fops,
+ .dev = NULL,
+ .owner = THIS_MODULE,
+};
+
+
+static int init_chrdev(void)
+{
+ driver.minor = lirc_register_driver(&driver);
+ if (driver.minor < 0) {
+ printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void drop_chrdev(void)
+{
+ lirc_unregister_driver(driver.minor);
+}
+
+/* SECTION: Hardware */
+static long delta(struct timeval *tv1, struct timeval *tv2)
+{
+ unsigned long deltv;
+
+ deltv = tv2->tv_sec - tv1->tv_sec;
+ if (deltv > 15)
+ deltv = 0xFFFFFF;
+ else
+ deltv = deltv*1000000 +
+ tv2->tv_usec -
+ tv1->tv_usec;
+ return deltv;
+}
+
+static void sir_timeout(unsigned long data)
+{
+ /*
+ * if last received signal was a pulse, but receiving stopped
+ * within the 9 bit frame, we need to finish this pulse and
+ * simulate a signal change to from pulse to space. Otherwise
+ * upper layers will receive two sequences next time.
+ */
+
+ unsigned long flags;
+ unsigned long pulse_end;
+
+ /* avoid interference with interrupt */
+ spin_lock_irqsave(&timer_lock, flags);
+ if (last_value) {
+#ifndef LIRC_ON_SA1100
+ /* clear unread bits in UART and restart */
+ outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
+#endif
+ /* determine 'virtual' pulse end: */
+ pulse_end = delta(&last_tv, &last_intr_tv);
+ dprintk("timeout add %d for %lu usec\n", last_value, pulse_end);
+ add_read_queue(last_value, pulse_end);
+ last_value = 0;
+ last_tv = last_intr_tv;
+ }
+ spin_unlock_irqrestore(&timer_lock, flags);
+}
+
+static irqreturn_t sir_interrupt(int irq, void *dev_id)
+{
+ unsigned char data;
+ struct timeval curr_tv;
+ static unsigned long deltv;
+#ifdef LIRC_ON_SA1100
+ int status;
+ static int n;
+
+ status = Ser2UTSR0;
+ /*
+ * Deal with any receive errors first. The bytes in error may be
+ * the only bytes in the receive FIFO, so we do this first.
+ */
+ while (status & UTSR0_EIF) {
+ int bstat;
+
+ if (debug) {
+ dprintk("EIF\n");
+ bstat = Ser2UTSR1;
+
+ if (bstat & UTSR1_FRE)
+ dprintk("frame error\n");
+ if (bstat & UTSR1_ROR)
+ dprintk("receive fifo overrun\n");
+ if (bstat & UTSR1_PRE)
+ dprintk("parity error\n");
+ }
+
+ bstat = Ser2UTDR;
+ n++;
+ status = Ser2UTSR0;
+ }
+
+ if (status & (UTSR0_RFS | UTSR0_RID)) {
+ do_gettimeofday(&curr_tv);
+ deltv = delta(&last_tv, &curr_tv);
+ do {
+ data = Ser2UTDR;
+ dprintk("%d data: %u\n", n, (unsigned int) data);
+ n++;
+ } while (status & UTSR0_RID && /* do not empty fifo in order to
+ * get UTSR0_RID in any case */
+ Ser2UTSR1 & UTSR1_RNE); /* data ready */
+
+ if (status&UTSR0_RID) {
+ add_read_queue(0 , deltv - n * TIME_CONST); /*space*/
+ add_read_queue(1, n * TIME_CONST); /*pulse*/
+ n = 0;
+ last_tv = curr_tv;
+ }
+ }
+
+ if (status & UTSR0_TFS)
+ printk(KERN_ERR "transmit fifo not full, shouldn't happen\n");
+
+ /* We must clear certain bits. */
+ status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+ if (status)
+ Ser2UTSR0 = status;
+#else
+ unsigned long deltintrtv;
+ unsigned long flags;
+ int iir, lsr;
+
+ while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
+ switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */
+ case UART_IIR_MSI:
+ (void) inb(io + UART_MSR);
+ break;
+ case UART_IIR_RLSI:
+ (void) inb(io + UART_LSR);
+ break;
+ case UART_IIR_THRI:
+#if 0
+ if (lsr & UART_LSR_THRE) /* FIFO is empty */
+ outb(data, io + UART_TX)
+#endif
+ break;
+ case UART_IIR_RDI:
+ /* avoid interference with timer */
+ spin_lock_irqsave(&timer_lock, flags);
+ do {
+ del_timer(&timerlist);
+ data = inb(io + UART_RX);
+ do_gettimeofday(&curr_tv);
+ deltv = delta(&last_tv, &curr_tv);
+ deltintrtv = delta(&last_intr_tv, &curr_tv);
+ dprintk("t %lu, d %d\n", deltintrtv, (int)data);
+ /*
+ * if nothing came in last X cycles,
+ * it was gap
+ */
+ if (deltintrtv > TIME_CONST * threshold) {
+ if (last_value) {
+ dprintk("GAP\n");
+ /* simulate signal change */
+ add_read_queue(last_value,
+ deltv -
+ deltintrtv);
+ last_value = 0;
+ last_tv.tv_sec =
+ last_intr_tv.tv_sec;
+ last_tv.tv_usec =
+ last_intr_tv.tv_usec;
+ deltv = deltintrtv;
+ }
+ }
+ data = 1;
+ if (data ^ last_value) {
+ /*
+ * deltintrtv > 2*TIME_CONST, remember?
+ * the other case is timeout
+ */
+ add_read_queue(last_value,
+ deltv-TIME_CONST);
+ last_value = data;
+ last_tv = curr_tv;
+ if (last_tv.tv_usec >= TIME_CONST) {
+ last_tv.tv_usec -= TIME_CONST;
+ } else {
+ last_tv.tv_sec--;
+ last_tv.tv_usec += 1000000 -
+ TIME_CONST;
+ }
+ }
+ last_intr_tv = curr_tv;
+ if (data) {
+ /*
+ * start timer for end of
+ * sequence detection
+ */
+ timerlist.expires = jiffies +
+ SIR_TIMEOUT;
+ add_timer(&timerlist);
+ }
+
+ lsr = inb(io + UART_LSR);
+ } while (lsr & UART_LSR_DR); /* data ready */
+ spin_unlock_irqrestore(&timer_lock, flags);
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+ return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+#ifdef LIRC_ON_SA1100
+static void send_pulse(unsigned long length)
+{
+ unsigned long k, delay;
+ int flag;
+
+ if (length == 0)
+ return;
+ /*
+ * this won't give us the carrier frequency we really want
+ * due to integer arithmetic, but we can accept this inaccuracy
+ */
+
+ for (k = flag = 0; k < length; k += delay, flag = !flag) {
+ if (flag) {
+ off();
+ delay = space_width;
+ } else {
+ on();
+ delay = pulse_width;
+ }
+ safe_udelay(delay);
+ }
+ off();
+}
+
+static void send_space(unsigned long length)
+{
+ if (length == 0)
+ return;
+ off();
+ safe_udelay(length);
+}
+#else
+static void send_space(unsigned long len)
+{
+ safe_udelay(len);
+}
+
+static void send_pulse(unsigned long len)
+{
+ long bytes_out = len / TIME_CONST;
+
+ if (bytes_out == 0)
+ bytes_out++;
+
+ while (bytes_out--) {
+ outb(PULSE, io + UART_TX);
+ /* FIXME treba seriozne cakanie z char/serial.c */
+ while (!(inb(io + UART_LSR) & UART_LSR_THRE))
+ ;
+ }
+}
+#endif
+
+#ifdef CONFIG_SA1100_COLLIE
+static int sa1100_irda_set_power_collie(int state)
+{
+ if (state) {
+ /*
+ * 0 - off
+ * 1 - short range, lowest power
+ * 2 - medium range, medium power
+ * 3 - maximum range, high power
+ */
+ ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
+ TC35143_IODIR_OUTPUT);
+ ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW);
+ udelay(100);
+ } else {
+ /* OFF */
+ ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
+ TC35143_IODIR_OUTPUT);
+ ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH);
+ }
+ return 0;
+}
+#endif
+
+static int init_hardware(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hardware_lock, flags);
+ /* reset UART */
+#ifdef LIRC_ON_SA1100
+#ifdef CONFIG_SA1100_BITSY
+ if (machine_is_bitsy()) {
+ printk(KERN_INFO "Power on IR module\n");
+ set_bitsy_egpio(EGPIO_BITSY_IR_ON);
+ }
+#endif
+#ifdef CONFIG_SA1100_COLLIE
+ sa1100_irda_set_power_collie(3); /* power on */
+#endif
+ sr.hscr0 = Ser2HSCR0;
+
+ sr.utcr0 = Ser2UTCR0;
+ sr.utcr1 = Ser2UTCR1;
+ sr.utcr2 = Ser2UTCR2;
+ sr.utcr3 = Ser2UTCR3;
+ sr.utcr4 = Ser2UTCR4;
+
+ sr.utdr = Ser2UTDR;
+ sr.utsr0 = Ser2UTSR0;
+ sr.utsr1 = Ser2UTSR1;
+
+ /* configure GPIO */
+ /* output */
+ PPDR |= PPC_TXD2;
+ PSDR |= PPC_TXD2;
+ /* set output to 0 */
+ off();
+
+ /* Enable HP-SIR modulation, and ensure that the port is disabled. */
+ Ser2UTCR3 = 0;
+ Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP);
+
+ /* clear status register to prevent unwanted interrupts */
+ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+
+ /* 7N1 */
+ Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData;
+ /* 115200 */
+ Ser2UTCR1 = 0;
+ Ser2UTCR2 = 1;
+ /* use HPSIR, 1.6 usec pulses */
+ Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us;
+
+ /* enable receiver, receive fifo interrupt */
+ Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
+
+ /* clear status register to prevent unwanted interrupts */
+ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+
+#elif defined(LIRC_SIR_TEKRAM)
+ /* disable FIFO */
+ soutp(UART_FCR,
+ UART_FCR_CLEAR_RCVR|
+ UART_FCR_CLEAR_XMIT|
+ UART_FCR_TRIGGER_1);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* First of all, disable all interrupts */
+ soutp(UART_IER, sinp(UART_IER) &
+ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+ /* Set divisor to 12 => 9600 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 12);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* power supply */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ safe_udelay(50*1000);
+
+ /* -DTR low -> reset PIC */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+ udelay(1*1000);
+
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(100);
+
+
+ /* -RTS low -> send control byte */
+ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(7);
+ soutp(UART_TX, TEKRAM_115200|TEKRAM_PW);
+
+ /* one byte takes ~1042 usec to transmit at 9600,8N1 */
+ udelay(1500);
+
+ /* back to normal operation */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(50);
+
+ udelay(1500);
+
+ /* read previous control byte */
+ printk(KERN_INFO LIRC_DRIVER_NAME
+ ": 0x%02x\n", sinp(UART_RX));
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+ /* Set divisor to 1 => 115200 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 1);
+
+ /* Set DLAB 0, 8 Bit */
+ soutp(UART_LCR, UART_LCR_WLEN8);
+ /* enable interrupts */
+ soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
+#else
+ outb(0, io + UART_MCR);
+ outb(0, io + UART_IER);
+ /* init UART */
+ /* set DLAB, speed = 115200 */
+ outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
+ outb(1, io + UART_DLL); outb(0, io + UART_DLM);
+ /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
+ outb(UART_LCR_WLEN7, io + UART_LCR);
+ /* FIFO operation */
+ outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
+ /* interrupts */
+ /* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */
+ outb(UART_IER_RDI, io + UART_IER);
+ /* turn on UART */
+ outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR);
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+ init_act200();
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+ init_act220();
+#endif
+#endif
+ spin_unlock_irqrestore(&hardware_lock, flags);
+ return 0;
+}
+
+static void drop_hardware(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hardware_lock, flags);
+
+#ifdef LIRC_ON_SA1100
+ Ser2UTCR3 = 0;
+
+ Ser2UTCR0 = sr.utcr0;
+ Ser2UTCR1 = sr.utcr1;
+ Ser2UTCR2 = sr.utcr2;
+ Ser2UTCR4 = sr.utcr4;
+ Ser2UTCR3 = sr.utcr3;
+
+ Ser2HSCR0 = sr.hscr0;
+#ifdef CONFIG_SA1100_BITSY
+ if (machine_is_bitsy())
+ clr_bitsy_egpio(EGPIO_BITSY_IR_ON);
+#endif
+#ifdef CONFIG_SA1100_COLLIE
+ sa1100_irda_set_power_collie(0); /* power off */
+#endif
+#else
+ /* turn off interrupts */
+ outb(0, io + UART_IER);
+#endif
+ spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+/* SECTION: Initialisation */
+
+static int init_port(void)
+{
+ int retval;
+
+ /* get I/O port access and IRQ line */
+#ifndef LIRC_ON_SA1100
+ if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": i/o port 0x%.4x already in use.\n", io);
+ return -EBUSY;
+ }
+#endif
+ retval = request_irq(irq, sir_interrupt, 0,
+ LIRC_DRIVER_NAME, NULL);
+ if (retval < 0) {
+# ifndef LIRC_ON_SA1100
+ release_region(io, 8);
+# endif
+ printk(KERN_ERR LIRC_DRIVER_NAME
+ ": IRQ %d already in use.\n",
+ irq);
+ return retval;
+ }
+#ifndef LIRC_ON_SA1100
+ printk(KERN_INFO LIRC_DRIVER_NAME
+ ": I/O port 0x%.4x, IRQ %d.\n",
+ io, irq);
+#endif
+
+ init_timer(&timerlist);
+ timerlist.function = sir_timeout;
+ timerlist.data = 0xabadcafe;
+
+ return 0;
+}
+
+static void drop_port(void)
+{
+ free_irq(irq, NULL);
+ del_timer_sync(&timerlist);
+#ifndef LIRC_ON_SA1100
+ release_region(io, 8);
+#endif
+}
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */
+/* some code borrowed from Linux IRDA driver */
+
+/* Register 0: Control register #1 */
+#define ACT200L_REG0 0x00
+#define ACT200L_TXEN 0x01 /* Enable transmitter */
+#define ACT200L_RXEN 0x02 /* Enable receiver */
+#define ACT200L_ECHO 0x08 /* Echo control chars */
+
+/* Register 1: Control register #2 */
+#define ACT200L_REG1 0x10
+#define ACT200L_LODB 0x01 /* Load new baud rate count value */
+#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */
+
+/* Register 3: Transmit mode register #2 */
+#define ACT200L_REG3 0x30
+#define ACT200L_B0 0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */
+#define ACT200L_B1 0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */
+#define ACT200L_CHSY 0x04 /* StartBit Synced 0=bittime, 1=startbit */
+
+/* Register 4: Output Power register */
+#define ACT200L_REG4 0x40
+#define ACT200L_OP0 0x01 /* Enable LED1C output */
+#define ACT200L_OP1 0x02 /* Enable LED2C output */
+#define ACT200L_BLKR 0x04
+
+/* Register 5: Receive Mode register */
+#define ACT200L_REG5 0x50
+#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */
+ /*.. other various IRDA bit modes, and TV remote modes..*/
+
+/* Register 6: Receive Sensitivity register #1 */
+#define ACT200L_REG6 0x60
+#define ACT200L_RS0 0x01 /* receive threshold bit 0 */
+#define ACT200L_RS1 0x02 /* receive threshold bit 1 */
+
+/* Register 7: Receive Sensitivity register #2 */
+#define ACT200L_REG7 0x70
+#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */
+
+/* Register 8,9: Baud Rate Divider register #1,#2 */
+#define ACT200L_REG8 0x80
+#define ACT200L_REG9 0x90
+
+#define ACT200L_2400 0x5f
+#define ACT200L_9600 0x17
+#define ACT200L_19200 0x0b
+#define ACT200L_38400 0x05
+#define ACT200L_57600 0x03
+#define ACT200L_115200 0x01
+
+/* Register 13: Control register #3 */
+#define ACT200L_REG13 0xd0
+#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */
+
+/* Register 15: Status register */
+#define ACT200L_REG15 0xf0
+
+/* Register 21: Control register #4 */
+#define ACT200L_REG21 0x50
+#define ACT200L_EXCK 0x02 /* Disable clock output driver */
+#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */
+
+static void init_act200(void)
+{
+ int i;
+ __u8 control[] = {
+ ACT200L_REG15,
+ ACT200L_REG13 | ACT200L_SHDW,
+ ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
+ ACT200L_REG13,
+ ACT200L_REG7 | ACT200L_ENPOS,
+ ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1,
+ ACT200L_REG5 | ACT200L_RWIDL,
+ ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR,
+ ACT200L_REG3 | ACT200L_B0,
+ ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN,
+ ACT200L_REG8 | (ACT200L_115200 & 0x0f),
+ ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f),
+ ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE
+ };
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
+
+ /* Set divisor to 12 => 9600 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 12);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, UART_LCR_WLEN8);
+ /* Set divisor to 12 => 9600 Baud */
+
+ /* power supply */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ for (i = 0; i < 50; i++)
+ safe_udelay(1000);
+
+ /* Reset the dongle : set RTS low for 25 ms */
+ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+ for (i = 0; i < 25; i++)
+ udelay(1000);
+
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(100);
+
+ /* Clear DTR and set RTS to enter command mode */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+ udelay(7);
+
+ /* send out the control register settings for 115K 7N1 SIR operation */
+ for (i = 0; i < sizeof(control); i++) {
+ soutp(UART_TX, control[i]);
+ /* one byte takes ~1042 usec to transmit at 9600,8N1 */
+ udelay(1500);
+ }
+
+ /* back to normal operation */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(50);
+
+ udelay(1500);
+ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
+
+ /* Set divisor to 1 => 115200 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 1);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* Set DLAB 0, 7 Bit */
+ soutp(UART_LCR, UART_LCR_WLEN7);
+
+ /* enable interrupts */
+ soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
+}
+#endif
+
+#ifdef LIRC_SIR_ACTISYS_ACT220L
+/*
+ * Derived from linux IrDA driver (net/irda/actisys.c)
+ * Drop me a mail for any kind of comment: maxx@spaceboyz.net
+ */
+
+void init_act220(void)
+{
+ int i;
+
+ /* DLAB 1 */
+ soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7);
+
+ /* 9600 baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 12);
+
+ /* DLAB 0 */
+ soutp(UART_LCR, UART_LCR_WLEN7);
+
+ /* reset the dongle, set DTR low for 10us */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+ udelay(10);
+
+ /* back to normal (still 9600) */
+ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2);
+
+ /*
+ * send RTS pulses until we reach 115200
+ * i hope this is really the same for act220l/act220l+
+ */
+ for (i = 0; i < 3; i++) {
+ udelay(10);
+ /* set RTS low for 10 us */
+ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(10);
+ /* set RTS high for 10 us */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ }
+
+ /* back to normal operation */
+ udelay(1500); /* better safe than sorry ;) */
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
+
+ /* Set divisor to 1 => 115200 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 1);
+
+ /* Set DLAB 0, 7 Bit */
+ /* The dongle doesn't seem to have any problems with operation at 7N1 */
+ soutp(UART_LCR, UART_LCR_WLEN7);
+
+ /* enable interrupts */
+ soutp(UART_IER, UART_IER_RDI);
+}
+#endif
+
+static int init_lirc_sir(void)
+{
+ int retval;
+
+ init_waitqueue_head(&lirc_read_queue);
+ retval = init_port();
+ if (retval < 0)
+ return retval;
+ init_hardware();
+ printk(KERN_INFO LIRC_DRIVER_NAME
+ ": Installed.\n");
+ return 0;
+}
+
+
+static int __init lirc_sir_init(void)
+{
+ int retval;
+
+ retval = init_chrdev();
+ if (retval < 0)
+ return retval;
+ retval = init_lirc_sir();
+ if (retval) {
+ drop_chrdev();
+ return retval;
+ }
+ return 0;
+}
+
+static void __exit lirc_sir_exit(void)
+{
+ drop_hardware();
+ drop_chrdev();
+ drop_port();
+ printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
+}
+
+module_init(lirc_sir_init);
+module_exit(lirc_sir_exit);
+
+#ifdef LIRC_SIR_TEKRAM
+MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210");
+MODULE_AUTHOR("Christoph Bartelmus");
+#elif defined(LIRC_ON_SA1100)
+MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor");
+MODULE_AUTHOR("Christoph Bartelmus");
+#elif defined(LIRC_SIR_ACTISYS_ACT200L)
+MODULE_DESCRIPTION("LIRC driver for Actisys Act200L");
+MODULE_AUTHOR("Karl Bongers");
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)");
+MODULE_AUTHOR("Jan Roemisch");
+#else
+MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
+MODULE_AUTHOR("Milan Pikula");
+#endif
+MODULE_LICENSE("GPL");
+
+#ifdef LIRC_ON_SA1100
+module_param(irq, int, S_IRUGO);
+MODULE_PARM_DESC(irq, "Interrupt (16)");
+#else
+module_param(io, int, S_IRUGO);
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
+
+module_param(irq, int, S_IRUGO);
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
+
+module_param(threshold, int, S_IRUGO);
+MODULE_PARM_DESC(threshold, "space detection threshold (3)");
+#endif
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
--- /dev/null
+/*
+ * lirc_ttusbir.c
+ *
+ * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver
+ *
+ * Copyright (C) 2007 Stefan Macher <st_maker-lirc@yahoo.de>
+ *
+ * This LIRC driver provides access to the TechnoTrend USB IR Receiver.
+ * The receiver delivers the IR signal as raw sampled true/false data in
+ * isochronous USB packets each of size 128 byte.
+ * Currently the driver reduces the sampling rate by factor of 8 as this
+ * is still more than enough to decode RC-5 - others should be analyzed.
+ * But the driver does not rely on RC-5 it should be able to decode every
+ * IR signal that is not too fast.
+ */
+
+/*
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC");
+MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)");
+MODULE_LICENSE("GPL");
+
+/* #define DEBUG */
+#ifdef DEBUG
+#define DPRINTK printk
+#else
+#define DPRINTK(_x_, a...)
+#endif
+
+/* function declarations */
+static int probe(struct usb_interface *intf, const struct usb_device_id *id);
+static void disconnect(struct usb_interface *intf);
+static void urb_complete(struct urb *urb);
+static int set_use_inc(void *data);
+static void set_use_dec(void *data);
+
+static int num_urbs = 2;
+module_param(num_urbs, int, S_IRUGO);
+MODULE_PARM_DESC(num_urbs,
+ "Number of URBs in queue. Try to increase to 4 in case "
+ "of problems (default: 2; minimum: 2)");
+
+/* table of devices that work with this driver */
+static struct usb_device_id device_id_table[] = {
+ /* TechnoTrend USB IR Receiver */
+ { USB_DEVICE(0x0B48, 0x2003) },
+ /* Terminating entry */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, device_id_table);
+
+/* USB driver definition */
+static struct usb_driver usb_driver = {
+ .name = "TTUSBIR",
+ .id_table = &(device_id_table[0]),
+ .probe = probe,
+ .disconnect = disconnect,
+};
+
+/* USB device definition */
+struct ttusbir_device {
+ struct usb_driver *usb_driver;
+ struct usb_device *udev;
+ struct usb_interface *interf;
+ struct usb_class_driver class_driver;
+ unsigned int ifnum; /* Interface number to use */
+ unsigned int alt_setting; /* alternate setting to use */
+ unsigned int endpoint; /* Endpoint to use */
+ struct urb **urb; /* num_urb URB pointers*/
+ char **buffer; /* 128 byte buffer for each URB */
+ struct lirc_buffer rbuf; /* Buffer towards LIRC */
+ struct lirc_driver driver;
+ int minor;
+ int last_pulse; /* remembers if last received byte was pulse or space */
+ int last_num; /* remembers how many last bytes appeared */
+ int opened;
+};
+
+/*** LIRC specific functions ***/
+static int set_use_inc(void *data)
+{
+ int i, retval;
+ struct ttusbir_device *ttusbir = data;
+
+ DPRINTK("Sending first URBs\n");
+ /* @TODO Do I need to check if I am already opened */
+ ttusbir->opened = 1;
+
+ for (i = 0; i < num_urbs; i++) {
+ retval = usb_submit_urb(ttusbir->urb[i], GFP_KERNEL);
+ if (retval) {
+ err("%s: usb_submit_urb failed on urb %d",
+ __func__, i);
+ return retval;
+ }
+ }
+ return 0;
+}
+
+static void set_use_dec(void *data)
+{
+ struct ttusbir_device *ttusbir = data;
+
+ DPRINTK("Device closed\n");
+
+ ttusbir->opened = 0;
+}
+
+/*** USB specific functions ***/
+
+/*
+ * This mapping table is used to do a very simple filtering of the
+ * input signal.
+ * For a value with at least 4 bits set it returns 0xFF otherwise
+ * 0x00. For faster IR signals this can not be used. But for RC-5 we
+ * still have about 14 samples per pulse/space, i.e. we sample with 14
+ * times higher frequency than the signal frequency
+ */
+const unsigned char map_table[] = {
+ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static void urb_complete(struct urb *urb)
+{
+ struct ttusbir_device *ttusbir;
+ unsigned char *buf;
+ int i;
+ int l;
+
+ ttusbir = urb->context;
+
+ if (!ttusbir->opened)
+ return;
+
+ buf = (unsigned char *)urb->transfer_buffer;
+
+ for (i = 0; i < 128; i++) {
+ /* Here we do the filtering and some kind of down sampling */
+ buf[i] = ~map_table[buf[i]];
+ if (ttusbir->last_pulse == buf[i]) {
+ if (ttusbir->last_num < PULSE_MASK/63)
+ ttusbir->last_num++;
+ /*
+ * else we are in a idle period and do not need to
+ * increment any longer
+ */
+ } else {
+ l = ttusbir->last_num * 62; /* about 62 = us/byte */
+ if (ttusbir->last_pulse) /* pulse or space? */
+ l |= PULSE_BIT;
+ if (!lirc_buffer_full(&ttusbir->rbuf)) {
+ lirc_buffer_write(&ttusbir->rbuf, (void *)&l);
+ wake_up_interruptible(&ttusbir->rbuf.wait_poll);
+ }
+ ttusbir->last_num = 0;
+ ttusbir->last_pulse = buf[i];
+ }
+ }
+ usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */
+}
+
+/*
+ * Called whenever the USB subsystem thinks we could be the right driver
+ * to handle this device
+ */
+static int probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ int alt_set, endp;
+ int found = 0;
+ int i, j;
+ int struct_size;
+ struct usb_host_interface *host_interf;
+ struct usb_interface_descriptor *interf_desc;
+ struct usb_host_endpoint *host_endpoint;
+ struct ttusbir_device *ttusbir;
+
+ DPRINTK("Module ttusbir probe\n");
+
+ /* To reduce memory fragmentation we use only one allocation */
+ struct_size = sizeof(struct ttusbir_device) +
+ (sizeof(struct urb *) * num_urbs) +
+ (sizeof(char *) * num_urbs) +
+ (num_urbs * 128);
+ ttusbir = kzalloc(struct_size, GFP_KERNEL);
+ if (!ttusbir)
+ return -ENOMEM;
+
+ ttusbir->urb = (struct urb **)((char *)ttusbir +
+ sizeof(struct ttusbir_device));
+ ttusbir->buffer = (char **)((char *)ttusbir->urb +
+ (sizeof(struct urb *) * num_urbs));
+ for (i = 0; i < num_urbs; i++)
+ ttusbir->buffer[i] = (char *)ttusbir->buffer +
+ (sizeof(char *)*num_urbs) + (i * 128);
+
+ ttusbir->usb_driver = &usb_driver;
+ ttusbir->alt_setting = -1;
+ /* @TODO check if error can be returned */
+ ttusbir->udev = usb_get_dev(interface_to_usbdev(intf));
+ ttusbir->interf = intf;
+ ttusbir->last_pulse = 0x00;
+ ttusbir->last_num = 0;
+
+ /*
+ * Now look for interface setting we can handle
+ * We are searching for the alt setting where end point
+ * 0x82 has max packet size 16
+ */
+ for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) {
+ host_interf = &intf->altsetting[alt_set];
+ interf_desc = &host_interf->desc;
+ for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) {
+ host_endpoint = &host_interf->endpoint[endp];
+ if ((host_endpoint->desc.bEndpointAddress == 0x82) &&
+ (host_endpoint->desc.wMaxPacketSize == 0x10)) {
+ ttusbir->alt_setting = alt_set;
+ ttusbir->endpoint = endp;
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (ttusbir->alt_setting != -1)
+ DPRINTK("alt setting: %d\n", ttusbir->alt_setting);
+ else {
+ err("Could not find alternate setting\n");
+ kfree(ttusbir);
+ return -EINVAL;
+ }
+
+ /* OK lets setup this interface setting */
+ usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting);
+
+ /* Store device info in interface structure */
+ usb_set_intfdata(intf, ttusbir);
+
+ /* Register as a LIRC driver */
+ if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) {
+ err("Could not get memory for LIRC data buffer\n");
+ usb_set_intfdata(intf, NULL);
+ kfree(ttusbir);
+ return -ENOMEM;
+ }
+ strcpy(ttusbir->driver.name, "TTUSBIR");
+ ttusbir->driver.minor = -1;
+ ttusbir->driver.code_length = 1;
+ ttusbir->driver.sample_rate = 0;
+ ttusbir->driver.data = ttusbir;
+ ttusbir->driver.add_to_buf = NULL;
+ ttusbir->driver.rbuf = &ttusbir->rbuf;
+ ttusbir->driver.set_use_inc = set_use_inc;
+ ttusbir->driver.set_use_dec = set_use_dec;
+ ttusbir->driver.dev = &intf->dev;
+ ttusbir->driver.owner = THIS_MODULE;
+ ttusbir->driver.features = LIRC_CAN_REC_MODE2;
+ ttusbir->minor = lirc_register_driver(&ttusbir->driver);
+ if (ttusbir->minor < 0) {
+ err("Error registering as LIRC driver\n");
+ usb_set_intfdata(intf, NULL);
+ lirc_buffer_free(&ttusbir->rbuf);
+ kfree(ttusbir);
+ return -EIO;
+ }
+
+ /* Allocate and setup the URB that we will use to talk to the device */
+ for (i = 0; i < num_urbs; i++) {
+ ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL);
+ if (!ttusbir->urb[i]) {
+ err("Could not allocate memory for the URB\n");
+ for (j = i - 1; j >= 0; j--)
+ kfree(ttusbir->urb[j]);
+ lirc_buffer_free(&ttusbir->rbuf);
+ lirc_unregister_driver(ttusbir->minor);
+ kfree(ttusbir);
+ usb_set_intfdata(intf, NULL);
+ return -ENOMEM;
+ }
+ ttusbir->urb[i]->dev = ttusbir->udev;
+ ttusbir->urb[i]->context = ttusbir;
+ ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev,
+ ttusbir->endpoint);
+ ttusbir->urb[i]->interval = 1;
+ ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP;
+ ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0];
+ ttusbir->urb[i]->complete = urb_complete;
+ ttusbir->urb[i]->number_of_packets = 8;
+ ttusbir->urb[i]->transfer_buffer_length = 128;
+ for (j = 0; j < 8; j++) {
+ ttusbir->urb[i]->iso_frame_desc[j].offset = j*16;
+ ttusbir->urb[i]->iso_frame_desc[j].length = 16;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Called when the driver is unloaded or the device is unplugged
+ */
+static void disconnect(struct usb_interface *intf)
+{
+ int i;
+ struct ttusbir_device *ttusbir;
+
+ DPRINTK("Module ttusbir disconnect\n");
+
+ ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf);
+ usb_set_intfdata(intf, NULL);
+ lirc_unregister_driver(ttusbir->minor);
+ DPRINTK("unregistered\n");
+
+ for (i = 0; i < num_urbs; i++) {
+ usb_kill_urb(ttusbir->urb[i]);
+ usb_free_urb(ttusbir->urb[i]);
+ }
+ DPRINTK("URBs killed\n");
+ lirc_buffer_free(&ttusbir->rbuf);
+ kfree(ttusbir);
+}
+
+static int ttusbir_init_module(void)
+{
+ int result;
+
+ DPRINTK(KERN_DEBUG "Module ttusbir init\n");
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&usb_driver);
+ if (result)
+ err("usb_register failed. Error number %d", result);
+ return result;
+}
+
+static void ttusbir_exit_module(void)
+{
+ printk(KERN_DEBUG "Module ttusbir exit\n");
+ usb_deregister(&usb_driver);
+}
+
+module_init(ttusbir_init_module);
+module_exit(ttusbir_exit_module);
--- /dev/null
+/*
+ * i2c IR lirc driver for devices with zilog IR processors
+ *
+ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ * modified for PixelView (BT878P+W/FM) by
+ * Michal Kochanowicz <mkochano@pld.org.pl>
+ * Christoph Bartelmus <lirc@bartelmus.de>
+ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
+ * Ulrich Mueller <ulrich.mueller42@web.de>
+ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
+ * Stefan Jahn <stefan@lkcc.org>
+ * modified for inclusion into kernel sources by
+ * Jerome Brock <jbrock@users.sourceforge.net>
+ * modified for Leadtek Winfast PVR2000 by
+ * Thomas Reitmayr (treitmayr@yahoo.com)
+ * modified for Hauppauge PVR-150 IR TX device by
+ * Mark Weaver <mark@npsl.co.uk>
+ * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150
+ * Jarod Wilson <jarod@redhat.com>
+ *
+ * parts are cut&pasted from the lirc_i2c.c driver
+ *
+ * Numerous changes updating lirc_zilog.c in kernel 2.6.38 and later are
+ * Copyright (C) 2011 Andy Walls <awalls@md.metrocast.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/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+
+#include <media/lirc_dev.h>
+#include <media/lirc.h>
+
+struct IR;
+
+struct IR_rx {
+ struct kref ref;
+ struct IR *ir;
+
+ /* RX device */
+ struct mutex client_lock;
+ struct i2c_client *c;
+
+ /* RX polling thread data */
+ struct task_struct *task;
+
+ /* RX read data */
+ unsigned char b[3];
+ bool hdpvr_data_fmt;
+};
+
+struct IR_tx {
+ struct kref ref;
+ struct IR *ir;
+
+ /* TX device */
+ struct mutex client_lock;
+ struct i2c_client *c;
+
+ /* TX additional actions needed */
+ int need_boot;
+ bool post_tx_ready_poll;
+};
+
+struct IR {
+ struct kref ref;
+ struct list_head list;
+
+ /* FIXME spinlock access to l.features */
+ struct lirc_driver l;
+ struct lirc_buffer rbuf;
+
+ struct mutex ir_lock;
+ atomic_t open_count;
+
+ struct i2c_adapter *adapter;
+
+ spinlock_t rx_ref_lock; /* struct IR_rx kref get()/put() */
+ struct IR_rx *rx;
+
+ spinlock_t tx_ref_lock; /* struct IR_tx kref get()/put() */
+ struct IR_tx *tx;
+};
+
+/* IR transceiver instance object list */
+/*
+ * This lock is used for the following:
+ * a. ir_devices_list access, insertions, deletions
+ * b. struct IR kref get()s and put()s
+ * c. serialization of ir_probe() for the two i2c_clients for a Z8
+ */
+static DEFINE_MUTEX(ir_devices_lock);
+static LIST_HEAD(ir_devices_list);
+
+/* Block size for IR transmitter */
+#define TX_BLOCK_SIZE 99
+
+/* Hauppauge IR transmitter data */
+struct tx_data_struct {
+ /* Boot block */
+ unsigned char *boot_data;
+
+ /* Start of binary data block */
+ unsigned char *datap;
+
+ /* End of binary data block */
+ unsigned char *endp;
+
+ /* Number of installed codesets */
+ unsigned int num_code_sets;
+
+ /* Pointers to codesets */
+ unsigned char **code_sets;
+
+ /* Global fixed data template */
+ int fixed[TX_BLOCK_SIZE];
+};
+
+static struct tx_data_struct *tx_data;
+static struct mutex tx_data_lock;
+
+#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \
+ ## args)
+#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
+#define zilog_info(s, args...) printk(KERN_INFO KBUILD_MODNAME ": " s, ## args)
+
+/* module parameters */
+static int debug; /* debug output */
+static int tx_only; /* only handle the IR Tx function */
+static int minor = -1; /* minor number */
+
+#define dprintk(fmt, args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG KBUILD_MODNAME ": " fmt, \
+ ## args); \
+ } while (0)
+
+
+/* struct IR reference counting */
+static struct IR *get_ir_device(struct IR *ir, bool ir_devices_lock_held)
+{
+ if (ir_devices_lock_held) {
+ kref_get(&ir->ref);
+ } else {
+ mutex_lock(&ir_devices_lock);
+ kref_get(&ir->ref);
+ mutex_unlock(&ir_devices_lock);
+ }
+ return ir;
+}
+
+static void release_ir_device(struct kref *ref)
+{
+ struct IR *ir = container_of(ref, struct IR, ref);
+
+ /*
+ * Things should be in this state by now:
+ * ir->rx set to NULL and deallocated - happens before ir->rx->ir put()
+ * ir->rx->task kthread stopped - happens before ir->rx->ir put()
+ * ir->tx set to NULL and deallocated - happens before ir->tx->ir put()
+ * ir->open_count == 0 - happens on final close()
+ * ir_lock, tx_ref_lock, rx_ref_lock, all released
+ */
+ if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
+ lirc_unregister_driver(ir->l.minor);
+ ir->l.minor = MAX_IRCTL_DEVICES;
+ }
+ if (ir->rbuf.fifo_initialized)
+ lirc_buffer_free(&ir->rbuf);
+ list_del(&ir->list);
+ kfree(ir);
+}
+
+static int put_ir_device(struct IR *ir, bool ir_devices_lock_held)
+{
+ int released;
+
+ if (ir_devices_lock_held)
+ return kref_put(&ir->ref, release_ir_device);
+
+ mutex_lock(&ir_devices_lock);
+ released = kref_put(&ir->ref, release_ir_device);
+ mutex_unlock(&ir_devices_lock);
+
+ return released;
+}
+
+/* struct IR_rx reference counting */
+static struct IR_rx *get_ir_rx(struct IR *ir)
+{
+ struct IR_rx *rx;
+
+ spin_lock(&ir->rx_ref_lock);
+ rx = ir->rx;
+ if (rx != NULL)
+ kref_get(&rx->ref);
+ spin_unlock(&ir->rx_ref_lock);
+ return rx;
+}
+
+static void destroy_rx_kthread(struct IR_rx *rx, bool ir_devices_lock_held)
+{
+ /* end up polling thread */
+ if (!IS_ERR_OR_NULL(rx->task)) {
+ kthread_stop(rx->task);
+ rx->task = NULL;
+ /* Put the ir ptr that ir_probe() gave to the rx poll thread */
+ put_ir_device(rx->ir, ir_devices_lock_held);
+ }
+}
+
+static void release_ir_rx(struct kref *ref)
+{
+ struct IR_rx *rx = container_of(ref, struct IR_rx, ref);
+ struct IR *ir = rx->ir;
+
+ /*
+ * This release function can't do all the work, as we want
+ * to keep the rx_ref_lock a spinlock, and killing the poll thread
+ * and releasing the ir reference can cause a sleep. That work is
+ * performed by put_ir_rx()
+ */
+ ir->l.features &= ~LIRC_CAN_REC_LIRCCODE;
+ /* Don't put_ir_device(rx->ir) here; lock can't be freed yet */
+ ir->rx = NULL;
+ /* Don't do the kfree(rx) here; we still need to kill the poll thread */
+ return;
+}
+
+static int put_ir_rx(struct IR_rx *rx, bool ir_devices_lock_held)
+{
+ int released;
+ struct IR *ir = rx->ir;
+
+ spin_lock(&ir->rx_ref_lock);
+ released = kref_put(&rx->ref, release_ir_rx);
+ spin_unlock(&ir->rx_ref_lock);
+ /* Destroy the rx kthread while not holding the spinlock */
+ if (released) {
+ destroy_rx_kthread(rx, ir_devices_lock_held);
+ kfree(rx);
+ /* Make sure we're not still in a poll_table somewhere */
+ wake_up_interruptible(&ir->rbuf.wait_poll);
+ }
+ /* Do a reference put() for the rx->ir reference, if we released rx */
+ if (released)
+ put_ir_device(ir, ir_devices_lock_held);
+ return released;
+}
+
+/* struct IR_tx reference counting */
+static struct IR_tx *get_ir_tx(struct IR *ir)
+{
+ struct IR_tx *tx;
+
+ spin_lock(&ir->tx_ref_lock);
+ tx = ir->tx;
+ if (tx != NULL)
+ kref_get(&tx->ref);
+ spin_unlock(&ir->tx_ref_lock);
+ return tx;
+}
+
+static void release_ir_tx(struct kref *ref)
+{
+ struct IR_tx *tx = container_of(ref, struct IR_tx, ref);
+ struct IR *ir = tx->ir;
+
+ ir->l.features &= ~LIRC_CAN_SEND_PULSE;
+ /* Don't put_ir_device(tx->ir) here, so our lock doesn't get freed */
+ ir->tx = NULL;
+ kfree(tx);
+}
+
+static int put_ir_tx(struct IR_tx *tx, bool ir_devices_lock_held)
+{
+ int released;
+ struct IR *ir = tx->ir;
+
+ spin_lock(&ir->tx_ref_lock);
+ released = kref_put(&tx->ref, release_ir_tx);
+ spin_unlock(&ir->tx_ref_lock);
+ /* Do a reference put() for the tx->ir reference, if we released tx */
+ if (released)
+ put_ir_device(ir, ir_devices_lock_held);
+ return released;
+}
+
+static int add_to_buf(struct IR *ir)
+{
+ __u16 code;
+ unsigned char codes[2];
+ unsigned char keybuf[6];
+ int got_data = 0;
+ int ret;
+ int failures = 0;
+ unsigned char sendbuf[1] = { 0 };
+ struct lirc_buffer *rbuf = ir->l.rbuf;
+ struct IR_rx *rx;
+ struct IR_tx *tx;
+
+ if (lirc_buffer_full(rbuf)) {
+ dprintk("buffer overflow\n");
+ return -EOVERFLOW;
+ }
+
+ rx = get_ir_rx(ir);
+ if (rx == NULL)
+ return -ENXIO;
+
+ /* Ensure our rx->c i2c_client remains valid for the duration */
+ mutex_lock(&rx->client_lock);
+ if (rx->c == NULL) {
+ mutex_unlock(&rx->client_lock);
+ put_ir_rx(rx, false);
+ return -ENXIO;
+ }
+
+ tx = get_ir_tx(ir);
+
+ /*
+ * service the device as long as it is returning
+ * data and we have space
+ */
+ do {
+ if (kthread_should_stop()) {
+ ret = -ENODATA;
+ break;
+ }
+
+ /*
+ * Lock i2c bus for the duration. RX/TX chips interfere so
+ * this is worth it
+ */
+ mutex_lock(&ir->ir_lock);
+
+ if (kthread_should_stop()) {
+ mutex_unlock(&ir->ir_lock);
+ ret = -ENODATA;
+ break;
+ }
+
+ /*
+ * Send random "poll command" (?) Windows driver does this
+ * and it is a good point to detect chip failure.
+ */
+ ret = i2c_master_send(rx->c, sendbuf, 1);
+ if (ret != 1) {
+ zilog_error("i2c_master_send failed with %d\n", ret);
+ if (failures >= 3) {
+ mutex_unlock(&ir->ir_lock);
+ zilog_error("unable to read from the IR chip "
+ "after 3 resets, giving up\n");
+ break;
+ }
+
+ /* Looks like the chip crashed, reset it */
+ zilog_error("polling the IR receiver chip failed, "
+ "trying reset\n");
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (kthread_should_stop()) {
+ mutex_unlock(&ir->ir_lock);
+ ret = -ENODATA;
+ break;
+ }
+ schedule_timeout((100 * HZ + 999) / 1000);
+ if (tx != NULL)
+ tx->need_boot = 1;
+
+ ++failures;
+ mutex_unlock(&ir->ir_lock);
+ ret = 0;
+ continue;
+ }
+
+ if (kthread_should_stop()) {
+ mutex_unlock(&ir->ir_lock);
+ ret = -ENODATA;
+ break;
+ }
+ ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf));
+ mutex_unlock(&ir->ir_lock);
+ if (ret != sizeof(keybuf)) {
+ zilog_error("i2c_master_recv failed with %d -- "
+ "keeping last read buffer\n", ret);
+ } else {
+ rx->b[0] = keybuf[3];
+ rx->b[1] = keybuf[4];
+ rx->b[2] = keybuf[5];
+ dprintk("key (0x%02x/0x%02x)\n", rx->b[0], rx->b[1]);
+ }
+
+ /* key pressed ? */
+ if (rx->hdpvr_data_fmt) {
+ if (got_data && (keybuf[0] == 0x80)) {
+ ret = 0;
+ break;
+ } else if (got_data && (keybuf[0] == 0x00)) {
+ ret = -ENODATA;
+ break;
+ }
+ } else if ((rx->b[0] & 0x80) == 0) {
+ ret = got_data ? 0 : -ENODATA;
+ break;
+ }
+
+ /* look what we have */
+ code = (((__u16)rx->b[0] & 0x7f) << 6) | (rx->b[1] >> 2);
+
+ codes[0] = (code >> 8) & 0xff;
+ codes[1] = code & 0xff;
+
+ /* return it */
+ lirc_buffer_write(rbuf, codes);
+ ++got_data;
+ ret = 0;
+ } while (!lirc_buffer_full(rbuf));
+
+ mutex_unlock(&rx->client_lock);
+ if (tx != NULL)
+ put_ir_tx(tx, false);
+ put_ir_rx(rx, false);
+ return ret;
+}
+
+/*
+ * Main function of the polling thread -- from lirc_dev.
+ * We don't fit the LIRC model at all anymore. This is horrible, but
+ * basically we have a single RX/TX device with a nasty failure mode
+ * that needs to be accounted for across the pair. lirc lets us provide
+ * fops, but prevents us from using the internal polling, etc. if we do
+ * so. Hence the replication. Might be neater to extend the LIRC model
+ * to account for this but I'd think it's a very special case of seriously
+ * messed up hardware.
+ */
+static int lirc_thread(void *arg)
+{
+ struct IR *ir = arg;
+ struct lirc_buffer *rbuf = ir->l.rbuf;
+
+ dprintk("poll thread started\n");
+
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* if device not opened, we can sleep half a second */
+ if (atomic_read(&ir->open_count) == 0) {
+ schedule_timeout(HZ/2);
+ continue;
+ }
+
+ /*
+ * This is ~113*2 + 24 + jitter (2*repeat gap + code length).
+ * We use this interval as the chip resets every time you poll
+ * it (bad!). This is therefore just sufficient to catch all
+ * of the button presses. It makes the remote much more
+ * responsive. You can see the difference by running irw and
+ * holding down a button. With 100ms, the old polling
+ * interval, you'll notice breaks in the repeat sequence
+ * corresponding to lost keypresses.
+ */
+ schedule_timeout((260 * HZ) / 1000);
+ if (kthread_should_stop())
+ break;
+ if (!add_to_buf(ir))
+ wake_up_interruptible(&rbuf->wait_poll);
+ }
+
+ dprintk("poll thread ended\n");
+ return 0;
+}
+
+static int set_use_inc(void *data)
+{
+ return 0;
+}
+
+static void set_use_dec(void *data)
+{
+ return;
+}
+
+/* safe read of a uint32 (always network byte order) */
+static int read_uint32(unsigned char **data,
+ unsigned char *endp, unsigned int *val)
+{
+ if (*data + 4 > endp)
+ return 0;
+ *val = ((*data)[0] << 24) | ((*data)[1] << 16) |
+ ((*data)[2] << 8) | (*data)[3];
+ *data += 4;
+ return 1;
+}
+
+/* safe read of a uint8 */
+static int read_uint8(unsigned char **data,
+ unsigned char *endp, unsigned char *val)
+{
+ if (*data + 1 > endp)
+ return 0;
+ *val = *((*data)++);
+ return 1;
+}
+
+/* safe skipping of N bytes */
+static int skip(unsigned char **data,
+ unsigned char *endp, unsigned int distance)
+{
+ if (*data + distance > endp)
+ return 0;
+ *data += distance;
+ return 1;
+}
+
+/* decompress key data into the given buffer */
+static int get_key_data(unsigned char *buf,
+ unsigned int codeset, unsigned int key)
+{
+ unsigned char *data, *endp, *diffs, *key_block;
+ unsigned char keys, ndiffs, id;
+ unsigned int base, lim, pos, i;
+
+ /* Binary search for the codeset */
+ for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) {
+ pos = base + (lim >> 1);
+ data = tx_data->code_sets[pos];
+
+ if (!read_uint32(&data, tx_data->endp, &i))
+ goto corrupt;
+
+ if (i == codeset)
+ break;
+ else if (codeset > i) {
+ base = pos + 1;
+ --lim;
+ }
+ }
+ /* Not found? */
+ if (!lim)
+ return -EPROTO;
+
+ /* Set end of data block */
+ endp = pos < tx_data->num_code_sets - 1 ?
+ tx_data->code_sets[pos + 1] : tx_data->endp;
+
+ /* Read the block header */
+ if (!read_uint8(&data, endp, &keys) ||
+ !read_uint8(&data, endp, &ndiffs) ||
+ ndiffs > TX_BLOCK_SIZE || keys == 0)
+ goto corrupt;
+
+ /* Save diffs & skip */
+ diffs = data;
+ if (!skip(&data, endp, ndiffs))
+ goto corrupt;
+
+ /* Read the id of the first key */
+ if (!read_uint8(&data, endp, &id))
+ goto corrupt;
+
+ /* Unpack the first key's data */
+ for (i = 0; i < TX_BLOCK_SIZE; ++i) {
+ if (tx_data->fixed[i] == -1) {
+ if (!read_uint8(&data, endp, &buf[i]))
+ goto corrupt;
+ } else {
+ buf[i] = (unsigned char)tx_data->fixed[i];
+ }
+ }
+
+ /* Early out key found/not found */
+ if (key == id)
+ return 0;
+ if (keys == 1)
+ return -EPROTO;
+
+ /* Sanity check */
+ key_block = data;
+ if (!skip(&data, endp, (keys - 1) * (ndiffs + 1)))
+ goto corrupt;
+
+ /* Binary search for the key */
+ for (base = 0, lim = keys - 1; lim; lim >>= 1) {
+ /* Seek to block */
+ unsigned char *key_data;
+ pos = base + (lim >> 1);
+ key_data = key_block + (ndiffs + 1) * pos;
+
+ if (*key_data == key) {
+ /* skip key id */
+ ++key_data;
+
+ /* found, so unpack the diffs */
+ for (i = 0; i < ndiffs; ++i) {
+ unsigned char val;
+ if (!read_uint8(&key_data, endp, &val) ||
+ diffs[i] >= TX_BLOCK_SIZE)
+ goto corrupt;
+ buf[diffs[i]] = val;
+ }
+
+ return 0;
+ } else if (key > *key_data) {
+ base = pos + 1;
+ --lim;
+ }
+ }
+ /* Key not found */
+ return -EPROTO;
+
+corrupt:
+ zilog_error("firmware is corrupt\n");
+ return -EFAULT;
+}
+
+/* send a block of data to the IR TX device */
+static int send_data_block(struct IR_tx *tx, unsigned char *data_block)
+{
+ int i, j, ret;
+ unsigned char buf[5];
+
+ for (i = 0; i < TX_BLOCK_SIZE;) {
+ int tosend = TX_BLOCK_SIZE - i;
+ if (tosend > 4)
+ tosend = 4;
+ buf[0] = (unsigned char)(i + 1);
+ for (j = 0; j < tosend; ++j)
+ buf[1 + j] = data_block[i + j];
+ dprintk("%02x %02x %02x %02x %02x",
+ buf[0], buf[1], buf[2], buf[3], buf[4]);
+ ret = i2c_master_send(tx->c, buf, tosend + 1);
+ if (ret != tosend + 1) {
+ zilog_error("i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+ i += tosend;
+ }
+ return 0;
+}
+
+/* send boot data to the IR TX device */
+static int send_boot_data(struct IR_tx *tx)
+{
+ int ret, i;
+ unsigned char buf[4];
+
+ /* send the boot block */
+ ret = send_data_block(tx, tx_data->boot_data);
+ if (ret != 0)
+ return ret;
+
+ /* Hit the go button to activate the new boot data */
+ buf[0] = 0x00;
+ buf[1] = 0x20;
+ ret = i2c_master_send(tx->c, buf, 2);
+ if (ret != 2) {
+ zilog_error("i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /*
+ * Wait for zilog to settle after hitting go post boot block upload.
+ * Without this delay, the HD-PVR and HVR-1950 both return an -EIO
+ * upon attempting to get firmware revision, and tx probe thus fails.
+ */
+ for (i = 0; i < 10; i++) {
+ ret = i2c_master_send(tx->c, buf, 1);
+ if (ret == 1)
+ break;
+ udelay(100);
+ }
+
+ if (ret != 1) {
+ zilog_error("i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /* Here comes the firmware version... (hopefully) */
+ ret = i2c_master_recv(tx->c, buf, 4);
+ if (ret != 4) {
+ zilog_error("i2c_master_recv failed with %d\n", ret);
+ return 0;
+ }
+ if ((buf[0] != 0x80) && (buf[0] != 0xa0)) {
+ zilog_error("unexpected IR TX init response: %02x\n", buf[0]);
+ return 0;
+ }
+ zilog_notify("Zilog/Hauppauge IR blaster firmware version "
+ "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]);
+
+ return 0;
+}
+
+/* unload "firmware", lock held */
+static void fw_unload_locked(void)
+{
+ if (tx_data) {
+ if (tx_data->code_sets)
+ vfree(tx_data->code_sets);
+
+ if (tx_data->datap)
+ vfree(tx_data->datap);
+
+ vfree(tx_data);
+ tx_data = NULL;
+ dprintk("successfully unloaded IR blaster firmware\n");
+ }
+}
+
+/* unload "firmware" for the IR TX device */
+static void fw_unload(void)
+{
+ mutex_lock(&tx_data_lock);
+ fw_unload_locked();
+ mutex_unlock(&tx_data_lock);
+}
+
+/* load "firmware" for the IR TX device */
+static int fw_load(struct IR_tx *tx)
+{
+ int ret;
+ unsigned int i;
+ unsigned char *data, version, num_global_fixed;
+ const struct firmware *fw_entry;
+
+ /* Already loaded? */
+ mutex_lock(&tx_data_lock);
+ if (tx_data) {
+ ret = 0;
+ goto out;
+ }
+
+ /* Request codeset data file */
+ ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", tx->ir->l.dev);
+ if (ret != 0) {
+ zilog_error("firmware haup-ir-blaster.bin not available "
+ "(%d)\n", ret);
+ ret = ret < 0 ? ret : -EFAULT;
+ goto out;
+ }
+ dprintk("firmware of size %zu loaded\n", fw_entry->size);
+
+ /* Parse the file */
+ tx_data = vmalloc(sizeof(*tx_data));
+ if (tx_data == NULL) {
+ zilog_error("out of memory\n");
+ release_firmware(fw_entry);
+ ret = -ENOMEM;
+ goto out;
+ }
+ tx_data->code_sets = NULL;
+
+ /* Copy the data so hotplug doesn't get confused and timeout */
+ tx_data->datap = vmalloc(fw_entry->size);
+ if (tx_data->datap == NULL) {
+ zilog_error("out of memory\n");
+ release_firmware(fw_entry);
+ vfree(tx_data);
+ ret = -ENOMEM;
+ goto out;
+ }
+ memcpy(tx_data->datap, fw_entry->data, fw_entry->size);
+ tx_data->endp = tx_data->datap + fw_entry->size;
+ release_firmware(fw_entry); fw_entry = NULL;
+
+ /* Check version */
+ data = tx_data->datap;
+ if (!read_uint8(&data, tx_data->endp, &version))
+ goto corrupt;
+ if (version != 1) {
+ zilog_error("unsupported code set file version (%u, expected"
+ "1) -- please upgrade to a newer driver",
+ version);
+ fw_unload_locked();
+ ret = -EFAULT;
+ goto out;
+ }
+
+ /* Save boot block for later */
+ tx_data->boot_data = data;
+ if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE))
+ goto corrupt;
+
+ if (!read_uint32(&data, tx_data->endp,
+ &tx_data->num_code_sets))
+ goto corrupt;
+
+ dprintk("%u IR blaster codesets loaded\n", tx_data->num_code_sets);
+
+ tx_data->code_sets = vmalloc(
+ tx_data->num_code_sets * sizeof(char *));
+ if (tx_data->code_sets == NULL) {
+ fw_unload_locked();
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < TX_BLOCK_SIZE; ++i)
+ tx_data->fixed[i] = -1;
+
+ /* Read global fixed data template */
+ if (!read_uint8(&data, tx_data->endp, &num_global_fixed) ||
+ num_global_fixed > TX_BLOCK_SIZE)
+ goto corrupt;
+ for (i = 0; i < num_global_fixed; ++i) {
+ unsigned char pos, val;
+ if (!read_uint8(&data, tx_data->endp, &pos) ||
+ !read_uint8(&data, tx_data->endp, &val) ||
+ pos >= TX_BLOCK_SIZE)
+ goto corrupt;
+ tx_data->fixed[pos] = (int)val;
+ }
+
+ /* Filch out the position of each code set */
+ for (i = 0; i < tx_data->num_code_sets; ++i) {
+ unsigned int id;
+ unsigned char keys;
+ unsigned char ndiffs;
+
+ /* Save the codeset position */
+ tx_data->code_sets[i] = data;
+
+ /* Read header */
+ if (!read_uint32(&data, tx_data->endp, &id) ||
+ !read_uint8(&data, tx_data->endp, &keys) ||
+ !read_uint8(&data, tx_data->endp, &ndiffs) ||
+ ndiffs > TX_BLOCK_SIZE || keys == 0)
+ goto corrupt;
+
+ /* skip diff positions */
+ if (!skip(&data, tx_data->endp, ndiffs))
+ goto corrupt;
+
+ /*
+ * After the diffs we have the first key id + data -
+ * global fixed
+ */
+ if (!skip(&data, tx_data->endp,
+ 1 + TX_BLOCK_SIZE - num_global_fixed))
+ goto corrupt;
+
+ /* Then we have keys-1 blocks of key id+diffs */
+ if (!skip(&data, tx_data->endp,
+ (ndiffs + 1) * (keys - 1)))
+ goto corrupt;
+ }
+ ret = 0;
+ goto out;
+
+corrupt:
+ zilog_error("firmware is corrupt\n");
+ fw_unload_locked();
+ ret = -EFAULT;
+
+out:
+ mutex_unlock(&tx_data_lock);
+ return ret;
+}
+
+/* copied from lirc_dev */
+static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
+{
+ struct IR *ir = filep->private_data;
+ struct IR_rx *rx;
+ struct lirc_buffer *rbuf = ir->l.rbuf;
+ int ret = 0, written = 0, retries = 0;
+ unsigned int m;
+ DECLARE_WAITQUEUE(wait, current);
+
+ dprintk("read called\n");
+ if (n % rbuf->chunk_size) {
+ dprintk("read result = -EINVAL\n");
+ return -EINVAL;
+ }
+
+ rx = get_ir_rx(ir);
+ if (rx == NULL)
+ return -ENXIO;
+
+ /*
+ * we add ourselves to the task queue before buffer check
+ * to avoid losing scan code (in case when queue is awaken somewhere
+ * between while condition checking and scheduling)
+ */
+ add_wait_queue(&rbuf->wait_poll, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /*
+ * while we didn't provide 'length' bytes, device is opened in blocking
+ * mode and 'copy_to_user' is happy, wait for data.
+ */
+ while (written < n && ret == 0) {
+ if (lirc_buffer_empty(rbuf)) {
+ /*
+ * According to the read(2) man page, 'written' can be
+ * returned as less than 'n', instead of blocking
+ * again, returning -EWOULDBLOCK, or returning
+ * -ERESTARTSYS
+ */
+ if (written)
+ break;
+ if (filep->f_flags & O_NONBLOCK) {
+ ret = -EWOULDBLOCK;
+ break;
+ }
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ } else {
+ unsigned char buf[rbuf->chunk_size];
+ m = lirc_buffer_read(rbuf, buf);
+ if (m == rbuf->chunk_size) {
+ ret = copy_to_user((void *)outbuf+written, buf,
+ rbuf->chunk_size);
+ written += rbuf->chunk_size;
+ } else {
+ retries++;
+ }
+ if (retries >= 5) {
+ zilog_error("Buffer read failed!\n");
+ ret = -EIO;
+ }
+ }
+ }
+
+ remove_wait_queue(&rbuf->wait_poll, &wait);
+ put_ir_rx(rx, false);
+ set_current_state(TASK_RUNNING);
+
+ dprintk("read result = %d (%s)\n", ret, ret ? "Error" : "OK");
+
+ return ret ? ret : written;
+}
+
+/* send a keypress to the IR TX device */
+static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key)
+{
+ unsigned char data_block[TX_BLOCK_SIZE];
+ unsigned char buf[2];
+ int i, ret;
+
+ /* Get data for the codeset/key */
+ ret = get_key_data(data_block, code, key);
+
+ if (ret == -EPROTO) {
+ zilog_error("failed to get data for code %u, key %u -- check "
+ "lircd.conf entries\n", code, key);
+ return ret;
+ } else if (ret != 0)
+ return ret;
+
+ /* Send the data block */
+ ret = send_data_block(tx, data_block);
+ if (ret != 0)
+ return ret;
+
+ /* Send data block length? */
+ buf[0] = 0x00;
+ buf[1] = 0x40;
+ ret = i2c_master_send(tx->c, buf, 2);
+ if (ret != 2) {
+ zilog_error("i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /* Give the z8 a moment to process data block */
+ for (i = 0; i < 10; i++) {
+ ret = i2c_master_send(tx->c, buf, 1);
+ if (ret == 1)
+ break;
+ udelay(100);
+ }
+
+ if (ret != 1) {
+ zilog_error("i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /* Send finished download? */
+ ret = i2c_master_recv(tx->c, buf, 1);
+ if (ret != 1) {
+ zilog_error("i2c_master_recv failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+ if (buf[0] != 0xA0) {
+ zilog_error("unexpected IR TX response #1: %02x\n",
+ buf[0]);
+ return -EFAULT;
+ }
+
+ /* Send prepare command? */
+ buf[0] = 0x00;
+ buf[1] = 0x80;
+ ret = i2c_master_send(tx->c, buf, 2);
+ if (ret != 2) {
+ zilog_error("i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /*
+ * The sleep bits aren't necessary on the HD PVR, and in fact, the
+ * last i2c_master_recv always fails with a -5, so for now, we're
+ * going to skip this whole mess and say we're done on the HD PVR
+ */
+ if (!tx->post_tx_ready_poll) {
+ dprintk("sent code %u, key %u\n", code, key);
+ return 0;
+ }
+
+ /*
+ * This bit NAKs until the device is ready, so we retry it
+ * sleeping a bit each time. This seems to be what the windows
+ * driver does, approximately.
+ * Try for up to 1s.
+ */
+ for (i = 0; i < 20; ++i) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((50 * HZ + 999) / 1000);
+ ret = i2c_master_send(tx->c, buf, 1);
+ if (ret == 1)
+ break;
+ dprintk("NAK expected: i2c_master_send "
+ "failed with %d (try %d)\n", ret, i+1);
+ }
+ if (ret != 1) {
+ zilog_error("IR TX chip never got ready: last i2c_master_send "
+ "failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /* Seems to be an 'ok' response */
+ i = i2c_master_recv(tx->c, buf, 1);
+ if (i != 1) {
+ zilog_error("i2c_master_recv failed with %d\n", ret);
+ return -EFAULT;
+ }
+ if (buf[0] != 0x80) {
+ zilog_error("unexpected IR TX response #2: %02x\n", buf[0]);
+ return -EFAULT;
+ }
+
+ /* Oh good, it worked */
+ dprintk("sent code %u, key %u\n", code, key);
+ return 0;
+}
+
+/*
+ * Write a code to the device. We take in a 32-bit number (an int) and then
+ * decode this to a codeset/key index. The key data is then decompressed and
+ * sent to the device. We have a spin lock as per i2c documentation to prevent
+ * multiple concurrent sends which would probably cause the device to explode.
+ */
+static ssize_t write(struct file *filep, const char *buf, size_t n,
+ loff_t *ppos)
+{
+ struct IR *ir = filep->private_data;
+ struct IR_tx *tx;
+ size_t i;
+ int failures = 0;
+
+ /* Validate user parameters */
+ if (n % sizeof(int))
+ return -EINVAL;
+
+ /* Get a struct IR_tx reference */
+ tx = get_ir_tx(ir);
+ if (tx == NULL)
+ return -ENXIO;
+
+ /* Ensure our tx->c i2c_client remains valid for the duration */
+ mutex_lock(&tx->client_lock);
+ if (tx->c == NULL) {
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ return -ENXIO;
+ }
+
+ /* Lock i2c bus for the duration */
+ mutex_lock(&ir->ir_lock);
+
+ /* Send each keypress */
+ for (i = 0; i < n;) {
+ int ret = 0;
+ int command;
+
+ if (copy_from_user(&command, buf + i, sizeof(command))) {
+ mutex_unlock(&ir->ir_lock);
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ return -EFAULT;
+ }
+
+ /* Send boot data first if required */
+ if (tx->need_boot == 1) {
+ /* Make sure we have the 'firmware' loaded, first */
+ ret = fw_load(tx);
+ if (ret != 0) {
+ mutex_unlock(&ir->ir_lock);
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ if (ret != -ENOMEM)
+ ret = -EIO;
+ return ret;
+ }
+ /* Prep the chip for transmitting codes */
+ ret = send_boot_data(tx);
+ if (ret == 0)
+ tx->need_boot = 0;
+ }
+
+ /* Send the code */
+ if (ret == 0) {
+ ret = send_code(tx, (unsigned)command >> 16,
+ (unsigned)command & 0xFFFF);
+ if (ret == -EPROTO) {
+ mutex_unlock(&ir->ir_lock);
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ return ret;
+ }
+ }
+
+ /*
+ * Hmm, a failure. If we've had a few then give up, otherwise
+ * try a reset
+ */
+ if (ret != 0) {
+ /* Looks like the chip crashed, reset it */
+ zilog_error("sending to the IR transmitter chip "
+ "failed, trying reset\n");
+
+ if (failures >= 3) {
+ zilog_error("unable to send to the IR chip "
+ "after 3 resets, giving up\n");
+ mutex_unlock(&ir->ir_lock);
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ return ret;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((100 * HZ + 999) / 1000);
+ tx->need_boot = 1;
+ ++failures;
+ } else
+ i += sizeof(int);
+ }
+
+ /* Release i2c bus */
+ mutex_unlock(&ir->ir_lock);
+
+ mutex_unlock(&tx->client_lock);
+
+ /* Give back our struct IR_tx reference */
+ put_ir_tx(tx, false);
+
+ /* All looks good */
+ return n;
+}
+
+/* copied from lirc_dev */
+static unsigned int poll(struct file *filep, poll_table *wait)
+{
+ struct IR *ir = filep->private_data;
+ struct IR_rx *rx;
+ struct lirc_buffer *rbuf = ir->l.rbuf;
+ unsigned int ret;
+
+ dprintk("poll called\n");
+
+ rx = get_ir_rx(ir);
+ if (rx == NULL) {
+ /*
+ * Revisit this, if our poll function ever reports writeable
+ * status for Tx
+ */
+ dprintk("poll result = POLLERR\n");
+ return POLLERR;
+ }
+
+ /*
+ * Add our lirc_buffer's wait_queue to the poll_table. A wake up on
+ * that buffer's wait queue indicates we may have a new poll status.
+ */
+ poll_wait(filep, &rbuf->wait_poll, wait);
+
+ /* Indicate what ops could happen immediately without blocking */
+ ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM);
+
+ dprintk("poll result = %s\n", ret ? "POLLIN|POLLRDNORM" : "none");
+ return ret;
+}
+
+static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ struct IR *ir = filep->private_data;
+ int result;
+ unsigned long mode, features;
+
+ features = ir->l.features;
+
+ switch (cmd) {
+ case LIRC_GET_LENGTH:
+ result = put_user((unsigned long)13,
+ (unsigned long *)arg);
+ break;
+ case LIRC_GET_FEATURES:
+ result = put_user(features, (unsigned long *) arg);
+ break;
+ case LIRC_GET_REC_MODE:
+ if (!(features&LIRC_CAN_REC_MASK))
+ return -ENOSYS;
+
+ result = put_user(LIRC_REC2MODE
+ (features&LIRC_CAN_REC_MASK),
+ (unsigned long *)arg);
+ break;
+ case LIRC_SET_REC_MODE:
+ if (!(features&LIRC_CAN_REC_MASK))
+ return -ENOSYS;
+
+ result = get_user(mode, (unsigned long *)arg);
+ if (!result && !(LIRC_MODE2REC(mode) & features))
+ result = -EINVAL;
+ break;
+ case LIRC_GET_SEND_MODE:
+ if (!(features&LIRC_CAN_SEND_MASK))
+ return -ENOSYS;
+
+ result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
+ break;
+ case LIRC_SET_SEND_MODE:
+ if (!(features&LIRC_CAN_SEND_MASK))
+ return -ENOSYS;
+
+ result = get_user(mode, (unsigned long *) arg);
+ if (!result && mode != LIRC_MODE_PULSE)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return result;
+}
+
+static struct IR *get_ir_device_by_minor(unsigned int minor)
+{
+ struct IR *ir;
+ struct IR *ret = NULL;
+
+ mutex_lock(&ir_devices_lock);
+
+ if (!list_empty(&ir_devices_list)) {
+ list_for_each_entry(ir, &ir_devices_list, list) {
+ if (ir->l.minor == minor) {
+ ret = get_ir_device(ir, true);
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&ir_devices_lock);
+ return ret;
+}
+
+/*
+ * Open the IR device. Get hold of our IR structure and
+ * stash it in private_data for the file
+ */
+static int open(struct inode *node, struct file *filep)
+{
+ struct IR *ir;
+ unsigned int minor = MINOR(node->i_rdev);
+
+ /* find our IR struct */
+ ir = get_ir_device_by_minor(minor);
+
+ if (ir == NULL)
+ return -ENODEV;
+
+ atomic_inc(&ir->open_count);
+
+ /* stash our IR struct */
+ filep->private_data = ir;
+
+ nonseekable_open(node, filep);
+ return 0;
+}
+
+/* Close the IR device */
+static int close(struct inode *node, struct file *filep)
+{
+ /* find our IR struct */
+ struct IR *ir = filep->private_data;
+ if (ir == NULL) {
+ zilog_error("close: no private_data attached to the file!\n");
+ return -ENODEV;
+ }
+
+ atomic_dec(&ir->open_count);
+
+ put_ir_device(ir, false);
+ return 0;
+}
+
+static int ir_remove(struct i2c_client *client);
+static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
+
+#define ID_FLAG_TX 0x01
+#define ID_FLAG_HDPVR 0x02
+
+static const struct i2c_device_id ir_transceiver_id[] = {
+ { "ir_tx_z8f0811_haup", ID_FLAG_TX },
+ { "ir_rx_z8f0811_haup", 0 },
+ { "ir_tx_z8f0811_hdpvr", ID_FLAG_HDPVR | ID_FLAG_TX },
+ { "ir_rx_z8f0811_hdpvr", ID_FLAG_HDPVR },
+ { }
+};
+
+static struct i2c_driver driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "Zilog/Hauppauge i2c IR",
+ },
+ .probe = ir_probe,
+ .remove = ir_remove,
+ .id_table = ir_transceiver_id,
+};
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = read,
+ .write = write,
+ .poll = poll,
+ .unlocked_ioctl = ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ioctl,
+#endif
+ .open = open,
+ .release = close
+};
+
+static struct lirc_driver lirc_template = {
+ .name = "lirc_zilog",
+ .minor = -1,
+ .code_length = 13,
+ .buffer_size = BUFLEN / 2,
+ .sample_rate = 0, /* tell lirc_dev to not start its own kthread */
+ .chunk_size = 2,
+ .set_use_inc = set_use_inc,
+ .set_use_dec = set_use_dec,
+ .fops = &lirc_fops,
+ .owner = THIS_MODULE,
+};
+
+static int ir_remove(struct i2c_client *client)
+{
+ if (strncmp("ir_tx_z8", client->name, 8) == 0) {
+ struct IR_tx *tx = i2c_get_clientdata(client);
+ if (tx != NULL) {
+ mutex_lock(&tx->client_lock);
+ tx->c = NULL;
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ }
+ } else if (strncmp("ir_rx_z8", client->name, 8) == 0) {
+ struct IR_rx *rx = i2c_get_clientdata(client);
+ if (rx != NULL) {
+ mutex_lock(&rx->client_lock);
+ rx->c = NULL;
+ mutex_unlock(&rx->client_lock);
+ put_ir_rx(rx, false);
+ }
+ }
+ return 0;
+}
+
+
+/* ir_devices_lock must be held */
+static struct IR *get_ir_device_by_adapter(struct i2c_adapter *adapter)
+{
+ struct IR *ir;
+
+ if (list_empty(&ir_devices_list))
+ return NULL;
+
+ list_for_each_entry(ir, &ir_devices_list, list)
+ if (ir->adapter == adapter) {
+ get_ir_device(ir, true);
+ return ir;
+ }
+
+ return NULL;
+}
+
+static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct IR *ir;
+ struct IR_tx *tx;
+ struct IR_rx *rx;
+ struct i2c_adapter *adap = client->adapter;
+ int ret;
+ bool tx_probe = false;
+
+ dprintk("%s: %s on i2c-%d (%s), client addr=0x%02x\n",
+ __func__, id->name, adap->nr, adap->name, client->addr);
+
+ /*
+ * The IR receiver is at i2c address 0x71.
+ * The IR transmitter is at i2c address 0x70.
+ */
+
+ if (id->driver_data & ID_FLAG_TX)
+ tx_probe = true;
+ else if (tx_only) /* module option */
+ return -ENXIO;
+
+ zilog_info("probing IR %s on %s (i2c-%d)\n",
+ tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
+
+ mutex_lock(&ir_devices_lock);
+
+ /* Use a single struct IR instance for both the Rx and Tx functions */
+ ir = get_ir_device_by_adapter(adap);
+ if (ir == NULL) {
+ ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
+ if (ir == NULL) {
+ ret = -ENOMEM;
+ goto out_no_ir;
+ }
+ kref_init(&ir->ref);
+
+ /* store for use in ir_probe() again, and open() later on */
+ INIT_LIST_HEAD(&ir->list);
+ list_add_tail(&ir->list, &ir_devices_list);
+
+ ir->adapter = adap;
+ mutex_init(&ir->ir_lock);
+ atomic_set(&ir->open_count, 0);
+ spin_lock_init(&ir->tx_ref_lock);
+ spin_lock_init(&ir->rx_ref_lock);
+
+ /* set lirc_dev stuff */
+ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
+ /*
+ * FIXME this is a pointer reference to us, but no refcount.
+ *
+ * This OK for now, since lirc_dev currently won't touch this
+ * buffer as we provide our own lirc_fops.
+ *
+ * Currently our own lirc_fops rely on this ir->l.rbuf pointer
+ */
+ ir->l.rbuf = &ir->rbuf;
+ ir->l.dev = &adap->dev;
+ ret = lirc_buffer_init(ir->l.rbuf,
+ ir->l.chunk_size, ir->l.buffer_size);
+ if (ret)
+ goto out_put_ir;
+ }
+
+ if (tx_probe) {
+ /* Get the IR_rx instance for later, if already allocated */
+ rx = get_ir_rx(ir);
+
+ /* Set up a struct IR_tx instance */
+ tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL);
+ if (tx == NULL) {
+ ret = -ENOMEM;
+ goto out_put_xx;
+ }
+ kref_init(&tx->ref);
+ ir->tx = tx;
+
+ ir->l.features |= LIRC_CAN_SEND_PULSE;
+ mutex_init(&tx->client_lock);
+ tx->c = client;
+ tx->need_boot = 1;
+ tx->post_tx_ready_poll =
+ (id->driver_data & ID_FLAG_HDPVR) ? false : true;
+
+ /* An ir ref goes to the struct IR_tx instance */
+ tx->ir = get_ir_device(ir, true);
+
+ /* A tx ref goes to the i2c_client */
+ i2c_set_clientdata(client, get_ir_tx(ir));
+
+ /*
+ * Load the 'firmware'. We do this before registering with
+ * lirc_dev, so the first firmware load attempt does not happen
+ * after a open() or write() call on the device.
+ *
+ * Failure here is not deemed catastrophic, so the receiver will
+ * still be usable. Firmware load will be retried in write(),
+ * if it is needed.
+ */
+ fw_load(tx);
+
+ /* Proceed only if the Rx client is also ready or not needed */
+ if (rx == NULL && !tx_only) {
+ zilog_info("probe of IR Tx on %s (i2c-%d) done. Waiting"
+ " on IR Rx.\n", adap->name, adap->nr);
+ goto out_ok;
+ }
+ } else {
+ /* Get the IR_tx instance for later, if already allocated */
+ tx = get_ir_tx(ir);
+
+ /* Set up a struct IR_rx instance */
+ rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL);
+ if (rx == NULL) {
+ ret = -ENOMEM;
+ goto out_put_xx;
+ }
+ kref_init(&rx->ref);
+ ir->rx = rx;
+
+ ir->l.features |= LIRC_CAN_REC_LIRCCODE;
+ mutex_init(&rx->client_lock);
+ rx->c = client;
+ rx->hdpvr_data_fmt =
+ (id->driver_data & ID_FLAG_HDPVR) ? true : false;
+
+ /* An ir ref goes to the struct IR_rx instance */
+ rx->ir = get_ir_device(ir, true);
+
+ /* An rx ref goes to the i2c_client */
+ i2c_set_clientdata(client, get_ir_rx(ir));
+
+ /*
+ * Start the polling thread.
+ * It will only perform an empty loop around schedule_timeout()
+ * until we register with lirc_dev and the first user open()
+ */
+ /* An ir ref goes to the new rx polling kthread */
+ rx->task = kthread_run(lirc_thread, get_ir_device(ir, true),
+ "zilog-rx-i2c-%d", adap->nr);
+ if (IS_ERR(rx->task)) {
+ ret = PTR_ERR(rx->task);
+ zilog_error("%s: could not start IR Rx polling thread"
+ "\n", __func__);
+ /* Failed kthread, so put back the ir ref */
+ put_ir_device(ir, true);
+ /* Failure exit, so put back rx ref from i2c_client */
+ i2c_set_clientdata(client, NULL);
+ put_ir_rx(rx, true);
+ ir->l.features &= ~LIRC_CAN_REC_LIRCCODE;
+ goto out_put_xx;
+ }
+
+ /* Proceed only if the Tx client is also ready */
+ if (tx == NULL) {
+ zilog_info("probe of IR Rx on %s (i2c-%d) done. Waiting"
+ " on IR Tx.\n", adap->name, adap->nr);
+ goto out_ok;
+ }
+ }
+
+ /* register with lirc */
+ ir->l.minor = minor; /* module option: user requested minor number */
+ ir->l.minor = lirc_register_driver(&ir->l);
+ if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
+ zilog_error("%s: \"minor\" must be between 0 and %d (%d)!\n",
+ __func__, MAX_IRCTL_DEVICES-1, ir->l.minor);
+ ret = -EBADRQC;
+ goto out_put_xx;
+ }
+ zilog_info("IR unit on %s (i2c-%d) registered as lirc%d and ready\n",
+ adap->name, adap->nr, ir->l.minor);
+
+out_ok:
+ if (rx != NULL)
+ put_ir_rx(rx, true);
+ if (tx != NULL)
+ put_ir_tx(tx, true);
+ put_ir_device(ir, true);
+ zilog_info("probe of IR %s on %s (i2c-%d) done\n",
+ tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
+ mutex_unlock(&ir_devices_lock);
+ return 0;
+
+out_put_xx:
+ if (rx != NULL)
+ put_ir_rx(rx, true);
+ if (tx != NULL)
+ put_ir_tx(tx, true);
+out_put_ir:
+ put_ir_device(ir, true);
+out_no_ir:
+ zilog_error("%s: probing IR %s on %s (i2c-%d) failed with %d\n",
+ __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr,
+ ret);
+ mutex_unlock(&ir_devices_lock);
+ return ret;
+}
+
+static int __init zilog_init(void)
+{
+ int ret;
+
+ zilog_notify("Zilog/Hauppauge IR driver initializing\n");
+
+ mutex_init(&tx_data_lock);
+
+ request_module("firmware_class");
+
+ ret = i2c_add_driver(&driver);
+ if (ret)
+ zilog_error("initialization failed\n");
+ else
+ zilog_notify("initialization complete\n");
+
+ return ret;
+}
+
+static void __exit zilog_exit(void)
+{
+ i2c_del_driver(&driver);
+ /* if loaded */
+ fw_unload();
+ zilog_notify("Zilog/Hauppauge IR driver unloaded\n");
+}
+
+module_init(zilog_init);
+module_exit(zilog_exit);
+
+MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
+ "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, "
+ "Andy Walls");
+MODULE_LICENSE("GPL");
+/* for compat with old name, which isn't all that accurate anymore */
+MODULE_ALIAS("lirc_pvr150");
+
+module_param(minor, int, 0444);
+MODULE_PARM_DESC(minor, "Preferred minor device number");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+module_param(tx_only, bool, 0644);
+MODULE_PARM_DESC(tx_only, "Only handle the IR transmit function");
--- /dev/null
+config SOLO6X10
+ tristate "Softlogic 6x10 MPEG codec cards"
+ depends on PCI && VIDEO_DEV && SND && I2C
+ select VIDEOBUF_DMA_SG
+ select SND_PCM
+ ---help---
+ This driver supports the Softlogic based MPEG-4 and h.264 codec
+ codec cards.
--- /dev/null
+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
+TODO (staging => main):
+
+ * Motion detection flags need to be moved to v4l2
+ * Some private CIDs need to be moved to v4l2
+
+TODO (general):
+
+ * encoder on/off controls
+ * mpeg cid bitrate mode (vbr/cbr)
+ * mpeg cid bitrate/bitrate-peak
+ * mpeg encode of user data
+ * mpeg decode of user data
+ * switch between 4 frames/irq to 1 when using mjpeg (and then back
+ when not)
+ * implement a CID control for motion areas/thresholds
+ * implement CID controls for mozaic areas
+ * allow for higher level of interval (for < 1 fps)
+ * sound:
+ - implement playback via external sound jack
+ - implement loopback of external sound jack with incoming audio?
+ - implement pause/resume
+
+Plase send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc Ben Collins
+<bcollins@bluecherry.net>
--- /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/slab.h>
+#include <linux/videodev2.h>
+#include "solo6x10.h"
+#include "tw28.h"
+
+MODULE_DESCRIPTION("Softlogic 6x10 MP4/H.264 Encoder/Decoder V4L2/ALSA Driver");
+MODULE_AUTHOR("Ben Collins <bcollins@bluecherry.net>");
+MODULE_VERSION(SOLO6X10_VERSION);
+MODULE_LICENSE("GPL");
+
+void solo_irq_on(struct solo_dev *solo_dev, u32 mask)
+{
+ solo_dev->irq_mask |= mask;
+ solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask);
+}
+
+void solo_irq_off(struct solo_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 solo_isr(int irq, void *data)
+{
+ struct solo_dev *solo_dev = data;
+ u32 status;
+ int i;
+
+ status = solo_reg_read(solo_dev, SOLO_IRQ_STAT);
+ if (!status)
+ return IRQ_NONE;
+
+ if (status & ~solo_dev->irq_mask) {
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT,
+ status & ~solo_dev->irq_mask);
+ status &= solo_dev->irq_mask;
+ }
+
+ if (status & SOLO_IRQ_PCI_ERR) {
+ 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 solo_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) {
+ solo_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 solo_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct solo_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, SOLO6X10_NAME);
+ if (ret)
+ goto fail_probe;
+
+ solo_dev->reg_base = pci_ioremap_bar(pdev, 0);
+ if (solo_dev->reg_base == NULL) {
+ ret = -ENOMEM;
+ goto fail_probe;
+ }
+
+ chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) &
+ SOLO_CHIP_ID_MASK;
+ switch (chip_id) {
+ case 7:
+ solo_dev->nr_chans = 16;
+ solo_dev->nr_ext = 5;
+ break;
+ case 6:
+ solo_dev->nr_chans = 8;
+ solo_dev->nr_ext = 2;
+ break;
+ default:
+ dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, "
+ "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 */
+ solo_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, solo_isr, IRQF_SHARED, SOLO6X10_NAME,
+ solo_dev);
+ if (ret)
+ goto fail_probe;
+
+ /* Handle this from the start */
+ solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR);
+
+ ret = solo_i2c_init(solo_dev);
+ if (ret)
+ goto fail_probe;
+
+ /* Setup the DMA engine */
+ 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 solo_pci_remove(struct pci_dev *pdev)
+{
+ struct solo_dev *solo_dev = pci_get_drvdata(pdev);
+
+ free_solo_dev(solo_dev);
+}
+
+static struct pci_device_id solo_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, solo_id_table);
+
+static struct pci_driver solo_pci_driver = {
+ .name = SOLO6X10_NAME,
+ .id_table = solo_id_table,
+ .probe = solo_pci_probe,
+ .remove = solo_pci_remove,
+};
+
+static int __init solo_module_init(void)
+{
+ return pci_register_driver(&solo_pci_driver);
+}
+
+static void __exit solo_module_exit(void)
+{
+ pci_unregister_driver(&solo_pci_driver);
+}
+
+module_init(solo_module_init);
+module_exit(solo_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 solo_dev *solo_dev)
+{
+ solo_dev->vin_hstart = 8;
+ solo_dev->vin_vstart = 2;
+
+ solo_reg_write(solo_dev, SOLO_SYS_VCLK,
+ SOLO_VCLK_SELECT(2) |
+ SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) |
+ SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) |
+ SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) |
+ SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) |
+ SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) |
+ SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) |
+ SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) |
+ SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY));
+
+ solo_reg_write(solo_dev, SOLO_VI_ACT_I_P,
+ SOLO_VI_H_START(solo_dev->vin_hstart) |
+ SOLO_VI_V_START(solo_dev->vin_vstart) |
+ SOLO_VI_V_STOP(solo_dev->vin_vstart +
+ solo_dev->video_vsize));
+
+ solo_reg_write(solo_dev, SOLO_VI_ACT_I_S,
+ SOLO_VI_H_START(solo_dev->vout_hstart) |
+ SOLO_VI_V_START(solo_dev->vout_vstart) |
+ SOLO_VI_V_STOP(solo_dev->vout_vstart +
+ solo_dev->video_vsize));
+
+ solo_reg_write(solo_dev, SOLO_VI_ACT_P,
+ SOLO_VI_H_START(0) |
+ SOLO_VI_V_START(1) |
+ SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE));
+
+ solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT,
+ SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0));
+
+ 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 solo_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 solo_dev *solo_dev, u32 off,
+ u16 val, int reg_size)
+{
+ u16 buf[64];
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < sizeof(buf) >> 1; i++)
+ buf[i] = 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 solo_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 solo_dev *solo_dev)
+{
+ int i;
+
+ for (i = 0; i < solo_dev->nr_chans; i++) {
+ /* Clear motion flag area */
+ solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000,
+ SOLO_MOT_FLAG_SIZE);
+
+ /* Clear working cache table */
+ solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +
+ 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 solo_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 solo_dev *solo_dev)
+{
+ int i;
+
+ solo_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 <linux/slab.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 solo_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 solo_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 solo_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 solo_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 solo_dev *solo_dev)
+{
+ int i;
+
+ solo_capture_config(solo_dev);
+ solo_mp4e_config(solo_dev);
+ solo_jpeg_config(solo_dev);
+
+ for (i = 0; i < solo_dev->nr_chans; i++) {
+ solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0);
+ solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0);
+ }
+
+ solo_irq_on(solo_dev, SOLO_IRQ_ENCODER);
+
+ return 0;
+}
+
+void solo_enc_exit(struct solo_dev *solo_dev)
+{
+ int i;
+
+ solo_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/slab.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 solo_dev *solo_dev;
+ unsigned char g723_buf[G723_PERIOD_BYTES];
+};
+
+static void solo_g723_config(struct solo_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 solo_dev *solo_dev)
+{
+ struct snd_pcm_str *pstr =
+ &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE];
+ struct snd_pcm_substream *ss;
+ struct solo_snd_pcm *solo_pcm;
+
+ 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 solo_dev *solo_dev = snd_pcm_substream_chip(ss);
+ struct solo_snd_pcm *solo_pcm;
+
+ solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL);
+ if (solo_pcm == NULL)
+ 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 solo_dev *solo_dev = solo_pcm->solo_dev;
+ int ret = 0;
+
+ spin_lock(&solo_pcm->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (solo_pcm->on == 0) {
+ /* If this is the first user, switch on interrupts */
+ if (atomic_inc_return(&solo_dev->snd_users) == 1)
+ solo_irq_on(solo_dev, SOLO_IRQ_G723);
+ solo_pcm->on = 1;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (solo_pcm->on) {
+ /* If this was our last user, switch them off */
+ if (atomic_dec_return(&solo_dev->snd_users) == 0)
+ solo_irq_off(solo_dev, SOLO_IRQ_G723);
+ solo_pcm->on = 0;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ spin_unlock(&solo_pcm->lock);
+
+ return ret;
+}
+
+static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss)
+{
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss)
+{
+ struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
+ struct solo_dev *solo_dev = solo_pcm->solo_dev;
+ snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f;
+
+ return idx * G723_FRAMES_PER_PAGE;
+}
+
+static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel,
+ snd_pcm_uframes_t pos, void __user *dst,
+ snd_pcm_uframes_t count)
+{
+ struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
+ struct solo_dev *solo_dev = solo_pcm->solo_dev;
+ int err, i;
+
+ for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) {
+ int page = (pos / G723_FRAMES_PER_PAGE) + i;
+
+ err = solo_p2m_dma(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 solo_dev *solo_dev = snd_kcontrol_chip(kcontrol);
+ u8 ch = value->id.numid - 1;
+
+ value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch);
+
+ return 0;
+}
+
+static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol);
+ u8 ch = value->id.numid - 1;
+ u8 old_val;
+
+ old_val = tw28_get_audio_gain(solo_dev, ch);
+ if (old_val == value->value.integer.value[0])
+ return 0;
+
+ tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]);
+
+ return 1;
+}
+
+static struct snd_kcontrol_new snd_solo_capture_volume = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Volume",
+ .info = snd_solo_capture_volume_info,
+ .get = snd_solo_capture_volume_get,
+ .put = snd_solo_capture_volume_put,
+};
+
+static int solo_snd_pcm_init(struct solo_dev *solo_dev)
+{
+ struct snd_card *card = solo_dev->snd_card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *ss;
+ int ret;
+ int i;
+
+ ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans,
+ &pcm);
+ if (ret < 0)
+ return ret;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_solo_pcm_ops);
+
+ snd_pcm_chip(pcm) = solo_dev;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, card->shortname);
+
+ for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ ss; ss = ss->next, i++)
+ sprintf(ss->name, "Camera #%d Audio", i);
+
+ ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ MAX_BUFFER, MAX_BUFFER);
+ if (ret < 0)
+ return ret;
+
+ solo_dev->snd_pcm = pcm;
+
+ return 0;
+}
+
+int solo_g723_init(struct solo_dev *solo_dev)
+{
+ static struct snd_device_ops ops = { NULL };
+ struct snd_card *card;
+ struct snd_kcontrol_new kctl;
+ char name[32];
+ int ret;
+
+ atomic_set(&solo_dev->snd_users, 0);
+
+ /* Allows for easier mapping between video and audio */
+ sprintf(name, "Softlogic%d", solo_dev->vfd->num);
+
+ ret = snd_card_create(SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0,
+ &solo_dev->snd_card);
+ if (ret < 0)
+ return ret;
+
+ card = solo_dev->snd_card;
+
+ strcpy(card->driver, SOLO6X10_NAME);
+ strcpy(card->shortname, "SOLO-6x10 Audio");
+ sprintf(card->longname, "%s on %s IRQ %d", card->shortname,
+ pci_name(solo_dev->pdev), solo_dev->pdev->irq);
+ snd_card_set_dev(card, &solo_dev->pdev->dev);
+
+ ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops);
+ if (ret < 0)
+ goto snd_error;
+
+ /* Mixer controls */
+ strcpy(card->mixername, "SOLO-6x10");
+ kctl = snd_solo_capture_volume;
+ kctl.count = solo_dev->nr_chans;
+ ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev));
+ if (ret < 0)
+ return ret;
+
+ ret = solo_snd_pcm_init(solo_dev);
+ if (ret < 0)
+ goto snd_error;
+
+ ret = snd_card_register(card);
+ if (ret < 0)
+ goto snd_error;
+
+ solo_g723_config(solo_dev);
+
+ dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name);
+
+ return 0;
+
+snd_error:
+ snd_card_free(card);
+ return ret;
+}
+
+void solo_g723_exit(struct solo_dev *solo_dev)
+{
+ solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0);
+ solo_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 solo_dev *solo_dev,
+ unsigned int port_mask, unsigned int mode)
+{
+ int port;
+ unsigned int ret;
+
+ ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0);
+
+ /* To set gpio */
+ for (port = 0; port < 16; port++) {
+ if (!((1 << port) & port_mask))
+ continue;
+
+ ret &= (~(3 << (port << 1)));
+ ret |= ((mode & 3) << (port << 1));
+ }
+
+ solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret);
+
+ /* To set extended gpio - sensor */
+ ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1);
+
+ for (port = 0; port < 16; port++) {
+ if (!((1 << (port + 16)) & port_mask))
+ continue;
+
+ if (!mode)
+ ret &= ~(1 << port);
+ else
+ ret |= 1 << port;
+ }
+
+ solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret);
+}
+
+static void solo_gpio_set(struct solo_dev *solo_dev, unsigned int value)
+{
+ solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT,
+ solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value);
+}
+
+static void solo_gpio_clear(struct solo_dev *solo_dev, unsigned int value)
+{
+ solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT,
+ solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value);
+}
+
+static void solo_gpio_config(struct solo_dev *solo_dev)
+{
+ /* Video reset */
+ solo_gpio_mode(solo_dev, 0x30, 1);
+ solo_gpio_clear(solo_dev, 0x30);
+ udelay(100);
+ solo_gpio_set(solo_dev, 0x30);
+ udelay(100);
+
+ /* Warning: Don't touch the next line unless you're sure of what
+ * you're doing: first four gpio [0-3] are used for video. */
+ solo_gpio_mode(solo_dev, 0x0f, 2);
+
+ /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */
+ solo_gpio_mode(solo_dev, 0xff00, 1);
+
+ /* Initially set relay status to 0 */
+ solo_gpio_clear(solo_dev, 0xff00);
+}
+
+int solo_gpio_init(struct solo_dev *solo_dev)
+{
+ solo_gpio_config(solo_dev);
+ return 0;
+}
+
+void solo_gpio_exit(struct solo_dev *solo_dev)
+{
+ solo_gpio_clear(solo_dev, 0x30);
+ solo_gpio_config(solo_dev);
+}
--- /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 SOLO6x10 i2c does not have separate interrupts for each i2c
+ * channel. The bus can only handle one i2c event at a time. The below handles
+ * this all wrong. We should be using the status registers to see if the bus
+ * is in use, and have a global lock to check the status register. Also,
+ * the bulk of the work should be handled out-of-interrupt. The ugly loops
+ * that occur during interrupt scare me. The ISR should merely signal
+ * thread context, ACK the interrupt, and move on. -- BenC */
+
+#include <linux/kernel.h>
+#include "solo6x10.h"
+
+u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off)
+{
+ struct i2c_msg msgs[2];
+ u8 data;
+
+ msgs[0].flags = 0;
+ msgs[0].addr = addr;
+ msgs[0].len = 1;
+ msgs[0].buf = &off;
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = addr;
+ msgs[1].len = 1;
+ msgs[1].buf = &data;
+
+ i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2);
+
+ return data;
+}
+
+void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr,
+ u8 off, u8 data)
+{
+ struct i2c_msg msgs;
+ u8 buf[2];
+
+ buf[0] = off;
+ buf[1] = data;
+ msgs.flags = 0;
+ msgs.addr = addr;
+ msgs.len = 2;
+ msgs.buf = buf;
+
+ i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1);
+}
+
+static void solo_i2c_flush(struct solo_dev *solo_dev, int wr)
+{
+ u32 ctrl;
+
+ ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id);
+
+ if (solo_dev->i2c_state == IIC_STATE_START)
+ ctrl |= SOLO_IIC_START;
+
+ if (wr) {
+ ctrl |= SOLO_IIC_WRITE;
+ } else {
+ ctrl |= SOLO_IIC_READ;
+ if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK))
+ ctrl |= SOLO_IIC_ACK_EN;
+ }
+
+ if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len)
+ ctrl |= SOLO_IIC_STOP;
+
+ solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl);
+}
+
+static void solo_i2c_start(struct solo_dev *solo_dev)
+{
+ u32 addr = solo_dev->i2c_msg->addr << 1;
+
+ if (solo_dev->i2c_msg->flags & I2C_M_RD)
+ addr |= 1;
+
+ solo_dev->i2c_state = IIC_STATE_START;
+ solo_reg_write(solo_dev, SOLO_IIC_TXD, addr);
+ solo_i2c_flush(solo_dev, 1);
+}
+
+static void solo_i2c_stop(struct solo_dev *solo_dev)
+{
+ solo_irq_off(solo_dev, SOLO_IRQ_IIC);
+ solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0);
+ solo_dev->i2c_state = IIC_STATE_STOP;
+ wake_up(&solo_dev->i2c_wait);
+}
+
+static int solo_i2c_handle_read(struct solo_dev *solo_dev)
+{
+prepare_read:
+ if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) {
+ solo_i2c_flush(solo_dev, 0);
+ return 0;
+ }
+
+ solo_dev->i2c_msg_ptr = 0;
+ solo_dev->i2c_msg++;
+ solo_dev->i2c_msg_num--;
+
+ if (solo_dev->i2c_msg_num == 0) {
+ solo_i2c_stop(solo_dev);
+ return 0;
+ }
+
+ if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) {
+ solo_i2c_start(solo_dev);
+ } else {
+ if (solo_dev->i2c_msg->flags & I2C_M_RD)
+ goto prepare_read;
+ else
+ solo_i2c_stop(solo_dev);
+ }
+
+ return 0;
+}
+
+static int solo_i2c_handle_write(struct solo_dev *solo_dev)
+{
+retry_write:
+ if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) {
+ solo_reg_write(solo_dev, SOLO_IIC_TXD,
+ solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]);
+ solo_dev->i2c_msg_ptr++;
+ solo_i2c_flush(solo_dev, 1);
+ return 0;
+ }
+
+ solo_dev->i2c_msg_ptr = 0;
+ solo_dev->i2c_msg++;
+ solo_dev->i2c_msg_num--;
+
+ if (solo_dev->i2c_msg_num == 0) {
+ solo_i2c_stop(solo_dev);
+ return 0;
+ }
+
+ if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) {
+ solo_i2c_start(solo_dev);
+ } else {
+ if (solo_dev->i2c_msg->flags & I2C_M_RD)
+ solo_i2c_stop(solo_dev);
+ else
+ goto retry_write;
+ }
+
+ return 0;
+}
+
+int solo_i2c_isr(struct solo_dev *solo_dev)
+{
+ u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL);
+ int ret = -EINVAL;
+
+ 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 solo_dev *solo_dev = adap->algo_data;
+ unsigned long timeout;
+ int ret;
+ int i;
+ DEFINE_WAIT(wait);
+
+ for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
+ if (&solo_dev->i2c_adap[i] == adap)
+ break;
+ }
+
+ if (i == SOLO_I2C_ADAPTERS)
+ return num; /* XXX Right return value for failure? */
+
+ mutex_lock(&solo_dev->i2c_mutex);
+ solo_dev->i2c_id = i;
+ solo_dev->i2c_msg = msgs;
+ solo_dev->i2c_msg_num = num;
+ solo_dev->i2c_msg_ptr = 0;
+
+ solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0);
+ solo_irq_on(solo_dev, SOLO_IRQ_IIC);
+ solo_i2c_start(solo_dev);
+
+ timeout = HZ / 2;
+
+ for (;;) {
+ prepare_to_wait(&solo_dev->i2c_wait, &wait, TASK_INTERRUPTIBLE);
+
+ if (solo_dev->i2c_state == IIC_STATE_STOP)
+ break;
+
+ timeout = schedule_timeout(timeout);
+ if (!timeout)
+ break;
+
+ if (signal_pending(current))
+ break;
+ }
+
+ finish_wait(&solo_dev->i2c_wait, &wait);
+ ret = num - solo_dev->i2c_msg_num;
+ solo_dev->i2c_state = IIC_STATE_IDLE;
+ solo_dev->i2c_id = -1;
+
+ mutex_unlock(&solo_dev->i2c_mutex);
+
+ return ret;
+}
+
+static u32 solo_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm solo_i2c_algo = {
+ .master_xfer = solo_i2c_master_xfer,
+ .functionality = solo_i2c_functionality,
+};
+
+int solo_i2c_init(struct solo_dev *solo_dev)
+{
+ int i;
+ int ret;
+
+ solo_reg_write(solo_dev, SOLO_IIC_CFG,
+ SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE);
+
+ solo_dev->i2c_id = -1;
+ solo_dev->i2c_state = IIC_STATE_IDLE;
+ init_waitqueue_head(&solo_dev->i2c_wait);
+ mutex_init(&solo_dev->i2c_mutex);
+
+ for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
+ struct i2c_adapter *adap = &solo_dev->i2c_adap[i];
+
+ snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", SOLO6X10_NAME, i);
+ adap->algo = &solo_i2c_algo;
+ adap->algo_data = solo_dev;
+ adap->retries = 1;
+ adap->dev.parent = &solo_dev->pdev->dev;
+
+ ret = i2c_add_adapter(adap);
+ if (ret) {
+ adap->algo_data = NULL;
+ break;
+ }
+ }
+
+ if (ret) {
+ for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
+ if (!solo_dev->i2c_adap[i].algo_data)
+ break;
+ i2c_del_adapter(&solo_dev->i2c_adap[i]);
+ solo_dev->i2c_adap[i].algo_data = NULL;
+ }
+ return ret;
+ }
+
+ dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n",
+ SOLO_I2C_ADAPTERS);
+
+ return 0;
+}
+
+void solo_i2c_exit(struct solo_dev *solo_dev)
+{
+ int i;
+
+ for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
+ if (!solo_dev->i2c_adap[i].algo_data)
+ continue;
+ i2c_del_adapter(&solo_dev->i2c_adap[i]);
+ solo_dev->i2c_adap[i].algo_data = NULL;
+ }
+}
--- /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 __SOLO6X10_JPEG_H
+#define __SOLO6X10_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 /* __SOLO6X10_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 __SOLO6X10_OFFSETS_H
+#define __SOLO6X10_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 /* __SOLO6X10_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 __SOLO6X10_OSD_FONT_H
+#define __SOLO6X10_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 /* __SOLO6X10_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/slab.h>
+#include <linux/scatterlist.h>
+#include "solo6x10.h"
+
+/* #define SOLO_TEST_P2M */
+
+int solo_p2m_dma(struct solo_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 solo_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 solo_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 solo_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 solo_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(SOLO6X10_NAME ": Failed to malloc for p2m_test\n");
+ return size;
+ }
+
+ rd_buf = kmalloc(size, GFP_KERNEL);
+ if (!rd_buf) {
+ printk(SOLO6X10_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 solo_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",
+ SOLO6X10_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",
+ SOLO6X10_NAME, errs);
+
+ return;
+}
+#else
+#define run_p2m_test(__solo) do {} while (0)
+#endif
+
+void solo_p2m_isr(struct solo_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 solo_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 solo_dev *solo_dev)
+{
+ int i;
+
+ for (i = 0; i < SOLO_NR_P2M; i++)
+ solo_irq_off(solo_dev, SOLO_IRQ_P2M(i));
+}
+
+int solo_p2m_init(struct solo_dev *solo_dev)
+{
+ struct solo_p2m_dev *p2m_dev;
+ int i;
+
+ for (i = 0; i < SOLO_NR_P2M; i++) {
+ p2m_dev = &solo_dev->p2m_dev[i];
+
+ mutex_init(&p2m_dev->mutex);
+ init_completion(&p2m_dev->completion);
+
+ solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0);
+ solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i),
+ SOLO_P2M_CSC_16BIT_565 |
+ SOLO_P2M_DMA_INTERVAL(3) |
+ SOLO_P2M_DESC_INTR_OPT |
+ SOLO_P2M_PCI_MASTER_MODE);
+ solo_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 __SOLO6X10_REGISTERS_H
+#define __SOLO6X10_REGISTERS_H
+
+#include "offsets.h"
+
+/* Global 6X10 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 /* __SOLO6X10_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.
+ */
+
+#ifndef __SOLO6X10_H
+#define __SOLO6X10_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 <linux/slab.h>
+#include <asm/io.h>
+#include <linux/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 SOLO6X10_NAME "solo6x10"
+
+#define SOLO_MAX_CHANNELS 16
+
+/* Make sure these two match */
+#define SOLO6X10_VERSION "2.1.0"
+#define SOLO6X10_VER_MAJOR 2
+#define SOLO6X10_VER_MINOR 0
+#define SOLO6X10_VER_SUB 0
+#define SOLO6X10_VER_NUM \
+ KERNEL_VERSION(SOLO6X10_VER_MAJOR, SOLO6X10_VER_MINOR, SOLO6X10_VER_SUB)
+
+#define FLAGS_6110 1
+
+/*
+ * The SOLO6x10 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 solo_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 SOLO6x10 PCI Device */
+struct solo_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 solo_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 solo_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 solo_irq_on(struct solo_dev *solo_dev, u32 mask);
+void solo_irq_off(struct solo_dev *solo_dev, u32 mask);
+
+/* Init/exit routeines for subsystems */
+int solo_disp_init(struct solo_dev *solo_dev);
+void solo_disp_exit(struct solo_dev *solo_dev);
+
+int solo_gpio_init(struct solo_dev *solo_dev);
+void solo_gpio_exit(struct solo_dev *solo_dev);
+
+int solo_i2c_init(struct solo_dev *solo_dev);
+void solo_i2c_exit(struct solo_dev *solo_dev);
+
+int solo_p2m_init(struct solo_dev *solo_dev);
+void solo_p2m_exit(struct solo_dev *solo_dev);
+
+int solo_v4l2_init(struct solo_dev *solo_dev);
+void solo_v4l2_exit(struct solo_dev *solo_dev);
+
+int solo_enc_init(struct solo_dev *solo_dev);
+void solo_enc_exit(struct solo_dev *solo_dev);
+
+int solo_enc_v4l2_init(struct solo_dev *solo_dev);
+void solo_enc_v4l2_exit(struct solo_dev *solo_dev);
+
+int solo_g723_init(struct solo_dev *solo_dev);
+void solo_g723_exit(struct solo_dev *solo_dev);
+
+/* ISR's */
+int solo_i2c_isr(struct solo_dev *solo_dev);
+void solo_p2m_isr(struct solo_dev *solo_dev, int id);
+void solo_p2m_error_isr(struct solo_dev *solo_dev, u32 status);
+void solo_enc_v4l2_isr(struct solo_dev *solo_dev);
+void solo_g723_isr(struct solo_dev *solo_dev);
+void solo_motion_isr(struct solo_dev *solo_dev);
+void solo_video_in_isr(struct solo_dev *solo_dev);
+
+/* i2c read/write */
+u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off);
+void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off,
+ u8 data);
+
+/* P2M DMA */
+int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr,
+ dma_addr_t dma_addr, u32 ext_addr, u32 size);
+int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr,
+ void *sys_addr, u32 ext_addr, u32 size);
+int solo_p2m_dma_sg(struct solo_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 solo_dev *solo_dev, u8 id,
+ struct p2m_desc *desc, int desc_count);
+
+/* Set the threshold for motion detection */
+void solo_set_motion_threshold(struct solo_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 /* __SOLO6X10_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 solo_dev *solo_dev, int chip_id, u8 tw6x_off,
+ u8 tw_off)
+{
+ if (is_tw286x(solo_dev, chip_id))
+ return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
+ TW_CHIP_OFFSET_ADDR(chip_id),
+ tw6x_off);
+ else
+ return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
+ TW_CHIP_OFFSET_ADDR(chip_id),
+ tw_off);
+}
+
+static void tw_writebyte(struct solo_dev *solo_dev, int chip_id,
+ u8 tw6x_off, u8 tw_off, u8 val)
+{
+ if (is_tw286x(solo_dev, chip_id))
+ solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
+ TW_CHIP_OFFSET_ADDR(chip_id),
+ tw6x_off, val);
+ else
+ solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
+ TW_CHIP_OFFSET_ADDR(chip_id),
+ tw_off, val);
+}
+
+static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
+ u8 val)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
+ if (rval == val)
+ return;
+
+ solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
+ msleep_interruptible(1);
+ }
+
+/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n",
+ addr, off, val); */
+}
+
+static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
+{
+ u8 tbl_tw2865_common[256];
+ int i;
+
+ if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
+ memcpy(tbl_tw2865_common, tbl_tw2865_pal_template,
+ sizeof(tbl_tw2865_common));
+ else
+ memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
+ sizeof(tbl_tw2865_common));
+
+ /* ALINK Mode */
+ if (solo_dev->nr_chans == 4) {
+ tbl_tw2865_common[0xd2] = 0x01;
+ tbl_tw2865_common[0xcf] = 0x00;
+ } else if (solo_dev->nr_chans == 8) {
+ tbl_tw2865_common[0xd2] = 0x02;
+ if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
+ tbl_tw2865_common[0xcf] = 0x80;
+ } else if (solo_dev->nr_chans == 16) {
+ tbl_tw2865_common[0xd2] = 0x03;
+ if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
+ tbl_tw2865_common[0xcf] = 0x83;
+ else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
+ tbl_tw2865_common[0xcf] = 0x83;
+ else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
+ tbl_tw2865_common[0xcf] = 0x80;
+ }
+
+ for (i = 0; i < 0xff; i++) {
+ /* Skip read only registers */
+ 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 solo_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 solo_dev *solo_dev, u8 dev_addr)
+{
+ u8 tbl_ntsc_tw2815_common[] = {
+ 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
+ 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
+ };
+
+ u8 tbl_pal_tw2815_common[] = {
+ 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
+ 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
+ };
+
+ u8 tbl_tw2815_sfr[] = {
+ 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */
+ 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */
+ 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
+ 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */
+ 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
+ 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */
+ };
+ u8 *tbl_tw2815_common;
+ int i;
+ int ch;
+
+ tbl_ntsc_tw2815_common[0x06] = 0;
+
+ /* Horizontal Delay Control */
+ tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff;
+ tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8);
+
+ /* Horizontal Active Control */
+ tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff;
+ tbl_ntsc_tw2815_common[0x06] |=
+ ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2);
+
+ /* Vertical Delay Control */
+ tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff;
+ tbl_ntsc_tw2815_common[0x06] |=
+ ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4);
+
+ /* Vertical Active Control */
+ tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff;
+ tbl_ntsc_tw2815_common[0x06] |=
+ ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5);
+
+ tbl_pal_tw2815_common[0x06] = 0;
+
+ /* Horizontal Delay Control */
+ tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff;
+ tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8);
+
+ /* Horizontal Active Control */
+ tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff;
+ tbl_pal_tw2815_common[0x06] |=
+ ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2);
+
+ /* Vertical Delay Control */
+ tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff;
+ tbl_pal_tw2815_common[0x06] |=
+ ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4);
+
+ /* Vertical Active Control */
+ tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff;
+ tbl_pal_tw2815_common[0x06] |=
+ ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5);
+
+ tbl_tw2815_common =
+ (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
+ tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
+
+ /* Dual ITU-R BT.656 format */
+ tbl_tw2815_common[0x0d] |= 0x04;
+
+ /* Audio configuration */
+ tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
+
+ if (solo_dev->nr_chans == 4) {
+ tbl_tw2815_sfr[0x63 - 0x40] |= 1;
+ tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6;
+ } else if (solo_dev->nr_chans == 8) {
+ tbl_tw2815_sfr[0x63 - 0x40] |= 2;
+ if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
+ tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
+ else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
+ tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
+ } else if (solo_dev->nr_chans == 16) {
+ tbl_tw2815_sfr[0x63 - 0x40] |= 3;
+ if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
+ tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
+ else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
+ tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
+ else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
+ tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
+ else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
+ tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
+ }
+
+ /* Output mode of R_ADATM pin (0 mixing, 1 record) */
+ /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
+
+ /* 8KHz, used to be 16KHz, but changed for remote client compat */
+ tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2;
+ tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2;
+
+ /* Playback of right channel */
+ tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
+
+ /* Reserved value (XXX ??) */
+ tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
+
+ /* Analog output gain and mix ratio playback on full */
+ tbl_tw2815_sfr[0x70 - 0x40] |= 0xff;
+ /* Select playback audio and mute all except */
+ tbl_tw2815_sfr[0x71 - 0x40] |= 0x10;
+ tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f;
+
+ /* End of audio configuration */
+
+ for (ch = 0; ch < 4; ch++) {
+ tbl_tw2815_common[0x0d] &= ~3;
+ switch (ch) {
+ case 0:
+ tbl_tw2815_common[0x0d] |= 0x21;
+ break;
+ case 1:
+ tbl_tw2815_common[0x0d] |= 0x20;
+ break;
+ case 2:
+ tbl_tw2815_common[0x0d] |= 0x23;
+ break;
+ case 3:
+ tbl_tw2815_common[0x0d] |= 0x22;
+ break;
+ }
+
+ for (i = 0; i < 0x0f; i++) {
+ if (i == 0x00)
+ continue; /* read-only */
+ solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
+ dev_addr, (ch * 0x10) + i,
+ tbl_tw2815_common[i]);
+ }
+ }
+
+ for (i = 0x40; i < 0x76; i++) {
+ /* Skip read-only and nop registers */
+ if (i == 0x40 || i == 0x59 || i == 0x5a ||
+ i == 0x5d || i == 0x5e || i == 0x5f)
+ continue;
+
+ solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
+ tbl_tw2815_sfr[i - 0x40]);
+ }
+
+ return 0;
+}
+
+#define FIRST_ACTIVE_LINE 0x0008
+#define LAST_ACTIVE_LINE 0x0102
+
+static void saa7128_setup(struct solo_dev *solo_dev)
+{
+ int i;
+ unsigned char regs[128] = {
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
+ 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00,
+ 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00,
+ 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
+ 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
+ 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
+ 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
+ 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
+ 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00,
+ 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff,
+ };
+
+ regs[0x7A] = FIRST_ACTIVE_LINE & 0xff;
+ regs[0x7B] = LAST_ACTIVE_LINE & 0xff;
+ regs[0x7C] = ((1 << 7) |
+ (((LAST_ACTIVE_LINE >> 8) & 1) << 6) |
+ (((FIRST_ACTIVE_LINE >> 8) & 1) << 4));
+
+ /* PAL: XXX: We could do a second set of regs to avoid this */
+ if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) {
+ regs[0x28] = 0xE1;
+
+ regs[0x5A] = 0x0F;
+ regs[0x61] = 0x02;
+ regs[0x62] = 0x35;
+ regs[0x63] = 0xCB;
+ regs[0x64] = 0x8A;
+ regs[0x65] = 0x09;
+ regs[0x66] = 0x2A;
+
+ regs[0x6C] = 0xf1;
+ regs[0x6E] = 0x20;
+
+ regs[0x7A] = 0x06 + 12;
+ regs[0x7b] = 0x24 + 12;
+ regs[0x7c] |= 1 << 6;
+ }
+
+ /* First 0x25 bytes are read-only? */
+ for (i = 0x26; i < 128; i++) {
+ if (i == 0x60 || i == 0x7D)
+ continue;
+ solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]);
+ }
+
+ return;
+}
+
+int solo_tw28_init(struct solo_dev *solo_dev)
+{
+ int i;
+ u8 value;
+
+ /* 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 solo_dev *solo_dev, u8 ch)
+{
+ u8 val, chip_num;
+
+ /* Get the right chip and on-chip channel */
+ chip_num = ch / 4;
+ ch %= 4;
+
+ val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR,
+ TW_AV_STAT_ADDR) & 0x0f;
+
+ return val & (1 << ch) ? 1 : 0;
+}
+
+#if 0
+/* Status of audio from up to 4 techwell chips are combined into 1 variable.
+ * See techwell datasheet for details. */
+u16 tw28_get_audio_status(struct solo_dev *solo_dev)
+{
+ u8 val;
+ u16 status = 0;
+ int i;
+
+ for (i = 0; i < solo_dev->tw28_cnt; i++) {
+ val = (tw_readbyte(solo_dev, i, TW286X_AV_STAT_ADDR,
+ TW_AV_STAT_ADDR) & 0xf0) >> 4;
+ status |= val << (i * 4);
+ }
+
+ return status;
+}
+#endif
+
+int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val)
+{
+ char sval;
+ u8 chip_num;
+
+ /* Get the right chip and on-chip channel */
+ chip_num = ch / 4;
+ ch %= 4;
+
+ if (val > 255 || val < 0)
+ return -ERANGE;
+
+ switch (ctrl) {
+ case V4L2_CID_SHARPNESS:
+ /* Only 286x has sharpness */
+ if (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 solo_dev *solo_dev, u32 ctrl, u8 ch,
+ s32 *val)
+{
+ u8 rval, chip_num;
+
+ /* Get the right chip and on-chip channel */
+ chip_num = ch / 4;
+ ch %= 4;
+
+ switch (ctrl) {
+ case V4L2_CID_SHARPNESS:
+ /* Only 286x has sharpness */
+ if (is_tw286x(solo_dev, chip_num)) {
+ rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
+ TW_CHIP_OFFSET_ADDR(chip_num),
+ TW286x_SHARPNESS(chip_num));
+ *val = rval & 0x0f;
+ } else
+ *val = 0;
+ break;
+ case V4L2_CID_HUE:
+ rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
+ TW_HUE_ADDR(ch));
+ if (is_tw286x(solo_dev, chip_num))
+ *val = (s32)((char)rval) + 128;
+ else
+ *val = rval;
+ break;
+ case V4L2_CID_SATURATION:
+ *val = tw_readbyte(solo_dev, chip_num,
+ TW286x_SATURATIONU_ADDR(ch),
+ TW_SATURATION_ADDR(ch));
+ break;
+ case V4L2_CID_CONTRAST:
+ *val = tw_readbyte(solo_dev, chip_num,
+ TW286x_CONTRAST_ADDR(ch),
+ TW_CONTRAST_ADDR(ch));
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ rval = tw_readbyte(solo_dev, chip_num,
+ TW286x_BRIGHTNESS_ADDR(ch),
+ TW_BRIGHTNESS_ADDR(ch));
+ if (is_tw286x(solo_dev, chip_num))
+ *val = (s32)((char)rval) + 128;
+ else
+ *val = rval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#if 0
+/*
+ * For audio output volume, the output channel is only 1. In this case we
+ * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
+ * is the base address of the techwell chip.
+ */
+void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val)
+{
+ unsigned int val;
+ unsigned int chip_num;
+
+ chip_num = (solo_dev->nr_chans - 1) / 4;
+
+ val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
+ TW_AUDIO_OUTPUT_VOL_ADDR);
+
+ u_val = (val & 0x0f) | (u_val << 4);
+
+ tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
+ TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
+}
+#endif
+
+u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch)
+{
+ u8 val;
+ u8 chip_num;
+
+ /* Get the right chip and on-chip channel */
+ chip_num = ch / 4;
+ ch %= 4;
+
+ val = tw_readbyte(solo_dev, chip_num,
+ TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
+ TW_AUDIO_INPUT_GAIN_ADDR(ch));
+
+ return (ch % 2) ? (val >> 4) : (val & 0x0f);
+}
+
+void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val)
+{
+ u8 old_val;
+ u8 chip_num;
+
+ /* Get the right chip and on-chip channel */
+ chip_num = ch / 4;
+ ch %= 4;
+
+ old_val = tw_readbyte(solo_dev, chip_num,
+ TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
+ TW_AUDIO_INPUT_GAIN_ADDR(ch));
+
+ val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
+ ((ch % 2) ? (val << 4) : val);
+
+ tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
+ TW_AUDIO_INPUT_GAIN_ADDR(ch), val);
+}
--- /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 __SOLO6X10_TW28_H
+#define __SOLO6X10_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 solo_dev *solo_dev);
+
+int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val);
+int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 *val);
+
+u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch);
+void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val);
+int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch);
+
+#if 0
+unsigned int tw2815_get_audio_status(struct SOLO *solo);
+void tw2815_Set_AudioOutVol(struct SOLO *solo, unsigned int u_val);
+#endif
+
+#endif /* __SOLO6X10_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 solo_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 solo_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)
+ solo_irq_on(solo_dev, SOLO_IRQ_MOTION);
+ else
+ solo_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 solo_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 solo_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 solo_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, SOLO6X10_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_dev *solo_dev = solo_enc->solo_dev;
+
+ strcpy(cap->driver, SOLO6X10_NAME);
+ snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d",
+ solo_enc->ch);
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
+ pci_name(solo_dev->pdev));
+ cap->version = SOLO6X10_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 solo_dev *solo_dev = solo_enc->solo_dev;
+
+ if (input->index)
+ return -EINVAL;
+
+ snprintf(input->name, sizeof(input->name), "Encoder %d",
+ solo_enc->ch + 1);
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+
+ 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 solo_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 solo_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 solo_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 solo_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 solo_dev *solo_dev = solo_enc->solo_dev;
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = solo_enc->interval;
+ cp->timeperframe.denominator = solo_dev->fps;
+ cp->capturemode = 0;
+ /* XXX: Shouldn't we be able to get/set this from videobuf? */
+ cp->readbuffers = 2;
+
+ return 0;
+}
+
+static int solo_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *sp)
+{
+ struct solo_enc_fh *fh = priv;
+ struct solo_enc_dev *solo_enc = fh->enc;
+ struct solo_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 solo_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 solo_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 solo_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 = SOLO6X10_NAME,
+ .fops = &solo_enc_fops,
+ .ioctl_ops = &solo_enc_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+
+ .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static struct solo_enc_dev *solo_enc_alloc(struct solo_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)", SOLO6X10_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 solo_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 solo_dev *solo_dev)
+{
+ int i;
+
+ solo_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 solo_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 solo_dev *solo_dev)
+{
+ solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
+ solo_dev->erasing = 1;
+ solo_dev->frame_blank = 0;
+}
+
+static int erase_off(struct solo_dev *solo_dev)
+{
+ if (!solo_dev->erasing)
+ return 0;
+
+ /* First time around, assert erase off */
+ if (!solo_dev->frame_blank)
+ solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0);
+ /* Keep the erasing flag on for 8 frames minimum */
+ if (solo_dev->frame_blank++ >= 8)
+ solo_dev->erasing = 0;
+
+ return 1;
+}
+
+void solo_video_in_isr(struct solo_dev *solo_dev)
+{
+ 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 solo_dev *solo_dev, u8 ch,
+ int sx, int sy, int ex, int ey, int scale)
+{
+ if (ch >= solo_dev->nr_chans)
+ return;
+
+ /* Here, we just keep window/channel the same */
+ solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch),
+ SOLO_VI_WIN_CHANNEL(ch) |
+ SOLO_VI_WIN_SX(sx) |
+ SOLO_VI_WIN_EX(ex) |
+ SOLO_VI_WIN_SCALE(scale));
+
+ solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch),
+ SOLO_VI_WIN_SY(sy) |
+ SOLO_VI_WIN_EY(ey));
+}
+
+static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on)
+{
+ u8 ch = idx * 4;
+
+ if (ch >= solo_dev->nr_chans)
+ return -EINVAL;
+
+ if (!on) {
+ u8 i;
+ for (i = ch; i < ch + 4; i++)
+ solo_win_setup(solo_dev, i, solo_dev->video_hsize,
+ solo_vlines(solo_dev),
+ solo_dev->video_hsize,
+ solo_vlines(solo_dev), 0);
+ return 0;
+ }
+
+ /* Row 1 */
+ solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2,
+ solo_vlines(solo_dev) / 2, 3);
+ solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0,
+ solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3);
+ /* Row 2 */
+ solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2,
+ solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3);
+ solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2,
+ solo_vlines(solo_dev) / 2, solo_dev->video_hsize,
+ solo_vlines(solo_dev), 3);
+
+ return 0;
+}
+
+static int solo_v4l2_ch_ext_16up(struct solo_dev *solo_dev, int on)
+{
+ int sy, ysize, hsize, i;
+
+ if (!on) {
+ for (i = 0; i < 16; i++)
+ solo_win_setup(solo_dev, i, solo_dev->video_hsize,
+ solo_vlines(solo_dev),
+ solo_dev->video_hsize,
+ solo_vlines(solo_dev), 0);
+ return 0;
+ }
+
+ ysize = solo_vlines(solo_dev) / 4;
+ hsize = solo_dev->video_hsize / 4;
+
+ for (sy = 0, i = 0; i < 4; i++, sy += ysize) {
+ solo_win_setup(solo_dev, i * 4, 0, sy, hsize,
+ sy + ysize, 5);
+ solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy,
+ hsize * 2, sy + ysize, 5);
+ solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy,
+ hsize * 3, sy + ysize, 5);
+ solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy,
+ solo_dev->video_hsize, sy + ysize, 5);
+ }
+
+ return 0;
+}
+
+static int solo_v4l2_ch(struct solo_dev *solo_dev, u8 ch, int on)
+{
+ u8 ext_ch;
+
+ if (ch < solo_dev->nr_chans) {
+ solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize,
+ on ? 0 : solo_vlines(solo_dev),
+ solo_dev->video_hsize, solo_vlines(solo_dev),
+ on ? 1 : 0);
+ return 0;
+ }
+
+ if (ch >= solo_dev->nr_chans + solo_dev->nr_ext)
+ return -EINVAL;
+
+ ext_ch = ch - solo_dev->nr_chans;
+
+ /* 4up's first */
+ if (ext_ch < 4)
+ return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on);
+
+ /* Remaining case is 16up for 16-port */
+ return solo_v4l2_ch_ext_16up(solo_dev, on);
+}
+
+static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch)
+{
+ if (ch >= solo_dev->nr_chans + solo_dev->nr_ext)
+ return -EINVAL;
+
+ erase_on(solo_dev);
+
+ solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0);
+ solo_v4l2_ch(solo_dev, ch, 1);
+
+ solo_dev->cur_disp_ch = ch;
+
+ return 0;
+}
+
+static void 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 solo_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 solo_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, SOLO6X10_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 solo_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 solo_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 solo_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 solo_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 solo_dev *solo_dev = fh->solo_dev;
+
+ strcpy(cap->driver, SOLO6X10_NAME);
+ strcpy(cap->card, "Softlogic 6x10");
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
+ pci_name(solo_dev->pdev));
+ cap->version = SOLO6X10_VER_NUM;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int solo_enum_ext_input(struct solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 = SOLO6X10_NAME,
+ .fops = &solo_v4l2_fops,
+ .ioctl_ops = &solo_v4l2_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+
+ .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+int solo_v4l2_init(struct solo_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)",
+ SOLO6X10_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 */
+
+ solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
+
+ return 0;
+}
+
+void solo_v4l2_exit(struct solo_dev *solo_dev)
+{
+ solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN);
+ if (solo_dev->vfd) {
+ video_unregister_device(solo_dev->vfd);
+ solo_dev->vfd = NULL;
+ }
+}
+++ /dev/null
-config SOLO6X10
- tristate "Softlogic 6x10 MPEG codec cards"
- depends on PCI && VIDEO_DEV && SND && I2C
- select VIDEOBUF_DMA_SG
- select SND_PCM
- ---help---
- This driver supports the Softlogic based MPEG-4 and h.264 codec
- codec cards.
+++ /dev/null
-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
-TODO (staging => main):
-
- * Motion detection flags need to be moved to v4l2
- * Some private CIDs need to be moved to v4l2
-
-TODO (general):
-
- * encoder on/off controls
- * mpeg cid bitrate mode (vbr/cbr)
- * mpeg cid bitrate/bitrate-peak
- * mpeg encode of user data
- * mpeg decode of user data
- * switch between 4 frames/irq to 1 when using mjpeg (and then back
- when not)
- * implement a CID control for motion areas/thresholds
- * implement CID controls for mozaic areas
- * allow for higher level of interval (for < 1 fps)
- * sound:
- - implement playback via external sound jack
- - implement loopback of external sound jack with incoming audio?
- - implement pause/resume
-
-Plase send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc Ben Collins
-<bcollins@bluecherry.net>
+++ /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/slab.h>
-#include <linux/videodev2.h>
-#include "solo6x10.h"
-#include "tw28.h"
-
-MODULE_DESCRIPTION("Softlogic 6x10 MP4/H.264 Encoder/Decoder V4L2/ALSA Driver");
-MODULE_AUTHOR("Ben Collins <bcollins@bluecherry.net>");
-MODULE_VERSION(SOLO6X10_VERSION);
-MODULE_LICENSE("GPL");
-
-void solo_irq_on(struct solo_dev *solo_dev, u32 mask)
-{
- solo_dev->irq_mask |= mask;
- solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask);
-}
-
-void solo_irq_off(struct solo_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 solo_isr(int irq, void *data)
-{
- struct solo_dev *solo_dev = data;
- u32 status;
- int i;
-
- status = solo_reg_read(solo_dev, SOLO_IRQ_STAT);
- if (!status)
- return IRQ_NONE;
-
- if (status & ~solo_dev->irq_mask) {
- solo_reg_write(solo_dev, SOLO_IRQ_STAT,
- status & ~solo_dev->irq_mask);
- status &= solo_dev->irq_mask;
- }
-
- if (status & SOLO_IRQ_PCI_ERR) {
- 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 solo_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) {
- solo_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 solo_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- struct solo_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, SOLO6X10_NAME);
- if (ret)
- goto fail_probe;
-
- solo_dev->reg_base = pci_ioremap_bar(pdev, 0);
- if (solo_dev->reg_base == NULL) {
- ret = -ENOMEM;
- goto fail_probe;
- }
-
- chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) &
- SOLO_CHIP_ID_MASK;
- switch (chip_id) {
- case 7:
- solo_dev->nr_chans = 16;
- solo_dev->nr_ext = 5;
- break;
- case 6:
- solo_dev->nr_chans = 8;
- solo_dev->nr_ext = 2;
- break;
- default:
- dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, "
- "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 */
- solo_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, solo_isr, IRQF_SHARED, SOLO6X10_NAME,
- solo_dev);
- if (ret)
- goto fail_probe;
-
- /* Handle this from the start */
- solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR);
-
- ret = solo_i2c_init(solo_dev);
- if (ret)
- goto fail_probe;
-
- /* Setup the DMA engine */
- 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 solo_pci_remove(struct pci_dev *pdev)
-{
- struct solo_dev *solo_dev = pci_get_drvdata(pdev);
-
- free_solo_dev(solo_dev);
-}
-
-static struct pci_device_id solo_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, solo_id_table);
-
-static struct pci_driver solo_pci_driver = {
- .name = SOLO6X10_NAME,
- .id_table = solo_id_table,
- .probe = solo_pci_probe,
- .remove = solo_pci_remove,
-};
-
-static int __init solo_module_init(void)
-{
- return pci_register_driver(&solo_pci_driver);
-}
-
-static void __exit solo_module_exit(void)
-{
- pci_unregister_driver(&solo_pci_driver);
-}
-
-module_init(solo_module_init);
-module_exit(solo_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 solo_dev *solo_dev)
-{
- solo_dev->vin_hstart = 8;
- solo_dev->vin_vstart = 2;
-
- solo_reg_write(solo_dev, SOLO_SYS_VCLK,
- SOLO_VCLK_SELECT(2) |
- SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) |
- SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) |
- SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) |
- SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) |
- SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) |
- SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) |
- SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) |
- SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY));
-
- solo_reg_write(solo_dev, SOLO_VI_ACT_I_P,
- SOLO_VI_H_START(solo_dev->vin_hstart) |
- SOLO_VI_V_START(solo_dev->vin_vstart) |
- SOLO_VI_V_STOP(solo_dev->vin_vstart +
- solo_dev->video_vsize));
-
- solo_reg_write(solo_dev, SOLO_VI_ACT_I_S,
- SOLO_VI_H_START(solo_dev->vout_hstart) |
- SOLO_VI_V_START(solo_dev->vout_vstart) |
- SOLO_VI_V_STOP(solo_dev->vout_vstart +
- solo_dev->video_vsize));
-
- solo_reg_write(solo_dev, SOLO_VI_ACT_P,
- SOLO_VI_H_START(0) |
- SOLO_VI_V_START(1) |
- SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE));
-
- solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT,
- SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0));
-
- 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 solo_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 solo_dev *solo_dev, u32 off,
- u16 val, int reg_size)
-{
- u16 buf[64];
- int i;
- int ret = 0;
-
- for (i = 0; i < sizeof(buf) >> 1; i++)
- buf[i] = 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 solo_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 solo_dev *solo_dev)
-{
- int i;
-
- for (i = 0; i < solo_dev->nr_chans; i++) {
- /* Clear motion flag area */
- solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000,
- SOLO_MOT_FLAG_SIZE);
-
- /* Clear working cache table */
- solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +
- 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 solo_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 solo_dev *solo_dev)
-{
- int i;
-
- solo_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 <linux/slab.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 solo_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 solo_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 solo_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 solo_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 solo_dev *solo_dev)
-{
- int i;
-
- solo_capture_config(solo_dev);
- solo_mp4e_config(solo_dev);
- solo_jpeg_config(solo_dev);
-
- for (i = 0; i < solo_dev->nr_chans; i++) {
- solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0);
- solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0);
- }
-
- solo_irq_on(solo_dev, SOLO_IRQ_ENCODER);
-
- return 0;
-}
-
-void solo_enc_exit(struct solo_dev *solo_dev)
-{
- int i;
-
- solo_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/slab.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 solo_dev *solo_dev;
- unsigned char g723_buf[G723_PERIOD_BYTES];
-};
-
-static void solo_g723_config(struct solo_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 solo_dev *solo_dev)
-{
- struct snd_pcm_str *pstr =
- &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE];
- struct snd_pcm_substream *ss;
- struct solo_snd_pcm *solo_pcm;
-
- 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 solo_dev *solo_dev = snd_pcm_substream_chip(ss);
- struct solo_snd_pcm *solo_pcm;
-
- solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL);
- if (solo_pcm == NULL)
- 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 solo_dev *solo_dev = solo_pcm->solo_dev;
- int ret = 0;
-
- spin_lock(&solo_pcm->lock);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- if (solo_pcm->on == 0) {
- /* If this is the first user, switch on interrupts */
- if (atomic_inc_return(&solo_dev->snd_users) == 1)
- solo_irq_on(solo_dev, SOLO_IRQ_G723);
- solo_pcm->on = 1;
- }
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- if (solo_pcm->on) {
- /* If this was our last user, switch them off */
- if (atomic_dec_return(&solo_dev->snd_users) == 0)
- solo_irq_off(solo_dev, SOLO_IRQ_G723);
- solo_pcm->on = 0;
- }
- break;
- default:
- ret = -EINVAL;
- }
-
- spin_unlock(&solo_pcm->lock);
-
- return ret;
-}
-
-static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss)
-{
- return 0;
-}
-
-static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss)
-{
- struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
- struct solo_dev *solo_dev = solo_pcm->solo_dev;
- snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f;
-
- return idx * G723_FRAMES_PER_PAGE;
-}
-
-static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel,
- snd_pcm_uframes_t pos, void __user *dst,
- snd_pcm_uframes_t count)
-{
- struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
- struct solo_dev *solo_dev = solo_pcm->solo_dev;
- int err, i;
-
- for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) {
- int page = (pos / G723_FRAMES_PER_PAGE) + i;
-
- err = solo_p2m_dma(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 solo_dev *solo_dev = snd_kcontrol_chip(kcontrol);
- u8 ch = value->id.numid - 1;
-
- value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch);
-
- return 0;
-}
-
-static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol);
- u8 ch = value->id.numid - 1;
- u8 old_val;
-
- old_val = tw28_get_audio_gain(solo_dev, ch);
- if (old_val == value->value.integer.value[0])
- return 0;
-
- tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]);
-
- return 1;
-}
-
-static struct snd_kcontrol_new snd_solo_capture_volume = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Volume",
- .info = snd_solo_capture_volume_info,
- .get = snd_solo_capture_volume_get,
- .put = snd_solo_capture_volume_put,
-};
-
-static int solo_snd_pcm_init(struct solo_dev *solo_dev)
-{
- struct snd_card *card = solo_dev->snd_card;
- struct snd_pcm *pcm;
- struct snd_pcm_substream *ss;
- int ret;
- int i;
-
- ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans,
- &pcm);
- if (ret < 0)
- return ret;
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_solo_pcm_ops);
-
- snd_pcm_chip(pcm) = solo_dev;
- pcm->info_flags = 0;
- strcpy(pcm->name, card->shortname);
-
- for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
- ss; ss = ss->next, i++)
- sprintf(ss->name, "Camera #%d Audio", i);
-
- ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- MAX_BUFFER, MAX_BUFFER);
- if (ret < 0)
- return ret;
-
- solo_dev->snd_pcm = pcm;
-
- return 0;
-}
-
-int solo_g723_init(struct solo_dev *solo_dev)
-{
- static struct snd_device_ops ops = { NULL };
- struct snd_card *card;
- struct snd_kcontrol_new kctl;
- char name[32];
- int ret;
-
- atomic_set(&solo_dev->snd_users, 0);
-
- /* Allows for easier mapping between video and audio */
- sprintf(name, "Softlogic%d", solo_dev->vfd->num);
-
- ret = snd_card_create(SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0,
- &solo_dev->snd_card);
- if (ret < 0)
- return ret;
-
- card = solo_dev->snd_card;
-
- strcpy(card->driver, SOLO6X10_NAME);
- strcpy(card->shortname, "SOLO-6x10 Audio");
- sprintf(card->longname, "%s on %s IRQ %d", card->shortname,
- pci_name(solo_dev->pdev), solo_dev->pdev->irq);
- snd_card_set_dev(card, &solo_dev->pdev->dev);
-
- ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops);
- if (ret < 0)
- goto snd_error;
-
- /* Mixer controls */
- strcpy(card->mixername, "SOLO-6x10");
- kctl = snd_solo_capture_volume;
- kctl.count = solo_dev->nr_chans;
- ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev));
- if (ret < 0)
- return ret;
-
- ret = solo_snd_pcm_init(solo_dev);
- if (ret < 0)
- goto snd_error;
-
- ret = snd_card_register(card);
- if (ret < 0)
- goto snd_error;
-
- solo_g723_config(solo_dev);
-
- dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name);
-
- return 0;
-
-snd_error:
- snd_card_free(card);
- return ret;
-}
-
-void solo_g723_exit(struct solo_dev *solo_dev)
-{
- solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0);
- solo_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 solo_dev *solo_dev,
- unsigned int port_mask, unsigned int mode)
-{
- int port;
- unsigned int ret;
-
- ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0);
-
- /* To set gpio */
- for (port = 0; port < 16; port++) {
- if (!((1 << port) & port_mask))
- continue;
-
- ret &= (~(3 << (port << 1)));
- ret |= ((mode & 3) << (port << 1));
- }
-
- solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret);
-
- /* To set extended gpio - sensor */
- ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1);
-
- for (port = 0; port < 16; port++) {
- if (!((1 << (port + 16)) & port_mask))
- continue;
-
- if (!mode)
- ret &= ~(1 << port);
- else
- ret |= 1 << port;
- }
-
- solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret);
-}
-
-static void solo_gpio_set(struct solo_dev *solo_dev, unsigned int value)
-{
- solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT,
- solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value);
-}
-
-static void solo_gpio_clear(struct solo_dev *solo_dev, unsigned int value)
-{
- solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT,
- solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value);
-}
-
-static void solo_gpio_config(struct solo_dev *solo_dev)
-{
- /* Video reset */
- solo_gpio_mode(solo_dev, 0x30, 1);
- solo_gpio_clear(solo_dev, 0x30);
- udelay(100);
- solo_gpio_set(solo_dev, 0x30);
- udelay(100);
-
- /* Warning: Don't touch the next line unless you're sure of what
- * you're doing: first four gpio [0-3] are used for video. */
- solo_gpio_mode(solo_dev, 0x0f, 2);
-
- /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */
- solo_gpio_mode(solo_dev, 0xff00, 1);
-
- /* Initially set relay status to 0 */
- solo_gpio_clear(solo_dev, 0xff00);
-}
-
-int solo_gpio_init(struct solo_dev *solo_dev)
-{
- solo_gpio_config(solo_dev);
- return 0;
-}
-
-void solo_gpio_exit(struct solo_dev *solo_dev)
-{
- solo_gpio_clear(solo_dev, 0x30);
- solo_gpio_config(solo_dev);
-}
+++ /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 SOLO6x10 i2c does not have separate interrupts for each i2c
- * channel. The bus can only handle one i2c event at a time. The below handles
- * this all wrong. We should be using the status registers to see if the bus
- * is in use, and have a global lock to check the status register. Also,
- * the bulk of the work should be handled out-of-interrupt. The ugly loops
- * that occur during interrupt scare me. The ISR should merely signal
- * thread context, ACK the interrupt, and move on. -- BenC */
-
-#include <linux/kernel.h>
-#include "solo6x10.h"
-
-u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off)
-{
- struct i2c_msg msgs[2];
- u8 data;
-
- msgs[0].flags = 0;
- msgs[0].addr = addr;
- msgs[0].len = 1;
- msgs[0].buf = &off;
-
- msgs[1].flags = I2C_M_RD;
- msgs[1].addr = addr;
- msgs[1].len = 1;
- msgs[1].buf = &data;
-
- i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2);
-
- return data;
-}
-
-void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr,
- u8 off, u8 data)
-{
- struct i2c_msg msgs;
- u8 buf[2];
-
- buf[0] = off;
- buf[1] = data;
- msgs.flags = 0;
- msgs.addr = addr;
- msgs.len = 2;
- msgs.buf = buf;
-
- i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1);
-}
-
-static void solo_i2c_flush(struct solo_dev *solo_dev, int wr)
-{
- u32 ctrl;
-
- ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id);
-
- if (solo_dev->i2c_state == IIC_STATE_START)
- ctrl |= SOLO_IIC_START;
-
- if (wr) {
- ctrl |= SOLO_IIC_WRITE;
- } else {
- ctrl |= SOLO_IIC_READ;
- if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK))
- ctrl |= SOLO_IIC_ACK_EN;
- }
-
- if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len)
- ctrl |= SOLO_IIC_STOP;
-
- solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl);
-}
-
-static void solo_i2c_start(struct solo_dev *solo_dev)
-{
- u32 addr = solo_dev->i2c_msg->addr << 1;
-
- if (solo_dev->i2c_msg->flags & I2C_M_RD)
- addr |= 1;
-
- solo_dev->i2c_state = IIC_STATE_START;
- solo_reg_write(solo_dev, SOLO_IIC_TXD, addr);
- solo_i2c_flush(solo_dev, 1);
-}
-
-static void solo_i2c_stop(struct solo_dev *solo_dev)
-{
- solo_irq_off(solo_dev, SOLO_IRQ_IIC);
- solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0);
- solo_dev->i2c_state = IIC_STATE_STOP;
- wake_up(&solo_dev->i2c_wait);
-}
-
-static int solo_i2c_handle_read(struct solo_dev *solo_dev)
-{
-prepare_read:
- if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) {
- solo_i2c_flush(solo_dev, 0);
- return 0;
- }
-
- solo_dev->i2c_msg_ptr = 0;
- solo_dev->i2c_msg++;
- solo_dev->i2c_msg_num--;
-
- if (solo_dev->i2c_msg_num == 0) {
- solo_i2c_stop(solo_dev);
- return 0;
- }
-
- if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) {
- solo_i2c_start(solo_dev);
- } else {
- if (solo_dev->i2c_msg->flags & I2C_M_RD)
- goto prepare_read;
- else
- solo_i2c_stop(solo_dev);
- }
-
- return 0;
-}
-
-static int solo_i2c_handle_write(struct solo_dev *solo_dev)
-{
-retry_write:
- if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) {
- solo_reg_write(solo_dev, SOLO_IIC_TXD,
- solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]);
- solo_dev->i2c_msg_ptr++;
- solo_i2c_flush(solo_dev, 1);
- return 0;
- }
-
- solo_dev->i2c_msg_ptr = 0;
- solo_dev->i2c_msg++;
- solo_dev->i2c_msg_num--;
-
- if (solo_dev->i2c_msg_num == 0) {
- solo_i2c_stop(solo_dev);
- return 0;
- }
-
- if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) {
- solo_i2c_start(solo_dev);
- } else {
- if (solo_dev->i2c_msg->flags & I2C_M_RD)
- solo_i2c_stop(solo_dev);
- else
- goto retry_write;
- }
-
- return 0;
-}
-
-int solo_i2c_isr(struct solo_dev *solo_dev)
-{
- u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL);
- int ret = -EINVAL;
-
- 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 solo_dev *solo_dev = adap->algo_data;
- unsigned long timeout;
- int ret;
- int i;
- DEFINE_WAIT(wait);
-
- for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
- if (&solo_dev->i2c_adap[i] == adap)
- break;
- }
-
- if (i == SOLO_I2C_ADAPTERS)
- return num; /* XXX Right return value for failure? */
-
- mutex_lock(&solo_dev->i2c_mutex);
- solo_dev->i2c_id = i;
- solo_dev->i2c_msg = msgs;
- solo_dev->i2c_msg_num = num;
- solo_dev->i2c_msg_ptr = 0;
-
- solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0);
- solo_irq_on(solo_dev, SOLO_IRQ_IIC);
- solo_i2c_start(solo_dev);
-
- timeout = HZ / 2;
-
- for (;;) {
- prepare_to_wait(&solo_dev->i2c_wait, &wait, TASK_INTERRUPTIBLE);
-
- if (solo_dev->i2c_state == IIC_STATE_STOP)
- break;
-
- timeout = schedule_timeout(timeout);
- if (!timeout)
- break;
-
- if (signal_pending(current))
- break;
- }
-
- finish_wait(&solo_dev->i2c_wait, &wait);
- ret = num - solo_dev->i2c_msg_num;
- solo_dev->i2c_state = IIC_STATE_IDLE;
- solo_dev->i2c_id = -1;
-
- mutex_unlock(&solo_dev->i2c_mutex);
-
- return ret;
-}
-
-static u32 solo_i2c_functionality(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm solo_i2c_algo = {
- .master_xfer = solo_i2c_master_xfer,
- .functionality = solo_i2c_functionality,
-};
-
-int solo_i2c_init(struct solo_dev *solo_dev)
-{
- int i;
- int ret;
-
- solo_reg_write(solo_dev, SOLO_IIC_CFG,
- SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE);
-
- solo_dev->i2c_id = -1;
- solo_dev->i2c_state = IIC_STATE_IDLE;
- init_waitqueue_head(&solo_dev->i2c_wait);
- mutex_init(&solo_dev->i2c_mutex);
-
- for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
- struct i2c_adapter *adap = &solo_dev->i2c_adap[i];
-
- snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", SOLO6X10_NAME, i);
- adap->algo = &solo_i2c_algo;
- adap->algo_data = solo_dev;
- adap->retries = 1;
- adap->dev.parent = &solo_dev->pdev->dev;
-
- ret = i2c_add_adapter(adap);
- if (ret) {
- adap->algo_data = NULL;
- break;
- }
- }
-
- if (ret) {
- for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
- if (!solo_dev->i2c_adap[i].algo_data)
- break;
- i2c_del_adapter(&solo_dev->i2c_adap[i]);
- solo_dev->i2c_adap[i].algo_data = NULL;
- }
- return ret;
- }
-
- dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n",
- SOLO_I2C_ADAPTERS);
-
- return 0;
-}
-
-void solo_i2c_exit(struct solo_dev *solo_dev)
-{
- int i;
-
- for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
- if (!solo_dev->i2c_adap[i].algo_data)
- continue;
- i2c_del_adapter(&solo_dev->i2c_adap[i]);
- solo_dev->i2c_adap[i].algo_data = NULL;
- }
-}
+++ /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 __SOLO6X10_JPEG_H
-#define __SOLO6X10_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 /* __SOLO6X10_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 __SOLO6X10_OFFSETS_H
-#define __SOLO6X10_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 /* __SOLO6X10_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 __SOLO6X10_OSD_FONT_H
-#define __SOLO6X10_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 /* __SOLO6X10_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/slab.h>
-#include <linux/scatterlist.h>
-#include "solo6x10.h"
-
-/* #define SOLO_TEST_P2M */
-
-int solo_p2m_dma(struct solo_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 solo_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 solo_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 solo_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 solo_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(SOLO6X10_NAME ": Failed to malloc for p2m_test\n");
- return size;
- }
-
- rd_buf = kmalloc(size, GFP_KERNEL);
- if (!rd_buf) {
- printk(SOLO6X10_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 solo_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",
- SOLO6X10_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",
- SOLO6X10_NAME, errs);
-
- return;
-}
-#else
-#define run_p2m_test(__solo) do {} while (0)
-#endif
-
-void solo_p2m_isr(struct solo_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 solo_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 solo_dev *solo_dev)
-{
- int i;
-
- for (i = 0; i < SOLO_NR_P2M; i++)
- solo_irq_off(solo_dev, SOLO_IRQ_P2M(i));
-}
-
-int solo_p2m_init(struct solo_dev *solo_dev)
-{
- struct solo_p2m_dev *p2m_dev;
- int i;
-
- for (i = 0; i < SOLO_NR_P2M; i++) {
- p2m_dev = &solo_dev->p2m_dev[i];
-
- mutex_init(&p2m_dev->mutex);
- init_completion(&p2m_dev->completion);
-
- solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0);
- solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i),
- SOLO_P2M_CSC_16BIT_565 |
- SOLO_P2M_DMA_INTERVAL(3) |
- SOLO_P2M_DESC_INTR_OPT |
- SOLO_P2M_PCI_MASTER_MODE);
- solo_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 __SOLO6X10_REGISTERS_H
-#define __SOLO6X10_REGISTERS_H
-
-#include "offsets.h"
-
-/* Global 6X10 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 /* __SOLO6X10_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.
- */
-
-#ifndef __SOLO6X10_H
-#define __SOLO6X10_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 <linux/slab.h>
-#include <asm/io.h>
-#include <linux/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 SOLO6X10_NAME "solo6x10"
-
-#define SOLO_MAX_CHANNELS 16
-
-/* Make sure these two match */
-#define SOLO6X10_VERSION "2.1.0"
-#define SOLO6X10_VER_MAJOR 2
-#define SOLO6X10_VER_MINOR 0
-#define SOLO6X10_VER_SUB 0
-#define SOLO6X10_VER_NUM \
- KERNEL_VERSION(SOLO6X10_VER_MAJOR, SOLO6X10_VER_MINOR, SOLO6X10_VER_SUB)
-
-#define FLAGS_6110 1
-
-/*
- * The SOLO6x10 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 solo_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 SOLO6x10 PCI Device */
-struct solo_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 solo_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 solo_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 solo_irq_on(struct solo_dev *solo_dev, u32 mask);
-void solo_irq_off(struct solo_dev *solo_dev, u32 mask);
-
-/* Init/exit routeines for subsystems */
-int solo_disp_init(struct solo_dev *solo_dev);
-void solo_disp_exit(struct solo_dev *solo_dev);
-
-int solo_gpio_init(struct solo_dev *solo_dev);
-void solo_gpio_exit(struct solo_dev *solo_dev);
-
-int solo_i2c_init(struct solo_dev *solo_dev);
-void solo_i2c_exit(struct solo_dev *solo_dev);
-
-int solo_p2m_init(struct solo_dev *solo_dev);
-void solo_p2m_exit(struct solo_dev *solo_dev);
-
-int solo_v4l2_init(struct solo_dev *solo_dev);
-void solo_v4l2_exit(struct solo_dev *solo_dev);
-
-int solo_enc_init(struct solo_dev *solo_dev);
-void solo_enc_exit(struct solo_dev *solo_dev);
-
-int solo_enc_v4l2_init(struct solo_dev *solo_dev);
-void solo_enc_v4l2_exit(struct solo_dev *solo_dev);
-
-int solo_g723_init(struct solo_dev *solo_dev);
-void solo_g723_exit(struct solo_dev *solo_dev);
-
-/* ISR's */
-int solo_i2c_isr(struct solo_dev *solo_dev);
-void solo_p2m_isr(struct solo_dev *solo_dev, int id);
-void solo_p2m_error_isr(struct solo_dev *solo_dev, u32 status);
-void solo_enc_v4l2_isr(struct solo_dev *solo_dev);
-void solo_g723_isr(struct solo_dev *solo_dev);
-void solo_motion_isr(struct solo_dev *solo_dev);
-void solo_video_in_isr(struct solo_dev *solo_dev);
-
-/* i2c read/write */
-u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off);
-void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off,
- u8 data);
-
-/* P2M DMA */
-int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr,
- dma_addr_t dma_addr, u32 ext_addr, u32 size);
-int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr,
- void *sys_addr, u32 ext_addr, u32 size);
-int solo_p2m_dma_sg(struct solo_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 solo_dev *solo_dev, u8 id,
- struct p2m_desc *desc, int desc_count);
-
-/* Set the threshold for motion detection */
-void solo_set_motion_threshold(struct solo_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 /* __SOLO6X10_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 solo_dev *solo_dev, int chip_id, u8 tw6x_off,
- u8 tw_off)
-{
- if (is_tw286x(solo_dev, chip_id))
- return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
- TW_CHIP_OFFSET_ADDR(chip_id),
- tw6x_off);
- else
- return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
- TW_CHIP_OFFSET_ADDR(chip_id),
- tw_off);
-}
-
-static void tw_writebyte(struct solo_dev *solo_dev, int chip_id,
- u8 tw6x_off, u8 tw_off, u8 val)
-{
- if (is_tw286x(solo_dev, chip_id))
- solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
- TW_CHIP_OFFSET_ADDR(chip_id),
- tw6x_off, val);
- else
- solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
- TW_CHIP_OFFSET_ADDR(chip_id),
- tw_off, val);
-}
-
-static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
- u8 val)
-{
- int i;
-
- for (i = 0; i < 5; i++) {
- u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
- if (rval == val)
- return;
-
- solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
- msleep_interruptible(1);
- }
-
-/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n",
- addr, off, val); */
-}
-
-static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
-{
- u8 tbl_tw2865_common[256];
- int i;
-
- if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
- memcpy(tbl_tw2865_common, tbl_tw2865_pal_template,
- sizeof(tbl_tw2865_common));
- else
- memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
- sizeof(tbl_tw2865_common));
-
- /* ALINK Mode */
- if (solo_dev->nr_chans == 4) {
- tbl_tw2865_common[0xd2] = 0x01;
- tbl_tw2865_common[0xcf] = 0x00;
- } else if (solo_dev->nr_chans == 8) {
- tbl_tw2865_common[0xd2] = 0x02;
- if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
- tbl_tw2865_common[0xcf] = 0x80;
- } else if (solo_dev->nr_chans == 16) {
- tbl_tw2865_common[0xd2] = 0x03;
- if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
- tbl_tw2865_common[0xcf] = 0x83;
- else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
- tbl_tw2865_common[0xcf] = 0x83;
- else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
- tbl_tw2865_common[0xcf] = 0x80;
- }
-
- for (i = 0; i < 0xff; i++) {
- /* Skip read only registers */
- 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 solo_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 solo_dev *solo_dev, u8 dev_addr)
-{
- u8 tbl_ntsc_tw2815_common[] = {
- 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
- 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
- };
-
- u8 tbl_pal_tw2815_common[] = {
- 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
- 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
- };
-
- u8 tbl_tw2815_sfr[] = {
- 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */
- 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
- 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */
- 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
- 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */
- 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
- 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */
- };
- u8 *tbl_tw2815_common;
- int i;
- int ch;
-
- tbl_ntsc_tw2815_common[0x06] = 0;
-
- /* Horizontal Delay Control */
- tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff;
- tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8);
-
- /* Horizontal Active Control */
- tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff;
- tbl_ntsc_tw2815_common[0x06] |=
- ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2);
-
- /* Vertical Delay Control */
- tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff;
- tbl_ntsc_tw2815_common[0x06] |=
- ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4);
-
- /* Vertical Active Control */
- tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff;
- tbl_ntsc_tw2815_common[0x06] |=
- ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5);
-
- tbl_pal_tw2815_common[0x06] = 0;
-
- /* Horizontal Delay Control */
- tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff;
- tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8);
-
- /* Horizontal Active Control */
- tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff;
- tbl_pal_tw2815_common[0x06] |=
- ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2);
-
- /* Vertical Delay Control */
- tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff;
- tbl_pal_tw2815_common[0x06] |=
- ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4);
-
- /* Vertical Active Control */
- tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff;
- tbl_pal_tw2815_common[0x06] |=
- ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5);
-
- tbl_tw2815_common =
- (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
- tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
-
- /* Dual ITU-R BT.656 format */
- tbl_tw2815_common[0x0d] |= 0x04;
-
- /* Audio configuration */
- tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
-
- if (solo_dev->nr_chans == 4) {
- tbl_tw2815_sfr[0x63 - 0x40] |= 1;
- tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6;
- } else if (solo_dev->nr_chans == 8) {
- tbl_tw2815_sfr[0x63 - 0x40] |= 2;
- if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
- tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
- else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
- tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
- } else if (solo_dev->nr_chans == 16) {
- tbl_tw2815_sfr[0x63 - 0x40] |= 3;
- if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
- tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
- else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
- tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
- else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
- tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
- else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
- tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
- }
-
- /* Output mode of R_ADATM pin (0 mixing, 1 record) */
- /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
-
- /* 8KHz, used to be 16KHz, but changed for remote client compat */
- tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2;
- tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2;
-
- /* Playback of right channel */
- tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
-
- /* Reserved value (XXX ??) */
- tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
-
- /* Analog output gain and mix ratio playback on full */
- tbl_tw2815_sfr[0x70 - 0x40] |= 0xff;
- /* Select playback audio and mute all except */
- tbl_tw2815_sfr[0x71 - 0x40] |= 0x10;
- tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f;
-
- /* End of audio configuration */
-
- for (ch = 0; ch < 4; ch++) {
- tbl_tw2815_common[0x0d] &= ~3;
- switch (ch) {
- case 0:
- tbl_tw2815_common[0x0d] |= 0x21;
- break;
- case 1:
- tbl_tw2815_common[0x0d] |= 0x20;
- break;
- case 2:
- tbl_tw2815_common[0x0d] |= 0x23;
- break;
- case 3:
- tbl_tw2815_common[0x0d] |= 0x22;
- break;
- }
-
- for (i = 0; i < 0x0f; i++) {
- if (i == 0x00)
- continue; /* read-only */
- solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
- dev_addr, (ch * 0x10) + i,
- tbl_tw2815_common[i]);
- }
- }
-
- for (i = 0x40; i < 0x76; i++) {
- /* Skip read-only and nop registers */
- if (i == 0x40 || i == 0x59 || i == 0x5a ||
- i == 0x5d || i == 0x5e || i == 0x5f)
- continue;
-
- solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
- tbl_tw2815_sfr[i - 0x40]);
- }
-
- return 0;
-}
-
-#define FIRST_ACTIVE_LINE 0x0008
-#define LAST_ACTIVE_LINE 0x0102
-
-static void saa7128_setup(struct solo_dev *solo_dev)
-{
- int i;
- unsigned char regs[128] = {
- 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
- 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00,
- 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00,
- 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
- 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
- 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
- 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
- 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
- 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00,
- 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff,
- };
-
- regs[0x7A] = FIRST_ACTIVE_LINE & 0xff;
- regs[0x7B] = LAST_ACTIVE_LINE & 0xff;
- regs[0x7C] = ((1 << 7) |
- (((LAST_ACTIVE_LINE >> 8) & 1) << 6) |
- (((FIRST_ACTIVE_LINE >> 8) & 1) << 4));
-
- /* PAL: XXX: We could do a second set of regs to avoid this */
- if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) {
- regs[0x28] = 0xE1;
-
- regs[0x5A] = 0x0F;
- regs[0x61] = 0x02;
- regs[0x62] = 0x35;
- regs[0x63] = 0xCB;
- regs[0x64] = 0x8A;
- regs[0x65] = 0x09;
- regs[0x66] = 0x2A;
-
- regs[0x6C] = 0xf1;
- regs[0x6E] = 0x20;
-
- regs[0x7A] = 0x06 + 12;
- regs[0x7b] = 0x24 + 12;
- regs[0x7c] |= 1 << 6;
- }
-
- /* First 0x25 bytes are read-only? */
- for (i = 0x26; i < 128; i++) {
- if (i == 0x60 || i == 0x7D)
- continue;
- solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]);
- }
-
- return;
-}
-
-int solo_tw28_init(struct solo_dev *solo_dev)
-{
- int i;
- u8 value;
-
- /* 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 solo_dev *solo_dev, u8 ch)
-{
- u8 val, chip_num;
-
- /* Get the right chip and on-chip channel */
- chip_num = ch / 4;
- ch %= 4;
-
- val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR,
- TW_AV_STAT_ADDR) & 0x0f;
-
- return val & (1 << ch) ? 1 : 0;
-}
-
-#if 0
-/* Status of audio from up to 4 techwell chips are combined into 1 variable.
- * See techwell datasheet for details. */
-u16 tw28_get_audio_status(struct solo_dev *solo_dev)
-{
- u8 val;
- u16 status = 0;
- int i;
-
- for (i = 0; i < solo_dev->tw28_cnt; i++) {
- val = (tw_readbyte(solo_dev, i, TW286X_AV_STAT_ADDR,
- TW_AV_STAT_ADDR) & 0xf0) >> 4;
- status |= val << (i * 4);
- }
-
- return status;
-}
-#endif
-
-int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val)
-{
- char sval;
- u8 chip_num;
-
- /* Get the right chip and on-chip channel */
- chip_num = ch / 4;
- ch %= 4;
-
- if (val > 255 || val < 0)
- return -ERANGE;
-
- switch (ctrl) {
- case V4L2_CID_SHARPNESS:
- /* Only 286x has sharpness */
- if (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 solo_dev *solo_dev, u32 ctrl, u8 ch,
- s32 *val)
-{
- u8 rval, chip_num;
-
- /* Get the right chip and on-chip channel */
- chip_num = ch / 4;
- ch %= 4;
-
- switch (ctrl) {
- case V4L2_CID_SHARPNESS:
- /* Only 286x has sharpness */
- if (is_tw286x(solo_dev, chip_num)) {
- rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
- TW_CHIP_OFFSET_ADDR(chip_num),
- TW286x_SHARPNESS(chip_num));
- *val = rval & 0x0f;
- } else
- *val = 0;
- break;
- case V4L2_CID_HUE:
- rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
- TW_HUE_ADDR(ch));
- if (is_tw286x(solo_dev, chip_num))
- *val = (s32)((char)rval) + 128;
- else
- *val = rval;
- break;
- case V4L2_CID_SATURATION:
- *val = tw_readbyte(solo_dev, chip_num,
- TW286x_SATURATIONU_ADDR(ch),
- TW_SATURATION_ADDR(ch));
- break;
- case V4L2_CID_CONTRAST:
- *val = tw_readbyte(solo_dev, chip_num,
- TW286x_CONTRAST_ADDR(ch),
- TW_CONTRAST_ADDR(ch));
- break;
- case V4L2_CID_BRIGHTNESS:
- rval = tw_readbyte(solo_dev, chip_num,
- TW286x_BRIGHTNESS_ADDR(ch),
- TW_BRIGHTNESS_ADDR(ch));
- if (is_tw286x(solo_dev, chip_num))
- *val = (s32)((char)rval) + 128;
- else
- *val = rval;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-#if 0
-/*
- * For audio output volume, the output channel is only 1. In this case we
- * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
- * is the base address of the techwell chip.
- */
-void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val)
-{
- unsigned int val;
- unsigned int chip_num;
-
- chip_num = (solo_dev->nr_chans - 1) / 4;
-
- val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
- TW_AUDIO_OUTPUT_VOL_ADDR);
-
- u_val = (val & 0x0f) | (u_val << 4);
-
- tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
- TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
-}
-#endif
-
-u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch)
-{
- u8 val;
- u8 chip_num;
-
- /* Get the right chip and on-chip channel */
- chip_num = ch / 4;
- ch %= 4;
-
- val = tw_readbyte(solo_dev, chip_num,
- TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
- TW_AUDIO_INPUT_GAIN_ADDR(ch));
-
- return (ch % 2) ? (val >> 4) : (val & 0x0f);
-}
-
-void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val)
-{
- u8 old_val;
- u8 chip_num;
-
- /* Get the right chip and on-chip channel */
- chip_num = ch / 4;
- ch %= 4;
-
- old_val = tw_readbyte(solo_dev, chip_num,
- TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
- TW_AUDIO_INPUT_GAIN_ADDR(ch));
-
- val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
- ((ch % 2) ? (val << 4) : val);
-
- tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
- TW_AUDIO_INPUT_GAIN_ADDR(ch), val);
-}
+++ /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 __SOLO6X10_TW28_H
-#define __SOLO6X10_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 solo_dev *solo_dev);
-
-int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val);
-int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 *val);
-
-u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch);
-void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val);
-int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch);
-
-#if 0
-unsigned int tw2815_get_audio_status(struct SOLO *solo);
-void tw2815_Set_AudioOutVol(struct SOLO *solo, unsigned int u_val);
-#endif
-
-#endif /* __SOLO6X10_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 solo_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 solo_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)
- solo_irq_on(solo_dev, SOLO_IRQ_MOTION);
- else
- solo_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 solo_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 solo_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 solo_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, SOLO6X10_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 solo_dev *solo_dev = solo_enc->solo_dev;
-
- strcpy(cap->driver, SOLO6X10_NAME);
- snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d",
- solo_enc->ch);
- snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
- pci_name(solo_dev->pdev));
- cap->version = SOLO6X10_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 solo_dev *solo_dev = solo_enc->solo_dev;
-
- if (input->index)
- return -EINVAL;
-
- snprintf(input->name, sizeof(input->name), "Encoder %d",
- solo_enc->ch + 1);
- input->type = V4L2_INPUT_TYPE_CAMERA;
-
- 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 solo_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 solo_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 solo_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 solo_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 solo_dev *solo_dev = solo_enc->solo_dev;
- struct v4l2_captureparm *cp = &sp->parm.capture;
-
- cp->capability = V4L2_CAP_TIMEPERFRAME;
- cp->timeperframe.numerator = solo_enc->interval;
- cp->timeperframe.denominator = solo_dev->fps;
- cp->capturemode = 0;
- /* XXX: Shouldn't we be able to get/set this from videobuf? */
- cp->readbuffers = 2;
-
- return 0;
-}
-
-static int solo_s_parm(struct file *file, void *priv,
- struct v4l2_streamparm *sp)
-{
- struct solo_enc_fh *fh = priv;
- struct solo_enc_dev *solo_enc = fh->enc;
- struct solo_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 solo_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 solo_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 solo_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 = SOLO6X10_NAME,
- .fops = &solo_enc_fops,
- .ioctl_ops = &solo_enc_ioctl_ops,
- .minor = -1,
- .release = video_device_release,
-
- .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-static struct solo_enc_dev *solo_enc_alloc(struct solo_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)", SOLO6X10_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 solo_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 solo_dev *solo_dev)
-{
- int i;
-
- solo_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 solo_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 solo_dev *solo_dev)
-{
- solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
- solo_dev->erasing = 1;
- solo_dev->frame_blank = 0;
-}
-
-static int erase_off(struct solo_dev *solo_dev)
-{
- if (!solo_dev->erasing)
- return 0;
-
- /* First time around, assert erase off */
- if (!solo_dev->frame_blank)
- solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0);
- /* Keep the erasing flag on for 8 frames minimum */
- if (solo_dev->frame_blank++ >= 8)
- solo_dev->erasing = 0;
-
- return 1;
-}
-
-void solo_video_in_isr(struct solo_dev *solo_dev)
-{
- 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 solo_dev *solo_dev, u8 ch,
- int sx, int sy, int ex, int ey, int scale)
-{
- if (ch >= solo_dev->nr_chans)
- return;
-
- /* Here, we just keep window/channel the same */
- solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch),
- SOLO_VI_WIN_CHANNEL(ch) |
- SOLO_VI_WIN_SX(sx) |
- SOLO_VI_WIN_EX(ex) |
- SOLO_VI_WIN_SCALE(scale));
-
- solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch),
- SOLO_VI_WIN_SY(sy) |
- SOLO_VI_WIN_EY(ey));
-}
-
-static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on)
-{
- u8 ch = idx * 4;
-
- if (ch >= solo_dev->nr_chans)
- return -EINVAL;
-
- if (!on) {
- u8 i;
- for (i = ch; i < ch + 4; i++)
- solo_win_setup(solo_dev, i, solo_dev->video_hsize,
- solo_vlines(solo_dev),
- solo_dev->video_hsize,
- solo_vlines(solo_dev), 0);
- return 0;
- }
-
- /* Row 1 */
- solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2,
- solo_vlines(solo_dev) / 2, 3);
- solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0,
- solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3);
- /* Row 2 */
- solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2,
- solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3);
- solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2,
- solo_vlines(solo_dev) / 2, solo_dev->video_hsize,
- solo_vlines(solo_dev), 3);
-
- return 0;
-}
-
-static int solo_v4l2_ch_ext_16up(struct solo_dev *solo_dev, int on)
-{
- int sy, ysize, hsize, i;
-
- if (!on) {
- for (i = 0; i < 16; i++)
- solo_win_setup(solo_dev, i, solo_dev->video_hsize,
- solo_vlines(solo_dev),
- solo_dev->video_hsize,
- solo_vlines(solo_dev), 0);
- return 0;
- }
-
- ysize = solo_vlines(solo_dev) / 4;
- hsize = solo_dev->video_hsize / 4;
-
- for (sy = 0, i = 0; i < 4; i++, sy += ysize) {
- solo_win_setup(solo_dev, i * 4, 0, sy, hsize,
- sy + ysize, 5);
- solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy,
- hsize * 2, sy + ysize, 5);
- solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy,
- hsize * 3, sy + ysize, 5);
- solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy,
- solo_dev->video_hsize, sy + ysize, 5);
- }
-
- return 0;
-}
-
-static int solo_v4l2_ch(struct solo_dev *solo_dev, u8 ch, int on)
-{
- u8 ext_ch;
-
- if (ch < solo_dev->nr_chans) {
- solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize,
- on ? 0 : solo_vlines(solo_dev),
- solo_dev->video_hsize, solo_vlines(solo_dev),
- on ? 1 : 0);
- return 0;
- }
-
- if (ch >= solo_dev->nr_chans + solo_dev->nr_ext)
- return -EINVAL;
-
- ext_ch = ch - solo_dev->nr_chans;
-
- /* 4up's first */
- if (ext_ch < 4)
- return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on);
-
- /* Remaining case is 16up for 16-port */
- return solo_v4l2_ch_ext_16up(solo_dev, on);
-}
-
-static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch)
-{
- if (ch >= solo_dev->nr_chans + solo_dev->nr_ext)
- return -EINVAL;
-
- erase_on(solo_dev);
-
- solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0);
- solo_v4l2_ch(solo_dev, ch, 1);
-
- solo_dev->cur_disp_ch = ch;
-
- return 0;
-}
-
-static void 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 solo_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 solo_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, SOLO6X10_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 solo_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 solo_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 solo_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 solo_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 solo_dev *solo_dev = fh->solo_dev;
-
- strcpy(cap->driver, SOLO6X10_NAME);
- strcpy(cap->card, "Softlogic 6x10");
- snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
- pci_name(solo_dev->pdev));
- cap->version = SOLO6X10_VER_NUM;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- return 0;
-}
-
-static int solo_enum_ext_input(struct solo_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 solo_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 solo_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 solo_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 solo_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 solo_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 = SOLO6X10_NAME,
- .fops = &solo_v4l2_fops,
- .ioctl_ops = &solo_v4l2_ioctl_ops,
- .minor = -1,
- .release = video_device_release,
-
- .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-int solo_v4l2_init(struct solo_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)",
- SOLO6X10_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 */
-
- solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
-
- return 0;
-}
-
-void solo_v4l2_exit(struct solo_dev *solo_dev)
-{
- solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN);
- if (solo_dev->vfd) {
- video_unregister_device(solo_dev->vfd);
- solo_dev->vfd = NULL;
- }
-}