Staging: go7007: add sensoray 2250/2251 support
authorPete Eberlein <pete@sensoray.com>
Thu, 30 Oct 2008 19:56:41 +0000 (12:56 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 6 Jan 2009 21:52:06 +0000 (13:52 -0800)
This is the driver for the Sensoray 2250/2251 USB devices added to
go7007 driver.

Signed-off-by: Pete Eberlein <pete@sensoray.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/go7007/Kconfig
drivers/staging/go7007/Makefile
drivers/staging/go7007/go7007-driver.c
drivers/staging/go7007/go7007-priv.h
drivers/staging/go7007/go7007-usb.c
drivers/staging/go7007/s2250-board.c [new file with mode: 0644]
drivers/staging/go7007/s2250-loader.c [new file with mode: 0644]
drivers/staging/go7007/wis-i2c.h

index 593fdb7..f2cf7f6 100644 (file)
@@ -25,3 +25,13 @@ config VIDEO_GO7007_USB
          To compile this driver as a module, choose M here: the
          module will be called go7007-usb
 
+config VIDEO_GO7007_USB_S2250_BOARD
+       tristate "Sensoray 2250/2251 support"
+       depends on VIDEO_GO7007_USB && DVB_USB
+       default N
+       ---help---
+         This is a video4linux driver for the Sensoray 2250/2251 device
+
+         To compile this driver as a module, choose M here: the
+         module will be called s2250-board
+
index 9b9310c..e514b4a 100644 (file)
@@ -5,14 +5,23 @@
 
 obj-$(CONFIG_VIDEO_GO7007) += go7007.o
 obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o
+obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o
 
-go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o snd-go7007.o
+go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \
+               snd-go7007.o wis-saa7113.o
 
+s2250-objs += s2250-board.o s2250-loader.o
 
-#ifneq ($(SAA7134_BUILD),)
-#obj-m += saa7134-go7007.o
+# Uncompile when the saa7134 patches get into upstream
+#ifneq ($(CONFIG_VIDEO_SAA7134),)
+#obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o
+#EXTRA_CFLAGS += -Idrivers/media/video/saa7134
 #endif
 
+ifneq ($(CONFIG_VIDEO_GO7007_USB_S2250_BOARD),)
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-usb
+endif
+
 EXTRA_CFLAGS += -Idrivers/staging/saa7134
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
index 510c23b..58bfc8d 100644 (file)
@@ -217,6 +217,9 @@ static int init_i2c_module(struct i2c_adapter *adapter, int id, int addr)
        case I2C_DRIVERID_WIS_OV7640:
                modname = "wis-ov7640";
                break;
+       case I2C_DRIVERID_S2250:
+               modname = "s2250-board";
+               break;
        default:
                modname = NULL;
                break;
index f61d983..372f1f1 100644 (file)
@@ -40,6 +40,7 @@ struct go7007;
 #define GO7007_BOARDID_LIFEVIEW_LR192  21 /* TV Walker Ultra */
 #define GO7007_BOARDID_ENDURA          22
 #define GO7007_BOARDID_ADLINK_MPG24    23
+#define GO7007_BOARDID_SENSORAY_2250   24 /* Sensoray 2250/2251 */
 
 /* Various characteristics of each board */
 #define GO7007_BOARD_HAS_AUDIO         (1<<0)
index 04cc144..f18eaa4 100644 (file)
@@ -398,6 +398,41 @@ static struct go7007_usb_board board_adlink_mpg24 = {
        },
 };
 
+static struct go7007_usb_board board_sensoray_2250 = {
+       .flags          = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
+       .main_info      = {
+               .firmware        = "go7007tv.bin",
+               .audio_flags     = GO7007_AUDIO_I2S_MODE_1 |
+                                       GO7007_AUDIO_I2S_MASTER |
+                                       GO7007_AUDIO_WORD_16,
+               .flags           = GO7007_BOARD_HAS_AUDIO,
+               .audio_rate      = 48000,
+               .audio_bclk_div  = 8,
+               .audio_main_div  = 2,
+               .hpi_buffer_cap  = 7,
+               .sensor_flags    = GO7007_SENSOR_656 |
+                                       GO7007_SENSOR_TV,
+               .num_i2c_devs    = 1,
+               .i2c_devs        = {
+                       {
+                               .id     = I2C_DRIVERID_S2250,
+                               .addr   = 0x34,
+                       },
+               },
+               .num_inputs      = 2,
+               .inputs          = {
+                       {
+                               .video_input    = 0,
+                               .name           = "Composite",
+                       },
+                       {
+                               .video_input    = 1,
+                               .name           = "S-Video",
+                       },
+               },
+       },
+};
+
 static struct usb_device_id go7007_usb_id_table[] = {
        {
                .match_flags    = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
@@ -491,6 +526,14 @@ static struct usb_device_id go7007_usb_id_table[] = {
                .bcdDevice_hi   = 0x1,
                .driver_info    = (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192,
        },
+       {
+               .match_flags    = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+               .idVendor       = 0x1943,  /* Vendor ID Sensoray */
+               .idProduct      = 0x2250,  /* Product ID of 2250/2251 */
+               .bcdDevice_lo   = 0x1,
+               .bcdDevice_hi   = 0x1,
+               .driver_info    = (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250,
+       },
        { }                                     /* Terminating entry */
 };
 
@@ -965,6 +1008,11 @@ static int go7007_usb_probe(struct usb_interface *intf,
                name = "Lifeview TV Walker Ultra";
                board = &board_lifeview_lr192;
                break;
+       case GO7007_BOARDID_SENSORAY_2250:
+               printk(KERN_INFO "Sensoray 2250 found\n");
+               name = "Sensoray 2250/2251\n";
+               board = &board_sensoray_2250;
+               break;
        default:
                printk(KERN_ERR "go7007-usb: unknown board ID %d!\n",
                                (unsigned int)id->driver_info);
diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/go7007/s2250-board.c
new file mode 100644 (file)
index 0000000..fb6845e
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+ * Copyright (C) 2008 Sensoray Company Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include "go7007-priv.h"
+#include "wis-i2c.h"
+
+extern int s2250loader_init(void);
+extern void s2250loader_cleanup(void);
+
+#define TLV320_ADDRESS      0x34
+#define S2250_VIDDEC        0x86
+#define VPX322_ADDR_ANALOGCONTROL1     0x02
+#define VPX322_ADDR_BRIGHTNESS0                0x0127
+#define VPX322_ADDR_BRIGHTNESS1                0x0131
+#define VPX322_ADDR_CONTRAST0          0x0128
+#define VPX322_ADDR_CONTRAST1          0x0132
+#define VPX322_ADDR_HUE                        0x00dc
+#define VPX322_ADDR_SAT                        0x0030
+
+struct go7007_usb_board {
+       unsigned int flags;
+       struct go7007_board_info main_info;
+};
+
+struct go7007_usb {
+       struct go7007_usb_board *board;
+       struct semaphore i2c_lock;
+       struct usb_device *usbdev;
+       struct urb *video_urbs[8];
+       struct urb *audio_urbs[8];
+       struct urb *intr_urb;
+};
+
+static unsigned char aud_regs[] = {
+       0x1e, 0x00,
+       0x00, 0x17,
+       0x02, 0x17,
+       0x04, 0xf9,
+       0x06, 0xf9,
+       0x08, 0x02,
+       0x0a, 0x00,
+       0x0c, 0x00,
+       0x0a, 0x00,
+       0x0c, 0x00,
+       0x0e, 0x02,
+       0x10, 0x00,
+       0x12, 0x01,
+       0x00, 0x00,
+};
+
+
+static unsigned char vid_regs[] = {
+       0xF2, 0x0f,
+       0xAA, 0x00,
+       0xF8, 0xff,
+       0x00, 0x00,
+};
+
+static u16 vid_regs_fp[] = {
+       0x028, 0x067,
+       0x120, 0x016,
+       0x121, 0xcF2,
+       0x122, 0x0F2,
+       0x123, 0x00c,
+       0x124, 0x2d0,
+       0x125, 0x2e0,
+       0x126, 0x004,
+       0x128, 0x1E0,
+       0x12A, 0x016,
+       0x12B, 0x0F2,
+       0x12C, 0x0F2,
+       0x12D, 0x00c,
+       0x12E, 0x2d0,
+       0x12F, 0x2e0,
+       0x130, 0x004,
+       0x132, 0x1E0,
+       0x140, 0x060,
+       0x153, 0x00C,
+       0x154, 0x200,
+       0x150, 0x801,
+       0x000, 0x000
+};
+
+/* PAL specific values */
+static u16 vid_regs_fp_pal[] =
+{
+       0x120, 0x017,
+       0x121, 0xd22,
+       0x122, 0x122,
+       0x12A, 0x017,
+       0x12B, 0x122,
+       0x12C, 0x122,
+       0x140, 0x060,
+       0x000, 0x000,
+};
+
+struct s2250 {
+       int std;
+       int input;
+       int brightness;
+       int contrast;
+       int saturation;
+       int hue;
+       int reg12b_val;
+       int audio_input;
+};
+
+/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
+static int go7007_usb_vendor_request(struct go7007 *go, u16 request,
+       u16 value, u16 index, void *transfer_buffer, int length, int in)
+{
+       struct go7007_usb *usb = go->hpi_context;
+       int timeout = 5000;
+
+       if (in) {
+               return usb_control_msg(usb->usbdev,
+                               usb_rcvctrlpipe(usb->usbdev, 0), request,
+                               USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+                               value, index, transfer_buffer, length, timeout);
+       } else {
+               return usb_control_msg(usb->usbdev,
+                               usb_sndctrlpipe(usb->usbdev, 0), request,
+                               USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                               value, index, transfer_buffer, length, timeout);
+       }
+}
+/* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+       struct go7007 *go = i2c_get_adapdata(client->adapter);
+       struct go7007_usb *usb = go->hpi_context;
+       int rc;
+       int dev_addr = client->addr;
+       u8 *buf;
+
+       if (go == NULL)
+               return -ENODEV;
+
+       if (go->status == STATUS_SHUTDOWN)
+               return -EBUSY;
+
+       buf = kzalloc(16, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       if (down_interruptible(&usb->i2c_lock) != 0) {
+               printk(KERN_INFO "i2c lock failed\n");
+               return -EINTR;
+       }
+       rc = go7007_usb_vendor_request(go, 0x55, dev_addr,
+                                      (reg<<8 | value),
+                                      buf,
+                                      16, 1);
+
+       up(&usb->i2c_lock);
+       kfree(buf);
+       return rc;
+}
+
+static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val)
+{
+       struct go7007 *go = i2c_get_adapdata(client->adapter);
+       struct go7007_usb *usb = go->hpi_context;
+       u8 *buf;
+       struct s2250 *dec = i2c_get_clientdata(client);
+
+       if (go == NULL)
+               return -ENODEV;
+
+       if (go->status == STATUS_SHUTDOWN)
+               return -EBUSY;
+
+       buf = kzalloc(16, GFP_KERNEL);
+
+       if (buf == NULL)
+               return -ENOMEM;
+
+
+
+       memset(buf, 0xcd, 6);
+
+       if (down_interruptible(&usb->i2c_lock) != 0) {
+               printk(KERN_INFO "i2c lock failed\n");
+               return -EINTR;
+       }
+       if (go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1) < 0)
+               return -EFAULT;
+
+       up(&usb->i2c_lock);
+       if (buf[0] == 0) {
+               unsigned int subaddr, val_read;
+
+               subaddr = (buf[4] << 8) + buf[5];
+               val_read = (buf[2] << 8) + buf[3];
+               if (val_read != val) {
+                       printk(KERN_INFO "invalid fp write %x %x\n",
+                              val_read, val);
+                       return -EFAULT;
+               }
+               if (subaddr != addr) {
+                       printk(KERN_INFO "invalid fp write addr %x %x\n",
+                              subaddr, addr);
+                       return -EFAULT;
+               }
+       } else
+               return -EFAULT;
+
+       /* save last 12b value */
+       if (addr == 0x12b)
+               dec->reg12b_val = val;
+
+       return 0;
+}
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+       int i;
+
+       for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) {
+               if (write_reg(client, regs[i], regs[i+1]) < 0) {
+                       printk(KERN_INFO "s2250: failed\n");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static int write_regs_fp(struct i2c_client *client, u16 *regs)
+{
+       int i;
+
+       for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) {
+               if (write_reg_fp(client, regs[i], regs[i+1]) < 0) {
+                       printk(KERN_INFO "s2250: failed fp\n");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+
+static int s2250_command(struct i2c_client *client,
+                        unsigned int cmd, void *arg)
+{
+       struct s2250 *dec = i2c_get_clientdata(client);
+
+       switch (cmd) {
+       case VIDIOC_S_INPUT:
+       {
+               int vidsys;
+               int *input = arg;
+
+               vidsys = (dec->std == V4L2_STD_NTSC) ? 0x01 : 0x00;
+               if (*input == 0) {
+                       /* composite */
+                       write_reg_fp(client, 0x20, 0x020 | vidsys);
+                       write_reg_fp(client, 0x21, 0x662);
+                       write_reg_fp(client, 0x140, 0x060);
+               } else {
+                       /* S-Video */
+                       write_reg_fp(client, 0x20, 0x040 | vidsys);
+                       write_reg_fp(client, 0x21, 0x666);
+                       write_reg_fp(client, 0x140, 0x060);
+               }
+               dec->input = *input;
+               break;
+       }
+       case VIDIOC_S_STD:
+       {
+               v4l2_std_id *std = arg;
+               u16 vidsource;
+
+               vidsource = (dec->input == 1) ? 0x040 : 0x020;
+               dec->std = *std;
+               switch (dec->std) {
+               case V4L2_STD_NTSC:
+                       write_regs_fp(client, vid_regs_fp);
+                       write_reg_fp(client, 0x20, vidsource | 1);
+                       break;
+               case V4L2_STD_PAL:
+                       write_regs_fp(client, vid_regs_fp);
+                       write_regs_fp(client, vid_regs_fp_pal);
+                       write_reg_fp(client, 0x20, vidsource);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       }
+       case VIDIOC_QUERYCTRL:
+       {
+               struct v4l2_queryctrl *ctrl = arg;
+               static const u32 user_ctrls[] = {
+                       V4L2_CID_BRIGHTNESS,
+                       V4L2_CID_CONTRAST,
+                       V4L2_CID_SATURATION,
+                       V4L2_CID_HUE,
+                       0
+               };
+               static const u32 *ctrl_classes[] = {
+                       user_ctrls,
+                       NULL
+               };
+
+               ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id);
+               switch (ctrl->id) {
+               case V4L2_CID_BRIGHTNESS:
+                       v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
+                       break;
+               case V4L2_CID_CONTRAST:
+                       v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
+                       break;
+               case V4L2_CID_SATURATION:
+                       v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
+                       break;
+               case V4L2_CID_HUE:
+                       v4l2_ctrl_query_fill(ctrl, -50, 50, 1, 0);
+                       break;
+               default:
+                       ctrl->name[0] = '\0';
+                       return -EINVAL;
+               }
+               break;
+       }
+       case VIDIOC_S_CTRL:
+       {
+               struct v4l2_control *ctrl = arg;
+               int value1;
+
+               switch (ctrl->id) {
+               case V4L2_CID_BRIGHTNESS:
+                       printk(KERN_INFO "s2250: future setting\n");
+                       return -EINVAL;
+               case V4L2_CID_CONTRAST:
+                       printk(KERN_INFO "s2250: future setting\n");
+                       return -EINVAL;
+                       break;
+               case V4L2_CID_SATURATION:
+                       if (ctrl->value > 127)
+                               dec->saturation = 127;
+                       else if (ctrl->value < 0)
+                               dec->saturation = 0;
+                       else
+                               dec->saturation = ctrl->value;
+
+                       value1 = dec->saturation * 4140 / 100;
+                       if (value1 > 4094)
+                               value1 = 4094;
+                       write_reg_fp(client, VPX322_ADDR_SAT, value1);
+                       break;
+               case V4L2_CID_HUE:
+                       if (ctrl->value > 50)
+                               dec->hue = 50;
+                       else if (ctrl->value < -50)
+                               dec->hue = -50;
+                       else
+                               dec->hue = ctrl->value;
+                       /* clamp the hue range */
+                       value1 = dec->hue * 280 / 50;
+                       write_reg_fp(client, VPX322_ADDR_HUE, value1);
+                       break;
+               }
+               break;
+       }
+       case VIDIOC_G_CTRL:
+       {
+               struct v4l2_control *ctrl = arg;
+
+               switch (ctrl->id) {
+               case V4L2_CID_BRIGHTNESS:
+                       ctrl->value = dec->brightness;
+                       break;
+               case V4L2_CID_CONTRAST:
+                       ctrl->value = dec->contrast;
+                       break;
+               case V4L2_CID_SATURATION:
+                       ctrl->value = dec->saturation;
+                       break;
+               case V4L2_CID_HUE:
+                       ctrl->value = dec->hue;
+                       break;
+               }
+               break;
+       }
+       case VIDIOC_S_FMT:
+       {
+               struct v4l2_format *fmt = arg;
+               if (fmt->fmt.pix.height < 640) {
+                       write_reg_fp(client, 0x12b, dec->reg12b_val | 0x400);
+                       write_reg_fp(client, 0x140, 0x060);
+               } else {
+                       write_reg_fp(client, 0x12b, dec->reg12b_val & ~0x400);
+                       write_reg_fp(client, 0x140, 0x060);
+               }
+               return 0;
+       }
+       case VIDIOC_G_AUDIO:
+       {
+               struct v4l2_audio *audio = arg;
+
+               memset(audio, 0, sizeof(*audio));
+               audio->index = dec->audio_input;
+               /* fall through */
+       }
+       case VIDIOC_ENUMAUDIO:
+       {
+               struct v4l2_audio *audio = arg;
+
+               switch (audio->index) {
+               case 0:
+                       strcpy(audio->name, "Line In");
+                       break;
+               case 1:
+                       strcpy(audio->name, "Mic");
+                       break;
+               case 2:
+                       strcpy(audio->name, "Mic Boost");
+                       break;
+               default:
+                       audio->name[0] = '\0';
+                       return 0;
+               }
+               audio->capability = V4L2_AUDCAP_STEREO;
+               audio->mode = 0;
+               return 0;
+       }
+       case VIDIOC_S_AUDIO:
+       {
+               struct v4l2_audio *audio = arg;
+
+               client->addr = TLV320_ADDRESS;
+               switch (audio->index) {
+               case 0:
+                       write_reg(client, 0x08, 0x02); /* Line In */
+                       break;
+               case 1:
+                       write_reg(client, 0x08, 0x04); /* Mic */
+                       break;
+               case 2:
+                       write_reg(client, 0x08, 0x05); /* Mic Boost */
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               dec->audio_input = audio->index;
+               return 0;
+       }
+
+       default:
+               printk(KERN_INFO "s2250: unknown command 0x%x\n", cmd);
+               break;
+       }
+       return 0;
+}
+
+static struct i2c_driver s2250_driver;
+
+static struct i2c_client s2250_client_templ = {
+       .name           = "Sensoray 2250",
+       .driver         = &s2250_driver,
+};
+
+static int s2250_detect(struct i2c_adapter *adapter, int addr, int kind)
+{
+       struct i2c_client *client;
+       struct s2250 *dec;
+       u8 *data;
+       struct go7007 *go = i2c_get_adapdata(adapter);
+       struct go7007_usb *usb = go->hpi_context;
+
+       client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+       if (client == NULL)
+               return -ENOMEM;
+       memcpy(client, &s2250_client_templ,
+              sizeof(s2250_client_templ));
+       client->adapter = adapter;
+
+       dec = kmalloc(sizeof(struct s2250), GFP_KERNEL);
+       if (dec == NULL) {
+               kfree(client);
+               return -ENOMEM;
+       }
+
+       dec->std = V4L2_STD_NTSC;
+       dec->brightness = 50;
+       dec->contrast = 50;
+       dec->saturation = 50;
+       dec->hue = 0;
+       client->addr = TLV320_ADDRESS;
+       i2c_set_clientdata(client, dec);
+
+       printk(KERN_DEBUG
+              "s2250: initializing video decoder on %s\n",
+              adapter->name);
+
+       /* initialize the audio */
+       client->addr = TLV320_ADDRESS;
+       if (write_regs(client, aud_regs) < 0) {
+               printk(KERN_ERR
+                      "s2250: error initializing audio\n");
+               kfree(client);
+               kfree(dec);
+               return 0;
+       }
+       client->addr = S2250_VIDDEC;
+       i2c_set_clientdata(client, dec);
+
+       if (write_regs(client, vid_regs) < 0) {
+               printk(KERN_ERR
+                      "s2250: error initializing decoder\n");
+               kfree(client);
+               kfree(dec);
+               return 0;
+       }
+       if (write_regs_fp(client, vid_regs_fp) < 0) {
+               printk(KERN_ERR
+                      "s2250: error initializing decoder\n");
+               kfree(client);
+               kfree(dec);
+               return 0;
+       }
+       /* set default channel */
+       /* composite */
+       write_reg_fp(client, 0x20, 0x020 | 1);
+       write_reg_fp(client, 0x21, 0x662);
+       write_reg_fp(client, 0x140, 0x060);
+
+       /* set default audio input */
+       dec->audio_input = 0;
+       write_reg(client, 0x08, 0x02); /* Line In */
+
+       if (down_interruptible(&usb->i2c_lock) == 0) {
+               data = kzalloc(16, GFP_KERNEL);
+               if (data != NULL) {
+                       int rc;
+                       rc = go7007_usb_vendor_request(go, 0x41, 0, 0,
+                                                      data, 16, 1);
+                       if (rc > 0) {
+                               u8 mask;
+                               data[0] = 0;
+                               mask = 1<<5;
+                               data[0] &= ~mask;
+                               data[1] |= mask;
+                               go7007_usb_vendor_request(go, 0x40, 0,
+                                                         (data[1]<<8)
+                                                         + data[1],
+                                                         data, 16, 0);
+                       }
+                       kfree(data);
+               }
+               up(&usb->i2c_lock);
+       }
+
+       i2c_attach_client(client);
+       printk("s2250: initialized successfully\n");
+       return 0;
+}
+
+static int s2250_detach(struct i2c_client *client)
+{
+       struct s2250 *dec = i2c_get_clientdata(client);
+       int r;
+
+       r = i2c_detach_client(client);
+       if (r < 0)
+               return r;
+
+       kfree(client);
+       kfree(dec);
+       return 0;
+}
+
+static struct i2c_driver s2250_driver = {
+       .driver = {
+               .name   = "Sensoray 2250 board driver",
+       },
+       .id             = I2C_DRIVERID_S2250,
+       .detach_client  = s2250_detach,
+       .command        = s2250_command,
+};
+
+static int __init s2250_init(void)
+{
+       int r;
+
+       r = s2250loader_init();
+       if (r < 0)
+               return r;
+
+       r = i2c_add_driver(&s2250_driver);
+       if (r < 0)
+               return r;
+       return wis_i2c_add_driver(s2250_driver.id, s2250_detect);
+}
+
+static void __exit s2250_cleanup(void)
+{
+       wis_i2c_del_driver(s2250_detect);
+       i2c_del_driver(&s2250_driver);
+
+       s2250loader_cleanup();
+}
+
+module_init(s2250_init);
+module_exit(s2250_cleanup);
+
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("Board driver for Sensoryray 2250");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/s2250-loader.c b/drivers/staging/go7007/s2250-loader.c
new file mode 100644 (file)
index 0000000..a5e4aca
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Sensoray Company Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <dvb-usb.h>
+
+#define S2250_LOADER_FIRMWARE  "s2250_loader.fw"
+#define S2250_FIRMWARE         "s2250.fw"
+
+typedef struct device_extension_s {
+    struct kref     kref;
+    int minor;
+    struct usb_device *usbdev;
+} device_extension_t, *pdevice_extension_t;
+
+#define USB_s2250loader_MAJOR 240
+#define USB_s2250loader_MINOR_BASE 0
+#define MAX_DEVICES 256
+
+static pdevice_extension_t s2250_dev_table[MAX_DEVICES];
+static DECLARE_MUTEX(s2250_dev_table_mutex);
+
+#define to_s2250loader_dev_common(d) container_of(d, device_extension_t, kref)
+static void s2250loader_delete(struct kref *kref)
+{
+       pdevice_extension_t s = to_s2250loader_dev_common(kref);
+       s2250_dev_table[s->minor] = NULL;
+       kfree(s);
+}
+
+static int s2250loader_probe(struct usb_interface *interface,
+                               const struct usb_device_id *id)
+{
+       struct usb_device *usbdev;
+       int minor, ret;
+       pdevice_extension_t s = NULL;
+       const struct firmware *fw;
+
+       usbdev = usb_get_dev(interface_to_usbdev(interface));
+       if (!usbdev) {
+               printk(KERN_ERR "Enter s2250loader_probe failed\n");
+               return -1;
+       }
+       printk(KERN_INFO "Enter s2250loader_probe 2.6 kernel\n");
+       printk(KERN_INFO "vendor id 0x%x, device id 0x%x devnum:%d\n",
+          usbdev->descriptor.idVendor, usbdev->descriptor.idProduct,
+          usbdev->devnum);
+
+       if (usbdev->descriptor.bNumConfigurations != 1) {
+               printk(KERN_ERR "can't handle multiple config\n");
+               return -1;
+       }
+       down(&s2250_dev_table_mutex);
+
+       for (minor = 0; minor < MAX_DEVICES; minor++) {
+               if (s2250_dev_table[minor] == NULL)
+                       break;
+       }
+
+       if (minor < 0 || minor >= MAX_DEVICES) {
+               printk(KERN_ERR "Invalid minor: %d\n", minor);
+               goto failed;
+       }
+
+       /* Allocate dev data structure */
+       s = kmalloc(sizeof(device_extension_t), GFP_KERNEL);
+       if (s == NULL) {
+               printk(KERN_ERR "Out of memory\n");
+               goto failed;
+       }
+       s2250_dev_table[minor] = s;
+
+       printk(KERN_INFO "s2250loader_probe: Device %d on Bus %d Minor %d\n",
+               usbdev->devnum, usbdev->bus->busnum, minor);
+
+       memset(s, 0, sizeof(device_extension_t));
+       s->usbdev = usbdev;
+       printk(KERN_INFO "loading 2250 loader\n");
+
+       kref_init(&(s->kref));
+
+       up(&s2250_dev_table_mutex);
+
+       if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) {
+               printk(KERN_ERR
+                       "s2250: unable to load firmware from file \"%s\"\n",
+                       S2250_LOADER_FIRMWARE);
+               goto failed2;
+       }
+       ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2);
+       release_firmware(fw);
+       if (0 != ret) {
+               printk(KERN_ERR "loader download failed\n");
+               goto failed2;
+       }
+
+       if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) {
+               printk(KERN_ERR
+                       "s2250: unable to load firmware from file \"%s\"\n",
+                       S2250_FIRMWARE);
+               goto failed2;
+       }
+       ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2);
+       release_firmware(fw);
+       if (0 != ret) {
+               printk(KERN_ERR "firmware_s2250 download failed\n");
+               goto failed2;
+       }
+
+       usb_set_intfdata(interface, s);
+       return 0;
+
+failed:
+       up(&s2250_dev_table_mutex);
+failed2:
+       if (s)
+               kref_put(&(s->kref), s2250loader_delete);
+
+       printk(KERN_ERR "probe failed\n");
+       return -1;
+}
+
+static void s2250loader_disconnect(struct usb_interface *interface)
+{
+       pdevice_extension_t s = usb_get_intfdata(interface);
+       printk(KERN_INFO "s2250: disconnect\n");
+       lock_kernel();
+       s = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+       kref_put(&(s->kref), s2250loader_delete);
+       unlock_kernel();
+}
+
+static struct usb_device_id s2250loader_ids[] = {
+       {USB_DEVICE(0x1943, 0xa250)},
+       {}                          /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, s2250loader_ids);
+
+static struct usb_driver s2250loader_driver = {
+       .name           = "s2250-loader",
+       .probe          = s2250loader_probe,
+       .disconnect     = s2250loader_disconnect,
+       .id_table       = s2250loader_ids,
+};
+
+int s2250loader_init(void)
+{
+       int r;
+       unsigned i = 0;
+
+       for (i = 0; i < MAX_DEVICES; i++)
+               s2250_dev_table[i] = NULL;
+
+       r = usb_register(&s2250loader_driver);
+       if (r) {
+               printk(KERN_ERR "usb_register failed. Error number %d\n", r);
+               return -1;
+       }
+
+       printk(KERN_INFO "s2250loader_init: driver registered\n");
+       return 0;
+}
+EXPORT_SYMBOL(s2250loader_init);
+
+void s2250loader_cleanup(void)
+{
+       printk(KERN_INFO "s2250loader_cleanup\n");
+       usb_deregister(&s2250loader_driver);
+}
+EXPORT_SYMBOL(s2250loader_cleanup);
index 993f658..431f41d 100644 (file)
@@ -23,6 +23,7 @@
 #define        I2C_DRIVERID_WIS_SAA7113        0xf0f4
 #define        I2C_DRIVERID_WIS_OV7640         0xf0f5
 #define        I2C_DRIVERID_WIS_TW2804         0xf0f6
+#define        I2C_DRIVERID_S2250              0xf0f7
 #define        I2C_ALGO_GO7007                 0xf00000
 #define        I2C_ALGO_GO7007_USB             0xf10000