V4L/DVB (13087): cx25840: Improve detection of CX2388[578] A/V cores
authorAndy Walls <awalls@radix.net>
Sun, 27 Sep 2009 02:32:54 +0000 (23:32 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sat, 5 Dec 2009 20:40:17 +0000 (18:40 -0200)
Add improved logic to detect the exact CX2388[578] A/V core that is being
probed.  Also cleaned up detection and logging for CX2310[12], CX2583[67],
and CX2584[0123] cores and chips.

Also added code to identify a CX2388[578] A/V decoder core that is not
responding properly.  Typical symptoms include registers 0x00-0xff
responding properly but all other registers returning the same value
(0x13 and 0x5 have been observed).  This state will cause proper detection of
'885 vs. '887 vs. '888 to fail and the chip won't respond to get configured
properly anyway.  I have no method of reseting the core to a working state at
this time; but I didn't try too hard to work one out either.  The problem
likely only occurs in development.  I suspect configuring the SYS PLL VCO to
oscillate too slowly (286.3 MHz?) before post divide may be the root cause,
when encountered.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/cx25840/cx25840-core.c

index 1aeaf18a9bea3010c2889380e22cd81d344d6f08..e836567299915ba4eaa3dc763f2f83a85776675c 100644 (file)
@@ -1521,12 +1521,35 @@ static const struct v4l2_subdev_ops cx25840_ops = {
 
 /* ----------------------------------------------------------------------- */
 
+static u32 get_cx2388x_ident(struct i2c_client *client)
+{
+       u32 ret;
+
+       /* Come out of digital power down */
+       cx25840_write(client, 0x000, 0);
+
+       if (cx25840_read4(client, 0x204) & 0xffff) {
+               /* IR Tx Clk Divider register exists; chip must be a CX23885 */
+               ret = V4L2_IDENT_CX23885_AV;
+       } else if (cx25840_read4(client, 0x300) & 0x0fffffff) {
+               /* DIF PLL Freq Word reg exists; chip must be a CX23888 */
+               ret = V4L2_IDENT_CX23888_AV;
+       } else {
+               /* A CX23887 A/V core has neither IR nor DIF */
+               ret = V4L2_IDENT_CX23887_AV;
+       }
+
+       /* Back into digital power down */
+       cx25840_write(client, 0x000, 2);
+       return ret;
+}
+
 static int cx25840_probe(struct i2c_client *client,
                         const struct i2c_device_id *did)
 {
        struct cx25840_state *state;
        struct v4l2_subdev *sd;
-       u32 id;
+       u32 id = V4L2_IDENT_NONE;
        u16 device_id;
 
        /* Check if the adapter supports the needed features */
@@ -1543,17 +1566,22 @@ static int cx25840_probe(struct i2c_client *client,
         * 0x83 for the cx2583x and 0x84 for the cx2584x */
        if ((device_id & 0xff00) == 0x8300) {
                id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
-       }
-       else if ((device_id & 0xff00) == 0x8400) {
+       } else if ((device_id & 0xff00) == 0x8400) {
                id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
        } else if (device_id == 0x0000) {
-               id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
-       } else if (device_id == 0x1313) {
-               id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+               id = get_cx2388x_ident(client);
        } else if ((device_id & 0xfff0) == 0x5A30) {
-               id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
-       }
-       else {
+               /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */
+               id = V4L2_IDENT_CX2310X_AV;
+       } else if ((device_id & 0xff) == (device_id >> 8)) {
+               v4l_err(client,
+                       "likely a confused/unresponsive cx2388[578] A/V decoder"
+                       " found @ 0x%x (%s)\n",
+                       client->addr << 1, client->adapter->name);
+               v4l_err(client, "A method to reset it from the cx25840 driver"
+                       " software is not known at this time\n");
+               return -ENODEV;
+       } else {
                v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
                return -ENODEV;
        }
@@ -1564,17 +1592,50 @@ static int cx25840_probe(struct i2c_client *client,
 
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
-       /* Note: revision '(device_id & 0x0f) == 2' was never built. The
-          marking skips from 0x1 == 22 to 0x3 == 23. */
-       v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
-                   (device_id & 0xfff0) >> 4,
-                   (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f),
-                   client->addr << 1, client->adapter->name);
+       switch (id) {
+       case V4L2_IDENT_CX23885_AV:
+               state->is_cx23885 = 1;
+               v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
+                        client->addr << 1, client->adapter->name);
+               break;
+       case V4L2_IDENT_CX23887_AV:
+               state->is_cx23885 = 1;
+               v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n",
+                        client->addr << 1, client->adapter->name);
+               break;
+       case V4L2_IDENT_CX23888_AV:
+               state->is_cx23885 = 1;
+               v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n",
+                        client->addr << 1, client->adapter->name);
+               break;
+       case V4L2_IDENT_CX2310X_AV:
+               state->is_cx231xx = 1;
+               v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n",
+                        device_id, client->addr << 1, client->adapter->name);
+               break;
+       case V4L2_IDENT_CX25840:
+       case V4L2_IDENT_CX25841:
+       case V4L2_IDENT_CX25842:
+       case V4L2_IDENT_CX25843:
+               /* Note: revision '(device_id & 0x0f) == 2' was never built. The
+                  marking skips from 0x1 == 22 to 0x3 == 23. */
+               v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
+                        (device_id & 0xfff0) >> 4,
+                        (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1
+                                               : (device_id & 0x0f),
+                        client->addr << 1, client->adapter->name);
+               break;
+       case V4L2_IDENT_CX25836:
+       case V4L2_IDENT_CX25837:
+               state->is_cx25836 = 1;
+       default:
+               v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n",
+                        (device_id & 0xfff0) >> 4, device_id & 0x0f,
+                        client->addr << 1, client->adapter->name);
+               break;
+       }
 
        state->c = client;
-       state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
-       state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
-       state->is_cx231xx = (device_id == 0x5a3e);
        state->vid_input = CX25840_COMPOSITE7;
        state->aud_input = CX25840_AUDIO8;
        state->audclk_freq = 48000;