[media] dw2102: add support for Geniatech SU3000 USB DVB-S2 card
authorIgor M. Liplianin <liplianin@me.by>
Fri, 25 Feb 2011 21:41:22 +0000 (18:41 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 21 Mar 2011 23:32:25 +0000 (20:32 -0300)
The card uses ds3000 demod from Montage.

Signed-off-by: Igor M. Liplianin <liplianin@me.by>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/dvb/dvb-usb/dw2102.c
drivers/media/dvb/frontends/ds3000.c
drivers/media/dvb/frontends/ds3000.h

index f9affdd..461c96a 100644 (file)
@@ -1,7 +1,8 @@
 /* DVB USB framework compliant Linux driver for the
 *      DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101,
 *      TeVii S600, S630, S650,
-*      Prof 1100, 7500 Cards
+*      Prof 1100, 7500,
+ *     Geniatech SU3000 Cards
 * Copyright (C) 2008,2009 Igor M. Liplianin (liplianin@me.by)
 *
 *      This program is free software; you can redistribute it and/or modify it
@@ -67,6 +68,7 @@
 #define REG_21_SYMBOLRATE_BYTE2 0x21
 /* on my own*/
 #define DW2102_VOLTAGE_CTRL (0x1800)
+#define SU3000_STREAM_CTRL (0x1900)
 #define DW2102_RC_QUERY (0x1a00)
 
 #define        err_str "did not find the firmware file. (%s) " \
@@ -78,6 +80,10 @@ struct rc_map_dvb_usb_table_table {
        int rc_keys_size;
 };
 
+struct su3000_state {
+       u8 initialized;
+};
+
 /* debug */
 static int dvb_usb_dw2102_debug;
 module_param_named(debug, dvb_usb_dw2102_debug, int, 0644);
@@ -570,6 +576,70 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
        return num;
 }
 
+static int su3000_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+                                                               int num)
+{
+       struct dvb_usb_device *d = i2c_get_adapdata(adap);
+       u8 obuf[0x40], ibuf[0x40];
+
+       if (!d)
+               return -ENODEV;
+       if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+               return -EAGAIN;
+
+       switch (num) {
+       case 1:
+               switch (msg[0].addr) {
+               case SU3000_STREAM_CTRL:
+                       obuf[0] = msg[0].buf[0] + 0x36;
+                       obuf[1] = 3;
+                       obuf[2] = 0;
+                       if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 0, 0) < 0)
+                               err("i2c transfer failed.");
+                       break;
+               case DW2102_RC_QUERY:
+                       obuf[0] = 0x10;
+                       if (dvb_usb_generic_rw(d, obuf, 1, ibuf, 2, 0) < 0)
+                               err("i2c transfer failed.");
+                       msg[0].buf[1] = ibuf[0];
+                       msg[0].buf[0] = ibuf[1];
+                       break;
+               default:
+                       /* always i2c write*/
+                       obuf[0] = 0x08;
+                       obuf[1] = msg[0].addr;
+                       obuf[2] = msg[0].len;
+
+                       memcpy(&obuf[3], msg[0].buf, msg[0].len);
+
+                       if (dvb_usb_generic_rw(d, obuf, msg[0].len + 3,
+                                               ibuf, 1, 0) < 0)
+                               err("i2c transfer failed.");
+
+               }
+               break;
+       case 2:
+               /* always i2c read */
+               obuf[0] = 0x09;
+               obuf[1] = msg[0].len;
+               obuf[2] = msg[1].len;
+               obuf[3] = msg[0].addr;
+               memcpy(&obuf[4], msg[0].buf, msg[0].len);
+
+               if (dvb_usb_generic_rw(d, obuf, msg[0].len + 4,
+                                       ibuf, msg[1].len + 1, 0) < 0)
+                       err("i2c transfer failed.");
+
+               memcpy(msg[1].buf, &ibuf[1], msg[1].len);
+               break;
+       default:
+               warn("more than 2 i2c messages at a time is not handled yet.");
+               break;
+       }
+       mutex_unlock(&d->i2c_mutex);
+       return num;
+}
+
 static u32 dw210x_i2c_func(struct i2c_adapter *adapter)
 {
        return I2C_FUNC_I2C;
@@ -605,6 +675,11 @@ static struct i2c_algorithm s6x0_i2c_algo = {
        .functionality = dw210x_i2c_func,
 };
 
+static struct i2c_algorithm su3000_i2c_algo = {
+       .master_xfer = su3000_i2c_transfer,
+       .functionality = dw210x_i2c_func,
+};
+
 static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
 {
        int i;
@@ -669,6 +744,82 @@ static int s6x0_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
        return 0;
 };
 
+static int su3000_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+       static u8 command_start[] = {0x00};
+       static u8 command_stop[] = {0x01};
+       struct i2c_msg msg = {
+               .addr = SU3000_STREAM_CTRL,
+               .flags = 0,
+               .buf = onoff ? command_start : command_stop,
+               .len = 1
+       };
+
+       i2c_transfer(&adap->dev->i2c_adap, &msg, 1);
+
+       return 0;
+}
+
+static int su3000_power_ctrl(struct dvb_usb_device *d, int i)
+{
+       struct su3000_state *state = (struct su3000_state *)d->priv;
+       u8 obuf[] = {0xde, 0};
+
+       info("%s: %d, initialized %d\n", __func__, i, state->initialized);
+
+       if (i && !state->initialized) {
+               state->initialized = 1;
+               /* reset board */
+               dvb_usb_generic_rw(d, obuf, 2, NULL, 0, 0);
+       }
+
+       return 0;
+}
+
+static int su3000_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+       int i;
+       u8 obuf[] = { 0x1f, 0xf0 };
+       u8 ibuf[] = { 0 };
+       struct i2c_msg msg[] = {
+               {
+                       .addr = 0x51,
+                       .flags = 0,
+                       .buf = obuf,
+                       .len = 2,
+               }, {
+                       .addr = 0x51,
+                       .flags = I2C_M_RD,
+                       .buf = ibuf,
+                       .len = 1,
+
+               }
+       };
+
+       for (i = 0; i < 6; i++) {
+               obuf[1] = 0xf0 + i;
+               if (i2c_transfer(&d->i2c_adap, msg, 2) != 2)
+                       break;
+               else
+                       mac[i] = ibuf[0];
+
+               debug_dump(mac, 6, printk);
+       }
+
+       return 0;
+}
+
+static int su3000_identify_state(struct usb_device *udev,
+                                struct dvb_usb_device_properties *props,
+                                struct dvb_usb_device_description **desc,
+                                int *cold)
+{
+       info("%s\n", __func__);
+
+       *cold = 0;
+       return 0;
+}
+
 static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
 {
        static u8 command_13v[] = {0x00, 0x01};
@@ -774,6 +925,11 @@ static struct stv0900_config prof_7500_stv0900_config = {
        .tun1_type = 3,
 };
 
+static struct ds3000_config su3000_ds3000_config = {
+       .demod_address = 0x68,
+       .ci_mode = 1,
+};
+
 static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
 {
        struct dvb_tuner_ops *tuner_ops = NULL;
@@ -944,6 +1100,43 @@ static int prof_7500_frontend_attach(struct dvb_usb_adapter *d)
        return 0;
 }
 
+static int su3000_frontend_attach(struct dvb_usb_adapter *d)
+{
+       u8 obuf[3] = { 0xe, 0x80, 0 };
+       u8 ibuf[] = { 0 };
+
+       if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+               err("command 0x0e transfer failed.");
+
+       obuf[0] = 0xe;
+       obuf[1] = 0x83;
+       obuf[2] = 0;
+
+       if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+               err("command 0x0e transfer failed.");
+
+       obuf[0] = 0xe;
+       obuf[1] = 0x83;
+       obuf[2] = 1;
+
+       if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+               err("command 0x0e transfer failed.");
+
+       obuf[0] = 0x51;
+
+       if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0)
+               err("command 0x51 transfer failed.");
+
+       d->fe = dvb_attach(ds3000_attach, &su3000_ds3000_config,
+                                       &d->dev->i2c_adap);
+       if (d->fe == NULL)
+               return -EIO;
+
+       info("Attached DS3000!\n");
+
+       return 0;
+}
+
 static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
 {
        dvb_attach(dvb_pll_attach, adap->fe, 0x60,
@@ -1078,10 +1271,49 @@ static struct rc_map_table rc_map_tbs_table[] = {
        { 0xf89b, KEY_MODE }
 };
 
+static struct rc_map_table rc_map_su3000_table[] = {
+       { 0x25, KEY_POWER },    /* right-bottom Red */
+       { 0x0a, KEY_MUTE },     /* -/-- */
+       { 0x01, KEY_1 },
+       { 0x02, KEY_2 },
+       { 0x03, KEY_3 },
+       { 0x04, KEY_4 },
+       { 0x05, KEY_5 },
+       { 0x06, KEY_6 },
+       { 0x07, KEY_7 },
+       { 0x08, KEY_8 },
+       { 0x09, KEY_9 },
+       { 0x00, KEY_0 },
+       { 0x20, KEY_UP },       /* CH+ */
+       { 0x21, KEY_DOWN },     /* CH+ */
+       { 0x12, KEY_VOLUMEUP }, /* Brightness Up */
+       { 0x13, KEY_VOLUMEDOWN },/* Brightness Down */
+       { 0x1f, KEY_RECORD },
+       { 0x17, KEY_PLAY },
+       { 0x16, KEY_PAUSE },
+       { 0x0b, KEY_STOP },
+       { 0x27, KEY_FASTFORWARD },/* >> */
+       { 0x26, KEY_REWIND },   /* << */
+       { 0x0d, KEY_OK },       /* Mute */
+       { 0x11, KEY_LEFT },     /* VOL- */
+       { 0x10, KEY_RIGHT },    /* VOL+ */
+       { 0x29, KEY_BACK },     /* button under 9 */
+       { 0x2c, KEY_MENU },     /* TTX */
+       { 0x2b, KEY_EPG },      /* EPG */
+       { 0x1e, KEY_RED },      /* OSD */
+       { 0x0e, KEY_GREEN },    /* Window */
+       { 0x2d, KEY_YELLOW },   /* button under << */
+       { 0x0f, KEY_BLUE },     /* bottom yellow button */
+       { 0x14, KEY_AUDIO },    /* Snapshot */
+       { 0x38, KEY_TV },       /* TV/Radio */
+       { 0x0c, KEY_ESC }       /* upper Red buttton */
+};
+
 static struct rc_map_dvb_usb_table_table keys_tables[] = {
        { rc_map_dw210x_table, ARRAY_SIZE(rc_map_dw210x_table) },
        { rc_map_tevii_table, ARRAY_SIZE(rc_map_tevii_table) },
        { rc_map_tbs_table, ARRAY_SIZE(rc_map_tbs_table) },
+       { rc_map_su3000_table, ARRAY_SIZE(rc_map_su3000_table) },
 };
 
 static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
@@ -1137,6 +1369,7 @@ static struct usb_device_id dw2102_table[] = {
        {USB_DEVICE(0x3011, USB_PID_PROF_1100)},
        {USB_DEVICE(0x9022, USB_PID_TEVII_S660)},
        {USB_DEVICE(0x3034, 0x7500)},
+       {USB_DEVICE(0x1f4d, 0x3000)},
        { }
 };
 
@@ -1473,6 +1706,51 @@ static struct dvb_usb_device_description d7500 = {
        {NULL},
 };
 
+static struct dvb_usb_device_properties su3000_properties = {
+       .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+       .usb_ctrl = DEVICE_SPECIFIC,
+       .size_of_priv = sizeof(struct su3000_state),
+       .power_ctrl = su3000_power_ctrl,
+       .num_adapters = 1,
+       .identify_state = su3000_identify_state,
+       .i2c_algo = &su3000_i2c_algo,
+
+       .rc.legacy = {
+               .rc_map_table = rc_map_su3000_table,
+               .rc_map_size = ARRAY_SIZE(rc_map_su3000_table),
+               .rc_interval = 150,
+               .rc_query = dw2102_rc_query,
+       },
+
+       .read_mac_address = su3000_read_mac_address,
+
+       .generic_bulk_ctrl_endpoint = 0x01,
+
+       .adapter = {
+               {
+                       .streaming_ctrl   = su3000_streaming_ctrl,
+                       .frontend_attach  = su3000_frontend_attach,
+                       .stream = {
+                               .type = USB_BULK,
+                               .count = 8,
+                               .endpoint = 0x82,
+                               .u = {
+                                       .bulk = {
+                                               .buffersize = 4096,
+                                       }
+                               }
+                       }
+               }
+       },
+       .num_device_descs = 1,
+       .devices = {
+               { "SU3000HD DVB-S USB2.0",
+                       { &dw2102_table[10], NULL },
+                       { NULL },
+               },
+       }
+};
+
 static int dw2102_probe(struct usb_interface *intf,
                const struct usb_device_id *id)
 {
@@ -1527,7 +1805,9 @@ static int dw2102_probe(struct usb_interface *intf,
            0 == dvb_usb_device_init(intf, s660,
                        THIS_MODULE, NULL, adapter_nr) ||
            0 == dvb_usb_device_init(intf, p7500,
-                       THIS_MODULE, NULL, adapter_nr))
+                       THIS_MODULE, NULL, adapter_nr) ||
+           0 == dvb_usb_device_init(intf, &su3000_properties,
+                                    THIS_MODULE, NULL, adapter_nr))
                return 0;
 
        return -ENODEV;
@@ -1561,6 +1841,7 @@ MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by");
 MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104,"
                                " DVB-C 3101 USB2.0,"
                                " TeVii S600, S630, S650, S660 USB2.0,"
-                               " Prof 1100, 7500 USB2.0 devices");
+                               " Prof 1100, 7500 USB2.0,"
+                               " Geniatech SU3000 devices");
 MODULE_VERSION("0.1");
 MODULE_LICENSE("GPL");
index 9933573..3373890 100644 (file)
@@ -509,6 +509,33 @@ static int ds3000_load_firmware(struct dvb_frontend *fe,
        return 0;
 }
 
+static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       u8 data;
+
+       dprintk("%s(%d)\n", __func__, voltage);
+
+       data = ds3000_readreg(state, 0xa2);
+       data |= 0x03; /* bit0 V/H, bit1 off/on */
+
+       switch (voltage) {
+       case SEC_VOLTAGE_18:
+               data &= ~0x03;
+               break;
+       case SEC_VOLTAGE_13:
+               data &= ~0x03;
+               data |= 0x01;
+               break;
+       case SEC_VOLTAGE_OFF:
+               break;
+       }
+
+       ds3000_writereg(state, 0xa2, data);
+
+       return 0;
+}
+
 static void ds3000_dump_registers(struct dvb_frontend *fe)
 {
        struct ds3000_state *state = fe->demodulator_priv;
@@ -1255,6 +1282,18 @@ static int ds3000_tune(struct dvb_frontend *fe,
                ds3000_writereg(state, 0xfd, 0x42);
                ds3000_writereg(state, 0x08, 0x07);*/
 
+               if (state->config->ci_mode) {
+                       switch (c->delivery_system) {
+                       case SYS_DVBS:
+                       default:
+                               ds3000_writereg(state, 0xfd, 0x80);
+                       break;
+                       case SYS_DVBS2:
+                               ds3000_writereg(state, 0xfd, 0x01);
+                               break;
+                       }
+               }
+
                /* ds3000 out of software reset */
                ds3000_writereg(state, 0x00, 0x00);
                /* start ds3000 build-in uC */
@@ -1351,6 +1390,7 @@ static struct dvb_frontend_ops ds3000_ops = {
        .read_signal_strength = ds3000_read_signal_strength,
        .read_snr = ds3000_read_snr,
        .read_ucblocks = ds3000_read_ucblocks,
+       .set_voltage = ds3000_set_voltage,
        .set_tone = ds3000_set_tone,
        .diseqc_send_master_cmd = ds3000_send_diseqc_msg,
        .diseqc_send_burst = ds3000_diseqc_send_burst,
index 67f6703..00e4e95 100644 (file)
@@ -27,6 +27,7 @@
 struct ds3000_config {
        /* the demodulator's i2c address */
        u8 demod_address;
+       u8 ci_mode;
 };
 
 #if defined(CONFIG_DVB_DS3000) || \