summaryrefslogtreecommitdiff
path: root/drivers/staging/media/easycap/easycap_low.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/media/easycap/easycap_low.c')
-rw-r--r--drivers/staging/media/easycap/easycap_low.c1129
1 files changed, 1129 insertions, 0 deletions
diff --git a/drivers/staging/media/easycap/easycap_low.c b/drivers/staging/media/easycap/easycap_low.c
new file mode 100644
index 000000000000..0385735ac6df
--- /dev/null
+++ b/drivers/staging/media/easycap/easycap_low.c
@@ -0,0 +1,1129 @@
+/*****************************************************************************
+* *
+* *
+* 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;
+}
+/*****************************************************************************/