V4L/DVB: saa7115: support new vbi ops to set/get VBI format
[safe/jmp/linux-2.6] / drivers / media / video / saa7115.c
index b1c5f63..cf77318 100644 (file)
@@ -46,7 +46,7 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv-legacy.h>
+#include <media/v4l2-i2c-drv.h>
 #include <media/saa7115.h>
 #include <asm/div64.h>
 
@@ -62,12 +62,6 @@ module_param(debug, bool, 0644);
 
 MODULE_PARM_DESC(debug, "Debug level (0-1)");
 
-static unsigned short normal_i2c[] = {
-               0x4a >> 1, 0x48 >> 1,   /* SAA7111, SAA7111A and SAA7113 */
-               0x42 >> 1, 0x40 >> 1,   /* SAA7114, SAA7115 and SAA7118 */
-               I2C_CLIENT_END };
-
-I2C_CLIENT_INSMOD;
 
 struct saa711x_state {
        struct v4l2_subdev sd;
@@ -80,6 +74,7 @@ struct saa711x_state {
        int contrast;
        int hue;
        int sat;
+       int chroma_agc;
        int width;
        int height;
        u32 ident;
@@ -110,6 +105,10 @@ static int saa711x_has_reg(const int id, const u8 reg)
        if (id == V4L2_IDENT_SAA7111)
                return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
                       (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e;
+       if (id == V4L2_IDENT_SAA7111A)
+               return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
+                      reg != 0x14 && reg != 0x18 && reg != 0x19 &&
+                      reg != 0x1d && reg != 0x1e;
 
        /* common for saa7113/4/5/8 */
        if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f ||
@@ -594,7 +593,7 @@ static const unsigned char saa7115_init_misc[] = {
        R_5D_DID, 0xbd,
        R_5E_SDID, 0x35,
 
-       R_02_INPUT_CNTL_1, 0x84,                /* input tuner -> input 4, amplifier active */
+       R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */
 
        R_80_GLOBAL_CNTL_1, 0x20,               /* enable task B */
        R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,
@@ -745,6 +744,7 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
 static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
        struct saa711x_state *state = to_state(sd);
+       u8 val;
 
        switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
@@ -786,7 +786,21 @@ static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
                state->hue = ctrl->value;
                saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue);
                break;
-
+       case V4L2_CID_CHROMA_AGC:
+               val = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL);
+               state->chroma_agc = ctrl->value;
+               if (ctrl->value)
+                       val &= 0x7f;
+               else
+                       val |= 0x80;
+               saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, val);
+               break;
+       case V4L2_CID_CHROMA_GAIN:
+               /* Chroma gain cannot be set when AGC is enabled */
+               if (state->chroma_agc == 1)
+                       return -EINVAL;
+               saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, ctrl->value | 0x80);
+               break;
        default:
                return -EINVAL;
        }
@@ -811,6 +825,12 @@ static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
        case V4L2_CID_HUE:
                ctrl->value = state->hue;
                break;
+       case V4L2_CID_CHROMA_AGC:
+               ctrl->value = state->chroma_agc;
+               break;
+       case V4L2_CID_CHROMA_GAIN:
+               ctrl->value = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
+               break;
        default:
                return -EINVAL;
        }
@@ -931,8 +951,8 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
        /* Prevent unnecessary standard changes. During a standard
           change the I-Port is temporarily disabled. Any devices
           reading from that port can get confused.
-          Note that VIDIOC_S_STD is also used to switch from
-          radio to TV mode, so if a VIDIOC_S_STD is broadcast to
+          Note that s_std is also used to switch from
+          radio to TV mode, so if a s_std is broadcast to
           all I2C devices then you do not want to have an unwanted
           side-effect here. */
        if (std == state->std)
@@ -960,8 +980,7 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
        011 NTSC N (3.58MHz)            PAL M (3.58MHz)
        100 reserved                    NTSC-Japan (3.58MHz)
        */
-       if (state->ident == V4L2_IDENT_SAA7111 ||
-           state->ident == V4L2_IDENT_SAA7113) {
+       if (state->ident <= V4L2_IDENT_SAA7113) {
                u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
 
                if (std == V4L2_STD_PAL_M) {
@@ -1072,7 +1091,7 @@ static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_forma
                                saa7115_cfg_vbi_off);
 }
 
-static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced)
 {
        static u16 lcr2vbi[] = {
                0, V4L2_SLICED_TELETEXT_B, 0,   /* 1 */
@@ -1081,11 +1100,8 @@ static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
                V4L2_SLICED_VPS, 0, 0, 0, 0,    /* 7 */
                0, 0, 0, 0
        };
-       struct v4l2_sliced_vbi_format *sliced = &fmt->fmt.sliced;
        int i;
 
-       if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
-               return -EINVAL;
        memset(sliced, 0, sizeof(*sliced));
        /* done if using raw VBI */
        if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10)
@@ -1101,16 +1117,31 @@ static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
        return 0;
 }
 
+static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+       if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+               return -EINVAL;
+       return saa711x_g_sliced_fmt(sd, &fmt->fmt.sliced);
+}
+
+static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
+{
+       saa711x_set_lcr(sd, NULL);
+       return 0;
+}
+
+static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
+{
+       saa711x_set_lcr(sd, fmt);
+       return 0;
+}
+
 static int saa711x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
 {
-       if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
-               saa711x_set_lcr(sd, &fmt->fmt.sliced);
-               return 0;
-       }
-       if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
-               saa711x_set_lcr(sd, NULL);
-               return 0;
-       }
+       if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+               return saa711x_s_sliced_fmt(sd, &fmt->fmt.sliced);
+       if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+               return saa711x_s_raw_fmt(sd, &fmt->fmt.vbi);
        if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
 
@@ -1212,6 +1243,10 @@ static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
                return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
        case V4L2_CID_HUE:
                return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
+       case V4L2_CID_CHROMA_AGC:
+               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+       case V4L2_CID_CHROMA_GAIN:
+               return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48);
        default:
                return -EINVAL;
        }
@@ -1234,42 +1269,41 @@ static int saa711x_s_radio(struct v4l2_subdev *sd)
        return 0;
 }
 
-static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+static int saa711x_s_routing(struct v4l2_subdev *sd,
+                            u32 input, u32 output, u32 config)
 {
        struct saa711x_state *state = to_state(sd);
-       u32 input = route->input;
-       u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0;
+       u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0;
+
+       v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n",
+               input, output);
 
-       v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", route->input, route->output);
        /* saa7111/3 does not have these inputs */
-       if ((state->ident == V4L2_IDENT_SAA7113 ||
-            state->ident == V4L2_IDENT_SAA7111) &&
-           (route->input == SAA7115_COMPOSITE4 ||
-            route->input == SAA7115_COMPOSITE5)) {
+       if (state->ident <= V4L2_IDENT_SAA7113 &&
+           (input == SAA7115_COMPOSITE4 ||
+            input == SAA7115_COMPOSITE5)) {
                return -EINVAL;
        }
-       if (route->input > SAA7115_SVIDEO3)
-               return -EINVAL;
-       if (route->output > SAA7115_IPORT_ON)
+       if (input > SAA7115_SVIDEO3)
                return -EINVAL;
-       if (state->input == route->input && state->output == route->output)
+       if (state->input == input && state->output == output)
                return 0;
        v4l2_dbg(1, debug, sd, "now setting %s input %s output\n",
-               (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite",
-               (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
-       state->input = route->input;
+               (input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite",
+               (output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
+       state->input = input;
 
        /* saa7111 has slightly different input numbering */
-       if (state->ident == V4L2_IDENT_SAA7111) {
+       if (state->ident <= V4L2_IDENT_SAA7111A) {
                if (input >= SAA7115_COMPOSITE4)
                        input -= 2;
                /* saa7111 specific */
                saa711x_write(sd, R_10_CHROMA_CNTL_2,
                                (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) |
-                               ((route->output & 0xc0) ^ 0x40));
+                               ((output & 0xc0) ^ 0x40));
                saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL,
                                (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
-                               ((route->output & 2) ? 0x0a : 0));
+                               ((output & 2) ? 0x0a : 0));
        }
 
        /* select mode */
@@ -1282,7 +1316,7 @@ static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *
                        (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) |
                        (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
 
-       state->output = route->output;
+       state->output = output;
        if (state->ident == V4L2_IDENT_SAA7114 ||
                        state->ident == V4L2_IDENT_SAA7115) {
                saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
@@ -1296,7 +1330,7 @@ static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
 {
        struct saa711x_state *state = to_state(sd);
 
-       if (state->ident != V4L2_IDENT_SAA7111)
+       if (state->ident > V4L2_IDENT_SAA7111A)
                return -EINVAL;
        saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
                (val ? 0x80 : 0));
@@ -1319,17 +1353,16 @@ static int saa711x_s_stream(struct v4l2_subdev *sd, int enable)
        return 0;
 }
 
-static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq)
+static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags)
 {
        struct saa711x_state *state = to_state(sd);
 
-       if (freq->freq != SAA7115_FREQ_32_11_MHZ &&
-                       freq->freq != SAA7115_FREQ_24_576_MHZ)
+       if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ)
                return -EINVAL;
-       state->crystal_freq = freq->freq;
-       state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
-       state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
-       state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
+       state->crystal_freq = freq;
+       state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
+       state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
+       state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
        saa711x_s_clock_freq(sd, state->audclk_freq);
        return 0;
 }
@@ -1373,6 +1406,47 @@ static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_dat
        }
 }
 
+static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+       struct saa711x_state *state = to_state(sd);
+       int reg1e;
+
+       *std = V4L2_STD_ALL;
+       if (state->ident != V4L2_IDENT_SAA7115)
+               return 0;
+       reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+
+       switch (reg1e & 0x03) {
+       case 1:
+               *std = V4L2_STD_NTSC;
+               break;
+       case 2:
+               *std = V4L2_STD_PAL;
+               break;
+       case 3:
+               *std = V4L2_STD_SECAM;
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+       struct saa711x_state *state = to_state(sd);
+       int reg1e = 0x80;
+       int reg1f;
+
+       *status = V4L2_IN_ST_NO_SIGNAL;
+       if (state->ident == V4L2_IDENT_SAA7115)
+               reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+       reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+       if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80)
+               *status = 0;
+       return 0;
+}
+
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
 {
@@ -1457,11 +1531,6 @@ static int saa711x_log_status(struct v4l2_subdev *sd)
        return 0;
 }
 
-static int saa711x_command(struct i2c_client *client, unsigned cmd, void *arg)
-{
-       return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
-}
-
 /* ----------------------------------------------------------------------- */
 
 static const struct v4l2_subdev_core_ops saa711x_core_ops = {
@@ -1470,6 +1539,7 @@ static const struct v4l2_subdev_core_ops saa711x_core_ops = {
        .g_ctrl = saa711x_g_ctrl,
        .s_ctrl = saa711x_s_ctrl,
        .queryctrl = saa711x_queryctrl,
+       .s_std = saa711x_s_std,
        .reset = saa711x_reset,
        .s_gpio = saa711x_s_gpio,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -1479,7 +1549,6 @@ static const struct v4l2_subdev_core_ops saa711x_core_ops = {
 };
 
 static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = {
-       .s_std = saa711x_s_std,
        .s_radio = saa711x_s_radio,
        .g_tuner = saa711x_g_tuner,
 };
@@ -1493,9 +1562,17 @@ static const struct v4l2_subdev_video_ops saa711x_video_ops = {
        .s_crystal_freq = saa711x_s_crystal_freq,
        .g_fmt = saa711x_g_fmt,
        .s_fmt = saa711x_s_fmt,
+       .s_stream = saa711x_s_stream,
+       .querystd = saa711x_querystd,
+       .g_input_status = saa711x_g_input_status,
+};
+
+static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = {
        .g_vbi_data = saa711x_g_vbi_data,
        .decode_vbi_line = saa711x_decode_vbi_line,
-       .s_stream = saa711x_s_stream,
+       .g_sliced_fmt = saa711x_g_sliced_fmt,
+       .s_sliced_fmt = saa711x_s_sliced_fmt,
+       .s_raw_fmt = saa711x_s_raw_fmt,
 };
 
 static const struct v4l2_subdev_ops saa711x_ops = {
@@ -1503,6 +1580,7 @@ static const struct v4l2_subdev_ops saa711x_ops = {
        .tuner = &saa711x_tuner_ops,
        .audio = &saa711x_audio_ops,
        .video = &saa711x_video_ops,
+       .vbi = &saa711x_vbi_ops,
 };
 
 /* ----------------------------------------------------------------------- */
@@ -1560,9 +1638,14 @@ static int saa711x_probe(struct i2c_client *client,
        state->contrast = 64;
        state->hue = 0;
        state->sat = 64;
+       state->chroma_agc = 1;
        switch (chip_id) {
        case '1':
                state->ident = V4L2_IDENT_SAA7111;
+               if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) {
+                       v4l_info(client, "saa7111a variant found\n");
+                       state->ident = V4L2_IDENT_SAA7111A;
+               }
                break;
        case '3':
                state->ident = V4L2_IDENT_SAA7113;
@@ -1579,7 +1662,7 @@ static int saa711x_probe(struct i2c_client *client,
        default:
                state->ident = V4L2_IDENT_SAA7111;
                v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
-
+               break;
        }
 
        state->audclk_freq = 48000;
@@ -1590,6 +1673,7 @@ static int saa711x_probe(struct i2c_client *client,
        state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
        switch (state->ident) {
        case V4L2_IDENT_SAA7111:
+       case V4L2_IDENT_SAA7111A:
                saa711x_writeregs(sd, saa7111_init);
                break;
        case V4L2_IDENT_SAA7113:
@@ -1599,7 +1683,7 @@ static int saa711x_probe(struct i2c_client *client,
                state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
                saa711x_writeregs(sd, saa7115_init_auto_input);
        }
-       if (state->ident != V4L2_IDENT_SAA7111)
+       if (state->ident > V4L2_IDENT_SAA7111A)
                saa711x_writeregs(sd, saa7115_init_misc);
        saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
 
@@ -1633,10 +1717,7 @@ MODULE_DEVICE_TABLE(i2c, saa7115_id);
 
 static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .name = "saa7115",
-       .driverid = I2C_DRIVERID_SAA711X,
-       .command = saa711x_command,
        .probe = saa711x_probe,
        .remove = saa711x_remove,
-       .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
        .id_table = saa7115_id,
 };