V4L/DVB (11158): pvrusb2: New device attribute mechanism to specify sub-devices
authorMike Isely <isely@pobox.com>
Sat, 7 Mar 2009 02:42:20 +0000 (23:42 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 30 Mar 2009 15:43:33 +0000 (12:43 -0300)
Set up new mechanism for declaring and loading appropriate sub-devices
when driver initializes.  This is another part of the v4l2-subdev
adoption.

Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/pvrusb2/pvrusb2-devattr.h
drivers/media/video/pvrusb2/pvrusb2-hdw.c
drivers/media/video/pvrusb2/pvrusb2-i2c-core.c

index cb3a33e..f069239 100644 (file)
 */
 
 
+#define PVR2_CLIENT_ID_MSP3400 1
+#define PVR2_CLIENT_ID_CX25840 2
+#define PVR2_CLIENT_ID_SAA7115 3
+#define PVR2_CLIENT_ID_TUNER 4
+#define PVR2_CLIENT_ID_CS53132A 5
+
+struct pvr2_device_client_desc {
+       /* One ovr PVR2_CLIENT_ID_xxxx */
+       unsigned char module_id;
+
+       /* Null-terminated array of I2C addresses to try in order
+          initialize the module.  It's safe to make this null terminated
+          since we're never going to encounter an i2c device with an
+          address of zero.  If this is a null pointer or zero-length,
+          then no I2C addresses have been specified, in which case we'll
+          try some compiled in defaults for now. */
+       unsigned char *i2c_address_list;
+};
+
+struct pvr2_device_client_table {
+       const struct pvr2_device_client_desc *lst;
+       unsigned char cnt;
+};
+
+
 struct pvr2_string_table {
        const char **lst;
        unsigned int cnt;
@@ -66,6 +91,9 @@ struct pvr2_device_desc {
        /* List of additional client modules we need to load */
        struct pvr2_string_table client_modules;
 
+       /* List of defined client modules we need to load */
+       struct pvr2_device_client_table client_table;
+
        /* List of FX2 firmware file names we should search; if empty then
           FX2 firmware check / load is skipped and we assume the device
           was initialized from internal ROM. */
index e6c4660..faa94ce 100644 (file)
@@ -105,6 +105,20 @@ MODULE_PARM_DESC(radio_freq, "specify initial radio frequency");
 /* size of a firmware chunk */
 #define FIRMWARE_CHUNK_SIZE 0x2000
 
+static const char *module_names[] = {
+       [PVR2_CLIENT_ID_MSP3400] = "msp3400",
+       [PVR2_CLIENT_ID_CX25840] = "cx25840",
+       [PVR2_CLIENT_ID_SAA7115] = "saa7115",
+       [PVR2_CLIENT_ID_TUNER] = "tuner",
+       [PVR2_CLIENT_ID_CS53132A] = "cs53132a",
+};
+
+
+static const unsigned char *module_i2c_addresses[] = {
+       [PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63",
+};
+
+
 /* Define the list of additional controls we'll dynamically construct based
    on query of the cx2341x module. */
 struct pvr2_mpeg_ids {
@@ -1934,6 +1948,105 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
 }
 
 
+static unsigned int pvr2_copy_i2c_addr_list(
+       unsigned short *dst, const unsigned char *src,
+       unsigned int dst_max)
+{
+       unsigned int cnt;
+       if (!src) return 0;
+       while (src[cnt] && (cnt + 1) < dst_max) {
+               dst[cnt] = src[cnt];
+               cnt++;
+       }
+       dst[cnt] = I2C_CLIENT_END;
+       return cnt;
+}
+
+
+static void pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
+                                const struct pvr2_device_client_desc *cd)
+{
+       const char *fname;
+       unsigned char mid;
+       struct v4l2_subdev *sd;
+       unsigned int i2ccnt;
+       const unsigned char *p;
+       /* Arbitrary count - max # i2c addresses we will probe */
+       unsigned short i2caddr[25];
+
+       mid = cd->module_id;
+       fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL;
+       if (!fname) {
+               pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                          "Module ID %u for device %s is unknown"
+                          " (this is probably a bad thing...)",
+                          mid,
+                          hdw->hdw_desc->description);
+               return;
+       }
+
+       i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list,
+                                        ARRAY_SIZE(i2caddr));
+       if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ?
+                        module_i2c_addresses[mid] : NULL) != NULL)) {
+               /* Second chance: Try default i2c address list */
+               i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p,
+                                                ARRAY_SIZE(i2caddr));
+       }
+
+       if (!i2ccnt) {
+               pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                          "Module ID %u for device %s:"
+                          " No i2c addresses"
+                          " (this is probably a bad thing...)",
+                          mid, hdw->hdw_desc->description);
+               return;
+       }
+
+       /* Note how the 2nd and 3rd arguments are the same for both
+        * v4l2_i2c_new_subdev() and v4l2_i2c_new_probed_subdev().  Why?
+        * Well the 2nd argument is the module name to load, while the 3rd
+        * argument is documented in the framework as being the "chipid" -
+        * and every other place where I can find examples of this, the
+        * "chipid" appears to just be the module name again.  So here we
+        * just do the same thing. */
+       if (i2ccnt == 1) {
+               sd = v4l2_i2c_new_subdev(&hdw->i2c_adap,
+                                        fname, fname,
+                                        i2caddr[0]);
+       } else {
+               sd = v4l2_i2c_new_probed_subdev(&hdw->i2c_adap,
+                                               fname, fname,
+                                               i2caddr);
+       }
+
+       // ?????
+       /* Based on module ID, we should remember subdev pointers
+          so that we can send certain custom commands where
+          needed. */
+       // ?????
+
+}
+
+
+static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw)
+{
+       unsigned int idx;
+       const struct pvr2_string_table *cm;
+       const struct pvr2_device_client_table *ct;
+
+       cm = &hdw->hdw_desc->client_modules;
+       for (idx = 0; idx < cm->cnt; idx++) {
+               request_module(cm->lst[idx]);
+       }
+
+       ct = &hdw->hdw_desc->client_table;
+       for (idx = 0; idx < ct->cnt; idx++) {
+               pvr2_hdw_load_subdev(hdw,&ct->lst[idx]);
+       }
+}
+
+
 static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
 {
        int ret;
@@ -1973,10 +2086,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
 
        if (!pvr2_hdw_dev_ok(hdw)) return;
 
-       for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) {
-               request_module(hdw->hdw_desc->client_modules.lst[idx]);
-       }
-
        if (!hdw->hdw_desc->flag_no_powerup) {
                pvr2_hdw_cmd_powerup(hdw);
                if (!pvr2_hdw_dev_ok(hdw)) return;
@@ -1995,6 +2104,8 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
        pvr2_i2c_core_init(hdw);
        if (!pvr2_hdw_dev_ok(hdw)) return;
 
+       pvr2_hdw_load_modules(hdw);
+
        for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
                cptr = hdw->controls + idx;
                if (cptr->info->skip_init) continue;
index 2ba429f..1129fe4 100644 (file)
@@ -608,6 +608,7 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
        hdw->i2c_adap.algo = &hdw->i2c_algo;
        hdw->i2c_adap.algo_data = hdw;
        hdw->i2c_linked = !0;
+       i2c_set_adapdata(&hdw->i2c_adap, &hdw->v4l2_dev);
        i2c_add_adapter(&hdw->i2c_adap);
        if (hdw->i2c_func[0x18] == i2c_24xxx_ir) {
                /* Probe for a different type of IR receiver on this