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 <jirislaby@gmail.com> --- commit 926af968884e1e153ad2f91892ff71ec33005d22 tree f7d30907d7bd11128cd8f75a0aa2f3873c237980 parent c1b003cd9e5befbc3d915a9e37f46585127f9d1f author Jiri Slaby <jirislaby@gmail.com> Sun, 26 Aug 2007 16:02:31 +0200 committer Jiri Slaby <jirislaby@gmail.com> 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: chrisw@sous-sol.org L: stable@kernel.org S: Maintained +STK11XX WEBCAM DRIVER +P: Jiri Slaby +M: jirislaby@gmail.com +S: Maintained + TPM DEVICE DRIVER P: Kylene Hall M: tpmdd-devel@lists.sourceforge.net 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 <jirislaby@gmail.com> + * + * 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 <jirislaby@gmail.com> + * + * 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 <jirislaby@gmail.com> + * + * 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_ini
| Greg KH | [GIT PATCH] driver core patches against 2.6.24 |
| Tarkan Erimer | Re: Dual-Licensing Linux Kernel with GPL V2 and GPL V3 |
| Amit K. Arora | [RFC] Heads up on sys_fallocate() |
| Chuck Ebbert | Why do so many machines need "noapic"? |
git: | |
| Jarek Poplawski | [PATCH] pkt_sched: Destroy gen estimators under rtnl_lock(). |
| David Miller | [GIT]: Networking |
| Gerrit Renker | [PATCH 27/37] dccp: Integration of dynamic feature activation - part 2 (server side) |
| Natalie Protasevich | [BUG] New Kernel Bugs |
