2007-08-26 14:09:23

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 1/1] V4L: stk11xx, add a new webcam driver

Hi,

is it possible to have this driver in the -mm tree for testing purposes until
v4l library will be developped and image resize with bayer->rgb conversion
will be moved there? (Then, I'll push it through v4l people.)

--
stk11xx, add a new webcam driver

Adds support for stk1125, stk1135 and stkdcnew webcam built-in some
notebooks.

Signed-off-by: Jiri Slaby <[email protected]>

---
commit 926af968884e1e153ad2f91892ff71ec33005d22
tree f7d30907d7bd11128cd8f75a0aa2f3873c237980
parent c1b003cd9e5befbc3d915a9e37f46585127f9d1f
author Jiri Slaby <[email protected]> Sun, 26 Aug 2007 16:02:31 +0200
committer Jiri Slaby <[email protected]> Sun, 26 Aug 2007 16:02:31 +0200

MAINTAINERS | 5
drivers/media/video/Kconfig | 9
drivers/media/video/Makefile | 4
drivers/media/video/stk1125.c | 667 +++++++++++++++++++++++++
drivers/media/video/stk1135.c | 549 +++++++++++++++++++++
drivers/media/video/stk11xx-core.c | 962 ++++++++++++++++++++++++++++++++++++
drivers/media/video/stk11xx-v4l.c | 790 ++++++++++++++++++++++++++++++
drivers/media/video/stk11xx.h | 217 ++++++++
drivers/media/video/stkdcnew.c | 669 +++++++++++++++++++++++++
9 files changed, 3872 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index c986d11..40d7c56 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3525,6 +3525,11 @@ M: [email protected]
L: [email protected]
S: Maintained

+STK11XX WEBCAM DRIVER
+P: Jiri Slaby
+M: [email protected]
+S: Maintained
+
TPM DEVICE DRIVER
P: Kylene Hall
M: [email protected]
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 0e1d2cc..c51dde9 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -809,6 +809,15 @@ config USB_ZR364XX
To compile this driver as a module, choose M here: the
module will be called zr364xx.

+config USB_STK11XX
+ tristate "Syntek chip based webcams"
+ depends on VIDEO_V4L2
+ ---help---
+ This will add support for Syntek webcams such as dc1125, stk1135 and
+ DC NEW. Some notebooks have these cards built-in in their displays.
+
+ If you choose to build this as module, it will be called stk11xx.
+
endif # V4L_USB_DRIVERS

endif # VIDEO_CAPTURE_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 113e525..b2268c7 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -6,6 +6,8 @@ zr36067-objs := zoran_procfs.o zoran_device.o \
zoran_driver.o zoran_card.o
tuner-objs := tuner-core.o tuner-types.o tuner-simple.o \
mt20xx.o tda8290.o tea5767.o tda9887.o
+stk11xx-objs := stk11xx-core.o stk11xx-v4l.o stk1125.o stk1135.o \
+ stkdcnew.o

tuner-$(CONFIG_TUNER_TEA5761) += tea5761.o

@@ -107,6 +109,8 @@ obj-$(CONFIG_USB_STV680) += stv680.o
obj-$(CONFIG_USB_W9968CF) += w9968cf.o
obj-$(CONFIG_USB_ZR364XX) += zr364xx.o

+obj-$(CONFIG_USB_STK11XX) += stk11xx.o
+
obj-$(CONFIG_USB_SN9C102) += sn9c102/
obj-$(CONFIG_USB_ET61X251) += et61x251/
obj-$(CONFIG_USB_PWC) += pwc/
diff --git a/drivers/media/video/stk1125.c b/drivers/media/video/stk1125.c
new file mode 100644
index 0000000..ed3cc58
--- /dev/null
+++ b/drivers/media/video/stk1125.c
@@ -0,0 +1,667 @@
+/*
+ * STK-1125 part
+ *
+ * Copyright (c) 2007 Jiri Slaby <[email protected]>
+ *
+ * Based on Nicolas VIVIEN's driver
+ *
+ * Licences
+ *
+ * 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.
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/types.h>
+
+#include "stk11xx.h"
+
+static int stk1125_load_microcode(struct stk11xx *dev)
+{
+ unsigned int i;
+ const u8 *values_204, *values_205;
+ int retok;
+
+ /* From 80x60 to 640x480 */
+ const u8 values_1_204[] = {
+ 0x12, 0x11, 0x3b, 0x6a, 0x13, 0x10, 0x00, 0x01, 0x02, 0x13,
+ 0x39, 0x38, 0x37, 0x35, 0x0e, 0x12, 0x04, 0x0c, 0x0d, 0x17,
+ 0x18, 0x32, 0x19, 0x1a, 0x03, 0x1b, 0x16, 0x33, 0x34, 0x41,
+ 0x96, 0x3d, 0x69, 0x3a, 0x8e, 0x3c, 0x8f, 0x8b, 0x8c, 0x94,
+ 0x95, 0x40, 0x29, 0x0f, 0xa5, 0x1e, 0xa9, 0xaa, 0xab, 0x90,
+ 0x91, 0x9f, 0xa0, 0x24, 0x25, 0x26, 0x14, 0x2a, 0x2b
+ };
+ const u8 values_1_205[] = {
+ 0x45, 0x80, 0x01, 0x7d, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
+ 0x50, 0x93, 0x00, 0x81, 0x20, 0x45, 0x00, 0x00, 0x00, 0x24,
+ 0xc4, 0xb6, 0x00, 0x3c, 0x36, 0x00, 0x07, 0xe2, 0xbf, 0x00,
+ 0x04, 0x19, 0x40, 0x0d, 0x00, 0x73, 0xdf, 0x06, 0x20, 0x88,
+ 0x88, 0xc1, 0x3f, 0x42, 0x80, 0x04, 0xb8, 0x92, 0x0a, 0x00,
+ 0x00, 0x00, 0x00, 0x68, 0x5c, 0xc3, 0x2e, 0x00, 0x00
+ };
+
+ /* From 800x600 to 1280x1024 */
+ const u8 values_2_204[] = {
+ 0x12, 0x11, 0x3b, 0x6a, 0x13, 0x10, 0x00, 0x01, 0x02, 0x13,
+ 0x39, 0x38, 0x37, 0x35, 0x0e, 0x12, 0x04, 0x0c, 0x0d, 0x17,
+ 0x18, 0x32, 0x19, 0x1a, 0x03, 0x1b, 0x16, 0x33, 0x34, 0x41,
+ 0x96, 0x3d, 0x69, 0x3a, 0x8e, 0x3c, 0x8f, 0x8b, 0x8c, 0x94,
+ 0x95, 0x40, 0x29, 0x0f, 0xa5, 0x1e, 0xa9, 0xaa, 0xab, 0x90,
+ 0x91, 0x9f, 0xa0, 0x24, 0x25, 0x26, 0x14, 0x2a, 0x2b
+ };
+ const u8 values_2_205[] = {
+ 0x05, 0x80, 0x01, 0x7d, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
+ 0x50, 0x93, 0x00, 0x81, 0x20, 0x05, 0x00, 0x00, 0x00, 0x1b,
+ 0xbb, 0xa4, 0x01, 0x81, 0x12, 0x00, 0x07, 0xe2, 0xbf, 0x00,
+ 0x04, 0x19, 0x40, 0x0d, 0x00, 0x73, 0xdf, 0x06, 0x20, 0x88,
+ 0x88, 0xc1, 0x3f, 0x42, 0x80, 0x04, 0xb8, 0x92, 0x0a, 0x00,
+ 0x00, 0x00, 0x00, 0x68, 0x5c, 0xc3, 0x2e, 0x00, 0x00
+ };
+
+ /* From the resolution */
+ switch (dev->resolution) {
+ case STK11XX_1280x1024:
+ case STK11XX_1024x768:
+ case STK11XX_800x600:
+ values_204 = values_2_204;
+ values_205 = values_2_205;
+ break;
+
+ case STK11XX_640x480:
+ case STK11XX_320x240:
+ case STK11XX_160x120:
+ case STK11XX_80x60:
+ default:
+ values_204 = values_1_204;
+ values_205 = values_1_205;
+ break;
+ }
+
+ for (i = 0; i < 59; i++) {
+ stk11xx_read_dummy(dev, 0x02ff);
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+
+ stk11xx_write_reg(dev, 0x0204, values_204[i]);
+ stk11xx_write_reg(dev, 0x0205, values_205[i]);
+ stk11xx_write_reg(dev, 0x0200, 0x01);
+
+ retok = stk11xx_check_device(dev, 500);
+ if (retok != 1) {
+ dev_err(&dev->udev->dev, "load microcode fail\n");
+ return -EIO;
+ }
+
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+ }
+
+ stk11xx_check_device(dev, 500);
+
+ return 0;
+}
+
+/*
+ * The configuration of device is composed of 11 steps.
+ * This function is called by the initialization process.
+ *
+ * We don't know the meaning of these steps! We only replay the USB log.
+ *
+ * The steps 0 to 9 are called during the initialization.
+ * Then, the driver choose the last step :
+ * 10 : for a resolution from 80x60 to 640x480
+ * 11 : for a resolution from 800x600 to 1280x1024
+ */
+static int stk1125_dev_configure(struct stk11xx *dev, unsigned int step)
+{
+ int ret;
+ /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11 */
+
+ const u8 vals_001B[] = {
+ 0x0e, 0x03, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e
+ };
+ const u8 vals_001C[] = {
+ 0x06, 0x02, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
+ 0x46, 0x0e
+ };
+ const u8 vals_0202[] = {
+ 0x1e, 0x0a, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e
+ };
+ const u8 vals_0110[] = {
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3e, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+ };
+ const u8 vals_0112[] = {
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+ };
+ const u8 vals_0114[] = {
+ 0x87, 0x80, 0x80, 0x80, 0x80, 0xbe, 0xbe, 0x80, 0x80, 0x80,
+ 0x80, 0x00
+ };
+ const u8 vals_0115[] = {
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x05
+ };
+ const u8 vals_0116[] = {
+ 0xe7, 0xe0, 0xe0, 0xe0, 0xe0, 0xe9, 0xe9, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0x00
+ };
+ const u8 vals_0117[] = {
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x04
+ };
+ const u8 vals_0100[] = {
+ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21
+ };
+ struct stk11xx_table table_1[] = {
+ { 0x0000, 0x24, 2 }, { 0x0002, 0x68, 2 }, { 0x0003, 0x80, 2 },
+ { 0x0005, 0x00, 2 }, { 0x0007, 0x03, 2 }, { 0x000d, 0x00, 2 },
+ { 0x000f, 0x02, 2 }, { 0x0300, 0x12, 2 }, { 0x0350, 0x41, 2 },
+ { 0x0351, 0x00, 2 }, { 0x0352, 0x00, 2 }, { 0x0353, 0x00, 2 },
+ { 0x0018, 0x10, 2 }, { 0x0019, 0x00, 2 },
+ { 0x001b, vals_001B[step], 2 }, { 0x001c, vals_001C[step], 2 },
+ { 0x0300, 0x80, 2 }, { 0x001a, 0x04, 2 },
+ { 0x0202, vals_0202[step], 2 },
+
+ { 0x0110, vals_0110[step], 2 }, { 0x0111, 0x00, 2 },
+ { 0x0112, vals_0112[step], 2 }, { 0x0113, 0x00, 2 },
+ { 0x0114, vals_0114[step], 2 }, { 0x0115, vals_0115[step], 2 },
+ { 0x0116, vals_0116[step], 2 }, { 0x0117, vals_0117[step], 2 },
+
+ { 0x0100, 0x00, 1 },
+ { 0x0100, vals_0100[step], 2 },
+
+ { 0x0200, 0x80, 2 }, { 0x0200, 0x00, 2 }, { 0x02ff, 0x00, 2 },
+ { }
+ };
+
+ dev_dbg(&dev->udev->dev, "%s: %u\n", __FUNCTION__, step);
+
+ ret = stk11xx_process_table(dev, table_1);
+ if (ret)
+ goto err;
+
+
+ switch (step) {
+ case 0: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x40, 2 }, { 0x0204, 0x41, 2 },
+ { 0x0205, 0x01, 2 }, { 0x0204, 0x1c, 2 },
+ { 0x0205, 0x02, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 1: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x22, 2 }, { 0x0204, 0x27, 2 },
+ { 0x0205, 0xa5, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 2: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xbf, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 3: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x42, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x24, 2 },
+ { 0x0205, 0xa5, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 4: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x42, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xe0, 2 }, { 0x0204, 0x24, 2 },
+ { 0x0205, 0xa5, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 5: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xff, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 6: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xff, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 7: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xb7, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 8:
+ stk11xx_write_reg(dev, 0x0203, 0x60);
+
+ ret = stk1125_load_microcode(dev);
+
+ stk11xx_write_reg(dev, 0x0200, 0x80);
+ stk11xx_write_reg(dev, 0x0200, 0x00);
+ stk11xx_write_reg(dev, 0x02ff, 0x01);
+ stk11xx_write_reg(dev, 0x0203, 0xa0);
+
+ break;
+
+ case 9:
+ stk11xx_write_reg(dev, 0x0203, 0x60);
+
+ ret = stk1125_load_microcode(dev);
+
+ stk11xx_write_reg(dev, 0x0104, 0x00);
+ stk11xx_write_reg(dev, 0x0105, 0x00);
+ stk11xx_write_reg(dev, 0x0106, 0x00);
+
+ break;
+
+ case 10:
+ case 11: {
+ const struct stk11xx_table table[] = {
+ { 0x0106, 0x00, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0204, 0x2a, 2 },
+ { 0x0205, 0x00, 2 }, { 0x0200, 0x01, 2 },
+ { 500, 0x00, 3 }, { 0x02ff, 0x00, 2 },
+ { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x0204, 0x2b, 2 }, { 0x0205, 0x00, 2 },
+ { 0x0200, 0x01, 2 }, { 500, 0x00, 3 },
+ { 0x02ff, 0x00, 2 }, { }
+ };
+
+ stk11xx_write_reg(dev, 0x0203, 0x60);
+
+ ret = stk1125_load_microcode(dev);
+ if (ret)
+ goto err;
+
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ }
+err:
+ return ret;
+}
+
+static int stk1125_cam_asleep(struct stk11xx *dev)
+{
+ const struct stk11xx_table table[] = {
+ { 0x0104, 0x00, 1 }, { 0x0105, 0x00, 1 }, { 0x0106, 0x00, 1 },
+ { 0x0100, 0x21, 2 }, { 0x0116, 0x00, 2 }, { 0x0117, 0x00, 2 },
+ { 0x0018, 0x00, 2 }, { 0x0000, 0x00, 1 }, { 0x0000, 0x4c, 2 },
+ { }
+ };
+
+ return stk11xx_process_table(dev, table);
+}
+
+static u16 stk1125_get_fps_code(unsigned int fps)
+{
+ switch (fps) {
+ case 10:
+ return 0x0004;
+ case 15:
+ return 0x0002;
+ case 20:
+ return 0x0001;
+ default:
+ case 25:
+ return 0x6400;
+ case 30:
+ return 0x0000;
+ }
+}
+
+/*
+ * This functions permits to modify the settings :
+ * - brightness
+ * - contrast
+ * - white balance
+ * - ...
+ *
+ * 0x204 = 0xa1 : unknown (by default 0x00)
+ * 0x204 = 0x10 : contrast (by default 0x7c)
+ * 0x204 = 0x04 : Mode (unknown) (by default 0x00) (=> already looked 0x01 and
+ * 0x02)
+ * 0x204 = 0x00 : brightness / white balance (by default 0x00)
+ * 0x204 = 0x2e : Fps MSB (by default 0x01)
+ * 0x204 = 0x2d : Fps LSB (by default 0x00)
+ *
+ * 0x2e | 0x2d | Nbr fps
+ * -----+------+--------
+ * 0x00 | 0x00 | 30
+ * 0x00 | 0x64 | 25
+ * 0x01 | 0x00 | 20
+ * 0x02 | 0x00 | 15
+ * 0x03 | 0x00 | 12
+ * 0x04 | 0x00 | 10
+ */
+static int stk1125_cam_setting(struct stk11xx *dev)
+{
+ int ret;
+ u16 fps = stk1125_get_fps_code(dev->vsettings.fps);
+
+ struct stk11xx_table table_1[] = {
+ { 0x0200, 0x00, 2 },
+ /* Unknown register */
+ { 0x0204, 0xa1, 2 }, { 0x0205, 0x00, 2 },
+ /* Contrast register */
+ { 0x0204, 0x10, 2 }, { 0x0205, dev->vsettings.contrast, 2 },
+ /* Unknown register */
+ { 0x0204, 0x04, 2 }, { 0x0205, 0x00, 2 },
+ /* Whiteness register */
+ { 0x0204, 0x00, 2 }, { 0x0205, dev->vsettings.whiteness, 2 },
+ { 0x0204, 0x2e, 2 }, { 0x0205, fps & 0xff, 2 },
+ { 0x0204, 0x2d, 2 }, { 0x0205, fps >> 8, 2 },
+ { 0x0200, 0x06, 2 }, { }
+ };
+
+ ret = stk11xx_process_table(dev, table_1);
+ if (ret)
+ goto end;
+
+ dev_dbg(&dev->udev->dev, "set contrast: %d, whiteness: %d, ",
+ dev->vsettings.contrast, dev->vsettings.whiteness);
+
+ ret = stk11xx_check_device(dev, 500);
+ if (!ret)
+ dev_dbg(&dev->udev->dev, "find not 0x4 ... seems OK\n");
+ ret = 0;
+end:
+ return ret;
+}
+
+static int stk1125_cam_init(struct stk11xx *dev)
+{
+ int ret;
+
+ const struct stk11xx_table table[] = {
+ { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 }, { 0x0204, 0x2a, 2 },
+ { 0x0205, 0x00, 2 }, { 0x0200, 0x01, 2 }, { 500, 0x00, 3 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x0204, 0x2b, 2 }, { 0x0205, 0x00, 2 }, { 0x0200, 0x01, 2 },
+ { 500, 0x00, 3 }, { 0x02ff, 0x00, 2 },
+ { }
+ };
+
+ stk1125_cam_asleep(dev);
+
+ stk11xx_set_feature(dev, 0);
+
+ stk11xx_write_reg(dev, 0x0000, 0xe0);
+ stk11xx_write_reg(dev, 0x0002, 0xe8);
+ stk11xx_write_reg(dev, 0x0002, 0x68);
+ stk11xx_write_reg(dev, 0x0000, 0x20);
+
+ stk1125_dev_configure(dev, 9);
+
+ stk11xx_cam_off(dev);
+
+ ret = stk11xx_process_table(dev, table);
+ if (ret)
+ return ret;
+
+ return stk1125_cam_setting(dev);
+}
+
+static int stk1125_stream_start(struct stk11xx *dev)
+{
+ u8 value_116, value_117;
+
+ stk11xx_read_reg(dev, 0x0116, &value_116);
+ stk11xx_read_reg(dev, 0x0117, &value_117);
+
+ stk11xx_write_reg(dev, 0x0116, 0x00);
+ stk11xx_write_reg(dev, 0x0117, 0x00);
+
+ stk11xx_read_dummy(dev, 0x0100); /* read 0x21 */
+ stk11xx_write_reg(dev, 0x0100, 0xa1);
+
+ stk11xx_write_reg(dev, 0x0116, value_116);
+ stk11xx_write_reg(dev, 0x0117, value_117);
+
+ return 0;
+}
+
+static int stk1125_stream_stop(struct stk11xx *dev)
+{
+ stk11xx_read_dummy(dev, 0x0100);
+ stk11xx_write_reg(dev, 0x0100, 0x21);
+
+ return 0;
+}
+
+static int stk1125_cam_reconf(struct stk11xx *dev)
+{
+ int step;
+
+ switch (dev->resolution) {
+ case STK11XX_1280x1024:
+ case STK11XX_1024x768:
+ case STK11XX_800x600:
+ step = 11;
+ break;
+
+ case STK11XX_640x480:
+ case STK11XX_320x240:
+ case STK11XX_160x120:
+ case STK11XX_80x60:
+ default:
+ step = 10;
+ break;
+ }
+
+ stk1125_dev_configure(dev, step);
+
+ return 0;
+}
+
+/* this function is written from the USB log */
+static int stk1125_dev_init(struct stk11xx *dev)
+{
+ unsigned int i;
+ int ret;
+ u8 value;
+
+ const struct stk11xx_table table_1[] = {
+ { 0x0000, 0x24, 2 }, { 0x0002, 0x68, 2 }, { 0x0003, 0x80, 2 },
+ { 0x0002, 0x6f, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0002, 0x6d, 2 },
+ { 0x0000, 0x24, 2 },
+ { }
+ };
+ const struct stk11xx_table table_2[] = {
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 }, { 0x0002, 0x6f, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0002, 0x6d, 2 }, { 0x0000, 0x24, 2 },
+ { }
+ };
+ const struct stk11xx_table table_3[] = {
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 }, { 0x0002, 0x6f, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0002, 0x6d, 2 }, { 0x0000, 0x24, 2 },
+ { }
+ };
+ const struct stk11xx_table table_4[] = {
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 }, { 0x0002, 0x6f, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x20, 2 }, { 0x0117, 0x00, 2 },
+ { 0x0103, 0x00, 1 }, { 0x0103, 0x01, 2 }, { 0x0103, 0x00, 1 },
+ { 0x0103, 0x00, 2 }, { 0x0000, 0xe0, 2 }, { 0x0002, 0xe8, 2 },
+ { 0x0002, 0x68, 2 }, { 0x0000, 0x20, 2 },
+
+ { 0, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 1, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+
+ { 2, 0x00, 4 }, { 500, 0x00, 3 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0208, 0x13, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 }, { 0x0208, 0x0a, 2 },
+ { 0x0200, 0x20, 2 }, { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x0208, 0x0b, 2 }, { 0x0200, 0x20, 2 }, { 500, 0x00, 3 },
+ { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+
+ { 3, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 4, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+
+ { 5, 0x00, 4 }, { 500, 0x00, 3 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0208, 0x13, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 }, { 0x0208, 0x0a, 2 },
+ { 0x0200, 0x20, 2 }, { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x0208, 0x0b, 2 }, { 0x0200, 0x20, 2 }, { 500, 0x00, 3 },
+ { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+
+ { 6, 0x00, 4 }, { 500, 0x00, 3 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0208, 0x13, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 }, { 0x0208, 0x0a, 2 },
+ { 0x0200, 0x20, 2 }, { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x0208, 0x0b, 2 }, { 0x0200, 0x20, 2 }, { 500, 0x00, 3 },
+ { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+
+ { 7, 0x00, 4 }, { 500, 0x00, 3 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0208, 0x13, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 }, { 0x0208, 0x0a, 2 },
+ { 0x0200, 0x20, 2 }, { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x0208, 0x0b, 2 }, { 0x0200, 0x20, 2 }, { 500, 0x00, 3 },
+ { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+
+ { 0x0002, 0x6f, 2 }, { 0x0000, 0x24, 2 }, { 0x0002, 0x6d, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 },
+
+ { 8, 0x00, 4 },
+ { }
+ };
+
+ ret = stk11xx_process_table(dev, table_1);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < 16; i++) {
+ stk11xx_write_reg(dev, 0x0000, 0x25);
+ stk11xx_write_reg(dev, 0x0000, 0x24);
+ stk11xx_read_reg(dev, 0x0000, &value);
+
+ dev_dbg(&dev->udev->dev, "Loop 1: Read 0x0000 = %02x\n", value);
+ }
+
+ ret = stk11xx_process_table(dev, table_2);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < 16; i++) {
+ stk11xx_write_reg(dev, 0x0000, 0x25);
+ stk11xx_write_reg(dev, 0x0000, 0x24);
+ stk11xx_read_reg(dev, 0x0000, &value);
+
+ dev_dbg(&dev->udev->dev, "Loop 2: Read 0x0000 = %02x\n", value);
+ }
+
+ ret = stk11xx_process_table(dev, table_3);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < 16; i++) {
+ stk11xx_write_reg(dev, 0x0000, 0x25);
+ stk11xx_write_reg(dev, 0x0000, 0x24);
+ stk11xx_read_reg(dev, 0x0000, &value);
+
+ dev_dbg(&dev->udev->dev, "Loop 3: Read 0x0000 = %02x\n", value);
+ }
+
+ ret = stk11xx_process_table(dev, table_4);
+ if (ret)
+ goto end;
+
+ stk1125_cam_asleep(dev);
+
+ stk11xx_set_feature(dev, 0);
+
+end:
+ return ret;
+}
+
+struct stk_model stk_model_1125 = {
+ .model = SYNTEK_STK1125,
+ .name = "1125",
+ .dev_init = stk1125_dev_init,
+ .dev_configure = stk1125_dev_configure,
+ .cam_init = stk1125_cam_init,
+ .cam_setting = stk1125_cam_setting,
+ .cam_asleep = stk1125_cam_asleep,
+ .cam_reconf = stk1125_cam_reconf,
+ .stream_start = stk1125_stream_start,
+ .stream_stop = stk1125_stream_stop,
+};
diff --git a/drivers/media/video/stk1135.c b/drivers/media/video/stk1135.c
new file mode 100644
index 0000000..c6e0361
--- /dev/null
+++ b/drivers/media/video/stk1135.c
@@ -0,0 +1,549 @@
+/*
+ * STK-1135 part
+ *
+ * Copyright (c) 2007 Jiri Slaby <[email protected]>
+ *
+ * Based on Nicolas VIVIEN's driver
+ *
+ * Licences
+ *
+ * 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.
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/types.h>
+
+#include "stk11xx.h"
+
+static int stk1135_load_microcode(struct stk11xx *dev)
+{
+ unsigned int i;
+ int retok;
+
+ const u8 values_204[] = {
+ 0x17, 0x19, 0xb4, 0xa6, 0x12, 0x13, 0x1e, 0x21, 0x24, 0x32,
+ 0x36, 0x39, 0x4d, 0x53, 0x5d, 0x5f, 0x60, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x82, 0x83, 0x85, 0x86, 0x89, 0x97, 0x98,
+ 0xad, 0xae, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbf, 0x48, 0xd8,
+ 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0xd8, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
+ 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0xd8, 0x76, 0x77, 0x78, 0x79,
+ 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x5c, 0xc0,
+ 0x59, 0x5a, 0x5b, 0xd4, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0xb3, 0x73, 0x06, 0x07, 0x0b, 0x15, 0x20,
+ 0x4e, 0x4f, 0x49, 0x4a, 0x4b, 0x4c, 0x46, 0x06, 0x07, 0xb9,
+ 0xba, 0xbb, 0xbc, 0x61, 0x62, 0x65, 0x66
+ };
+ const u8 values_205[] = {
+ 0x41, 0x41, 0x03, 0x06, 0x06, 0x08, 0x06, 0x00, 0x02, 0x69,
+ 0x35, 0x60, 0xfe, 0x1c, 0x04, 0x08, 0x08, 0x08, 0x08, 0x00,
+ 0x00, 0x10, 0x14, 0x01, 0x80, 0x0c, 0xb6, 0x00, 0x25, 0x25,
+ 0x3f, 0x24, 0x10, 0x07, 0xcc, 0x1f, 0x30, 0x02, 0x9c, 0x80,
+ 0x00, 0x0d, 0x18, 0x22, 0x2c, 0x3e, 0x4f, 0x6f, 0x8e, 0xac,
+ 0xc8, 0xe5, 0xa0, 0x00, 0x0d, 0x18, 0x22, 0x2c, 0x3e, 0x4f,
+ 0x6f, 0x8e, 0xac, 0xc8, 0xe5, 0xc0, 0x00, 0x0d, 0x18, 0x22,
+ 0x2c, 0x3e, 0x4f, 0x6f, 0x8e, 0xac, 0xc8, 0xe5, 0x70, 0x18,
+ 0x09, 0x07, 0x07, 0x3c, 0x3d, 0x95, 0x88, 0x89, 0x47, 0x9c,
+ 0x81, 0x9c, 0x3d, 0x76, 0x76, 0x01, 0xf3, 0x05, 0x00, 0x44,
+ 0x06, 0x0a, 0x96, 0x00, 0x7d, 0x00, 0x20, 0x01, 0xf3, 0x04,
+ 0xe4, 0x09, 0xc8, 0x08, 0x08, 0x10, 0x14
+ };
+
+ for (i = 0; i < 117; i++) {
+ stk11xx_read_dummy(dev, 0x02ff);
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+
+ stk11xx_write_reg(dev, 0x0204, values_204[i]);
+ stk11xx_write_reg(dev, 0x0205, values_205[i]);
+ stk11xx_write_reg(dev, 0x0200, 0x01);
+
+ retok = stk11xx_check_device(dev, 500);
+ if (retok != 1) {
+ dev_err(&dev->udev->dev, "load microcode failed\n");
+ return -EIO;
+ }
+
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+ }
+
+ stk11xx_check_device(dev, 500);
+
+ return 0;
+}
+
+/*
+ * The configuration of device is composed of 12 steps.
+ * This function is called by the initialization process.
+ *
+ * We don't know the meaning of these steps! We only replay the USB log.
+ */
+static int stk1135_dev_configure(struct stk11xx *dev, unsigned int step)
+{
+ int ret;
+ /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13 */
+
+ const u8 vals_001B[] = {
+ 0x0e, 0x03, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07,
+ 0x07, 0x07, 0x07, 0x07
+ };
+ const u8 vals_001C[] = {
+ 0x06, 0x02, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06
+ };
+ const u8 vals_0202[] = {
+ 0x1e, 0x0a, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e
+ };
+ const u8 vals_0110[] = {
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3e, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ const u8 vals_0112[] = {
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ const u8 vals_0114[] = {
+ 0x87, 0x80, 0x80, 0x80, 0x80, 0xbe, 0xbe, 0x80, 0x84, 0x80,
+ 0x80, 0x80, 0x80, 0x80
+ };
+ const u8 vals_0116[] = {
+ 0xe7, 0xe0, 0xe0, 0xe0, 0xe0, 0xe9, 0xe9, 0xe0, 0xe4, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0
+ };
+ const u8 vals_0100[] = {
+ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, 0x20,
+ 0x20, 0x20, 0x20, 0x20
+ };
+ struct stk11xx_table table_1[] = {
+ { 0x0000, 0x24, 2 }, { 0x0002, 0x68, 2 }, { 0x0003, 0x80, 2 },
+ { 0x0005, 0x00, 2 }, { 0x0007, 0x03, 2 }, { 0x000d, 0x00, 2 },
+ { 0x000f, 0x02, 2 }, { 0x0300, 0x12, 2 }, { 0x0350, 0x41, 2 },
+ { 0x0351, 0x00, 2 }, { 0x0352, 0x00, 2 }, { 0x0353, 0x00, 2 },
+ { 0x0018, 0x10, 2 }, { 0x0019, 0x00, 2 },
+
+ { 0x001b, vals_001B[step], 2 }, { 0x001c, vals_001C[step], 2 },
+ { 0x0300, 0x80, 2 }, { 0x001a, 0x04, 2 },
+ { 0x0202, vals_0202[step], 2 },
+
+ { 0x0110, vals_0110[step], 2 }, { 0x0111, 0x00, 2 },
+ { 0x0112, vals_0112[step], 2 }, { 0x0113, 0x00, 2 },
+ { 0x0114, vals_0114[step], 2 }, { 0x0115, 0x02, 2 },
+ { 0x0116, vals_0116[step], 2 }, { 0x0117, 0x01, 2 },
+
+ { 0x0100, 0x00, 1 }, { 0x0100, vals_0100[step], 2 },
+
+ { 0x0200, 0x80, 2 }, { 0x0200, 0x00, 2 }, { 0x02ff, 0x00, 2 },
+ { }
+ };
+
+ dev_dbg(&dev->udev->dev, "stk1135_dev_configure: %d\n", step);
+
+ ret = stk11xx_process_table(dev, table_1);
+ if (ret)
+ goto err;
+
+ switch (step) {
+ case 0: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x40, 2 }, { 0x0204, 0x41, 2 },
+ { 0x0205, 0x01, 2 }, { 0x0204, 0x1c, 2 },
+ { 0x0205, 0x02, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 1: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x22, 2 }, { 0x0204, 0x27, 2 },
+ { 0x0205, 0xa5, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 2: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xbf, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 3: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x42, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x24, 2 },
+ { 0x0205, 0xa5, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 4: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x42, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xe0, 2 }, { 0x0204, 0x24, 2 },
+ { 0x0205, 0xa5, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 5: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xff, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 6: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xff, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 7: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xb7, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 8: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x80, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x0a, 2 },
+ { 0x0205, 0xff, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 9: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0xdc, 2 }, { 0x0204, 0x15, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0200, 0x05, 2 },
+ { 500, 0x00, 3 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0208, 0x00, 2 },
+ { 0x0200, 0x20, 2 }, { 500, 0x00, 3 },
+ { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x02ff, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x0208, 0x01, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0208, 0x02, 2 },
+ { 0x0200, 0x20, 2 }, { 500, 0x00, 3 },
+ { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 },
+ { 0x0002, 0x6f, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 10:
+ stk11xx_write_reg(dev, 0x0203, 0xdc);
+
+ ret = stk1135_load_microcode(dev);
+
+ break;
+
+ case 11:
+ stk11xx_write_reg(dev, 0x0203, 0xdc);
+
+ ret = stk1135_load_microcode(dev);
+
+ stk11xx_write_reg(dev, 0x0104, 0x00);
+ stk11xx_write_reg(dev, 0x0105, 0x00);
+ stk11xx_write_reg(dev, 0x0106, 0x00);
+
+ break;
+
+ case 12:
+ stk11xx_write_reg(dev, 0x0203, 0xdc);
+
+ ret = stk1135_load_microcode(dev);
+
+ stk11xx_write_reg(dev, 0x0104, 0x00);
+ stk11xx_write_reg(dev, 0x0105, 0x00);
+ stk11xx_write_reg(dev, 0x0106, 0x00);
+
+ break;
+
+ case 13:
+ stk11xx_write_reg(dev, 0x0203, 0xdc);
+
+ ret = stk1135_load_microcode(dev);
+
+ stk11xx_write_reg(dev, 0x0106, 0x00);
+
+ break;
+ }
+
+err:
+ return ret;
+}
+
+static int stk1135_cam_asleep(struct stk11xx *dev)
+{
+ const struct stk11xx_table table[] = {
+ { 0x0104, 0x00, 1 }, { 0x0105, 0x00, 1 }, { 0x0106, 0x00, 1 },
+ { 0x0100, 0x21, 2 }, { 0x0116, 0x00, 2 }, { 0x0117, 0x00, 2 },
+ { 0x0018, 0x00, 2 }, { 0x0000, 0x00, 1 }, { 0x0000, 0x49, 2 },
+ { }
+ };
+
+ return stk11xx_process_table(dev, table);
+}
+
+static int stk1135_cam_setting(struct stk11xx *dev)
+{
+ unsigned int i;
+
+ const u8 values_204[] = {
+ 0xb3, 0x73, 0x46, 0x06, 0x07, 0xb9, 0xba, 0xbb, 0xbc, 0x61,
+ 0x62, 0x65, 0x66
+ };
+ const u8 values_205[] = {
+ 0x76, 0x76, 0x20, 0x01, 0xf3, 0x04, 0xe4, 0x09, 0xc8, 0x08,
+ 0x08, 0x10, 0x14
+ };
+
+ for (i = 0; i < ARRAY_SIZE(values_204); i++) {
+ stk11xx_read_dummy(dev, 0x02ff);
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+
+ stk11xx_write_reg(dev, 0x0204, values_204[i]);
+ stk11xx_write_reg(dev, 0x0205, values_205[i]);
+
+ stk11xx_write_reg(dev, 0x0200, 0x01);
+ stk11xx_check_device(dev, 500);
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+ }
+
+ return 0;
+}
+
+static int stk1135_cam_init(struct stk11xx *dev)
+{
+ stk1135_cam_asleep(dev);
+
+ stk11xx_set_feature(dev, 0);
+
+ stk11xx_write_reg(dev, 0x0000, 0xe0);
+ stk11xx_write_reg(dev, 0x0002, 0xe8);
+ stk11xx_write_reg(dev, 0x0002, 0x68);
+ stk11xx_write_reg(dev, 0x0000, 0x20);
+
+ stk1135_dev_configure(dev, 11);
+
+ stk11xx_cam_off(dev);
+ stk1135_cam_setting(dev);
+ stk1135_cam_asleep(dev);
+
+ stk11xx_set_feature(dev, 0);
+
+ stk11xx_write_reg(dev, 0x0000, 0xe0);
+ stk11xx_write_reg(dev, 0x0002, 0xe8);
+ stk11xx_write_reg(dev, 0x0002, 0x68);
+ stk11xx_write_reg(dev, 0x0000, 0x20);
+
+ stk1135_dev_configure(dev, 12);
+
+ stk11xx_cam_off(dev);
+ stk1135_cam_setting(dev);
+
+ return 0;
+}
+
+static int stk1135_cam_reconf(struct stk11xx *dev)
+{
+ stk1135_dev_configure(dev, 13);
+
+ return 0;
+}
+
+static int stk1135_stream_start(struct stk11xx *dev)
+{
+ u8 value_116, value_117;
+
+ stk11xx_read_reg(dev, 0x0116, &value_116);
+ stk11xx_read_reg(dev, 0x0117, &value_117);
+
+ stk11xx_write_reg(dev, 0x0116, 0x00);
+ stk11xx_write_reg(dev, 0x0117, 0x00);
+
+ stk11xx_read_dummy(dev, 0x0100); /* read 0x21 */
+ stk11xx_write_reg(dev, 0x0100, 0xa0);
+
+ stk11xx_write_reg(dev, 0x0116, value_116);
+ stk11xx_write_reg(dev, 0x0117, value_117);
+
+ return 0;
+}
+
+static int stk1135_stream_stop(struct stk11xx *dev)
+{
+ stk11xx_read_dummy(dev, 0x0100);
+ stk11xx_write_reg(dev, 0x0100, 0x20);
+
+ return 0;
+}
+
+/* this function is written from the USB log */
+static int stk1135_dev_init(struct stk11xx *dev)
+{
+ unsigned int i;
+ int ret;
+ u8 value;
+
+ const struct stk11xx_table table_1[] = {
+ { 0x0000, 0x24, 2 }, { 0x0002, 0x68, 2 }, { 0x0003, 0x80, 2 },
+ { 0x0002, 0x6f, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0002, 0x6d, 2 },
+ { 0x0000, 0x24, 2 },
+ { }
+ };
+ const struct stk11xx_table table_2[] = {
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 }, { 0x0002, 0x6f, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0002, 0x6d, 2 }, { 0x0000, 0x24, 2 },
+ { }
+ };
+ const struct stk11xx_table table_3[] = {
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 }, { 0x0002, 0x6f, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0002, 0x6d, 2 }, { 0x0000, 0x24, 2 },
+ { }
+ };
+ const struct stk11xx_table table_4[] = {
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 }, { 0x0002, 0x6f, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x20, 2 }, { 0x0117, 0x00, 2 },
+ { 0x0103, 0x00, 1 }, { 0x0103, 0x01, 2 }, { 0x0103, 0x00, 1 },
+ { 0x0103, 0x00, 2 }, { 0x0000, 0xe0, 2 }, { 0x0002, 0xe8, 2 },
+ { 0x0002, 0x68, 2 }, { 0x0000, 0x20, 2 },
+
+ { 0, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 1, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 2, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 3, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 4, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 5, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 6, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 7, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 8, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 9, 0x00, 4 }, { 0x0000, 0x24, 2 }, { 0x0002, 0x6d, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 },
+ { 10, 0x00, 4 }, { 0x0200, 0x80, 2 }, { 0x0200, 0x00, 2 },
+ { 0x02ff, 0x01, 2 }, { 0x0203, 0xa0, 2 },
+ { }
+ };
+
+ ret = stk11xx_process_table(dev, table_1);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < 16; i++) {
+ stk11xx_write_reg(dev, 0x0000, 0x25);
+ stk11xx_write_reg(dev, 0x0000, 0x24);
+ stk11xx_read_reg(dev, 0x0000, &value);
+
+ dev_dbg(&dev->udev->dev, "Loop 1: Read 0x0000 = %02x\n", value);
+ }
+
+ ret = stk11xx_process_table(dev, table_2);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < 16; i++) {
+ stk11xx_write_reg(dev, 0x0000, 0x25);
+ stk11xx_write_reg(dev, 0x0000, 0x24);
+ stk11xx_read_reg(dev, 0x0000, &value);
+
+ dev_dbg(&dev->udev->dev, "Loop 2: Read 0x0000 = %02x\n", value);
+ }
+
+ ret = stk11xx_process_table(dev, table_3);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < 16; i++) {
+ stk11xx_write_reg(dev, 0x0000, 0x25);
+ stk11xx_write_reg(dev, 0x0000, 0x24);
+ stk11xx_read_reg(dev, 0x0000, &value);
+
+ dev_dbg(&dev->udev->dev, "Loop 3: Read 0x0000 = %02x\n", value);
+ }
+
+ ret = stk11xx_process_table(dev, table_4);
+ if (ret)
+ goto end;
+
+ stk1135_cam_asleep(dev);
+
+ stk11xx_set_feature(dev, 0);
+
+end:
+ return ret;
+}
+
+struct stk_model stk_model_1135 = {
+ .model = SYNTEK_STK1135,
+ .name = "1135",
+ .dev_init = stk1135_dev_init,
+ .dev_configure = stk1135_dev_configure,
+ .cam_init = stk1135_cam_init,
+ .cam_setting = stk1135_cam_setting,
+ .cam_asleep = stk1135_cam_asleep,
+ .cam_reconf = stk1135_cam_reconf,
+ .stream_start = stk1135_stream_start,
+ .stream_stop = stk1135_stream_stop,
+};
diff --git a/drivers/media/video/stk11xx-core.c b/drivers/media/video/stk11xx-core.c
new file mode 100644
index 0000000..13549db
--- /dev/null
+++ b/drivers/media/video/stk11xx-core.c
@@ -0,0 +1,962 @@
+/*
+ * Driver for Syntek USB video camera
+ *
+ * Copyright (c) 2007 Jiri Slaby <[email protected]>
+ *
+ * Based on Nicolas VIVIEN's driver
+ *
+ * Licences
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/usb.h>
+#include <media/v4l2-common.h>
+
+#include "stk11xx.h"
+
+#define USB_SYNTEK1_VENDOR_ID 0x174f
+#define USB_SYNTEK2_VENDOR_ID 0x05e1
+
+#define USB_STK1125_PRODUCT_ID 0xa311
+#define USB_STK1135_PRODUCT_ID 0xa821
+#define USB_STKDCNEW_PRODUCT_ID 0x6a31
+#define USB_DC1125_PRODUCT_ID 0x0501
+
+static const struct stk11xx_coord stk11xx_image_sizes[STK11XX_NBR_SIZES] = {
+ { 80, 60 },
+ { 160, 120 },
+ { 320, 240 },
+ { 640, 480 },
+ { 800, 600 },
+ { 1024, 768 },
+ { 1280, 1024 }
+};
+
+static unsigned int fps = 10;
+module_param(fps, uint, 0444);
+MODULE_PARM_DESC(fps, "Frames per second [10-30]");
+
+int stk11xx_read_reg(struct stk11xx *dev, u16 index, u8 *value)
+{
+ struct usb_device *udev = dev->udev;
+ int result;
+
+ *value = 0;
+
+ result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x00,
+ index, value, sizeof(u8), 500);
+
+ if (result < 0)
+ dev_err(&udev->dev, "Read registry fails %02X\n", index);
+ else
+ result = 0;
+
+ return result;
+}
+
+int stk11xx_write_reg(struct stk11xx *dev, u16 index, u16 value)
+{
+ struct usb_device *udev = dev->udev;
+ int result;
+
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value,
+ index, NULL, 0, 500);
+
+ if (result < 0)
+ dev_err(&udev->dev, "Write registry fails %02X = %02X\n", index,
+ value);
+ else
+ result = 0;
+
+ return result;
+}
+
+int stk11xx_set_feature(struct stk11xx *dev, int index)
+{
+ struct usb_device *udev = dev->udev;
+ int result;
+
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE,
+ USB_TYPE_STANDARD | USB_DIR_OUT |
+ USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP,
+ index, NULL, 0, 500);
+
+ if (result < 0)
+ dev_err(&udev->dev, "SET FEATURE fail !\n");
+
+ return result;
+}
+
+int stk11xx_process_table(struct stk11xx *dev,
+ const struct stk11xx_table *table)
+{
+ int ret = 0;
+
+ for (; table->type; table++)
+ switch (table->type) {
+ case 1:
+ ret = stk11xx_read_dummy(dev, table->addr);
+ break;
+ case 2:
+ ret = stk11xx_write_reg(dev, table->addr, table->val);
+ break;
+ case 3:
+ ret = stk11xx_check_device(dev, table->addr);
+ if (ret > 0)
+ ret = 0;
+ break;
+ case 4:
+ dev->model->dev_configure(dev, table->addr);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Bayer conversion
+ */
+
+#define STK11XX_AVG2(x, y) (u8)(((int)x + (int)y) / 2)
+#define STK11XX_AVG4(a, b, c, d) (u8)(((int)a + (int)b + (int)c + (int)d) / 4)
+
+static void stk11xx_bayer_to_rgb(u8 *rgb, const u8 *bayer,
+ unsigned int width, unsigned int height, unsigned int factor)
+{
+ const u8 *src;
+ u8 *dst;
+ unsigned int x, y, pos, above, below;
+
+ /*
+ * Picture from cam is vertically flipped and is of this form:
+ * . . .
+ * . . .
+ * . . .
+ * GRGRGR . . .
+ * BGBGBG
+ * GRGRGR . . .
+ */
+ /* blit out initial data */
+ for (y = 0; y < height; y += factor) {
+ src = &bayer[(height - y - 1) * width];
+ dst = &rgb[y * width * 3 / (factor * factor)];
+ for (x = 0; x < width; x += factor, src += factor, dst += 3)
+ dst[!(y & 1) + (x & 1)] = *src;
+ }
+
+ /* blit in everything but the borders */
+ for (y = factor; y < height - factor; y += factor) {
+ pos = (height - y - 1) * width + factor;
+ dst = &rgb[y * width * 3 / (factor * factor) + 3];
+ for (x = factor; x < width - factor; x += factor, pos += factor,
+ dst += 3) {
+ above = pos - width;
+ below = pos + width;
+
+ if (y & 1) {
+ /* XGBGBG line */
+ if (x & 1) { /* G is known */
+ dst[2] = STK11XX_AVG2( /* R */
+ bayer[above], bayer[below]);
+ dst[0] = STK11XX_AVG2( /* B */
+ bayer[pos - 1], bayer[pos + 1]);
+ } else { /* B is known */
+ dst[2] = STK11XX_AVG4( /* R */
+ bayer[above-1], bayer[above+1],
+ bayer[below-1], bayer[below+1]);
+ dst[1] = STK11XX_AVG4( /* G */
+ bayer[above], bayer[below],
+ bayer[pos - 1], bayer[pos + 1]);
+ }
+ } else {
+ /* XRGRGR line */
+ if (x & 1) { /* R is known */
+ dst[0] = STK11XX_AVG4( /* B */
+ bayer[above-1], bayer[above+1],
+ bayer[below-1], bayer[below+1]);
+ dst[1] = STK11XX_AVG4( /* G */
+ bayer[above], bayer[below],
+ bayer[pos - 1], bayer[pos + 1]);
+ } else { /* G is known */
+ dst[0] = STK11XX_AVG2( /* B */
+ bayer[above], bayer[below]);
+ dst[2] = STK11XX_AVG2( /* R */
+ bayer[pos - 1], bayer[pos + 1]);
+ }
+ }
+ }
+ }
+}
+
+int stk11xx_decompress(struct stk11xx *dev)
+{
+ struct stk11xx_frame_buf *framebuf = dev->read_frame;
+ void *data, *image = dev->image_data;
+ unsigned int width, height, factor;
+
+ if (framebuf == NULL)
+ return -EFAULT;
+
+ image += dev->images[dev->fill_image].offset;
+
+ data = framebuf->data;
+
+ switch (dev->resolution) {
+ case STK11XX_80x60:
+ factor = 8;
+ width = stk11xx_image_sizes[STK11XX_640x480].x;
+ height = stk11xx_image_sizes[STK11XX_640x480].y;
+ break;
+
+ case STK11XX_160x120:
+ factor = 4;
+ width = stk11xx_image_sizes[STK11XX_640x480].x;
+ height = stk11xx_image_sizes[STK11XX_640x480].y;
+ break;
+
+ case STK11XX_320x240:
+ factor = 2;
+ width = stk11xx_image_sizes[STK11XX_640x480].x;
+ height = stk11xx_image_sizes[STK11XX_640x480].y;
+ break;
+
+ case STK11XX_640x480:
+ factor = 1;
+ width = stk11xx_image_sizes[STK11XX_640x480].x;
+ height = stk11xx_image_sizes[STK11XX_640x480].y;
+ break;
+
+ case STK11XX_800x600:
+ factor = 1;
+ width = stk11xx_image_sizes[STK11XX_1280x1024].x;
+ height = stk11xx_image_sizes[STK11XX_1280x1024].y;
+ break;
+
+ case STK11XX_1024x768:
+ factor = 1;
+ width = stk11xx_image_sizes[STK11XX_1280x1024].x;
+ height = stk11xx_image_sizes[STK11XX_1280x1024].y;
+ break;
+
+ case STK11XX_1280x1024:
+ factor = 1;
+ width = stk11xx_image_sizes[STK11XX_1280x1024].x;
+ height = stk11xx_image_sizes[STK11XX_1280x1024].y;
+ break;
+
+ default:
+ return -EFAULT;
+ }
+
+ stk11xx_bayer_to_rgb(image, data, width, height, factor);
+
+ return 0;
+}
+
+/*
+ * Device
+ */
+
+/*
+ * called when a frame is ready to prepare the next frame
+ */
+static int stk11xx_next_frame(struct stk11xx *dev)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ STK_STREAM("Select next frame\n");
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ if (dev->fill_frame != NULL) {
+ if (dev->full_frames == NULL) {
+ dev->full_frames = dev->fill_frame;
+ dev->full_frames_tail = dev->full_frames;
+ } else {
+ dev->full_frames_tail->next = dev->fill_frame;
+ dev->full_frames_tail = dev->fill_frame;
+ }
+ }
+
+ if (dev->empty_frames != NULL) {
+ dev->fill_frame = dev->empty_frames;
+ dev->empty_frames = dev->empty_frames->next;
+ } else {
+ if (dev->full_frames == NULL) {
+ dev_err(&dev->udev->dev, "neither empty or full frames "
+ "available!\n");
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ return -EINVAL;
+ }
+
+ dev->fill_frame = dev->full_frames;
+ dev->full_frames = dev->full_frames->next;
+
+ ret = 1;
+ }
+
+ dev->fill_frame->next = NULL;
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return ret;
+}
+
+/*
+ * This function is called as an URB transfert is complete (Isochronous pipe).
+ * So, the traitement is done in interrupt time, so it has be fast, not crash,
+ * ans not stall. Neat.
+ */
+static void stk11xx_isoc_handler(struct urb *urb)
+{
+ struct stk11xx *dev = urb->context;
+ struct stk11xx_frame_buf *framebuf;
+ unsigned char *fill = NULL, *iso_buf = NULL;
+ unsigned int i;
+ int ret, skip, awake = 0, framestatus, framelen;
+
+ STK_STREAM("Isoc handler\n");
+
+ if (dev == NULL) {
+ dev_err(&dev->udev->dev, "isoc_handler called with NULL "
+ "device\n");
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ case -ETIMEDOUT:
+ break;
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ return;
+ default:
+ dev_warn(&dev->udev->dev, "unknown urb status %d\n",
+ urb->status);
+
+ }
+
+ framebuf = dev->fill_frame;
+ if (framebuf == NULL) {
+ dev_err(&dev->udev->dev, "isoc_handler without valid fill "
+ "frame\n");
+
+ wake_up_interruptible(&dev->wait_frame);
+
+ urb->dev = dev->udev;
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (ret != 0)
+ dev_err(&dev->udev->dev, "error (%d) re-submitting urb "
+ "in isoc_handler\n", ret);
+
+ return;
+ }
+
+ fill = framebuf->data + framebuf->filled;
+
+ /* Compact data */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ framestatus = urb->iso_frame_desc[i].status;
+ framelen = urb->iso_frame_desc[i].actual_length;
+ iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ if (framestatus == 0) {
+ skip = 4;
+
+ if (framelen > 4) {
+ /* we found something informational from there */
+ /* the isoc frames have two type of headers */
+ /* type1: 00 xx 00 00 or 20 xx 00 00 */
+ /* type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00 */
+ /* xx is a sequencer which has never been seen over 0x3f */
+
+ /* imho data written down looks like bayer, i see similarities after */
+ /* every 640 bytes */
+ if (*iso_buf & 0x80)
+ skip = 8;
+
+ if (framelen - skip + framebuf->filled >
+ dev->frame_size) {
+ dev_err(&dev->udev->dev, "frame buffer "
+ "overflow\n");
+ } else {
+ memcpy(fill, iso_buf + skip,
+ framelen - skip);
+ fill += framelen - skip;
+ }
+
+ framebuf->filled += framelen - skip;
+ }
+
+ STK_STREAM("URB : Length = %d - Skip = %d - Buffer "
+ "size = %d\n", framelen, skip,
+ framebuf->filled);
+
+ if (framelen == 4) {
+ if (framebuf->filled > 0) {
+ stk11xx_next_frame(dev);
+
+ awake = 1;
+ framebuf = dev->fill_frame;
+ framebuf->filled = 0;
+ fill = framebuf->data;
+ }
+ }
+ } else
+ dev_err(&dev->udev->dev, "iso frame %d has error %d\n",
+ i, framestatus);
+ }
+
+ if (awake == 1)
+ wake_up_interruptible(&dev->wait_frame);
+
+ urb->dev = dev->udev;
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret != 0)
+ dev_err(&dev->udev->dev, "error (%d) re-submitting urb in "
+ "isoc_handler.\n", ret);
+}
+
+void stk11xx_isoc_cleanup(struct stk11xx *dev)
+{
+ unsigned int i;
+
+ dev_dbg(&dev->udev->dev, "isoc cleanup\n");
+
+ if (!test_bit(STK11XX_STAT_ISOC, dev->status))
+ return;
+
+ /* Unlinking ISOC buffers */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ struct urb *urb;
+
+ urb = dev->isobuf[i].urb;
+
+ if (urb != 0) {
+ if (test_bit(STK11XX_STAT_ISOC, dev->status))
+ usb_kill_urb(urb);
+
+ usb_free_urb(urb);
+ dev->isobuf[i].urb = NULL;
+ }
+ }
+
+ /* All is done */
+ clear_bit(STK11XX_STAT_ISOC, dev->status);;
+}
+
+int stk11xx_isoc_init(struct stk11xx *dev)
+{
+ struct usb_device *udev = dev->udev;
+ struct urb *urb;
+ unsigned int i, j;
+ int ret = 0;
+
+ if (test_bit(STK11XX_STAT_ISOC, dev->status))
+ return 0;
+
+ /* Allocate URB structure */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
+
+ if (urb == NULL) {
+ dev_err(&udev->dev, "failed to allocate URB %d\n", i);
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ dev->isobuf[i].urb = urb;
+ }
+
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ urb = dev->isobuf[i].urb;
+
+ urb->interval = 1;
+ urb->dev = udev;
+ urb->pipe = usb_rcvisocpipe(udev, dev->isoc_in_endpointAddr);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = dev->isobuf[i].data;
+ urb->transfer_buffer_length = ISO_BUFFER_SIZE;
+ urb->complete = stk11xx_isoc_handler;
+ urb->context = dev;
+ urb->start_frame = 0;
+ urb->number_of_packets = ISO_FRAMES_PER_DESC;
+
+ for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
+ urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
+ urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE;
+ }
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret)
+ dev_err(&udev->dev, "isoc_init submit_urb %d "
+ "failed with error %d\n", i, ret);
+ else
+ dev_dbg(&udev->dev, "URB 0x%p submitted\n", urb);
+ }
+
+ dev_dbg(&udev->dev, "isoc_in_endpointAddr = %x\n",
+ dev->isoc_in_endpointAddr);
+
+ /* All is done */
+ set_bit(STK11XX_STAT_ISOC, dev->status);
+
+ return 0;
+err_free:
+ for (; i > 0; i--) { /* i is unsigned */
+ usb_free_urb(dev->isobuf[i - 1].urb);
+ dev->isobuf[i - 1].urb = NULL;
+ }
+
+ return ret;
+}
+
+/*
+ * When we configure the stk11xx, this function is used to check the device
+ * status.
+ * - If the read value is 0x00, then the device isn't ready.
+ * - If the read value is 0x04, then the device is ready.
+ * - If the read value is other, then the device is misconfigured.
+ */
+int stk11xx_check_device(struct stk11xx *dev, unsigned int nbr)
+{
+ unsigned int i;
+ int ret = 0;
+ u8 value;
+
+ for (i = 0; i < nbr; i++) {
+ ret = stk11xx_read_reg(dev, 0x201, &value);
+ if (ret)
+ goto end;
+
+ switch (value) {
+ case 0x00:
+ break;
+ case 0x01:
+ case 0x04:
+ return 1;
+ default:
+ dev_err(&dev->udev->dev, "check device return error "
+ "(0x201 = %02X)\n", value);
+ return -EIO;
+ }
+ }
+end:
+ return ret;
+}
+
+int stk11xx_cam_on(struct stk11xx *dev)
+{
+ struct usb_device *udev = dev->udev;
+ int ret;
+
+ ret = usb_set_interface(udev, 0, 5);
+
+ if (ret < 0)
+ dev_err(&udev->dev, "usb_set_interface failed\n");
+
+ return ret;
+}
+
+int stk11xx_cam_off(struct stk11xx *dev)
+{
+ struct usb_device *udev = dev->udev;
+ int ret;
+
+ ret = usb_set_interface(udev, 0, 0);
+
+ if (ret < 0)
+ dev_err(&udev->dev, "usb_set_interface failed\n");
+
+ return 0;
+}
+
+/*
+ * This function reads periodically the value of register 0x0001.
+ * We don't know the purpose. I assume that it seems to a software watchdog.
+ */
+int stk11xx_cam_watchdog(struct stk11xx *dev)
+{
+ u8 value;
+
+ stk11xx_read_reg(dev, 0x0001, &value);
+
+ if (value != 0x03)
+ dev_err(&dev->udev->dev, "Error: Register 0x0001 = %02X\n",
+ value);
+
+ return value;
+}
+
+int stk11xx_check_image_size(struct stk11xx *dev, unsigned int *width,
+ unsigned int *height)
+{
+ unsigned int a;
+
+ for (a = 0; a < ARRAY_SIZE(stk11xx_image_sizes); a++)
+ if (*width == stk11xx_image_sizes[a].x &&
+ *height == stk11xx_image_sizes[a].y)
+ break;
+
+ if (*width < stk11xx_image_sizes[0].x)
+ *width = stk11xx_image_sizes[0].x;
+ else if (*width > stk11xx_image_sizes[STK11XX_NBR_SIZES - 1].x)
+ *width = stk11xx_image_sizes[STK11XX_NBR_SIZES - 1].x;
+
+ if (*height < stk11xx_image_sizes[0].y)
+ *height = stk11xx_image_sizes[0].y;
+ else if (*height > stk11xx_image_sizes[STK11XX_NBR_SIZES - 1].y)
+ *height = stk11xx_image_sizes[STK11XX_NBR_SIZES - 1].y;
+
+ if (a >= ARRAY_SIZE(stk11xx_image_sizes))
+ return -EINVAL;
+
+ return 0;
+}
+
+int stk11xx_select_video_mode(struct stk11xx *dev, int width, int height)
+{
+ unsigned int i, find;
+
+ /* Check width and height */
+ /* Driver can't build an image more little than the minimal
+ * resolution ! */
+ if ((width < stk11xx_image_sizes[0].x) ||
+ (height < stk11xx_image_sizes[0].y))
+ return -EINVAL;
+
+ /* Seek the best resolution */
+ switch (dev->model->model) {
+ case SYNTEK_STK1125:
+ case SYNTEK_STKDCNEW:
+ for (i = 0, find = 0; i < STK11XX_NBR_SIZES; i++) {
+ if (stk11xx_image_sizes[i].x <= width &&
+ stk11xx_image_sizes[i].y <= height)
+ find = i;
+ }
+ break;
+
+ case SYNTEK_STK1135:
+ for (i = 0, find = 0; i < STK11XX_NBR_SIZES - 3; i++) {
+ if (stk11xx_image_sizes[i].x <= width &&
+ stk11xx_image_sizes[i].y <= height)
+ find = i;
+ }
+ break;
+
+ default:
+ return -ENODEV;
+ }
+
+ /* Save the new resolution */
+ dev->resolution = find;
+
+ dev_dbg(&dev->udev->dev, "set mode %d [%dx%d]\n", dev->resolution,
+ stk11xx_image_sizes[dev->resolution].x,
+ stk11xx_image_sizes[dev->resolution].y);
+
+ /* Save the new size */
+ dev->view.x = stk11xx_image_sizes[dev->resolution].x;
+ dev->view.y = stk11xx_image_sizes[dev->resolution].y;
+
+ /* Calculate the frame size */
+ switch (dev->resolution) {
+ case STK11XX_80x60:
+ case STK11XX_160x120:
+ case STK11XX_320x240:
+ case STK11XX_640x480:
+ dev->frame_size = stk11xx_image_sizes[STK11XX_640x480].x *
+ stk11xx_image_sizes[STK11XX_640x480].y;
+ dev->image_size = 3 * dev->frame_size;
+ break;
+
+ case STK11XX_800x600:
+ case STK11XX_1024x768:
+ case STK11XX_1280x1024:
+ dev->frame_size = stk11xx_image_sizes[STK11XX_1280x1024].x *
+ stk11xx_image_sizes[STK11XX_1280x1024].y;
+ dev->image_size = 3 * dev->frame_size;
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * sysfs stuff
+ */
+
+static ssize_t show_contrast(struct class_device *class, char *buf)
+{
+ struct stk11xx *dev = video_get_drvdata(to_video_device(class));
+
+ return sprintf(buf, "%X\n", dev->vsettings.contrast);
+}
+
+static ssize_t show_whitebalance(struct class_device *class, char *buf)
+{
+ struct stk11xx *dev = video_get_drvdata(to_video_device(class));
+
+ return sprintf(buf, "%X\n", dev->vsettings.whiteness);
+}
+
+static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);
+static CLASS_DEVICE_ATTR(whitebalance, S_IRUGO, show_whitebalance, NULL);
+
+static inline int stk11xx_create_sysfs_file(const struct stk11xx *dev,
+ const struct class_device_attribute *cda)
+{
+ int ret;
+
+ ret = class_device_create_file(&dev->vdev->class_dev, cda);
+ if (ret)
+ dev_err(&dev->udev->dev, "can't create sysfs file %s: %d\n",
+ attr_name(*cda), ret);
+
+ return ret;
+}
+
+/*
+ * USB
+ */
+
+static int stk11xx_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct stk11xx *dev = NULL;
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ unsigned int i;
+ int retval;
+
+ /* The interface are probed one by one. */
+ /* We are interested in the video interface (always the interface '0')*/
+ /* The interfaces '1' or '2' (if presents) are the audio control. */
+ if (interface->cur_altsetting->desc.bInterfaceNumber > 0) {
+ retval = -ENODEV;
+ goto err;
+ }
+
+ dev = kzalloc(sizeof(struct stk11xx), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&udev->dev, "can't alloc device info!\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ mutex_init(&dev->open_lock);
+ spin_lock_init(&dev->spinlock);
+ init_waitqueue_head(&dev->wait_frame);
+
+ set_bit(STK11XX_STAT_PRESENT, dev->status);
+ dev->model = (void *)id->driver_info;
+ dev->udev = udev;
+
+ dev->vsettings.fps = fps;
+
+/* TODO: check why is no driver that we can see claiming the interfaces ? */
+/* the apis say it should be done, none of the video usb drivers are doing it */
+/* NICKLAS: Claiming the interface is usefull when the driver want manage
+ * severals interfaces. */
+/* For us, we have only one interface (the video) */
+
+ stk11xx_cam_on(dev);
+
+ /* Set up the endpoint information use only the first isoc-in */
+ /* endpoint for the current alternate setting */
+ iface_desc = interface->cur_altsetting;
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (!dev->isoc_in_endpointAddr && ((endpoint->bEndpointAddress &
+ USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
+ ((endpoint->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_ISOC)) {
+ /* we've found an isoc in endpoint */
+ dev->isoc_in_endpointAddr =
+ (endpoint->bEndpointAddress & 0xf);
+ }
+ }
+
+ if (!dev->isoc_in_endpointAddr) {
+ dev_err(&udev->dev, "can't find both isoc-in endpoint\n");
+ retval = -ENODEV;
+ goto err_free;
+ }
+
+ stk11xx_cam_off(dev);
+
+ dev->vdev = video_device_alloc();
+ if (dev->vdev == NULL) {
+ dev_err(&udev->dev, "can't allocate videodevice\n");
+ retval = -ENOMEM;
+ goto err_free;
+ }
+
+ retval = dev->model->dev_init(dev);
+ if (retval)
+ goto err_vfree;
+
+ memcpy(dev->vdev, &stk11xx_vdev_template, sizeof(*dev->vdev));
+
+ video_set_drvdata(dev->vdev, dev);
+
+ retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1);
+ if (retval) {
+ dev_err(&udev->dev, "can't register video device\n");
+ goto err_vfree;
+ }
+
+ stk11xx_create_sysfs_file(dev, &class_device_attr_contrast);
+ stk11xx_create_sysfs_file(dev, &class_device_attr_whitebalance);
+ usb_set_intfdata(interface, dev);
+
+ dev_info(&udev->dev, "Syntek USB2.0 - STK-%s based webcam found and "
+ "ready\n", dev->model->name);
+
+ return 0;
+err_vfree:
+ video_device_release(dev->vdev);
+err_free:
+ kfree(dev);
+err:
+ return retval;
+}
+
+static void stk11xx_usb_disconnect(struct usb_interface *interface)
+{
+ struct stk11xx *dev = usb_get_intfdata(interface);
+ unsigned int free = 0;
+
+ class_device_remove_file(&dev->vdev->class_dev,
+ &class_device_attr_contrast);
+ class_device_remove_file(&dev->vdev->class_dev,
+ &class_device_attr_whitebalance);
+ mutex_lock(&dev->open_lock);
+ clear_bit(STK11XX_STAT_PRESENT, dev->status);
+ if (dev->vopen == 0) {
+ free++;
+ video_unregister_device(dev->vdev);
+ } else {
+ dev->model->stream_stop(dev);
+ stk11xx_isoc_cleanup(dev);
+ stk11xx_cam_off(dev);
+ dev->model->cam_asleep(dev);
+ }
+ mutex_unlock(&dev->open_lock);
+ if (free)
+ kfree(dev);
+}
+
+#ifdef CONFIG_PM
+static int stk11xx_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct stk11xx *dev = usb_get_intfdata(intf);
+
+ mutex_lock(&dev->open_lock);
+ if (dev->vopen) {
+ dev->model->stream_stop(dev);
+ stk11xx_isoc_cleanup(dev);
+ stk11xx_cam_off(dev);
+ dev->model->cam_asleep(dev);
+ }
+ mutex_unlock(&dev->open_lock);
+ return 0;
+}
+
+static int stk11xx_usb_resume(struct usb_interface *intf)
+{
+ struct stk11xx *dev = usb_get_intfdata(intf);
+
+ mutex_lock(&dev->open_lock);
+ if (dev->vopen && test_bit(STK11XX_STAT_PRESENT, dev->status)) {
+ if (stk11xx_select_video_mode(dev, dev->view.x, dev->view.y)) {
+ dev_err(&dev->udev->dev, "Select video mode failed\n");
+ return -EINVAL;
+ }
+
+ dev->model->cam_init(dev);
+ stk11xx_cam_on(dev);
+ dev->model->cam_reconf(dev);
+ stk11xx_isoc_init(dev);
+ dev->model->stream_start(dev);
+ }
+ mutex_unlock(&dev->open_lock);
+ return 0;
+}
+#else
+#define stk11xx_usb_suspend NULL
+#define stk11xx_usb_resume NULL
+#endif
+
+static struct usb_device_id stk11xx_usb_ids[] = {
+ { USB_DEVICE(USB_SYNTEK1_VENDOR_ID, USB_STK1125_PRODUCT_ID),
+ .driver_info = (unsigned long)&stk_model_1125 },
+ { USB_DEVICE(USB_SYNTEK1_VENDOR_ID, USB_STK1135_PRODUCT_ID),
+ .driver_info = (unsigned long)&stk_model_1135 },
+ { USB_DEVICE(USB_SYNTEK1_VENDOR_ID, USB_STKDCNEW_PRODUCT_ID),
+ .driver_info = (unsigned long)&stk_model_dcnew},
+ { USB_DEVICE(USB_SYNTEK2_VENDOR_ID, USB_DC1125_PRODUCT_ID),
+ .driver_info = (unsigned long)&stk_model_1125 },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, stk11xx_usb_ids);
+
+static struct usb_driver stk11xx_usb_driver = {
+ .name = "stk11xx",
+ .id_table = stk11xx_usb_ids,
+ .probe = stk11xx_usb_probe,
+ .disconnect = stk11xx_usb_disconnect,
+ .suspend = stk11xx_usb_suspend,
+ .resume = stk11xx_usb_resume,
+};
+
+static int __init stk11xx_init(void)
+{
+ int result;
+
+ if (fps < 10 || fps > 30) {
+ printk(KERN_ERR "stk11xx: framerate out of bounds "
+ "[10-30]\n");
+ return -EINVAL;
+ }
+
+ result = usb_register(&stk11xx_usb_driver);
+ if (result)
+ printk(KERN_ERR "stk11xx: usb_register failed!\n");
+
+ return result;
+}
+
+static void __exit stk11xx_exit(void)
+{
+ usb_deregister(&stk11xx_usb_driver);
+}
+
+module_init(stk11xx_init);
+module_exit(stk11xx_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jiri Slaby");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_SUPPORTED_DEVICE("Syntek USB Camera: DC1125, STK1135");
diff --git a/drivers/media/video/stk11xx-v4l.c b/drivers/media/video/stk11xx-v4l.c
new file mode 100644
index 0000000..7f12860
--- /dev/null
+++ b/drivers/media/video/stk11xx-v4l.c
@@ -0,0 +1,790 @@
+/*
+ * STK V4L layer stuff
+ *
+ * Copyright (c) 2007 Jiri Slaby <[email protected]>
+ *
+ * Based on Nicolas VIVIEN's driver
+ *
+ * Licences
+ *
+ * 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.
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+
+#include "stk11xx.h"
+
+/*
+ * helper functions
+ */
+
+static void stk11xx_free_buffers(struct stk11xx *dev);
+
+static int stk11xx_allocate_buffers(struct stk11xx *dev)
+{
+ unsigned int i;
+
+ dev_dbg(&dev->udev->dev, "%s\n", __FUNCTION__);
+
+ /* Allocate isochronous pipe buffers */
+ for (i = 0; i < MAX_ISO_BUFS; i++)
+ if (dev->isobuf[i].data == NULL) {
+ dev->isobuf[i].data = kzalloc(ISO_BUFFER_SIZE,
+ GFP_KERNEL);
+ if (dev->isobuf[i].data == NULL) {
+ dev_err(&dev->udev->dev, "failed to allocate "
+ "iso buffer %d\n", i);
+ goto err;
+ }
+ }
+
+ /* Create frame buffers and make circular ring */
+ for (i = 0; i < STK11XX_FRAMEBUF_COUNT; i++)
+ if (dev->framebuf[i].data == NULL) {
+ dev->framebuf[i].data = vmalloc(STK11XX_FRAME_SIZE);
+ if (dev->framebuf[i].data == NULL) {
+ dev_err(&dev->udev->dev, "failed to allocate "
+ "frame buffer %d\n", i);
+ goto err;
+ }
+ memset(dev->framebuf[i].data, 0, STK11XX_FRAME_SIZE);
+ }
+
+ /* Allocate image buffer; double buffer for mmap() */
+ dev->image_data = vmalloc_32_user(STK11XX_MAX_IMAGES *
+ STK11XX_IMAGE_LEN);
+ if (dev->image_data == NULL) {
+ dev_err(&dev->udev->dev, "failed to allocate image buffer(s). "
+ "needed (%lu)\n", STK11XX_MAX_IMAGES*STK11XX_IMAGE_LEN);
+ goto err;
+ }
+
+ for (i = 0; i < STK11XX_MAX_IMAGES; i++)
+ dev->images[i].offset = i * STK11XX_IMAGE_LEN;
+
+ return 0;
+err:
+ stk11xx_free_buffers(dev);
+ return -ENOMEM;
+}
+
+static void stk11xx_free_buffers(struct stk11xx *dev)
+{
+ unsigned int i;
+
+ dev_dbg(&dev->udev->dev, "%s\n", __FUNCTION__);
+
+ if (WARN_ON(dev == NULL))
+ return;
+
+ /* Release iso pipe buffers */
+ for (i = 0; i < MAX_ISO_BUFS; i++)
+ if (dev->isobuf[i].data != NULL) {
+ kfree(dev->isobuf[i].data);
+ dev->isobuf[i].data = NULL;
+ }
+
+ /* Release frame buffers */
+ for (i = 0; i < STK11XX_FRAMEBUF_COUNT; i++)
+ if (dev->framebuf[i].data != NULL) {
+ vfree(dev->framebuf[i].data);
+ dev->framebuf[i].data = NULL;
+ }
+
+ /* Release image buffers */
+ if (dev->image_data != NULL)
+ vfree(dev->image_data);
+
+ dev->image_data = NULL;
+}
+
+static int stk11xx_reset_buffers(struct stk11xx *dev)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ dev_dbg(&dev->udev->dev, "%s\n", __FUNCTION__);
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ dev->full_frames = NULL;
+ dev->full_frames_tail = NULL;
+
+ for (i = 0; i < STK11XX_MAX_IMAGES; i++) {
+ dev->framebuf[i].filled = 0;
+
+ if (i > 0)
+ dev->framebuf[i].next = &dev->framebuf[i - 1];
+ else
+ dev->framebuf->next = NULL;
+ dev->images[i].flags = 0;
+ }
+
+ dev->empty_frames = &dev->framebuf[STK11XX_MAX_IMAGES - 1];
+ dev->empty_frames_tail = dev->framebuf;
+ dev->read_frame = NULL;
+ dev->fill_frame = dev->empty_frames;
+ dev->empty_frames = dev->empty_frames->next;
+
+ dev->image_read_pos = 0;
+ dev->fill_image = 0;
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return 0;
+}
+
+/*
+ * This function gets called for the isochronous pipe. This function is only
+ * called when a frame is ready. So we have to be fast to decompress the data.
+ */
+static int stk11xx_handle_frame(struct stk11xx *dev)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ STK_STREAM("Sync Handle Frame\n");
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ if (dev->read_frame != NULL) {
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ return ret;
+ }
+
+ if (dev->full_frames != NULL) {
+ dev->read_frame = dev->full_frames;
+ dev->full_frames = dev->full_frames->next;
+ dev->read_frame->next = NULL;
+ }
+
+ if (dev->read_frame != NULL) {
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ ret = stk11xx_decompress(dev);
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ if (dev->empty_frames == NULL) {
+ dev->empty_frames = dev->read_frame;
+ dev->empty_frames_tail = dev->empty_frames;
+ } else {
+ dev->empty_frames_tail->next = dev->read_frame;
+ dev->empty_frames_tail = dev->read_frame;
+ }
+
+ dev->read_frame = NULL;
+ }
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ stk11xx_cam_watchdog(dev);
+
+ return ret;
+}
+
+/* called when an image is ready to prepare the next image */
+static void stk11xx_next_image(struct stk11xx *dev)
+{
+ STK_STREAM("Select next image\n");
+
+ dev->images[dev->fill_image].flags = 0;
+ dev->fill_image = (dev->fill_image + 1) % STK11XX_MAX_IMAGES;
+}
+
+
+/*
+ * fops
+ */
+
+static int stk11xx_open(struct inode *inode, struct file *fp)
+{
+ struct video_device *vdev = video_devdata(fp);
+ struct stk11xx *dev = video_get_drvdata(vdev);
+ int retval;
+
+ BUG_ON(dev == NULL);
+
+ nonseekable_open(inode, fp);
+
+ if (mutex_lock_interruptible(&dev->open_lock))
+ return -ERESTARTSYS;
+
+ if (!test_bit(STK11XX_STAT_PRESENT, dev->status)) {
+ retval = -ENODEV;
+ goto end;
+ }
+
+ if (dev->vopen) {
+ retval = -EBUSY;
+ goto end;
+ }
+
+ retval = stk11xx_allocate_buffers(dev);
+ if (retval < 0) {
+ dev_err(&dev->udev->dev, "failed to allocate buffer memory\n");
+ goto end;
+ }
+
+ stk11xx_reset_buffers(dev);
+
+ dev->vsettings.contrast = 0x7c;
+ dev->vsettings.whiteness = 0x80;
+ dev->vsettings.pixformat = V4L2_PIX_FMT_BGR24;
+
+ stk11xx_select_video_mode(dev, 640, 480);
+
+ dev->model->cam_init(dev);
+ stk11xx_cam_on(dev);
+ dev->model->cam_reconf(dev);
+
+ retval = stk11xx_isoc_init(dev);
+ if (retval) {
+ dev_err(&dev->udev->dev, "failed to init ISOC stuff\n");
+ stk11xx_isoc_cleanup(dev);
+ stk11xx_free_buffers(dev);
+ goto end;
+ }
+
+ dev->model->stream_start(dev);
+ dev->model->cam_setting(dev);
+
+ dev->vopen++;
+ fp->private_data = dev;
+ retval = 0;
+end:
+ mutex_unlock(&dev->open_lock);
+
+ return retval;
+}
+
+static int stk11xx_release(struct inode *inode, struct file *fp)
+{
+ struct stk11xx *dev = fp->private_data;
+ unsigned int free = 0;
+
+ BUG_ON(dev == NULL);
+
+ mutex_lock(&dev->open_lock);
+ if (dev->vopen == 0)
+ dev_warn(&dev->udev->dev, "v4l_release called on closed "
+ "device\n");
+
+ if (--dev->vopen == 0) {
+ dev->model->stream_stop(dev);
+ stk11xx_isoc_cleanup(dev);
+ stk11xx_free_buffers(dev);
+ stk11xx_cam_off(dev);
+ dev->model->cam_asleep(dev);
+ if (!test_bit(STK11XX_STAT_PRESENT, dev->status)) {
+ free++;
+ video_unregister_device(dev->vdev);
+ }
+ }
+ mutex_unlock(&dev->open_lock);
+
+ if (free)
+ kfree(dev);
+
+ return 0;
+}
+
+static ssize_t stk11xx_read(struct file *fp, char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ struct stk11xx *dev = fp->private_data;
+ void *image_buffer_addr;
+ int bytes_to_read, retval;
+
+ STK_STREAM("Read buf=0x%p, count=%zd\n", buf, count);
+
+ if (dev->image_read_pos == 0) {
+ if ((fp->f_flags & O_NONBLOCK) && dev->full_frames == NULL)
+ return -EWOULDBLOCK;
+
+ retval = wait_event_interruptible(dev->wait_frame,
+ dev->full_frames != NULL);
+
+ if (retval)
+ return retval;
+
+ if (stk11xx_handle_frame(dev))
+ return -EFAULT;
+ }
+
+ bytes_to_read = dev->image_size;
+
+ if (count + dev->image_read_pos > bytes_to_read)
+ count = bytes_to_read - dev->image_read_pos;
+
+ image_buffer_addr = dev->image_data;
+ image_buffer_addr += dev->images[dev->fill_image].offset;
+ image_buffer_addr += dev->image_read_pos;
+
+ if (copy_to_user(buf, image_buffer_addr, count))
+ return -EFAULT;
+
+ dev->image_read_pos += count;
+
+ if (dev->image_read_pos >= bytes_to_read) {
+ dev->image_read_pos = 0;
+ stk11xx_next_image(dev);
+ }
+
+ return count;
+}
+
+static unsigned int stk11xx_poll(struct file *fp, poll_table * wait)
+{
+ struct stk11xx *dev = fp->private_data;
+
+ STK_STREAM("Poll\n");
+
+ poll_wait(fp, &dev->wait_frame, wait);
+
+ if (dev->full_frames != NULL)
+ return (POLLIN | POLLRDNORM);
+
+ return 0;
+}
+
+static int stk11xx_mmap(struct file *fp, struct vm_area_struct *vma)
+{
+ struct stk11xx *dev = fp->private_data;
+ unsigned long pos, size = vma->vm_end - vma->vm_start;
+ unsigned int i;
+
+ /* Find the buffer for this mapping... */
+ for (i = 0; i < STK11XX_MAX_IMAGES; i++) {
+ pos = dev->images[i].offset;
+
+ if ((pos >> PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+
+ if (i >= STK11XX_MAX_IMAGES) {
+ dev_err(&dev->udev->dev, "mmap no buffer found\n");
+ return -EINVAL;
+ }
+
+ /* map either whole space or only one buffer! */
+ if (size > STK11XX_IMAGE_LEN && (i > 0 || (i == 0 &&
+ size != STK11XX_MAX_IMAGES*STK11XX_IMAGE_LEN)))
+ return -EINVAL;
+
+ vma->vm_flags |= VM_IO;
+
+ return remap_vmalloc_range(vma, dev->image_data, vma->vm_pgoff);
+}
+
+/*
+ * v4l functions
+ */
+
+static struct v4l2_queryctrl stk11xx_controls[] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7c,
+ },
+ {
+ .id = V4L2_CID_WHITENESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Whiteness",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x80,
+ },
+};
+
+static int stk11xx_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct stk11xx *dev = fh;
+
+ strlcpy(cap->driver, "stk11xx", sizeof(cap->driver));
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ cap->version = DRIVER_VERSION_NUM;
+ strlcpy(cap->card, dev->vdev->name, sizeof(cap->card));
+
+ if (usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)) < 0)
+ strlcpy(cap->bus_info, dev->vdev->name, sizeof(cap->bus_info));
+
+ return 0;
+}
+static int stk11xx_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ if (i->index)
+ return -EINVAL;
+
+ strlcpy(i->name, "USB", sizeof(i->name));
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int stk11xx_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int stk11xx_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return i ? -EINVAL : 0;
+}
+
+static int stk11xx_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *c)
+{
+ unsigned int i;
+
+ pr_debug("VIDIOC_QUERYCTRL id = %d\n", c->id);
+
+ for (i = 0; i < ARRAY_SIZE(stk11xx_controls); i++)
+ if (stk11xx_controls[i].id == c->id) {
+ pr_debug("VIDIOC_QUERYCTRL found\n");
+ memcpy(c, &stk11xx_controls[i], sizeof(*c));
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(stk11xx_controls))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int stk11xx_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+ struct stk11xx *dev = fh;
+
+ dev_dbg(&dev->udev->dev, "GET CTRL id=%d\n", c->id);
+
+ switch (c->id) {
+ case V4L2_CID_CONTRAST:
+ c->value = dev->vsettings.contrast;
+ break;
+ case V4L2_CID_WHITENESS:
+ c->value = dev->vsettings.whiteness;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int stk11xx_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+ struct stk11xx *dev = fh;
+
+ dev_dbg(&dev->udev->dev, "SET CTRL id=%d\n", c->id);
+
+ switch (c->id) {
+ case V4L2_CID_CONTRAST:
+ dev->vsettings.contrast = c->value;
+ break;
+ case V4L2_CID_WHITENESS:
+ dev->vsettings.whiteness = c->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return dev->model->cam_setting(dev) ? -EIO : 0;
+}
+
+static int stk11xx_enum_fmt_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmtd)
+{
+ u32 index = fmtd->index;
+
+ pr_debug("VIDIOC_ENUM_FMT %d\n", index);
+
+ if (index)
+ return -EINVAL;
+
+ fmtd->pixelformat = V4L2_PIX_FMT_BGR24;
+ strcpy(fmtd->description, "rgb24");
+
+ return 0;
+}
+
+static int stk11xx_g_fmt_cap(struct file *file, void *fh,
+ struct v4l2_format *fmtd)
+{
+ struct stk11xx *dev = fh;
+ struct v4l2_pix_format *pix = &fmtd->fmt.pix;
+
+ pix->width = dev->view.x;
+ pix->height = dev->view.y;
+ pix->pixelformat = V4L2_PIX_FMT_BGR24;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = 3 * pix->width;
+ pix->sizeimage = pix->width * pix->height * 3;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ pix->priv = 0;
+
+ return 0;
+}
+
+static int stk11xx_s_fmt_cap(struct file *file, void *fh,
+ struct v4l2_format *fmtd)
+{
+ struct stk11xx *dev = fh;
+ struct v4l2_pix_format *pix = &fmtd->fmt.pix;
+
+ dev_dbg(&dev->udev->dev, "set width=%d, height=%d\n", pix->width,
+ pix->height);
+
+ if (pix->pixelformat && pix->pixelformat != V4L2_PIX_FMT_BGR24)
+ return -EINVAL;
+
+ if (stk11xx_check_image_size(dev, &pix->width, &pix->height))
+ return -EINVAL;
+
+ dev->model->stream_stop(dev);
+ stk11xx_isoc_cleanup(dev);
+ stk11xx_cam_off(dev);
+ dev->model->cam_asleep(dev);
+
+ /* Reset buffers and parameters */
+ stk11xx_reset_buffers(dev);
+
+ if (stk11xx_select_video_mode(dev, pix->width, pix->height)) {
+ dev_err(&dev->udev->dev, "Select video mode failed\n");
+ return -EINVAL;
+ }
+
+ dev->model->cam_init(dev);
+ stk11xx_cam_on(dev);
+ dev->model->cam_reconf(dev);
+ stk11xx_isoc_init(dev);
+ dev->model->stream_start(dev);
+ dev->model->cam_setting(dev);
+
+ return 0;
+}
+
+static int stk11xx_try_fmt_cap(struct file *file, void *fh,
+ struct v4l2_format *fmtd)
+{
+ struct v4l2_pix_format *pix = &fmtd->fmt.pix;
+
+ if (pix->pixelformat != V4L2_PIX_FMT_BGR24)
+ return -EINVAL;
+
+ stk11xx_check_image_size(fh, &pix->width, &pix->height);
+
+ return 0;
+}
+
+static int stk11xx_querystd(struct file *file, void *fh, v4l2_std_id *a)
+{
+ *a = V4L2_STD_ALL;
+ return 0;
+}
+
+static int stk11xx_s_std(struct file *file, void *fh, v4l2_std_id *a)
+{
+ return *a == V4L2_STD_ALL ? 0 : -EINVAL;
+}
+
+static int stk11xx_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *rb)
+{
+ if (rb->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ rb->count = STK11XX_MAX_IMAGES;
+
+ return 0;
+}
+
+static int stk11xx_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct stk11xx *dev = fh;
+ u32 index = buf->index;
+
+ dev_dbg(&dev->udev->dev, "QUERY BUFFERS %d\n", buf->index);
+
+ if (index >= STK11XX_MAX_IMAGES)
+ return -EINVAL;
+
+ memset(buf, 0, sizeof(struct v4l2_buffer));
+
+ buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf->index = index;
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->m.offset = index * STK11XX_IMAGE_LEN;
+ buf->bytesused = dev->image_size;
+ buf->field = V4L2_FIELD_NONE;
+ buf->length = STK11XX_IMAGE_LEN;
+ buf->flags = V4L2_BUF_FLAG_MAPPED | dev->images[index].flags;
+ if (dev->full_frames != NULL)
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+
+ return 0;
+}
+
+static int stk11xx_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct stk11xx *dev = fh;
+
+ dev_dbg(&dev->udev->dev, "VIDIOC_QBUF\n");
+
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (buf->index >= STK11XX_MAX_IMAGES)
+ return -EINVAL;
+
+ if (dev->images[buf->index].flags & V4L2_BUF_FLAG_QUEUED)
+ return -EBUSY;
+
+ dev->images[buf->index].flags |= V4L2_BUF_FLAG_QUEUED;
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ buf->flags &= ~V4L2_BUF_FLAG_DONE;
+
+ return 0;
+}
+
+static int stk11xx_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct stk11xx *dev = fh;
+ int retval;
+
+ dev_dbg(&dev->udev->dev, "VIDIOC_DQBUF\n");
+
+ if (dev->full_frames == NULL && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(dev->wait_frame,
+ dev->full_frames != NULL);
+ if (retval)
+ return retval;
+
+ dev_dbg(&dev->udev->dev, "VIDIOC_DQBUF: frame ready\n");
+
+ retval = stk11xx_handle_frame(dev);
+
+ if (retval)
+ return -EFAULT;
+
+ buf->index = dev->fill_image;
+ buf->bytesused = dev->image_size;
+ buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE;
+ buf->field = V4L2_FIELD_NONE;
+ do_gettimeofday(&buf->timestamp);
+ buf->sequence = 0;
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->m.offset = dev->fill_image * STK11XX_IMAGE_LEN;
+ buf->length = buf->bytesused;
+
+ stk11xx_next_image(dev);
+
+ return 0;
+}
+
+static int stk11xx_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ return i == V4L2_BUF_TYPE_VIDEO_CAPTURE ? stk11xx_isoc_init(fh):-EINVAL;
+}
+
+static int stk11xx_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ return i == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
+ stk11xx_isoc_cleanup(fh), 0 : -EINVAL;
+}
+
+static int stk11xx_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *sp)
+{
+ pr_debug("GET PARM %d\n", sp->type);
+
+ if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp->parm.capture.capability = 0;
+ sp->parm.capture.capturemode = 0;
+ sp->parm.capture.timeperframe.numerator = 1;
+ sp->parm.capture.timeperframe.denominator = 30;
+ sp->parm.capture.readbuffers = 2;
+ sp->parm.capture.extendedmode = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int stk11xx_gmbuf(struct file *file, void *fh, struct video_mbuf *vm)
+{
+ struct stk11xx *dev = fh;
+ unsigned int i;
+
+ dev_dbg(&dev->udev->dev, "VIDIOCGMBUF\n");
+
+ vm->size = STK11XX_MAX_IMAGES * STK11XX_IMAGE_LEN;
+ vm->frames = STK11XX_MAX_IMAGES;
+
+ for (i = 0; i < STK11XX_MAX_IMAGES; i++)
+ vm->offsets[i] = i * STK11XX_IMAGE_LEN;
+
+ return 0;
+}
+#else
+#define stk11xx_gmbuf NULL
+#endif
+
+static struct file_operations stk11xx_fops = {
+ .owner = THIS_MODULE,
+ .open = stk11xx_open,
+ .release = stk11xx_release,
+ .read = stk11xx_read,
+ .poll = stk11xx_poll,
+ .mmap = stk11xx_mmap,
+ .ioctl = video_ioctl2
+};
+
+const struct video_device stk11xx_vdev_template = {
+ .name = DRIVER_DESC,
+
+ .type2 = VID_TYPE_CAPTURE,
+ .tvnorms = V4L2_STD_UNKNOWN,
+ .current_norm = V4L2_STD_UNKNOWN,
+ .fops = &stk11xx_fops,
+ .release = video_device_release,
+ .minor = -1,
+
+ .vidioc_querycap = stk11xx_querycap,
+ .vidioc_enum_input = stk11xx_enum_input,
+ .vidioc_g_input = stk11xx_g_input,
+ .vidioc_s_input = stk11xx_s_input,
+ .vidioc_queryctrl = stk11xx_queryctrl,
+ .vidioc_g_ctrl = stk11xx_g_ctrl,
+ .vidioc_s_ctrl = stk11xx_s_ctrl,
+ .vidioc_enum_fmt_cap = stk11xx_enum_fmt_cap,
+ .vidioc_g_fmt_cap = stk11xx_g_fmt_cap,
+ .vidioc_s_fmt_cap = stk11xx_s_fmt_cap,
+ .vidioc_try_fmt_cap = stk11xx_try_fmt_cap,
+ .vidioc_querystd = stk11xx_querystd,
+ .vidioc_s_std = stk11xx_s_std,
+ .vidioc_reqbufs = stk11xx_reqbufs,
+ .vidioc_querybuf = stk11xx_querybuf,
+ .vidioc_qbuf = stk11xx_qbuf,
+ .vidioc_dqbuf = stk11xx_dqbuf,
+ .vidioc_streamon = stk11xx_streamon,
+ .vidioc_streamoff = stk11xx_streamoff,
+ .vidioc_g_parm = stk11xx_g_parm,
+
+ .vidiocgmbuf = stk11xx_gmbuf,
+};
diff --git a/drivers/media/video/stk11xx.h b/drivers/media/video/stk11xx.h
new file mode 100644
index 0000000..3d9dc2f
--- /dev/null
+++ b/drivers/media/video/stk11xx.h
@@ -0,0 +1,217 @@
+/*
+ * common include file
+ *
+ * Copyright (c) 2007 Jiri Slaby <[email protected]>
+ *
+ * Based on Nicolas VIVIEN's driver
+ *
+ * Licences
+ *
+ * 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.
+ *
+ */
+
+#ifndef STK11XX_H
+#define STK11XX_H
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/wait.h>
+#include <media/v4l2-common.h>
+
+#define DRIVER_DESC "Syntek USB Video Camera"
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_VERSION_NUM KERNEL_VERSION(0, 1, 0)
+
+/**
+ * @def MAX_ISO_BUFS
+ * Number maximal of ISOC buffers
+ *
+ * @def ISO_FRAMES_PER_DESC
+ * Number frames per ISOC descriptor
+ *
+ * @def ISO_MAX_FRAME_SIZE
+ * Maximale size of frame
+ *
+ * @def ISO_BUFFER_SIZE
+ * Maximal size of buffer
+ */
+#define MAX_ISO_BUFS 16 /* 2 */
+#define ISO_FRAMES_PER_DESC 10
+#define ISO_MAX_FRAME_SIZE 3 * 1024
+#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
+
+/**
+ * @def STK11XX_FRAME_SIZE
+ * Maximum size after decompression
+ */
+#define STK11XX_MAX_IMAGES 2
+#define STK11XX_FRAME_SIZE (1280 * 1024)
+#define STK11XX_IMAGE_LEN PAGE_ALIGN(1280 * 1024 * 3)
+#define STK11XX_FRAMEBUF_COUNT 3
+
+#ifndef STK11XX_DEBUG_STREAM
+#define STK11XX_DEBUG_STREAM 0
+#endif
+
+#if STK11XX_DEBUG_STREAM
+#define STK_STREAM(str, args...) printk(KERN_DEBUG "stk11xx: " str, \
+ ##args)
+#else
+#define STK_STREAM(str, args...) do { } while (0)
+#endif
+
+enum {
+ SYNTEK_STK1125 = 1,
+ SYNTEK_STK1135 = 2,
+ SYNTEK_STKDCNEW = 3
+};
+
+enum {
+ STK11XX_80x60,
+ STK11XX_160x120,
+ STK11XX_320x240,
+ STK11XX_640x480,
+ STK11XX_800x600,
+ STK11XX_1024x768,
+ STK11XX_1280x1024,
+ STK11XX_NBR_SIZES
+};
+
+struct stk11xx_table {
+ u16 addr;
+ u8 val;
+ u8 type;
+};
+
+struct stk11xx_iso_buf {
+ void *data;
+ struct urb *urb;
+};
+
+struct stk11xx_frame_buf {
+ void *data;
+ unsigned int filled;
+ struct stk11xx_frame_buf *next;
+};
+
+struct stk11xx_image_buf {
+ unsigned long offset;
+ u32 flags;
+};
+
+struct stk11xx_coord {
+ unsigned int x;
+ unsigned int y;
+};
+
+struct stk11xx_video {
+ unsigned int fps;
+ int contrast;
+ int whiteness;
+ u32 pixformat;
+};
+
+struct stk11xx;
+
+struct stk_model {
+ unsigned int model;
+ char *name;
+ /*
+ * It's the start. This function has to be called at first, before
+ * enabling the video stream.
+ */
+ int (*dev_init)(struct stk11xx *);
+ int (*dev_configure)(struct stk11xx *, unsigned int);
+
+ int (*cam_init)(struct stk11xx *);
+ int (*cam_setting)(struct stk11xx *);
+ int (*cam_asleep)(struct stk11xx *);
+ /* Before enabling the video stream, you have to reconfigure the
+ * device */
+ int (*cam_reconf)(struct stk11xx *);
+
+ /*
+ * After the initialization of the device and the initialization of
+ * the video stream, this function enables the stream.
+ */
+ int (*stream_start)(struct stk11xx *);
+ int (*stream_stop)(struct stk11xx *);
+};
+
+/* flags used in struct stk11xx.status */
+#define STK11XX_STAT_PRESENT 0
+#define STK11XX_STAT_ISOC 1
+
+struct stk11xx {
+ struct video_device *vdev;
+ struct usb_device *udev;
+
+ struct stk_model *model;
+
+ u8 isoc_in_endpointAddr; /**< Isochrone IN endpoint address */
+
+ struct stk11xx_video vsettings; /**< Video settings (contrast, whiteness...) */
+
+ struct mutex open_lock;
+ unsigned int vopen;
+ DECLARE_BITMAP(status, 16);
+
+ spinlock_t spinlock;
+ wait_queue_head_t wait_frame;
+
+ /* isoc */
+ struct stk11xx_iso_buf isobuf[MAX_ISO_BUFS];
+
+ /* frame */
+ unsigned int frame_size;
+ struct stk11xx_frame_buf framebuf[STK11XX_FRAMEBUF_COUNT];
+ struct stk11xx_frame_buf *empty_frames, *empty_frames_tail;
+ struct stk11xx_frame_buf *full_frames, *full_frames_tail;
+ struct stk11xx_frame_buf *fill_frame;
+ struct stk11xx_frame_buf *read_frame;
+
+ /* image */
+ unsigned int image_size;
+ void *image_data;
+ struct stk11xx_image_buf images[STK11XX_MAX_IMAGES];
+ int image_read_pos;
+ int fill_image;
+ unsigned int resolution;
+ struct stk11xx_coord view;
+};
+
+extern struct stk_model stk_model_1125, stk_model_1135, stk_model_dcnew;
+extern const struct video_device stk11xx_vdev_template;
+
+extern int stk11xx_decompress(struct stk11xx *);
+extern int stk11xx_check_image_size(struct stk11xx *, unsigned int *,
+ unsigned int *);
+extern int stk11xx_select_video_mode(struct stk11xx *, int, int);
+
+extern int stk11xx_isoc_init(struct stk11xx *);
+extern void stk11xx_isoc_cleanup(struct stk11xx *);
+
+extern int stk11xx_read_reg(struct stk11xx *, u16, u8 *);
+extern int stk11xx_write_reg(struct stk11xx *, u16, u16);
+extern int stk11xx_process_table(struct stk11xx *,
+ const struct stk11xx_table *);
+extern int stk11xx_check_device(struct stk11xx *, unsigned int);
+extern int stk11xx_set_feature(struct stk11xx *, int);
+
+extern int stk11xx_cam_on(struct stk11xx *);
+extern int stk11xx_cam_off(struct stk11xx *);
+extern int stk11xx_cam_watchdog(struct stk11xx *);
+
+static inline int stk11xx_read_dummy(struct stk11xx *dev, u16 index)
+{
+ u8 value;
+
+ return stk11xx_read_reg(dev, index, &value);
+}
+
+#endif
diff --git a/drivers/media/video/stkdcnew.c b/drivers/media/video/stkdcnew.c
new file mode 100644
index 0000000..52513e2
--- /dev/null
+++ b/drivers/media/video/stkdcnew.c
@@ -0,0 +1,669 @@
+/*
+ * STK-6A31 part
+ *
+ * Copyright (c) 2007 Jiri Slaby <[email protected]>
+ *
+ * Based on Nicolas VIVIEN's driver
+ *
+ * Licences
+ *
+ * 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.
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/types.h>
+
+#include "stk11xx.h"
+
+/*
+ * WARNING, the microcode can be different following the situation.
+ */
+static int stkdcnew_load_microcode(struct stk11xx *dev)
+{
+ unsigned int i;
+ int retok;
+
+ const u8 values_204[] = {
+ 0xf0, 0xf1, 0x0d, 0xf1, 0x0d, 0xf1, 0xf0, 0xf1, 0x35, 0xf1,
+ 0xf0, 0xf1, 0x06, 0xf1, 0xf0, 0xf1, 0xdd, 0xf1, 0xf0, 0xf1,
+ 0x1f, 0xf1, 0x20, 0xf1, 0x21, 0xf1, 0x22, 0xf1, 0x23, 0xf1,
+ 0x24, 0xf1, 0x28, 0xf1, 0x29, 0xf1, 0x5e, 0xf1, 0x5f, 0xf1,
+ 0x60, 0xf1, 0xef, 0xf1, 0xf2, 0xf1, 0x02, 0xf1, 0x03, 0xf1,
+ 0x04, 0xf1, 0x09, 0xf1, 0x0a, 0xf1, 0x0b, 0xf1, 0x0c, 0xf1,
+ 0x0d, 0xf1, 0x0e, 0xf1, 0x0f, 0xf1, 0x10, 0xf1, 0x11, 0xf1,
+ 0x15, 0xf1, 0x16, 0xf1, 0x17, 0xf1, 0x18, 0xf1, 0x19, 0xf1,
+ 0x1a, 0xf1, 0x1b, 0xf1, 0x1c, 0xf1, 0x1d, 0xf1, 0x1e, 0xf1,
+ 0xf0, 0xf1, 0x06, 0xf1, 0x06, 0xf1, 0xf0, 0xf1, 0x80, 0xf1,
+ 0x81, 0xf1, 0x82, 0xf1, 0x83, 0xf1, 0x84, 0xf1, 0x85, 0xf1,
+ 0x86, 0xf1, 0x87, 0xf1, 0x88, 0xf1, 0x89, 0xf1, 0x8a, 0xf1,
+ 0x8b, 0xf1, 0x8c, 0xf1, 0x8d, 0xf1, 0x8e, 0xf1, 0x8f, 0xf1,
+ 0x90, 0xf1, 0x91, 0xf1, 0x92, 0xf1, 0x93, 0xf1, 0x94, 0xf1,
+ 0x95, 0xf1, 0xb6, 0xf1, 0xb7, 0xf1, 0xb8, 0xf1, 0xb9, 0xf1,
+ 0xba, 0xf1, 0xbb, 0xf1, 0xbc, 0xf1, 0xbd, 0xf1, 0xbe, 0xf1,
+ 0xbf, 0xf1, 0xc0, 0xf1, 0xc1, 0xf1, 0xc2, 0xf1, 0xc3, 0xf1,
+ 0xc4, 0xf1, 0x06, 0xf1, 0xf0, 0xf1, 0x53, 0xf1, 0x54, 0xf1,
+ 0x55, 0xf1, 0x56, 0xf1, 0x57, 0xf1, 0x58, 0xf1, 0xdc, 0xf1,
+ 0xdd, 0xf1, 0xde, 0xf1, 0xdf, 0xf1, 0xe0, 0xf1, 0xe1, 0xf1,
+ 0xf0, 0xf1, 0xa7, 0xf1, 0xaa, 0xf1, 0x3a, 0xf1, 0xa1, 0xf1,
+ 0xa4, 0xf1, 0x9b, 0xf1, 0x08, 0xf1, 0xf0, 0xf1, 0x2f, 0xf1,
+ 0x9c, 0xf1, 0xd2, 0xf1, 0xcc, 0xf1, 0xcb, 0xf1, 0x2e, 0xf1,
+ 0x67, 0xf1, 0xf0, 0xf1, 0x65, 0xf1, 0x66, 0xf1, 0x67, 0xf1,
+ 0x65, 0xf1, 0xf0, 0xf1, 0x05, 0xf1, 0x07, 0xf1, 0xf0, 0xf1,
+ 0x39, 0xf1, 0x3b, 0xf1, 0x3a, 0xf1, 0x3c, 0xf1, 0x57, 0xf1,
+ 0x58, 0xf1, 0x59, 0xf1, 0x5a, 0xf1, 0x5c, 0xf1, 0x5d, 0xf1,
+ 0x64, 0xf1, 0xf0, 0xf1, 0x5b, 0xf1, 0xf0, 0xf1, 0x36, 0xf1,
+ 0x37, 0xf1, 0xf0, 0xf1, 0x08, 0xf1
+ };
+ const u8 values_205[] = {
+ 0x00, 0x00, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x00, 0x00, 0x22,
+ 0x00, 0x01, 0x70, 0x0e, 0x00, 0x02, 0x18, 0xe0, 0x00, 0x02,
+ 0x01, 0x80, 0xc8, 0x14, 0x80, 0x80, 0xa0, 0x78, 0xa0, 0x78,
+ 0x5f, 0x20, 0xea, 0x02, 0x86, 0x7a, 0x59, 0x4c, 0x4d, 0x51,
+ 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0xee, 0x39, 0x23,
+ 0x07, 0x24, 0x00, 0xcd, 0x00, 0x93, 0x00, 0x04, 0x00, 0x5c,
+ 0x00, 0xd9, 0x00, 0x53, 0x00, 0x08, 0x00, 0x91, 0x00, 0xcf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0xf0, 0x0e, 0x70, 0x0e, 0x00, 0x01, 0x00, 0x07,
+ 0xde, 0x13, 0xeb, 0xe2, 0x00, 0xf6, 0xe1, 0x14, 0xea, 0xdd,
+ 0xfd, 0xf6, 0xe5, 0x11, 0xed, 0xe6, 0xfb, 0xf7, 0xd6, 0x13,
+ 0xed, 0xec, 0xf9, 0xf2, 0x00, 0x00, 0xd8, 0x15, 0xe9, 0xea,
+ 0xf9, 0xf1, 0x00, 0x02, 0xde, 0x10, 0xef, 0xef, 0xfb, 0xf4,
+ 0x00, 0x02, 0x0e, 0x06, 0x27, 0x13, 0x11, 0x06, 0x27, 0x13,
+ 0x0c, 0x03, 0x2a, 0x0f, 0x12, 0x08, 0x1a, 0x16, 0x00, 0x22,
+ 0x15, 0x0a, 0x1c, 0x1a, 0x00, 0x2d, 0x11, 0x09, 0x14, 0x14,
+ 0x00, 0x2a, 0x74, 0x0e, 0x00, 0x01, 0x0b, 0x03, 0x47, 0x22,
+ 0xac, 0x82, 0xda, 0xc7, 0xf5, 0xe9, 0xff, 0x00, 0x0b, 0x03,
+ 0x47, 0x22, 0xac, 0x82, 0xda, 0xc7, 0xf5, 0xe9, 0xff, 0x00,
+ 0x00, 0x01, 0x02, 0x80, 0x01, 0xe0, 0x43, 0x00, 0x05, 0x00,
+ 0x04, 0x00, 0x43, 0x00, 0x01, 0x80, 0x00, 0x02, 0xd1, 0x00,
+ 0xd1, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x0c, 0x3c,
+ 0x10, 0x10, 0x00, 0x00, 0xa0, 0x00, 0x20, 0x03, 0x05, 0x01,
+ 0x20, 0x00, 0x00, 0x00, 0x01, 0xb8, 0x00, 0xd8, 0x00, 0x02,
+ 0x06, 0xc0, 0x04, 0x0e, 0x06, 0xc0, 0x05, 0x64, 0x02, 0x08,
+ 0x02, 0x71, 0x02, 0x09, 0x02, 0x71, 0x12, 0x0d, 0x17, 0x12,
+ 0x5e, 0x1c, 0x00, 0x02, 0x00, 0x03, 0x00, 0x02, 0x78, 0x10,
+ 0x83, 0x04, 0x00, 0x00, 0x00, 0x21
+ };
+
+
+ for (i = 0; i < ARRAY_SIZE(values_204); i++) {
+ stk11xx_read_dummy(dev, 0x02ff);
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+
+ stk11xx_write_reg(dev, 0x0203, 0xba);
+
+ stk11xx_write_reg(dev, 0x0204, values_204[i]);
+ stk11xx_write_reg(dev, 0x0205, values_205[i]);
+ stk11xx_write_reg(dev, 0x0200, 0x01);
+
+ retok = stk11xx_check_device(dev, 500);
+
+ if (retok != 1) {
+ dev_err(&dev->udev->dev, "load microcode fail!\n");
+ return -EIO;
+ }
+
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+ }
+
+ stk11xx_check_device(dev, 500);
+
+ return 0;
+}
+
+/*
+ * We don't know the meaning of these steps ! We only replay the USB log.
+ */
+static int stkdcnew_dev_configure(struct stk11xx *dev, unsigned int step)
+{
+ int ret;
+
+ /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16 */
+ const u8 vals_001B[] = {
+ 0x03, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07
+ };
+ const u8 vals_001C[] = {
+ 0x02, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06
+ };
+ const u8 vals_0202[] = {
+ 0x0a, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x0a, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f
+ };
+ const u8 vals_0110[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const u8 vals_0112[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const u8 vals_0114[] = {
+ 0x80, 0x80, 0x80, 0x80, 0x00, 0xbe, 0x80, 0x80, 0x00, 0x80,
+ 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80
+ };
+ const u8 vals_0115[] = {
+ 0x02, 0x02, 0x02, 0x02, 0x05, 0x02, 0x02, 0x02, 0x00, 0x02,
+ 0x02, 0x02, 0x05, 0x02, 0x02, 0x02, 0x02
+ };
+ const u8 vals_0116[] = {
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe9, 0xe0, 0xe0, 0x00, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0
+ };
+ const u8 vals_0117[] = {
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+ };
+ const u8 vals_0100[] = {
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
+ };
+ struct stk11xx_table table_1[] = {
+ { 0x0000, 0x24, 2 }, { 0x0002, 0x78, 2 }, { 0x0003, 0x80, 2 },
+ { 0x0005, 0x00, 2 }, { 0x0007, 0x03, 2 }, { 0x000d, 0x00, 2 },
+ { 0x000f, 0x02, 2 }, { 0x0300, 0x12, 2 }, { 0x0350, 0x41, 2 },
+ { 0x0351, 0x00, 2 }, { 0x0352, 0x00, 2 }, { 0x0353, 0x00, 2 },
+ { 0x0018, 0x10, 2 }, { 0x0019, 0x00, 2 },
+ { 0x001b, vals_001B[step], 2 }, { 0x001c, vals_001C[step], 2 },
+ { 0x0300, 0x80, 2 }, { 0x001a, 0x04, 2 },
+ { 0x0202, vals_0202[step], 2 }, { 0x0110, vals_0110[step], 2 },
+ { 0x0111, 0x00, 2 }, { 0x0112, vals_0112[step], 2 },
+ { 0x0113, 0x00, 2 }, { 0x0114, vals_0114[step], 2 },
+ { 0x0115, vals_0115[step], 2 }, { 0x0116, vals_0116[step], 2 },
+ { 0x0117, vals_0117[step], 2 }, { 0x0100, 0x00, 1 },
+ { 0x0100, vals_0100[step], 2 },/* { 0x0200, 0x80, 2 },
+ { 0x0200, 0x00, 2 }, */{ 0x02ff, 0x00, 2 },
+ { }
+ };
+
+ dev_dbg(&dev->udev->dev, "%s: %u\n", __func__, step);
+
+ ret = stk11xx_process_table(dev, table_1);
+ if (ret)
+ goto end;
+
+ switch (step) {
+ case 0: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x22, 2 }, { 0x0204, 0x27, 2 },
+ { 0x0205, 0xa5, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 1: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xbf, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 2: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x42, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x24, 2 },
+ { 0x0205, 0xa5, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 3: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x42, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xe0, 2 }, { 0x0204, 0x24, 2 },
+ { 0x0205, 0xa5, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 4: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x42, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xbf, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 5: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xff, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 6: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xB7, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+ break;
+ }
+ case 7: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x0204, 0x12, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0204, 0x13, 2 },
+ { 0x0205, 0xb7, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 8: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0x60, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0x60, 2 },
+ { 0x0204, 0xff, 2 }, { 0x0205, 0x01, 2 },
+ { 0x0200, 0x01, 2 }, { 500, 0x00, 3 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0x60, 2 },
+ { 0x0208, 0x0a, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0x60, 2 },
+ { 0x0208, 0x0b, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0x60, 2 },
+ { 0x0208, 0x1c, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0x60, 2 },
+ { 0x0208, 0x1d, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 },
+ { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 9: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0xdc, 2 }, { 0x0204, 0x15, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 10: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0xec, 2 }, { 0x0204, 0x15, 2 },
+ { 0x0205, 0x80, 2 }, { 0x0200, 0x05, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 11: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0xba, 2 }, { 0x0208, 0x00, 2 },
+ { 0x0200, 0x20, 2 }, { 500, 0x00, 3 },
+ { 0x0209, 0x00, 1 }, { 0x02ff, 0x00, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 12: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0xba, 2 }, { 0x0204, 0xf0, 2 },
+ { 0x0205, 0x00, 2 }, { 0x0200, 0x05, 2 },
+ { 500, 0x00, 3 }, { 0x0204, 0xf1, 2 },
+ { 0x0205, 0x00, 2 }, { 0x0200, 0x05, 2 },
+ { 500, 0x00, 3 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0xba, 2 },
+ { 0x0208, 0x00, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0xba, 2 },
+ { 0x0208, 0xf1, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 13: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0xba, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0xba, 2 },
+ { 0x0208, 0x00, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0xba, 2 },
+ { 0x0208, 0xf1, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 14: {
+ const struct stk11xx_table table[] = {
+ { 0x0203, 0xba, 2 }, { 0x0204, 0x01, 2 },
+ { 0x0205, 0x00, 2 }, { 0x0200, 0x05, 2 },
+ { 500, 0x00, 3 }, { 0x0204, 0xf1, 2 },
+ { 0x0205, 0x00, 2 }, { 0x0200, 0x05, 2 },
+ { 500, 0x00, 3 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0xba, 2 },
+ { 0x0208, 0x00, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x02ff, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { 0x0203, 0xba, 2 },
+ { 0x0208, 0xf1, 2 }, { 0x0200, 0x20, 2 },
+ { 500, 0x00, 3 }, { 0x0209, 0x00, 1 },
+ { 0x02ff, 0x00, 2 }, { }
+ };
+ ret = stk11xx_process_table(dev, table);
+
+ break;
+ }
+ case 15:
+ stk11xx_write_reg(dev, 0x0203, 0xba);
+
+ ret = stkdcnew_load_microcode(dev);
+ if (ret)
+ goto end;
+
+ stk11xx_write_reg(dev, 0x0200, 0x80);
+ stk11xx_write_reg(dev, 0x0200, 0x00);
+ stk11xx_write_reg(dev, 0x02ff, 0x01);
+ stk11xx_write_reg(dev, 0x0203, 0xa0);
+
+
+ break;
+
+ case 16:
+ stk11xx_write_reg(dev, 0x0203, 0xba);
+
+ ret = stkdcnew_load_microcode(dev);
+
+ break;
+ }
+end:
+ return ret;
+}
+
+static int stkdcnew_cam_asleep(struct stk11xx *dev)
+{
+ const struct stk11xx_table table[] = {
+ { 0x0104, 0x00, 1 }, { 0x0105, 0x00, 1 }, { 0x0106, 0x00, 1 },
+ { 0x0100, 0x21, 2 }, { 0x0116, 0x00, 2 }, { 0x0117, 0x00, 2 },
+ { 0x0018, 0x00, 2 }, { 0x0000, 0x00, 1 }, { 0x0000, 0x49, 2 },
+ { }
+ };
+
+ return stk11xx_process_table(dev, table);
+}
+
+static int stkdcnew_cam_init(struct stk11xx *dev)
+{
+ stkdcnew_cam_asleep(dev);
+
+ stk11xx_set_feature(dev, 0);
+
+ stkdcnew_cam_asleep(dev);
+
+ stk11xx_write_reg(dev, 0x0000, 0xe0);
+ stk11xx_write_reg(dev, 0x0002, 0xf8);
+ stk11xx_write_reg(dev, 0x0002, 0x78);
+ stk11xx_write_reg(dev, 0x0000, 0x20);
+
+ stkdcnew_dev_configure(dev, 15);
+
+ stk11xx_cam_off(dev);
+
+ return 0;
+}
+
+/**
+ * This functions permits to modify the settings :
+ * - brightness
+ * - contrast
+ * - white balance
+ * - ...
+ */
+static int stkdcnew_cam_setting(struct stk11xx *dev)
+{
+ unsigned int i;
+ int ret;
+
+ const u8 values_204[] = {
+ 0xf0, 0xf1, 0x2e, 0xf1, 0xf0, 0xf1, 0x5b, 0xf1, 0xf0, 0xf1,
+ 0x36, 0xf1, 0x37, 0xf1, 0xf0, 0xf1, 0x08, 0xf1
+ };
+ const u8 values_205[] = {
+ 0x00, 0x02, 0x0c, 0x3c, 0x00, 0x02, 0x00, 0x03, 0x00, 0x02,
+ 0x78, 0x10, 0x83, 0x04, 0x00, 0x00, 0x00, 0x21
+ };
+
+ /* Contrast register */
+ stk11xx_read_dummy(dev, 0x02ff);
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+
+ stk11xx_write_reg(dev, 0x0204, 0xb3);
+ stk11xx_write_reg(dev, 0x0205, (dev->vsettings.contrast >> 8));
+
+ stk11xx_write_reg(dev, 0x0200, 0x01);
+ ret = stk11xx_check_device(dev, 500);
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+
+ for (i = 0; i < ARRAY_SIZE(values_204); i++) {
+ stk11xx_read_dummy(dev, 0x02ff);
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+
+ stk11xx_write_reg(dev, 0x0204, values_204[i]);
+ stk11xx_write_reg(dev, 0x0205, values_205[i]);
+
+ stk11xx_write_reg(dev, 0x0200, 0x01);
+ ret = stk11xx_check_device(dev, 500);
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+ }
+
+ dev_dbg(&dev->udev->dev, "set contrast: %d, whiteness: %d\n",
+ dev->vsettings.contrast, dev->vsettings.whiteness);
+
+ return 0;
+}
+
+static int stkdcnew_stream_start(struct stk11xx *dev)
+{
+ u8 value_116, value_117;
+
+ stk11xx_read_dummy(dev, 0x0114); /* read 0x80 */
+ stk11xx_read_dummy(dev, 0x0115); /* read 0x02 */
+
+ stk11xx_read_reg(dev, 0x0116, &value_116);
+ stk11xx_read_reg(dev, 0x0117, &value_117);
+
+ stk11xx_write_reg(dev, 0x0116, 0x00);
+ stk11xx_write_reg(dev, 0x0117, 0x00);
+
+ stk11xx_read_dummy(dev, 0x0100); /* read 0x21 */
+ stk11xx_write_reg(dev, 0x0100, 0xa0);
+
+ stk11xx_write_reg(dev, 0x0116, value_116);
+ stk11xx_write_reg(dev, 0x0117, value_117);
+
+ return 0;
+}
+
+static int stkdcnew_cam_reconf(struct stk11xx *dev)
+{
+ stkdcnew_dev_configure(dev, 16);
+
+ stkdcnew_cam_setting(dev);
+
+ return 0;
+}
+
+static int stkdcnew_stream_stop(struct stk11xx *dev)
+{
+ return 0;
+}
+
+/*
+ * This function is written from the USB log.
+ */
+static int stkdcnew_dev_init(struct stk11xx *dev)
+{
+/* unsigned int i;
+ int value;*/
+ int ret;
+
+/* const struct stk11xx_table table_1[] = {
+ { 0x0000, 0x24, 2 }, { 0x0002, 0x78, 2 }, { 0x0003, 0x80, 2 },
+ { 0x0002, 0x7f, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x26, 2 }, { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x25, 2 }, { 0x0002, 0x7d, 2 },
+ { 0x0000, 0x24, 2 },
+ { }
+ };
+ const struct stk11xx_table table_2[] = {
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 }, { 0x0002, 0x7f, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0002, 0x7d, 2 }, { 0x0000, 0x24, 2 },
+ { }
+ };
+ const struct stk11xx_table table_3[] = {
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 }, { 0x0002, 0x7f, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0000, 0x24, 2 }, { 0x0000, 0x26, 2 },
+ { 0x0000, 0x27, 2 }, { 0x0000, 0x26, 2 }, { 0x0000, 0x24, 2 },
+ { 0x0000, 0x25, 2 }, { 0x0002, 0x7d, 2 }, { 0x0000, 0x24, 2 },
+ { }
+ };*/
+ const struct stk11xx_table table_4[] = {
+/* { 0x0000, 0x25, 2 }, { 0x0000, 0x20, 2 }, { 0x0002, 0x7f, 2 },
+ { 0x0000, 0x24, 2 }, { 0x0000, 0x20, 2 }, { 0x0117, 0x00, 2 },
+ { 0x0103, 0x00, 1 }, { 0x0103, 0x01, 2 }, { 0x0103, 0x00, 1 },
+ { 0x0103, 0x00, 2 },*/ { 0x0000, 0xe0, 2 }, { 0x0002, 0xf8, 2 },
+ { 0x0002, 0x78, 2 }, { 0x0000, 0x20, 2 },
+ { 0, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 1, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 2, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 3, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 4, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 5, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 6, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 7, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 8, 0x00, 4 },
+ { 9, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 10, 0x00, 4 }, { 65, 0x00, 3 }, { 0x0200, 0x08, 2 },
+ { 11, 0x00, 4 }, { 12, 0x00, 4 }, { 13, 0x00, 4 },
+ { 14, 0x00, 4 },
+ { }
+ };
+#if 0
+ ret = stk11xx_process_table(dev, table_1);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < 16; i++) {
+ stk11xx_write_reg(dev, 0x0000, 0x25);
+ stk11xx_write_reg(dev, 0x0000, 0x24);
+ stk11xx_read_reg(dev, 0x0000, &value);
+
+ dev_dbg(&dev->udev->dev, "loop 1: Read 0x0000 = %02X\n", value);
+ }
+
+ ret = stk11xx_process_table(dev, table_2);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < 16; i++) {
+ stk11xx_write_reg(dev, 0x0000, 0x25);
+ stk11xx_write_reg(dev, 0x0000, 0x24);
+ stk11xx_read_reg(dev, 0x0000, &value);
+
+ dev_dbg(&dev->udev->dev, "loop 2: Read 0x0000 = %02X\n", value);
+ }
+
+ ret = stk11xx_process_table(dev, table_3);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < 16; i++) {
+ stk11xx_write_reg(dev, 0x0000, 0x25);
+ stk11xx_write_reg(dev, 0x0000, 0x24);
+ stk11xx_read_reg(dev, 0x0000, &value);
+
+ dev_dbg(&dev->udev->dev, "loop 3: Read 0x0000 = %02X\n", value);
+ }
+#endif
+ ret = stk11xx_process_table(dev, table_4);
+ if (ret)
+ goto end;
+
+
+ stkdcnew_cam_asleep(dev);
+
+ stk11xx_set_feature(dev, 0);
+
+end:
+ return ret;
+}
+
+struct stk_model stk_model_dcnew = {
+ .model = SYNTEK_STKDCNEW,
+ .name = "DCNEW",
+ .dev_init = stkdcnew_dev_init,
+ .dev_configure = stkdcnew_dev_configure,
+ .cam_init = stkdcnew_cam_init,
+ .cam_setting = stkdcnew_cam_setting,
+ .cam_asleep = stkdcnew_cam_asleep,
+ .cam_reconf = stkdcnew_cam_reconf,
+ .stream_start = stkdcnew_stream_start,
+ .stream_stop = stkdcnew_stream_stop,
+};


2007-08-27 22:41:01

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 1/1] V4L: stk11xx, add a new webcam driver

On Sun, 26 Aug 2007 07:09:02 -0700
Jiri Slaby <[email protected]> wrote:

> Hi,
>
> is it possible to have this driver in the -mm tree for testing purposes until
> v4l library will be developped and image resize with bayer->rgb conversion
> will be moved there? (Then, I'll push it through v4l people.)
>
> --
> stk11xx, add a new webcam driver
>
> Adds support for stk1125, stk1135 and stkdcnew webcam built-in some
> notebooks.
>
> ...
>
> --- /dev/null
> +++ b/drivers/media/video/stk1125.c
> @@ -0,0 +1,667 @@
> +/*
> + * STK-1125 part
> + *
> + * Copyright (c) 2007 Jiri Slaby <[email protected]>
> + *
> + * Based on Nicolas VIVIEN's driver
> + *
> + * Licences
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/usb.h>
> +#include <linux/types.h>
> +
> +#include "stk11xx.h"
> +
> +static int stk1125_load_microcode(struct stk11xx *dev)
> +{
> + unsigned int i;
> + const u8 *values_204, *values_205;
> + int retok;
> +
> + /* From 80x60 to 640x480 */
> + const u8 values_1_204[] = {
> + 0x12, 0x11, 0x3b, 0x6a, 0x13, 0x10, 0x00, 0x01, 0x02, 0x13,
> + 0x39, 0x38, 0x37, 0x35, 0x0e, 0x12, 0x04, 0x0c, 0x0d, 0x17,
> + 0x18, 0x32, 0x19, 0x1a, 0x03, 0x1b, 0x16, 0x33, 0x34, 0x41,
> + 0x96, 0x3d, 0x69, 0x3a, 0x8e, 0x3c, 0x8f, 0x8b, 0x8c, 0x94,
> + 0x95, 0x40, 0x29, 0x0f, 0xa5, 0x1e, 0xa9, 0xaa, 0xab, 0x90,
> + 0x91, 0x9f, 0xa0, 0x24, 0x25, 0x26, 0x14, 0x2a, 0x2b
> + };
> + const u8 values_1_205[] = {
> + 0x45, 0x80, 0x01, 0x7d, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
> + 0x50, 0x93, 0x00, 0x81, 0x20, 0x45, 0x00, 0x00, 0x00, 0x24,
> + 0xc4, 0xb6, 0x00, 0x3c, 0x36, 0x00, 0x07, 0xe2, 0xbf, 0x00,
> + 0x04, 0x19, 0x40, 0x0d, 0x00, 0x73, 0xdf, 0x06, 0x20, 0x88,
> + 0x88, 0xc1, 0x3f, 0x42, 0x80, 0x04, 0xb8, 0x92, 0x0a, 0x00,
> + 0x00, 0x00, 0x00, 0x68, 0x5c, 0xc3, 0x2e, 0x00, 0x00
> + };
> +
> + /* From 800x600 to 1280x1024 */
> + const u8 values_2_204[] = {
> + 0x12, 0x11, 0x3b, 0x6a, 0x13, 0x10, 0x00, 0x01, 0x02, 0x13,
> + 0x39, 0x38, 0x37, 0x35, 0x0e, 0x12, 0x04, 0x0c, 0x0d, 0x17,
> + 0x18, 0x32, 0x19, 0x1a, 0x03, 0x1b, 0x16, 0x33, 0x34, 0x41,
> + 0x96, 0x3d, 0x69, 0x3a, 0x8e, 0x3c, 0x8f, 0x8b, 0x8c, 0x94,
> + 0x95, 0x40, 0x29, 0x0f, 0xa5, 0x1e, 0xa9, 0xaa, 0xab, 0x90,
> + 0x91, 0x9f, 0xa0, 0x24, 0x25, 0x26, 0x14, 0x2a, 0x2b
> + };
> + const u8 values_2_205[] = {
> + 0x05, 0x80, 0x01, 0x7d, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
> + 0x50, 0x93, 0x00, 0x81, 0x20, 0x05, 0x00, 0x00, 0x00, 0x1b,
> + 0xbb, 0xa4, 0x01, 0x81, 0x12, 0x00, 0x07, 0xe2, 0xbf, 0x00,
> + 0x04, 0x19, 0x40, 0x0d, 0x00, 0x73, 0xdf, 0x06, 0x20, 0x88,
> + 0x88, 0xc1, 0x3f, 0x42, 0x80, 0x04, 0xb8, 0x92, 0x0a, 0x00,
> + 0x00, 0x00, 0x00, 0x68, 0x5c, 0xc3, 0x2e, 0x00, 0x00
> + };

hm, OK, it seems that even though we've asked the compiler to build these
arrays on the stack at runtime it will, as an optimisation, create static
storage for them due to the `const'. I don't know that the compielr _has_
to do that, nor do I know if all versions of gcc and other compilers will
also do this.

Perhaps it would be safer to put the `static' in there? There are many
such sites.


> + /* From the resolution */
> + switch (dev->resolution) {
> + case STK11XX_1280x1024:
> + case STK11XX_1024x768:
> + case STK11XX_800x600:
> + values_204 = values_2_204;
> + values_205 = values_2_205;
> + break;
> +
> + case STK11XX_640x480:
> + case STK11XX_320x240:
> + case STK11XX_160x120:
> + case STK11XX_80x60:
> + default:
> + values_204 = values_1_204;
> + values_205 = values_1_205;
> + break;
> + }
> +
> + for (i = 0; i < 59; i++) {

ARRAY_SIZE would be nice, if only for clarity..

+ retok = stk11xx_check_device(dev, 500);
+ if (retok != 1) {
+ dev_err(&dev->udev->dev, "load microcode fail\n");
+ return -EIO;
+ }

Normally we'd use a return value of zero to indicate success. So that the
negative return value can tell people _why_ it failed.

> +
> + for (i = 0; i < 16; i++) {
> + stk11xx_write_reg(dev, 0x0000, 0x25);
> + stk11xx_write_reg(dev, 0x0000, 0x24);
> + stk11xx_read_reg(dev, 0x0000, &value);
> +
> + dev_dbg(&dev->udev->dev, "Loop 1: Read 0x0000 = %02x\n", value);
> + }
> +
> + ret = stk11xx_process_table(dev, table_2);
> + if (ret)
> + goto end;
> +
> + for (i = 0; i < 16; i++) {
> + stk11xx_write_reg(dev, 0x0000, 0x25);
> + stk11xx_write_reg(dev, 0x0000, 0x24);
> + stk11xx_read_reg(dev, 0x0000, &value);
> +
> + dev_dbg(&dev->udev->dev, "Loop 2: Read 0x0000 = %02x\n", value);
> + }
> +
> + ret = stk11xx_process_table(dev, table_3);
> + if (ret)
> + goto end;
> +
> + for (i = 0; i < 16; i++) {
> + stk11xx_write_reg(dev, 0x0000, 0x25);
> + stk11xx_write_reg(dev, 0x0000, 0x24);
> + stk11xx_read_reg(dev, 0x0000, &value);
> +
> + dev_dbg(&dev->udev->dev, "Loop 3: Read 0x0000 = %02x\n", value);
> + }

Again, ARRAY_SIZE() would be clearer here.

> + ret = stk11xx_process_table(dev, table_4);
> + if (ret)
> + goto end;
> +
> + stk1125_cam_asleep(dev);
> +
> + stk11xx_set_feature(dev, 0);
> +
> +end:
> + return ret;
> +}
> +
>
> ...
>
> + };
> +
> + for (i = 0; i < 117; i++) {

lots of places..


2007-08-28 05:33:19

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 1/1] V4L: stk11xx, add a new webcam driver

Andrew Morton napsal(a):
> On Sun, 26 Aug 2007 07:09:02 -0700
> Jiri Slaby <[email protected]> wrote:
> + retok = stk11xx_check_device(dev, 500);
> + if (retok != 1) {
> + dev_err(&dev->udev->dev, "load microcode fail\n");
> + return -EIO;
> + }
>
> Normally we'd use a return value of zero to indicate success. So that the
> negative return value can tell people _why_ it failed.

it returns
-EIO = fail
0 = we succeeded, but the device is not ready
1 = the device is OK

Seems proper to change the 0 -> EBUSY or something and 1 -> 0 anyway, if we
check only for 1.

>> +
>> + for (i = 0; i < 16; i++) {
>> + stk11xx_write_reg(dev, 0x0000, 0x25);
>> + stk11xx_write_reg(dev, 0x0000, 0x24);
>> + stk11xx_read_reg(dev, 0x0000, &value);
>> +
>> + dev_dbg(&dev->udev->dev, "Loop 1: Read 0x0000 = %02x\n", value);
>> + }
>> +
>> + ret = stk11xx_process_table(dev, table_2);
>> + if (ret)
>> + goto end;
>> +
>> + for (i = 0; i < 16; i++) {
>> + stk11xx_write_reg(dev, 0x0000, 0x25);
>> + stk11xx_write_reg(dev, 0x0000, 0x24);
>> + stk11xx_read_reg(dev, 0x0000, &value);
>> +
>> + dev_dbg(&dev->udev->dev, "Loop 2: Read 0x0000 = %02x\n", value);
>> + }
>> +
>> + ret = stk11xx_process_table(dev, table_3);
>> + if (ret)
>> + goto end;
>> +
>> + for (i = 0; i < 16; i++) {
>> + stk11xx_write_reg(dev, 0x0000, 0x25);
>> + stk11xx_write_reg(dev, 0x0000, 0x24);
>> + stk11xx_read_reg(dev, 0x0000, &value);
>> +
>> + dev_dbg(&dev->udev->dev, "Loop 3: Read 0x0000 = %02x\n", value);
>> + }
>
> Again, ARRAY_SIZE() would be clearer here.

No, this is only do this 16 times, no corresponding table :).

thanks for review,
--
Jiri Slaby ([email protected])
Faculty of Informatics, Masaryk University

2007-08-28 05:36:49

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 1/1] V4L: stk11xx, add a new webcam driver

On Tue, 28 Aug 2007 07:33:08 +0200 Jiri Slaby <[email protected]> wrote:

> > Again, ARRAY_SIZE() would be clearer here.
>
> No, this is only do this 16 times, no corresponding table :).

OK, poorly chosen example. But there are lots of others, like:

+
+ for (i = 0; i < 59; i++) {
+ stk11xx_read_dummy(dev, 0x02ff);
+ stk11xx_write_reg(dev, 0x02ff, 0x00);
+
+ stk11xx_write_reg(dev, 0x0204, values_204[i]);
+ stk11xx_write_reg(dev, 0x0205, values_205[i]);

2007-08-28 05:41:17

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 1/1] V4L: stk11xx, add a new webcam driver

Andrew Morton napsal(a):
> On Tue, 28 Aug 2007 07:33:08 +0200 Jiri Slaby <[email protected]> wrote:
>
>>> Again, ARRAY_SIZE() would be clearer here.
>> No, this is only do this 16 times, no corresponding table :).
>
> OK, poorly chosen example. But there are lots of others, like:

Yes, you mentioned them further in the mail and I'm going to change those. I
only commented this one to let you know, that it's not about to change (if you
check it later).

thanks,
--
Jiri Slaby ([email protected])
Faculty of Informatics, Masaryk University

2007-08-28 06:34:58

by Alexander E. Patrakov

[permalink] [raw]
Subject: Re: [PATCH 1/1] V4L: stk11xx, add a new webcam driver

Jiri Slaby wrote:
> + /* From 80x60 to 640x480 */
> + const u8 values_1_204[] = {
> + 0x12, 0x11, 0x3b, 0x6a, 0x13, 0x10, 0x00, 0x01, 0x02, 0x13,
> + 0x39, 0x38, 0x37, 0x35, 0x0e, 0x12, 0x04, 0x0c, 0x0d, 0x17,
> + 0x18, 0x32, 0x19, 0x1a, 0x03, 0x1b, 0x16, 0x33, 0x34, 0x41,
> + 0x96, 0x3d, 0x69, 0x3a, 0x8e, 0x3c, 0x8f, 0x8b, 0x8c, 0x94,
> + 0x95, 0x40, 0x29, 0x0f, 0xa5, 0x1e, 0xa9, 0xaa, 0xab, 0x90,
> + 0x91, 0x9f, 0xa0, 0x24, 0x25, 0x26, 0x14, 0x2a, 0x2b
> + };
> + const u8 values_1_205[] = {
> + 0x45, 0x80, 0x01, 0x7d, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
> + 0x50, 0x93, 0x00, 0x81, 0x20, 0x45, 0x00, 0x00, 0x00, 0x24,
> + 0xc4, 0xb6, 0x00, 0x3c, 0x36, 0x00, 0x07, 0xe2, 0xbf, 0x00,
> + 0x04, 0x19, 0x40, 0x0d, 0x00, 0x73, 0xdf, 0x06, 0x20, 0x88,
> + 0x88, 0xc1, 0x3f, 0x42, 0x80, 0x04, 0xb8, 0x92, 0x0a, 0x00,
> + 0x00, 0x00, 0x00, 0x68, 0x5c, 0xc3, 0x2e, 0x00, 0x00
> + };
> +
> + /* From 800x600 to 1280x1024 */
> + const u8 values_2_204[] = {
> + 0x12, 0x11, 0x3b, 0x6a, 0x13, 0x10, 0x00, 0x01, 0x02, 0x13,
> + 0x39, 0x38, 0x37, 0x35, 0x0e, 0x12, 0x04, 0x0c, 0x0d, 0x17,
> + 0x18, 0x32, 0x19, 0x1a, 0x03, 0x1b, 0x16, 0x33, 0x34, 0x41,
> + 0x96, 0x3d, 0x69, 0x3a, 0x8e, 0x3c, 0x8f, 0x8b, 0x8c, 0x94,
> + 0x95, 0x40, 0x29, 0x0f, 0xa5, 0x1e, 0xa9, 0xaa, 0xab, 0x90,
> + 0x91, 0x9f, 0xa0, 0x24, 0x25, 0x26, 0x14, 0x2a, 0x2b
> + };
> + const u8 values_2_205[] = {
> + 0x05, 0x80, 0x01, 0x7d, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
> + 0x50, 0x93, 0x00, 0x81, 0x20, 0x05, 0x00, 0x00, 0x00, 0x1b,
> + 0xbb, 0xa4, 0x01, 0x81, 0x12, 0x00, 0x07, 0xe2, 0xbf, 0x00,
> + 0x04, 0x19, 0x40, 0x0d, 0x00, 0x73, 0xdf, 0x06, 0x20, 0x88,
> + 0x88, 0xc1, 0x3f, 0x42, 0x80, 0x04, 0xb8, 0x92, 0x0a, 0x00,
> + 0x00, 0x00, 0x00, 0x68, 0x5c, 0xc3, 0x2e, 0x00, 0x00
> + };

How does one derive these values? At least Debian kernel people will
remove your driver from their tree because of this unexplained binary blob.

> + * We don't know the meaning of these steps! We only replay the USB log.

Maybe it is a good idea to convert this blob, as well as others, into a
firmware request.

--
Alexander E. Patrakov

2007-11-06 07:40:59

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 1/1] V4L: stk11xx, add a new webcam driver

On Sun, 26 Aug 2007 07:09:02 -0700 Jiri Slaby <[email protected]> wrote:

> stk11xx, add a new webcam driver
>
> Adds support for stk1125, stk1135 and stkdcnew webcam built-in some
> notebooks.

Seem that something in git-dvb (or mainline) broke this driver:

drivers/media/video/stk11xx-core.c: In function 'show_contrast':
drivers/media/video/stk11xx-core.c:707: warning: initialization from incompatible pointer type
drivers/media/video/stk11xx-core.c: In function 'show_whitebalance':
drivers/media/video/stk11xx-core.c:714: warning: initialization from incompatible pointer type
drivers/media/video/stk11xx-core.c: In function 'stk11xx_create_sysfs_file':
drivers/media/video/stk11xx-core.c:727: warning: passing argument 1 of 'class_device_create_file' from incompatible pointer type
drivers/media/video/stk11xx-core.c: In function 'stk11xx_usb_disconnect':
drivers/media/video/stk11xx-core.c:851: warning: passing argument 1 of 'class_device_remove_file' from incompatible pointer type
drivers/media/video/stk11xx-core.c:853: warning: passing argument 1 of 'class_device_remove_file' from incompatible pointer type

Also, please ask the v4l/dvb guys to look at this driver - there's not a lot
of point in sending it just to me.

2007-11-06 10:49:15

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 1/1] V4L: stk11xx, add a new webcam driver

Andrew Morton napsal(a):
> On Sun, 26 Aug 2007 07:09:02 -0700 Jiri Slaby <[email protected]> wrote:
>
>> stk11xx, add a new webcam driver
>>
>> Adds support for stk1125, stk1135 and stkdcnew webcam built-in some
>> notebooks.
>
> Seem that something in git-dvb (or mainline) broke this driver:
>
> drivers/media/video/stk11xx-core.c: In function 'show_contrast':
> drivers/media/video/stk11xx-core.c:707: warning: initialization from incompatible pointer type
> drivers/media/video/stk11xx-core.c: In function 'show_whitebalance':
> drivers/media/video/stk11xx-core.c:714: warning: initialization from incompatible pointer type
> drivers/media/video/stk11xx-core.c: In function 'stk11xx_create_sysfs_file':
> drivers/media/video/stk11xx-core.c:727: warning: passing argument 1 of 'class_device_create_file' from incompatible pointer type
> drivers/media/video/stk11xx-core.c: In function 'stk11xx_usb_disconnect':
> drivers/media/video/stk11xx-core.c:851: warning: passing argument 1 of 'class_device_remove_file' from incompatible pointer type
> drivers/media/video/stk11xx-core.c:853: warning: passing argument 1 of 'class_device_remove_file' from incompatible pointer type
>
> Also, please ask the v4l/dvb guys to look at this driver - there's not a lot
> of point in sending it just to me.

Dvb fellows reviewed it some time ago, but the problem is, that it converts
one color space to another in kernelspace. We are developping an userspace
library for drivers such this one, but it takes longer than I expect. Lets
have it dropped, after the library will be done I'll merge it through v4l tree.

thanks,
--
http://www.fi.muni.cz/~xslaby/ Jiri Slaby
faculty of informatics, masaryk university, brno, cz

2007-11-07 17:58:20

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: Re: [PATCH 1/1] V4L: stk11xx, add a new webcam driver


> > Seem that something in git-dvb (or mainline) broke this driver:
> >
> > drivers/media/video/stk11xx-core.c: In function 'show_contrast':
> > drivers/media/video/stk11xx-core.c:707: warning: initialization from incompatible pointer type
> > drivers/media/video/stk11xx-core.c: In function 'show_whitebalance':
> > drivers/media/video/stk11xx-core.c:714: warning: initialization from incompatible pointer type
> > drivers/media/video/stk11xx-core.c: In function 'stk11xx_create_sysfs_file':
> > drivers/media/video/stk11xx-core.c:727: warning: passing argument 1 of 'class_device_create_file' from incompatible pointer type
> > drivers/media/video/stk11xx-core.c: In function 'stk11xx_usb_disconnect':
> > drivers/media/video/stk11xx-core.c:851: warning: passing argument 1 of 'class_device_remove_file' from incompatible pointer type
> > drivers/media/video/stk11xx-core.c:853: warning: passing argument 1 of 'class_device_remove_file' from incompatible pointer type

Those breakages were caused by some changes at sysfs that happened in
mainstream.

> > Also, please ask the v4l/dvb guys to look at this driver - there's not a lot
> > of point in sending it just to me.
>
> Dvb fellows reviewed it some time ago, but the problem is, that it converts
> one color space to another in kernelspace. We are developping an userspace
> library for drivers such this one, but it takes longer than I expect. Lets
> have it dropped, after the library will be done I'll merge it through v4l tree.

Seems the proper way also to me.

--
Cheers,
Mauro