ALSA: usb-audio: add support for Akai MPD16
[safe/jmp/linux-2.6] / drivers / staging / tm6000 / tm6000-i2c.c
index 8569cfc..94ff489 100644 (file)
@@ -1,5 +1,5 @@
 /*
-   tm6000-i2c.c - driver for TM5600/TM6000 USB video capture devices
+   tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices
 
    Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
 
 #define I2C_HW_B_TM6000 I2C_HW_B_EM28XX
 /* ----------------------------------------------------------- */
 
-static unsigned int i2c_scan = 0;
-module_param(i2c_scan, int, 0444);
-MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
-
 static unsigned int i2c_debug = 0;
 module_param(i2c_debug, int, 0644);
 MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
@@ -48,45 +44,121 @@ MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
                        printk(KERN_DEBUG "%s at %s: " fmt, \
                        dev->name, __FUNCTION__ , ##args); } while (0)
 
+static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr,
+                               __u8 reg, char *buf, int len)
+{
+       int rc;
+       unsigned int tsleep;
+       unsigned int i2c_packet_limit = 16;
+
+       if (dev->dev_type == TM6010)
+               i2c_packet_limit = 64;
+
+       if (!buf)
+               return -1;
+
+       if (len < 1 || len > i2c_packet_limit) {
+               printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n",
+                       len, i2c_packet_limit);
+               return -1;
+       }
+
+       /* capture mutex */
+       rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR |
+               USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN,
+               addr | reg << 8, 0, buf, len);
+
+       if (rc < 0) {
+               /* release mutex */
+               return rc;
+       }
+
+       /* Calculate delay time, 14000us for 64 bytes */
+       tsleep = ((len * 200) + 200 + 1000) / 1000;
+       msleep(tsleep);
+
+       /* release mutex */
+       return rc;
+}
 
-/* Returns 0 if address is found */
-static int tm6000_i2c_scan(struct i2c_adapter *i2c_adap, int addr)
+/* Generic read - doesn't work fine with 16bit registers */
+static int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr,
+                               __u8 reg, char *buf, int len)
 {
-       struct tm6000_core *dev = i2c_adap->algo_data;
+       int rc;
+       u8 b[2];
+       unsigned int i2c_packet_limit = 16;
 
-#if 1
-       /* HACK: i2c scan is not working yet */
-       if (
-               (dev->caps.has_tuner   && (addr==dev->tuner_addr)) ||
-               (dev->caps.has_tda9874 && (addr==0xb0)) ||
-               (dev->caps.has_eeprom  && (addr==0xa0))
-          ) {
-               printk("Hack: enabling device at addr 0x%02x\n",addr);
-               return (1);
-       } else {
-               return -ENODEV;
+       if (dev->dev_type == TM6010)
+               i2c_packet_limit = 64;
+
+       if (!buf)
+               return -1;
+
+       if (len < 1 || len > i2c_packet_limit) {
+               printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n",
+                       len, i2c_packet_limit);
+               return -1;
        }
-#else
-       int rc=-ENODEV;
-       char buf[1];
-
-       /* This sends addr + 1 byte with 0 */
-       rc = tm6000_read_write_usb (dev,
-               USB_DIR_IN | USB_TYPE_VENDOR,
-               REQ_16_SET_GET_I2CSEQ,
-               addr, 0,
-               buf, 0);
-       msleep(10);
-
-       if (rc<0) {
-               if (i2c_debug>=2)
-                       printk("no device at addr 0x%02x\n",addr);
+
+       /* capture mutex */
+       if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) {
+               /*
+                * Workaround an I2C bug when reading from zl10353
+                */
+               reg -= 1;
+               len += 1;
+
+               rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len);
+
+               *buf = b[1];
+       } else {
+               rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len);
        }
 
-       printk("Hack: check on addr 0x%02x returned %d\n",addr,rc);
+       /* release mutex */
+       return rc;
+}
 
+/*
+ * read from a 16bit register
+ * for example xc2028, xc3028 or xc3028L
+ */
+static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr,
+                                 __u16 reg, char *buf, int len)
+{
+       int rc;
+       unsigned char ureg;
+
+       if (!buf || len != 2)
+               return -1;
+
+       /* capture mutex */
+       if (dev->dev_type == TM6010) {
+               ureg = reg & 0xFF;
+               rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR |
+                       USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN,
+                       addr | (reg & 0xFF00), 0, &ureg, 1);
+
+               if (rc < 0) {
+                       /* release mutex */
+                       return rc;
+               }
+
+               msleep(1400 / 1000);
+               rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR |
+                       USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ,
+                       reg, 0, buf, len);
+       } else {
+               rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR |
+                       USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN,
+                       addr, reg, buf, len);
+       }
+
+       /* release mutex */
        return rc;
-#endif
 }
 
 static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap,
@@ -94,82 +166,68 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap,
 {
        struct tm6000_core *dev = i2c_adap->algo_data;
        int addr, rc, i, byte;
-       int prev_reg = -1;
 
        if (num <= 0)
                return 0;
        for (i = 0; i < num; i++) {
-               addr = (msgs[i].addr << 1) &0xff;
+               addr = (msgs[i].addr << 1) & 0xff;
                i2c_dprintk(2,"%s %s addr=0x%x len=%d:",
                         (msgs[i].flags & I2C_M_RD) ? "read" : "write",
                         i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
-               if (!msgs[i].len) {
-                       /* Do I2C scan */
-                       rc=tm6000_i2c_scan(i2c_adap, addr);
-               } else if (msgs[i].flags & I2C_M_RD) {
-                       /* Read bytes */
-       /* I2C is assumed to have always a subaddr at the first byte of the
-          message bus. Also, the first i2c value of the answer is returned
-          out of message data.
-        */
-                       /* SMBus Read Byte command */
-                       if (prev_reg < 0)
-                               printk("XXX read from unknown prev_reg\n");
-                       rc = tm6000_read_write_usb (dev,
-                               USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                               REQ_16_SET_GET_I2CSEQ,
-                               addr | (prev_reg << 8), 0,
-                               msgs[i].buf, msgs[i].len);
-                       if (prev_reg >= 0)
-                               prev_reg += msgs[i].len;
-                       if (i2c_debug>=2) {
-                               for (byte = 0; byte < msgs[i].len; byte++) {
+               if (msgs[i].flags & I2C_M_RD) {
+                       /* read request without preceding register selection */
+                       /*
+                        * The TM6000 only supports a read transaction
+                        * immediately after a 1 or 2 byte write to select
+                        * a register.  We cannot fulfil this request.
+                        */
+                       i2c_dprintk(2, " read without preceding write not"
+                                      " supported");
+                       rc = -EOPNOTSUPP;
+                       goto err;
+               } else if (i + 1 < num && msgs[i].len <= 2 &&
+                          (msgs[i + 1].flags & I2C_M_RD) &&
+                          msgs[i].addr == msgs[i + 1].addr) {
+                       /* 1 or 2 byte write followed by a read */
+                       if (i2c_debug >= 2)
+                               for (byte = 0; byte < msgs[i].len; byte++)
                                        printk(" %02x", msgs[i].buf[byte]);
-                               }
+                       i2c_dprintk(2, "; joined to read %s len=%d:",
+                                   i == num - 2 ? "stop" : "nonstop",
+                                   msgs[i + 1].len);
+
+                       if (msgs[i].len == 2) {
+                               rc = tm6000_i2c_recv_regs16(dev, addr,
+                                       msgs[i].buf[0] << 8 | msgs[i].buf[1],
+                                       msgs[i + 1].buf, msgs[i + 1].len);
+                       } else {
+                               rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0],
+                                       msgs[i + 1].buf, msgs[i + 1].len);
                        }
-               } else if (i+1 < num && msgs[i].len == 2 &&
-                          (msgs[i+1].flags & I2C_M_RD) &&
-                          msgs[i].addr == msgs[i+1].addr) {
-                       i2c_dprintk(2, "msg %d: write 2, read %d", i,
-                                   msgs[i+1].len);
-                       /* Write 2 Read N command */
-                       rc = tm6000_read_write_usb (dev,
-                               USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                               REQ_14_SET_GET_EEPROM_PAGE, /* XXX wrong name */
-                               addr | msgs[i].buf[0] << 8, msgs[i].buf[1],
-                               msgs[i+1].buf, msgs[i+1].len);
+
                        i++;
-                       if (i2c_debug>=2) {
-                               for (byte = 0; byte < msgs[i].len; byte++) {
-                                       printk(" %02x", msgs[i].buf[byte]);
-                               }
+
+                       if (addr == dev->tuner_addr << 1) {
+                               tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
+                               tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
                        }
-                       prev_reg = -1;
+                       if (i2c_debug >= 2)
+                               for (byte = 0; byte < msgs[i].len; byte++)
+                                       printk(" %02x", msgs[i].buf[byte]);
                } else {
                        /* write bytes */
-                       if (i2c_debug>=2) {
+                       if (i2c_debug >= 2)
                                for (byte = 0; byte < msgs[i].len; byte++)
                                        printk(" %02x", msgs[i].buf[byte]);
-                       }
+                       rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0],
+                               msgs[i].buf + 1, msgs[i].len - 1);
 
-                       /* SMBus Write Byte command followed by a read command */
-                       if(msgs[i].len == 1 && i+1 < num && msgs[i+1].flags & I2C_M_RD
-                                           && msgs[i+1].addr == msgs[i].addr) {
-                               prev_reg = msgs[i].buf[0];
-                               if (i2c_debug >= 2)
-                                       printk("\n");
-                               continue;
+                       if (addr == dev->tuner_addr  << 1) {
+                               tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
+                               tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
                        }
-
-                       rc = tm6000_read_write_usb (dev,
-                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                               REQ_16_SET_GET_I2CSEQ,
-                               addr|(*msgs[i].buf)<<8, 0,
-                               msgs[i].buf+1, msgs[i].len-1);
-
-                       prev_reg = -1;
                }
-               if (i2c_debug>=2)
+               if (i2c_debug >= 2)
                        printk("\n");
                if (rc < 0)
                        goto err;
@@ -181,7 +239,6 @@ err:
        return rc;
 }
 
-
 static int tm6000_i2c_eeprom(struct tm6000_core *dev,
                             unsigned char *eedata, int len)
 {
@@ -194,9 +251,7 @@ static int tm6000_i2c_eeprom(struct tm6000_core *dev,
        bytes[16] = '\0';
        for (i = 0; i < len; ) {
        *p = i;
-       rc = tm6000_read_write_usb (dev,
-               USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-               REQ_16_SET_GET_I2CSEQ, 0xa0 | i<<8, 0, p, 1);
+       rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1);
                if (rc < 1) {
                        if (p == eedata)
                                goto noeeprom;
@@ -242,15 +297,6 @@ noeeprom:
 /* ----------------------------------------------------------- */
 
 /*
- * algo_control()
- */
-static int algo_control(struct i2c_adapter *adapter,
-                       unsigned int cmd, unsigned long arg)
-{
-       return 0;
-}
-
-/*
  * functionality()
  */
 static u32 functionality(struct i2c_adapter *adap)
@@ -258,22 +304,10 @@ static u32 functionality(struct i2c_adapter *adap)
        return I2C_FUNC_SMBUS_EMUL;
 }
 
-#ifndef I2C_PEC
-static void inc_use(struct i2c_adapter *adap)
-{
-       MOD_INC_USE_COUNT;
-}
-
-static void dec_use(struct i2c_adapter *adap)
-{
-       MOD_DEC_USE_COUNT;
-}
-#endif
-
 #define mass_write(addr, reg, data...)                                 \
        { const static u8 _val[] = data;                                \
        rc=tm6000_read_write_usb(dev,USB_DIR_OUT | USB_TYPE_VENDOR,     \
-       REQ_16_SET_GET_I2CSEQ,(reg<<8)+addr, 0x00, (u8 *) _val,         \
+       REQ_16_SET_GET_I2C_WR1_RDN,(reg<<8)+addr, 0x00, (u8 *) _val,    \
        ARRAY_SIZE(_val));                                              \
        if (rc<0) {                                                     \
                printk(KERN_ERR "Error on line %d: %d\n",__LINE__,rc);  \
@@ -282,110 +316,17 @@ static void dec_use(struct i2c_adapter *adap)
        msleep (10);                                                    \
        }
 
-/* Tuner callback to provide the proper gpio changes needed for xc2028 */
-
-static int tm6000_tuner_callback(void *ptr, int command, int arg)
-{
-       int rc=0;
-       struct tm6000_core *dev = ptr;
-
-       if (dev->tuner_type!=TUNER_XC2028)
-               return 0;
-
-       switch (command) {
-       case XC2028_RESET_CLK:
-               tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT,
-                                       0x02, arg);
-               msleep(10);
-               rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
-                                       TM6000_GPIO_CLK, 0);
-               if (rc<0)
-                       return rc;
-               msleep(10);
-               rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
-                                       TM6000_GPIO_CLK, 1);
-               break;
-       case XC2028_TUNER_RESET:
-               /* Reset codes during load firmware */
-               switch (arg) {
-               case 0:
-                       tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
-                                       dev->tuner_reset_gpio, 0x00);
-                       msleep(130);
-                       tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
-                                       dev->tuner_reset_gpio, 0x01);
-                       msleep(130);
-                       break;
-               case 1:
-                       tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT,
-                                               0x02, 0x01);
-                       msleep(10);
-                       break;
-
-               case 2:
-                       rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
-                                               TM6000_GPIO_CLK, 0);
-                       if (rc<0)
-                               return rc;
-                       msleep(100);
-                       rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
-                                               TM6000_GPIO_CLK, 1);
-                       msleep(100);
-                       break;
-               }
-       }
-       return (rc);
-}
-
-static int attach_inform(struct i2c_client *client)
-{
-       struct tm6000_core *dev = client->adapter->algo_data;
-       struct tuner_setup tun_setup;
-
-       i2c_dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
-               client->driver->driver.name, client->addr, client->name);
-
-       switch (client->addr<<1) {
-       case 0xb0:
-               request_module("tvaudio");
-               return 0;
-       }
-
-       /* If tuner, initialize the tuner part */
-       if ( dev->tuner_addr != client->addr<<1 ) {
-               return 0;
-       }
-
-       memset (&tun_setup, 0, sizeof(tun_setup));
-
-       tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
-       tun_setup.type = dev->tuner_type;
-       tun_setup.addr = dev->tuner_addr>>1;
-       tun_setup.tuner_callback = tm6000_tuner_callback;
-
-       client->driver->command (client,TUNER_SET_TYPE_ADDR, &tun_setup);
-
-       return 0;
-}
-
 static struct i2c_algorithm tm6000_algo = {
        .master_xfer   = tm6000_i2c_xfer,
-       .algo_control  = algo_control,
        .functionality = functionality,
 };
 
 static struct i2c_adapter tm6000_adap_template = {
-#ifdef I2C_PEC
        .owner = THIS_MODULE,
-#else
-       .inc_use = inc_use,
-       .dec_use = dec_use,
-#endif
-       .class = I2C_CLASS_TV_ANALOG,
+       .class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
        .name = "tm6000",
        .id = I2C_HW_B_TM6000,
        .algo = &tm6000_algo,
-       .client_register = attach_inform,
 };
 
 static struct i2c_client tm6000_client_template = {
@@ -395,43 +336,6 @@ static struct i2c_client tm6000_client_template = {
 /* ----------------------------------------------------------- */
 
 /*
- * i2c_devs
- * incomplete list of known devices
- */
-static char *i2c_devs[128] = {
-       [0xc2 >> 1] = "tuner (analog)",
-};
-
-/*
- * do_i2c_scan()
- * check i2c address range for devices
- */
-static void do_i2c_scan(char *name, struct i2c_client *c)
-{
-       unsigned char buf;
-       int i, rc;
-
-       for (i = 0; i < 128; i++) {
-               c->addr = i;
-               rc = i2c_master_recv(c, &buf, 0);
-               if (rc < 0)
-                       continue;
-               printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n", name,
-                      i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
-       }
-}
-
-/*
- * tm6000_i2c_call_clients()
- * send commands to all attached i2c devices
- */
-void tm6000_i2c_call_clients(struct tm6000_core *dev, unsigned int cmd, void *arg)
-{
-       BUG_ON(NULL == dev->i2c_adap.algo_data);
-       i2c_clients_command(&dev->i2c_adap, cmd, arg);
-}
-
-/*
  * tm6000_i2c_register()
  * register i2c bus
  */
@@ -448,8 +352,7 @@ int tm6000_i2c_register(struct tm6000_core *dev)
        dev->i2c_client = tm6000_client_template;
        dev->i2c_client.adapter = &dev->i2c_adap;
 
-       if (i2c_scan)
-               do_i2c_scan(dev->name, &dev->i2c_client);
+       i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
 
        tm6000_i2c_eeprom(dev, eedata, sizeof(eedata));