V4L/DVB (13656): tw9910: tw9910_set_hsync clean up
[deliverable/linux.git] / drivers / media / video / tw9910.c
index 269ab044072a86939d263aa82da0fbd36e23b0b5..2256ba6e614721ad00ddd3a55ad86e2bf6297d40 100644 (file)
@@ -29,7 +29,7 @@
 #include <media/tw9910.h>
 
 #define GET_ID(val)  ((val & 0xF8) >> 3)
-#define GET_ReV(val) (val & 0x07)
+#define GET_REV(val) (val & 0x07)
 
 /*
  * register offset
                         /* 1 : non-auto */
 #define VSCTL       0x08 /* 1 : Vertical out ctrl by DVALID */
                         /* 0 : Vertical out ctrl by HACTIVE and DVALID */
-#define OEN         0x04 /* Output Enable together with TRI_SEL. */
+#define OEN_TRI_SEL_MASK       0x07
+#define OEN_TRI_SEL_ALL_ON     0x00 /* Enable output for Rev0/Rev1 */
+#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */
+#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */
 
 /* OUTCTR1 */
 #define VSP_LO      0x00 /* 0 : VS pin output polarity is active low */
                          * but all register content remain unchanged.
                          * This bit is self-resetting.
                          */
+#define ACNTL1_PDN_MASK        0x0e
+#define CLK_PDN                0x08 /* system clock power down */
+#define Y_PDN          0x04 /* Luma ADC power down */
+#define C_PDN          0x02 /* Chroma ADC power down */
+
+/* ACNTL2 */
+#define ACNTL2_PDN_MASK        0x40
+#define PLL_PDN                0x40 /* PLL power down */
 
 /* VBICNTL */
-/* RTSEL : control the real time signal
-*          output from the MPOUT pin
-*/
+
+/* RTSEL : control the real time signal output from the MPOUT pin */
 #define RTSEL_MASK  0x07
 #define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
 #define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
@@ -226,6 +236,7 @@ struct tw9910_priv {
        struct v4l2_subdev                subdev;
        struct tw9910_video_info       *info;
        const struct tw9910_scale_ctrl *scale;
+       u32                             revision;
 };
 
 /*
@@ -236,7 +247,6 @@ struct tw9910_priv {
 
 static const struct regval_list tw9910_default_regs[] =
 {
-       { OPFORM,  0x00 },
        { OUTCTR1, VSP_LO | VSSL_VVALID | HSP_HI | HSSL_HSYNC },
        ENDMARKER,
 };
@@ -361,6 +371,19 @@ static struct tw9910_priv *to_tw9910(const struct i2c_client *client)
                            subdev);
 }
 
+static int tw9910_mask_set(struct i2c_client *client, u8 command,
+                          u8 mask, u8 set)
+{
+       s32 val = i2c_smbus_read_byte_data(client, command);
+       if (val < 0)
+               return val;
+
+       val &= ~mask;
+       val |= set & mask;
+
+       return i2c_smbus_write_byte_data(client, command, val);
+}
+
 static int tw9910_set_scale(struct i2c_client *client,
                            const struct tw9910_scale_ctrl *scale)
 {
@@ -435,14 +458,9 @@ static int tw9910_set_hsync(struct i2c_client *client,
                return ret;
 
        /* bit 2 - 0 */
-       ret = i2c_smbus_read_byte_data(client, HSLOWCTL);
-       if (ret < 0)
-               return ret;
-
-       ret = i2c_smbus_write_byte_data(client, HSLOWCTL,
-                                       (ret & 0x88)                 |
-                                       (hsync->start & 0x0007) << 4 |
-                                       (hsync->end   & 0x0007));
+       ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
+                             (hsync->start & 0x0007) << 4 |
+                             (hsync->end   & 0x0007));
 
        return ret;
 }
@@ -461,23 +479,31 @@ static int tw9910_write_array(struct i2c_client *client,
        return 0;
 }
 
-static int tw9910_mask_set(struct i2c_client *client, u8 command,
-                          u8 mask, u8 set)
+static void tw9910_reset(struct i2c_client *client)
 {
-       s32 val = i2c_smbus_read_byte_data(client, command);
-       if (val < 0)
-               return val;
-
-       val &= ~mask;
-       val |= set & mask;
-
-       return i2c_smbus_write_byte_data(client, command, val);
+       tw9910_mask_set(client, ACNTL1, SRESET, SRESET);
+       msleep(1);
 }
 
-static void tw9910_reset(struct i2c_client *client)
+static int tw9910_power(struct i2c_client *client, int enable)
 {
-       i2c_smbus_write_byte_data(client, ACNTL1, SRESET);
-       msleep(1);
+       int ret;
+       u8 acntl1;
+       u8 acntl2;
+
+       if (enable) {
+               acntl1 = 0;
+               acntl2 = 0;
+       } else {
+               acntl1 = CLK_PDN | Y_PDN | C_PDN;
+               acntl2 = PLL_PDN;
+       }
+
+       ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1);
+       if (ret < 0)
+               return ret;
+
+       return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
 }
 
 static const struct tw9910_scale_ctrl*
@@ -518,21 +544,40 @@ static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
 {
        struct i2c_client *client = sd->priv;
        struct tw9910_priv *priv = to_tw9910(client);
+       u8 val;
+       int ret;
 
-       if (!enable)
-               return 0;
+       if (!enable) {
+               switch (priv->revision) {
+               case 0:
+                       val = OEN_TRI_SEL_ALL_OFF_r0;
+                       break;
+               case 1:
+                       val = OEN_TRI_SEL_ALL_OFF_r1;
+                       break;
+               default:
+                       dev_err(&client->dev, "un-supported revision\n");
+                       return -EINVAL;
+               }
+       } else {
+               val = OEN_TRI_SEL_ALL_ON;
 
-       if (!priv->scale) {
-               dev_err(&client->dev, "norm select error\n");
-               return -EPERM;
+               if (!priv->scale) {
+                       dev_err(&client->dev, "norm select error\n");
+                       return -EPERM;
+               }
+
+               dev_dbg(&client->dev, "%s %dx%d\n",
+                       priv->scale->name,
+                       priv->scale->width,
+                       priv->scale->height);
        }
 
-       dev_dbg(&client->dev, "%s %dx%d\n",
-                priv->scale->name,
-                priv->scale->width,
-                priv->scale->height);
+       ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val);
+       if (ret < 0)
+               return ret;
 
-       return 0;
+       return tw9910_power(client, enable);
 }
 
 static int tw9910_set_bus_param(struct soc_camera_device *icd,
@@ -576,8 +621,11 @@ static int tw9910_enum_input(struct soc_camera_device *icd,
 static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
                               struct v4l2_dbg_chip_ident *id)
 {
+       struct i2c_client *client = sd->priv;
+       struct tw9910_priv *priv = to_tw9910(client);
+
        id->ident = V4L2_IDENT_TW9910;
-       id->revision = 0;
+       id->revision = priv->revision;
 
        return 0;
 }
@@ -596,7 +644,8 @@ static int tw9910_g_register(struct v4l2_subdev *sd,
        if (ret < 0)
                return ret;
 
-       /* ret      = int
+       /*
+        * ret      = int
         * reg->val = __u64
         */
        reg->val = (__u64)ret;
@@ -859,7 +908,7 @@ static int tw9910_video_probe(struct soc_camera_device *icd,
                              struct i2c_client *client)
 {
        struct tw9910_priv *priv = to_tw9910(client);
-       s32 val;
+       s32 id;
 
        /*
         * We must have a parent by now. And it cannot be a wrong one.
@@ -883,18 +932,22 @@ static int tw9910_video_probe(struct soc_camera_device *icd,
 
        /*
         * check and show Product ID
+        * So far only revisions 0 and 1 have been seen
         */
-       val = i2c_smbus_read_byte_data(client, ID);
+       id = i2c_smbus_read_byte_data(client, ID);
+       priv->revision = GET_REV(id);
+       id = GET_ID(id);
 
-       if (0x0B != GET_ID(val) ||
-           0x00 != GET_ReV(val)) {
+       if (0x0B != id ||
+           0x01 < priv->revision) {
                dev_err(&client->dev,
-                       "Product ID error %x:%x\n", GET_ID(val), GET_ReV(val));
+                       "Product ID error %x:%x\n",
+                       id, priv->revision);
                return -ENODEV;
        }
 
        dev_info(&client->dev,
-                "tw9910 Product ID %0x:%0x\n", GET_ID(val), GET_ReV(val));
+                "tw9910 Product ID %0x:%0x\n", id, priv->revision);
 
        icd->vdev->tvnorms      = V4L2_STD_NTSC | V4L2_STD_PAL;
        icd->vdev->current_norm = V4L2_STD_NTSC;
@@ -954,10 +1007,10 @@ static int tw9910_probe(struct i2c_client *client,
        }
 
        icl = to_soc_camera_link(icd);
-       if (!icl)
+       if (!icl || !icl->priv)
                return -EINVAL;
 
-       info = container_of(icl, struct tw9910_video_info, link);
+       info = icl->priv;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
                dev_err(&client->dev,
@@ -975,7 +1028,7 @@ static int tw9910_probe(struct i2c_client *client,
        v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
 
        icd->ops     = &tw9910_ops;
-       icd->iface   = info->link.bus_id;
+       icd->iface   = icl->bus_id;
 
        ret = tw9910_video_probe(icd, client);
        if (ret) {
This page took 0.046983 seconds and 5 git commands to generate.