V4L/DVB: saa7115: support new vbi ops to set/get VBI format
[safe/jmp/linux-2.6] / drivers / media / video / saa7115.c
index 389e518..cf77318 100644 (file)
@@ -44,7 +44,9 @@
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
 #include <media/saa7115.h>
 #include <asm/div64.h>
 
@@ -55,20 +57,14 @@ MODULE_AUTHOR(  "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, "
                "Hans Verkuil, Mauro Carvalho Chehab");
 MODULE_LICENSE("GPL");
 
-static int debug = 0;
+static int debug;
 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;
        v4l2_std_id std;
        int input;
        int output;
@@ -78,9 +74,10 @@ struct saa711x_state {
        int contrast;
        int hue;
        int sat;
+       int chroma_agc;
        int width;
        int height;
-       enum v4l2_chip_ident ident;
+       u32 ident;
        u32 audclk_freq;
        u32 crystal_freq;
        u8 ucgc;
@@ -88,10 +85,17 @@ struct saa711x_state {
        u8 apll;
 };
 
+static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct saa711x_state, sd);
+}
+
 /* ----------------------------------------------------------------------- */
 
-static inline int saa711x_write(struct i2c_client *client, u8 reg, u8 value)
+static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
 {
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
        return i2c_smbus_write_byte_data(client, reg, value);
 }
 
@@ -101,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 ||
@@ -127,9 +135,9 @@ static int saa711x_has_reg(const int id, const u8 reg)
        return 1;
 }
 
-static int saa711x_writeregs(struct i2c_client *client, const unsigned char *regs)
+static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs)
 {
-       struct saa711x_state *state = i2c_get_clientdata(client);
+       struct saa711x_state *state = to_state(sd);
        unsigned char reg, data;
 
        while (*regs != 0x00) {
@@ -138,18 +146,20 @@ static int saa711x_writeregs(struct i2c_client *client, const unsigned char *reg
 
                /* According with datasheets, reserved regs should be
                   filled with 0 - seems better not to touch on they */
-               if (saa711x_has_reg(state->ident,reg)) {
-                       if (saa711x_write(client, reg, data) < 0)
+               if (saa711x_has_reg(state->ident, reg)) {
+                       if (saa711x_write(sd, reg, data) < 0)
                                return -1;
                } else {
-                       v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg);
+                       v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg);
                }
        }
        return 0;
 }
 
-static inline int saa711x_read(struct i2c_client *client, u8 reg)
+static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg)
 {
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
        return i2c_smbus_read_byte_data(client, reg);
 }
 
@@ -583,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,
@@ -600,7 +610,7 @@ static int saa711x_odd_parity(u8 c)
        return c & 1;
 }
 
-static int saa711x_decode_vps(u8 * dst, u8 * p)
+static int saa711x_decode_vps(u8 *dst, u8 *p)
 {
        static const u8 biphase_tbl[] = {
                0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
@@ -647,7 +657,7 @@ static int saa711x_decode_vps(u8 * dst, u8 * p)
        return err & 0xf0;
 }
 
-static int saa711x_decode_wss(u8 * p)
+static int saa711x_decode_wss(u8 *p)
 {
        static const int wss_bits[8] = {
                0, 0, 0, 1, 0, 1, 1, 1
@@ -674,9 +684,9 @@ static int saa711x_decode_wss(u8 * p)
        return wss;
 }
 
-static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
+static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
 {
-       struct saa711x_state *state = i2c_get_clientdata(client);
+       struct saa711x_state *state = to_state(sd);
        u32 acpf;
        u32 acni;
        u32 hz;
@@ -684,10 +694,10 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
        u8 acc = 0;     /* reg 0x3a, audio clock control */
 
        /* Checks for chips that don't have audio clock (saa7111, saa7113) */
-       if (!saa711x_has_reg(state->ident,R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
+       if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
                return 0;
 
-       v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq);
+       v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq);
 
        /* sanity check */
        if (freq < 32000 || freq > 48000)
@@ -714,68 +724,83 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
        if (state->apll)
                acc |= 0x08;
 
-       saa711x_write(client, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
-       saa711x_write(client, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
-       saa711x_write(client, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
+       saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
+       saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
+       saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
 
-       saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
-       saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
+       saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
+       saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
                                                        (acpf >> 8) & 0xff);
-       saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
+       saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
                                                        (acpf >> 16) & 0x03);
 
-       saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
-       saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
-       saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
+       saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
+       saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
+       saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
        state->audclk_freq = freq;
        return 0;
 }
 
-static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-       struct saa711x_state *state = i2c_get_clientdata(client);
+       struct saa711x_state *state = to_state(sd);
+       u8 val;
 
        switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
                if (ctrl->value < 0 || ctrl->value > 255) {
-                       v4l_err(client, "invalid brightness setting %d\n", ctrl->value);
+                       v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
                        return -ERANGE;
                }
 
                state->bright = ctrl->value;
-               saa711x_write(client, R_0A_LUMA_BRIGHT_CNTL, state->bright);
+               saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright);
                break;
 
        case V4L2_CID_CONTRAST:
                if (ctrl->value < 0 || ctrl->value > 127) {
-                       v4l_err(client, "invalid contrast setting %d\n", ctrl->value);
+                       v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
                        return -ERANGE;
                }
 
                state->contrast = ctrl->value;
-               saa711x_write(client, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
+               saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
                break;
 
        case V4L2_CID_SATURATION:
                if (ctrl->value < 0 || ctrl->value > 127) {
-                       v4l_err(client, "invalid saturation setting %d\n", ctrl->value);
+                       v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
                        return -ERANGE;
                }
 
                state->sat = ctrl->value;
-               saa711x_write(client, R_0C_CHROMA_SAT_CNTL, state->sat);
+               saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat);
                break;
 
        case V4L2_CID_HUE:
-               if (ctrl->value < -127 || ctrl->value > 127) {
-                       v4l_err(client, "invalid hue setting %d\n", ctrl->value);
+               if (ctrl->value < -128 || ctrl->value > 127) {
+                       v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
                        return -ERANGE;
                }
 
                state->hue = ctrl->value;
-               saa711x_write(client, R_0D_CHROMA_HUE_CNTL, state->hue);
+               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;
        }
@@ -783,9 +808,9 @@ static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
        return 0;
 }
 
-static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-       struct saa711x_state *state = i2c_get_clientdata(client);
+       struct saa711x_state *state = to_state(sd);
 
        switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
@@ -800,6 +825,12 @@ static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c
        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;
        }
@@ -807,16 +838,16 @@ static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c
        return 0;
 }
 
-static int saa711x_set_size(struct i2c_client *client, int width, int height)
+static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height)
 {
-       struct saa711x_state *state = i2c_get_clientdata(client);
+       struct saa711x_state *state = to_state(sd);
        int HPSC, HFSC;
        int VSCY;
        int res;
        int is_50hz = state->std & V4L2_STD_625_50;
        int Vsrc = is_50hz ? 576 : 480;
 
-       v4l_dbg(1, debug, client, "decoder set size to %ix%i\n",width,height);
+       v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height);
 
        /* FIXME need better bounds checking here */
        if ((width < 1) || (width > 1440))
@@ -824,7 +855,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
        if ((height < 1) || (height > Vsrc))
                return -EINVAL;
 
-       if (!saa711x_has_reg(state->ident,R_D0_B_HORIZ_PRESCALING)) {
+       if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) {
                /* Decoder only supports 720 columns and 480 or 576 lines */
                if (width != 720)
                        return -EINVAL;
@@ -842,22 +873,22 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
        /* Set output width/height */
        /* width */
 
-       saa711x_write(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
+       saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
                                        (u8) (width & 0xff));
-       saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
+       saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
                                        (u8) ((width >> 8) & 0xff));
 
        /* Vertical Scaling uses height/2 */
-       res=height/2;
+       res = height / 2;
 
        /* On 60Hz, it is using a higher Vertical Output Size */
        if (!is_50hz)
                res += (VRES_60HZ - 480) >> 1;
 
                /* height */
-       saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
+       saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
                                        (u8) (res & 0xff));
-       saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
+       saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
                                        (u8) ((res >> 8) & 0xff));
 
        /* Scaling settings */
@@ -868,60 +899,60 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
        HFSC = (int)((1024 * 720) / (HPSC * width));
        /* FIXME hardcodes to "Task B"
         * write H prescaler integer */
-       saa711x_write(client, R_D0_B_HORIZ_PRESCALING,
+       saa711x_write(sd, R_D0_B_HORIZ_PRESCALING,
                                (u8) (HPSC & 0x3f));
 
-       v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
+       v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
        /* write H fine-scaling (luminance) */
-       saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC,
+       saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC,
                                (u8) (HFSC & 0xff));
-       saa711x_write(client, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
+       saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
                                (u8) ((HFSC >> 8) & 0xff));
        /* write H fine-scaling (chrominance)
         * must be lum/2, so i'll just bitshift :) */
-       saa711x_write(client, R_DC_B_HORIZ_CHROMA_SCALING,
+       saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING,
                                (u8) ((HFSC >> 1) & 0xff));
-       saa711x_write(client, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
+       saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
                                (u8) ((HFSC >> 9) & 0xff));
 
        VSCY = (int)((1024 * Vsrc) / height);
-       v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
+       v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
 
        /* Correct Contrast and Luminance */
-       saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL,
+       saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL,
                                        (u8) (64 * 1024 / VSCY));
-       saa711x_write(client, R_D6_B_CHROMA_SATURATION_CNTL,
+       saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL,
                                        (u8) (64 * 1024 / VSCY));
 
                /* write V fine-scaling (luminance) */
-       saa711x_write(client, R_E0_B_VERT_LUMA_SCALING_INC,
+       saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC,
                                        (u8) (VSCY & 0xff));
-       saa711x_write(client, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
+       saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
                                        (u8) ((VSCY >> 8) & 0xff));
                /* write V fine-scaling (chrominance) */
-       saa711x_write(client, R_E2_B_VERT_CHROMA_SCALING_INC,
+       saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC,
                                        (u8) (VSCY & 0xff));
-       saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
+       saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
                                        (u8) ((VSCY >> 8) & 0xff));
 
-       saa711x_writeregs(client, saa7115_cfg_reset_scaler);
+       saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
 
        /* Activates task "B" */
-       saa711x_write(client, R_80_GLOBAL_CNTL_1,
-                               saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20);
+       saa711x_write(sd, R_80_GLOBAL_CNTL_1,
+                               saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20);
 
        return 0;
 }
 
-static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
+static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
 {
-       struct saa711x_state *state = i2c_get_clientdata(client);
+       struct saa711x_state *state = to_state(sd);
 
        /* 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)
@@ -931,13 +962,13 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
 
        // This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
        if (std & V4L2_STD_525_60) {
-               v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n");
-               saa711x_writeregs(client, saa7115_cfg_60hz_video);
-               saa711x_set_size(client, 720, 480);
+               v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
+               saa711x_writeregs(sd, saa7115_cfg_60hz_video);
+               saa711x_set_size(sd, 720, 480);
        } else {
-               v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n");
-               saa711x_writeregs(client, saa7115_cfg_50hz_video);
-               saa711x_set_size(client, 720, 576);
+               v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
+               saa711x_writeregs(sd, saa7115_cfg_50hz_video);
+               saa711x_set_size(sd, 720, 576);
        }
 
        /* Register 0E - Bits D6-D4 on NO-AUTO mode
@@ -949,102 +980,45 @@ static void saa711x_set_v4lstd(struct i2c_client *client, 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) {
-               u8 reg = saa711x_read(client, R_0E_CHROMA_CNTL_1) & 0x8f;
+       if (state->ident <= V4L2_IDENT_SAA7113) {
+               u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
 
                if (std == V4L2_STD_PAL_M) {
                        reg |= 0x30;
-               } else if (std == V4L2_STD_PAL_N) {
+               } else if (std == V4L2_STD_PAL_Nc) {
                        reg |= 0x20;
                } else if (std == V4L2_STD_PAL_60) {
                        reg |= 0x10;
                } else if (std == V4L2_STD_NTSC_M_JP) {
                        reg |= 0x40;
-               } else if (std == V4L2_STD_SECAM) {
+               } else if (std & V4L2_STD_SECAM) {
                        reg |= 0x50;
                }
-               saa711x_write(client, R_0E_CHROMA_CNTL_1, reg);
+               saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg);
        } else {
                /* restart task B if needed */
-               int taskb = saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10;
+               int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10;
 
                if (taskb && state->ident == V4L2_IDENT_SAA7114) {
-                       saa711x_writeregs(client, saa7115_cfg_vbi_on);
+                       saa711x_writeregs(sd, saa7115_cfg_vbi_on);
                }
 
                /* switch audio mode too! */
-               saa711x_set_audio_clock_freq(client, state->audclk_freq);
+               saa711x_s_clock_freq(sd, state->audclk_freq);
        }
 }
 
-static v4l2_std_id saa711x_get_v4lstd(struct i2c_client *client)
-{
-       struct saa711x_state *state = i2c_get_clientdata(client);
-
-       return state->std;
-}
-
-static void saa711x_log_status(struct i2c_client *client)
-{
-       struct saa711x_state *state = i2c_get_clientdata(client);
-       int reg1e, reg1f;
-       int signalOk;
-       int vcr;
-
-       v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq);
-       if (state->ident != V4L2_IDENT_SAA7115) {
-               /* status for the saa7114 */
-               reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
-               signalOk = (reg1f & 0xc1) == 0x81;
-               v4l_info(client, "Video signal:    %s\n", signalOk ? "ok" : "bad");
-               v4l_info(client, "Frequency:       %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
-               return;
-       }
-
-       /* status for the saa7115 */
-       reg1e = saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC);
-       reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
-
-       signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
-       vcr = !(reg1f & 0x10);
-
-       if (state->input >= 6) {
-               v4l_info(client, "Input:           S-Video %d\n", state->input - 6);
-       } else {
-               v4l_info(client, "Input:           Composite %d\n", state->input);
-       }
-       v4l_info(client, "Video signal:    %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
-       v4l_info(client, "Frequency:       %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
-
-       switch (reg1e & 0x03) {
-               case 1:
-                       v4l_info(client, "Detected format: NTSC\n");
-                       break;
-               case 2:
-                       v4l_info(client, "Detected format: PAL\n");
-                       break;
-               case 3:
-                       v4l_info(client, "Detected format: SECAM\n");
-                       break;
-               default:
-                       v4l_info(client, "Detected format: BW/No color\n");
-                       break;
-       }
-       v4l_info(client, "Width, Height:   %d, %d\n", state->width, state->height);
-}
-
 /* setup the sliced VBI lcr registers according to the sliced VBI format */
-static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt)
+static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
 {
-       struct saa711x_state *state = i2c_get_clientdata(client);
+       struct saa711x_state *state = to_state(sd);
        int is_50hz = (state->std & V4L2_STD_625_50);
        u8 lcr[24];
        int i, x;
 
 #if 1
        /* saa7113/7114/7118 VBI support are experimental */
-       if (!saa711x_has_reg(state->ident,R_41_LCR_BASE))
+       if (!saa711x_has_reg(state->ident, R_41_LCR_BASE))
                return;
 
 #else
@@ -1056,7 +1030,7 @@ static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo
        for (i = 0; i <= 23; i++)
                lcr[i] = 0xff;
 
-       if (fmt->service_set == 0) {
+       if (fmt == NULL) {
                /* raw VBI */
                if (is_50hz)
                        for (i = 6; i <= 23; i++)
@@ -1108,16 +1082,16 @@ static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo
 
        /* write the lcr registers */
        for (i = 2; i <= 23; i++) {
-               saa711x_write(client, i - 2 + R_41_LCR_BASE, lcr[i]);
+               saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]);
        }
 
        /* enable/disable raw VBI capturing */
-       saa711x_writeregs(client, fmt->service_set == 0 ?
+       saa711x_writeregs(sd, fmt == NULL ?
                                saa7115_cfg_vbi_on :
                                saa7115_cfg_vbi_off);
 }
 
-static int saa711x_get_v4lfmt(struct i2c_client *client, 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 */
@@ -1126,17 +1100,14 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, 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(client, R_80_GLOBAL_CNTL_1) & 0x10)
+       if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10)
                return 0;
        for (i = 2; i <= 23; i++) {
-               u8 v = saa711x_read(client, i - 2 + R_41_LCR_BASE);
+               u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE);
 
                sliced->service_lines[0][i] = lcr2vbi[v >> 4];
                sliced->service_lines[1][i] = lcr2vbi[v & 0xf];
@@ -1146,16 +1117,35 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
        return 0;
 }
 
-static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
 {
-       if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
-               saa711x_set_lcr(client, &fmt->fmt.sliced);
-               return 0;
-       }
+       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)
+               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;
 
-       return saa711x_set_size(client,fmt->fmt.pix.width,fmt->fmt.pix.height);
+       return saa711x_set_size(sd, fmt->fmt.pix.width, fmt->fmt.pix.height);
 }
 
 /* Decode the sliced VBI data stream as created by the saa7115.
@@ -1164,13 +1154,12 @@ static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
    The current implementation uses SAV/EAV codes and not the ancillary data
    headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV
    code. */
-static void saa711x_decode_vbi_line(struct i2c_client *client,
-                                   struct v4l2_decode_vbi_line *vbi)
+static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
 {
+       struct saa711x_state *state = to_state(sd);
        static const char vbi_no_data_pattern[] = {
                0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0
        };
-       struct saa711x_state *state = i2c_get_clientdata(client);
        u8 *p = vbi->p;
        u32 wss;
        int id1, id2;   /* the ID1 and ID2 bytes from the internal header */
@@ -1197,7 +1186,7 @@ static void saa711x_decode_vbi_line(struct i2c_client *client,
        /* If the VBI slicer does not detect any signal it will fill up
           the payload buffer with 0xa0 bytes. */
        if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern)))
-               return;
+               return 0;
 
        /* decode payloads */
        switch (id2) {
@@ -1206,302 +1195,441 @@ static void saa711x_decode_vbi_line(struct i2c_client *client,
                break;
        case 4:
                if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
-                       return;
+                       return 0;
                vbi->type = V4L2_SLICED_CAPTION_525;
                break;
        case 5:
                wss = saa711x_decode_wss(p);
                if (wss == -1)
-                       return;
+                       return 0;
                p[0] = wss & 0xff;
                p[1] = wss >> 8;
                vbi->type = V4L2_SLICED_WSS_625;
                break;
        case 7:
                if (saa711x_decode_vps(p, p) != 0)
-                       return;
+                       return 0;
                vbi->type = V4L2_SLICED_VPS;
                break;
        default:
-               return;
+               break;
        }
+       return 0;
 }
 
 /* ============ SAA7115 AUDIO settings (end) ============= */
 
-static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
 {
-       struct saa711x_state *state = i2c_get_clientdata(client);
-       int *iarg = arg;
-
-       /* ioctls to allow direct access to the saa7115 registers for testing */
-       switch (cmd) {
-       case VIDIOC_S_FMT:
-               return saa711x_set_v4lfmt(client, (struct v4l2_format *)arg);
-
-       case VIDIOC_G_FMT:
-               return saa711x_get_v4lfmt(client, (struct v4l2_format *)arg);
+       struct saa711x_state *state = to_state(sd);
+       int status;
 
-       case VIDIOC_INT_AUDIO_CLOCK_FREQ:
-               return saa711x_set_audio_clock_freq(client, *(u32 *)arg);
-
-       case VIDIOC_G_TUNER:
-       {
-               struct v4l2_tuner *vt = arg;
-               int status;
+       if (state->radio)
+               return 0;
+       status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
 
-               if (state->radio)
-                       break;
-               status = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
+       v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status);
+       vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
+       return 0;
+}
 
-               v4l_dbg(1, debug, client, "status: 0x%02x\n", status);
-               vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
-               break;
+static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+       switch (qc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+       case V4L2_CID_CONTRAST:
+       case V4L2_CID_SATURATION:
+               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;
        }
+}
 
-       case VIDIOC_LOG_STATUS:
-               saa711x_log_status(client);
-               break;
-
-       case VIDIOC_G_CTRL:
-               return saa711x_get_v4lctrl(client, (struct v4l2_control *)arg);
-
-       case VIDIOC_S_CTRL:
-               return saa711x_set_v4lctrl(client, (struct v4l2_control *)arg);
-
-       case VIDIOC_QUERYCTRL:
-       {
-               struct v4l2_queryctrl *qc = arg;
+static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+       struct saa711x_state *state = to_state(sd);
 
-               switch (qc->id) {
-                       case V4L2_CID_BRIGHTNESS:
-                       case V4L2_CID_CONTRAST:
-                       case V4L2_CID_SATURATION:
-                       case V4L2_CID_HUE:
-                               return v4l2_ctrl_query_fill_std(qc);
-                       default:
-                               return -EINVAL;
-               }
-       }
+       state->radio = 0;
+       saa711x_set_v4lstd(sd, std);
+       return 0;
+}
 
-       case VIDIOC_G_STD:
-               *(v4l2_std_id *)arg = saa711x_get_v4lstd(client);
-               break;
+static int saa711x_s_radio(struct v4l2_subdev *sd)
+{
+       struct saa711x_state *state = to_state(sd);
 
-       case VIDIOC_S_STD:
-               state->radio = 0;
-               saa711x_set_v4lstd(client, *(v4l2_std_id *)arg);
-               break;
+       state->radio = 1;
+       return 0;
+}
 
-       case AUDC_SET_RADIO:
-               state->radio = 1;
-               break;
+static int saa711x_s_routing(struct v4l2_subdev *sd,
+                            u32 input, u32 output, u32 config)
+{
+       struct saa711x_state *state = to_state(sd);
+       u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0;
 
-       case VIDIOC_INT_G_VIDEO_ROUTING:
-       {
-               struct v4l2_routing *route = arg;
+       v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n",
+               input, output);
 
-               route->input = state->input;
-               route->output = state->output;
-               break;
+       /* saa7111/3 does not have these inputs */
+       if (state->ident <= V4L2_IDENT_SAA7113 &&
+           (input == SAA7115_COMPOSITE4 ||
+            input == SAA7115_COMPOSITE5)) {
+               return -EINVAL;
+       }
+       if (input > SAA7115_SVIDEO3)
+               return -EINVAL;
+       if (state->input == input && state->output == output)
+               return 0;
+       v4l2_dbg(1, debug, sd, "now setting %s input %s output\n",
+               (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_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) |
+                               ((output & 0xc0) ^ 0x40));
+               saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL,
+                               (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
+                               ((output & 2) ? 0x0a : 0));
        }
 
-       case VIDIOC_INT_S_VIDEO_ROUTING:
-       {
-               struct v4l2_routing *route = arg;
+       /* select mode */
+       saa711x_write(sd, R_02_INPUT_CNTL_1,
+                     (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) |
+                      input);
 
-               v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output);
-               /* saa7113 does not have these inputs */
-               if (state->ident == V4L2_IDENT_SAA7113 &&
-                   (route->input == SAA7115_COMPOSITE4 ||
-                    route->input == SAA7115_COMPOSITE5)) {
-                       return -EINVAL;
-               }
-               if (route->input > SAA7115_SVIDEO3)
-                       return -EINVAL;
-               if (route->output > SAA7115_IPORT_ON)
-                       return -EINVAL;
-               if (state->input == route->input && state->output == route->output)
-                       break;
-               v4l_dbg(1, debug, client, "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;
-
-               /* select mode */
-               saa711x_write(client, R_02_INPUT_CNTL_1,
-                             (saa711x_read(client, R_02_INPUT_CNTL_1) & 0xf0) |
-                              state->input);
-
-               /* bypass chrominance trap for S-Video modes */
-               saa711x_write(client, R_09_LUMA_CNTL,
-                             (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) |
-                              (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
-
-               state->output = route->output;
-               if (state->ident == V4L2_IDENT_SAA7114 ||
+       /* bypass chrominance trap for S-Video modes */
+       saa711x_write(sd, R_09_LUMA_CNTL,
+                       (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) |
+                       (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
+
+       state->output = output;
+       if (state->ident == V4L2_IDENT_SAA7114 ||
                        state->ident == V4L2_IDENT_SAA7115) {
-                       saa711x_write(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
-                             (saa711x_read(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
-                              (state->output & 0x01));
-               }
-               break;
+               saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
+                               (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
+                               (state->output & 0x01));
        }
+       return 0;
+}
 
-       case VIDIOC_STREAMON:
-       case VIDIOC_STREAMOFF:
-               v4l_dbg(1, debug, client, "%s output\n",
-                       (cmd == VIDIOC_STREAMON) ? "enable" : "disable");
+static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
+{
+       struct saa711x_state *state = to_state(sd);
 
-               if (state->enable != (cmd == VIDIOC_STREAMON)) {
-                       state->enable = (cmd == VIDIOC_STREAMON);
-                       saa711x_write(client,
-                               R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED,
-                               state->enable);
-               }
-               break;
+       if (state->ident > V4L2_IDENT_SAA7111A)
+               return -EINVAL;
+       saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
+               (val ? 0x80 : 0));
+       return 0;
+}
 
-       case VIDIOC_INT_S_CRYSTAL_FREQ:
-       {
-               struct v4l2_crystal_freq *freq = arg;
+static int saa711x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+       struct saa711x_state *state = to_state(sd);
 
-               if (freq->freq != SAA7115_FREQ_32_11_MHZ &&
-                   freq->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;
-               saa711x_set_audio_clock_freq(client, state->audclk_freq);
-               break;
-       }
+       v4l2_dbg(1, debug, sd, "%s output\n",
+                       enable ? "enable" : "disable");
 
-       case VIDIOC_INT_DECODE_VBI_LINE:
-               saa711x_decode_vbi_line(client, arg);
-               break;
+       if (state->enable == enable)
+               return 0;
+       state->enable = enable;
+       if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED))
+               return 0;
+       saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable);
+       return 0;
+}
 
-       case VIDIOC_INT_RESET:
-               v4l_dbg(1, debug, client, "decoder RESET\n");
-               saa711x_writeregs(client, saa7115_cfg_reset_scaler);
-               break;
+static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags)
+{
+       struct saa711x_state *state = to_state(sd);
 
-       case VIDIOC_INT_G_VBI_DATA:
-       {
-               struct v4l2_sliced_vbi_data *data = arg;
+       if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ)
+               return -EINVAL;
+       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;
+}
 
-               switch (data->id) {
-               case V4L2_SLICED_WSS_625:
-                       if (saa711x_read(client, 0x6b) & 0xc0)
-                               return -EIO;
-                       data->data[0] = saa711x_read(client, 0x6c);
-                       data->data[1] = saa711x_read(client, 0x6d);
-                       return 0;
-               case V4L2_SLICED_CAPTION_525:
-                       if (data->field == 0) {
-                               /* CC */
-                               if (saa711x_read(client, 0x66) & 0xc0)
-                                       return -EIO;
-                               data->data[0] = saa711x_read(client, 0x67);
-                               data->data[1] = saa711x_read(client, 0x68);
-                               return 0;
-                       }
-                       /* XDS */
-                       if (saa711x_read(client, 0x66) & 0x30)
+static int saa711x_reset(struct v4l2_subdev *sd, u32 val)
+{
+       v4l2_dbg(1, debug, sd, "decoder RESET\n");
+       saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
+       return 0;
+}
+
+static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data)
+{
+       /* Note: the internal field ID is inverted for NTSC,
+          so data->field 0 maps to the saa7115 even field,
+          whereas for PAL it maps to the saa7115 odd field. */
+       switch (data->id) {
+       case V4L2_SLICED_WSS_625:
+               if (saa711x_read(sd, 0x6b) & 0xc0)
+                       return -EIO;
+               data->data[0] = saa711x_read(sd, 0x6c);
+               data->data[1] = saa711x_read(sd, 0x6d);
+               return 0;
+       case V4L2_SLICED_CAPTION_525:
+               if (data->field == 0) {
+                       /* CC */
+                       if (saa711x_read(sd, 0x66) & 0x30)
                                return -EIO;
-                       data->data[0] = saa711x_read(client, 0x69);
-                       data->data[1] = saa711x_read(client, 0x6a);
+                       data->data[0] = saa711x_read(sd, 0x69);
+                       data->data[1] = saa711x_read(sd, 0x6a);
                        return 0;
-               default:
-                       return -EINVAL;
                }
-               break;
+               /* XDS */
+               if (saa711x_read(sd, 0x66) & 0xc0)
+                       return -EIO;
+               data->data[0] = saa711x_read(sd, 0x67);
+               data->data[1] = saa711x_read(sd, 0x68);
+               return 0;
+       default:
+               return -EINVAL;
        }
+}
 
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-       case VIDIOC_INT_G_REGISTER:
-       {
-               struct v4l2_register *reg = arg;
+static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+       struct saa711x_state *state = to_state(sd);
+       int reg1e;
 
-               if (reg->i2c_id != I2C_DRIVERID_SAA711X)
-                       return -EINVAL;
-               reg->val = saa711x_read(client, reg->reg & 0xff);
+       *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;
+}
 
-       case VIDIOC_INT_S_REGISTER:
-       {
-               struct v4l2_register *reg = arg;
+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;
+}
 
-               if (reg->i2c_id != I2C_DRIVERID_SAA711X)
-                       return -EINVAL;
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
-               saa711x_write(client, reg->reg & 0xff, reg->val & 0xff);
-               break;
-       }
-#endif
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-       case VIDIOC_INT_G_CHIP_IDENT:
-               *iarg = state->ident;
-               break;
+       if (!v4l2_chip_match_i2c_client(client, &reg->match))
+               return -EINVAL;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       reg->val = saa711x_read(sd, reg->reg & 0xff);
+       reg->size = 1;
+       return 0;
+}
 
-       default:
+static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       if (!v4l2_chip_match_i2c_client(client, &reg->match))
                return -EINVAL;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff);
+       return 0;
+}
+#endif
+
+static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+       struct saa711x_state *state = to_state(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
+
+static int saa711x_log_status(struct v4l2_subdev *sd)
+{
+       struct saa711x_state *state = to_state(sd);
+       int reg1e, reg1f;
+       int signalOk;
+       int vcr;
+
+       v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq);
+       if (state->ident != V4L2_IDENT_SAA7115) {
+               /* status for the saa7114 */
+               reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+               signalOk = (reg1f & 0xc1) == 0x81;
+               v4l2_info(sd, "Video signal:    %s\n", signalOk ? "ok" : "bad");
+               v4l2_info(sd, "Frequency:       %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+               return 0;
        }
 
+       /* status for the saa7115 */
+       reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+       reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+
+       signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
+       vcr = !(reg1f & 0x10);
+
+       if (state->input >= 6)
+               v4l2_info(sd, "Input:           S-Video %d\n", state->input - 6);
+       else
+               v4l2_info(sd, "Input:           Composite %d\n", state->input);
+       v4l2_info(sd, "Video signal:    %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
+       v4l2_info(sd, "Frequency:       %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+
+       switch (reg1e & 0x03) {
+       case 1:
+               v4l2_info(sd, "Detected format: NTSC\n");
+               break;
+       case 2:
+               v4l2_info(sd, "Detected format: PAL\n");
+               break;
+       case 3:
+               v4l2_info(sd, "Detected format: SECAM\n");
+               break;
+       default:
+               v4l2_info(sd, "Detected format: BW/No color\n");
+               break;
+       }
+       v4l2_info(sd, "Width, Height:   %d, %d\n", state->width, state->height);
        return 0;
 }
 
 /* ----------------------------------------------------------------------- */
 
-static struct i2c_driver i2c_driver_saa711x;
+static const struct v4l2_subdev_core_ops saa711x_core_ops = {
+       .log_status = saa711x_log_status,
+       .g_chip_ident = saa711x_g_chip_ident,
+       .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
+       .g_register = saa711x_g_register,
+       .s_register = saa711x_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = {
+       .s_radio = saa711x_s_radio,
+       .g_tuner = saa711x_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {
+       .s_clock_freq = saa711x_s_clock_freq,
+};
+
+static const struct v4l2_subdev_video_ops saa711x_video_ops = {
+       .s_routing = saa711x_s_routing,
+       .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,
+       .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 = {
+       .core = &saa711x_core_ops,
+       .tuner = &saa711x_tuner_ops,
+       .audio = &saa711x_audio_ops,
+       .video = &saa711x_video_ops,
+       .vbi = &saa711x_vbi_ops,
+};
+
+/* ----------------------------------------------------------------------- */
 
-static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind)
+static int saa711x_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
 {
-       struct i2c_client *client;
        struct saa711x_state *state;
+       struct v4l2_subdev *sd;
        int     i;
        char    name[17];
-       u8 chip_id;
+       char chip_id;
+       int autodetect = !id || id->driver_data == 1;
 
        /* Check if the adapter supports the needed features */
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
-               return 0;
-
-       client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-       if (client == 0)
-               return -ENOMEM;
-       client->addr = address;
-       client->adapter = adapter;
-       client->driver = &i2c_driver_saa711x;
-       snprintf(client->name, sizeof(client->name) - 1, "saa7115");
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -EIO;
 
        for (i = 0; i < 0x0f; i++) {
-               saa711x_write(client, 0, i);
-               name[i] = (saa711x_read(client, 0) & 0x0f) + '0';
+               i2c_smbus_write_byte_data(client, 0, i);
+               name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0';
                if (name[i] > '9')
                        name[i] += 'a' - '9' - 1;
        }
        name[i] = '\0';
 
-       saa711x_write(client, 0, 5);
-       chip_id = saa711x_read(client, 0) & 0x0f;
+       chip_id = name[5];
 
        /* Check whether this chip is part of the saa711x series */
        if (memcmp(name, "1f711", 5)) {
                v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n",
-                       address << 1, name);
-               return 0;
+                       client->addr << 1, name);
+               return -ENODEV;
        }
 
-       snprintf(client->name, sizeof(client->name) - 1, "saa711%d",chip_id);
-       v4l_info(client, "saa711%d found (%s) @ 0x%x (%s)\n", chip_id, name, address << 1, adapter->name);
+       /* Safety check */
+       if (!autodetect && id->name[6] != chip_id) {
+               v4l_warn(client, "found saa711%c while %s was expected\n",
+                        chip_id, id->name);
+       }
+       snprintf(client->name, sizeof(client->name), "saa711%c", chip_id);
+       v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name,
+                client->addr << 1, client->adapter->name);
 
        state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
-       i2c_set_clientdata(client, state);
-       if (state == NULL) {
-               kfree(client);
+       if (state == NULL)
                return -ENOMEM;
-       }
+       sd = &state->sd;
+       v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
        state->input = -1;
        state->output = SAA7115_IPORT_ON;
        state->enable = 1;
@@ -1510,101 +1638,86 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind)
        state->contrast = 64;
        state->hue = 0;
        state->sat = 64;
+       state->chroma_agc = 1;
        switch (chip_id) {
-       case 1:
+       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:
+       case '3':
                state->ident = V4L2_IDENT_SAA7113;
                break;
-       case 4:
+       case '4':
                state->ident = V4L2_IDENT_SAA7114;
                break;
-       case 5:
+       case '5':
                state->ident = V4L2_IDENT_SAA7115;
                break;
-       case 8:
+       case '8':
                state->ident = V4L2_IDENT_SAA7118;
                break;
        default:
                state->ident = V4L2_IDENT_SAA7111;
-               v4l_info(client, "WARNING: Chip is not known - Falling back to saa7111\n");
-
+               v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
+               break;
        }
 
        state->audclk_freq = 48000;
 
-       v4l_dbg(1, debug, client, "writing init values\n");
+       v4l2_dbg(1, debug, sd, "writing init values\n");
 
        /* init to 60hz/48khz */
        state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
        switch (state->ident) {
        case V4L2_IDENT_SAA7111:
-               saa711x_writeregs(client, saa7111_init);
+       case V4L2_IDENT_SAA7111A:
+               saa711x_writeregs(sd, saa7111_init);
                break;
        case V4L2_IDENT_SAA7113:
-               saa711x_writeregs(client, saa7113_init);
+               saa711x_writeregs(sd, saa7113_init);
                break;
        default:
                state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
-               saa711x_writeregs(client, saa7115_init_auto_input);
+               saa711x_writeregs(sd, saa7115_init_auto_input);
        }
-       saa711x_writeregs(client, saa7115_init_misc);
-       saa711x_set_v4lstd(client, V4L2_STD_NTSC);
-
-       i2c_attach_client(client);
-
-       v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n",
-               saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC));
+       if (state->ident > V4L2_IDENT_SAA7111A)
+               saa711x_writeregs(sd, saa7115_init_misc);
+       saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
 
+       v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
+               saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
+               saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC));
        return 0;
 }
 
-static int saa711x_probe(struct i2c_adapter *adapter)
-{
-       if (adapter->class & I2C_CLASS_TV_ANALOG || adapter->class & I2C_CLASS_TV_DIGITAL)
-               return i2c_probe(adapter, &addr_data, &saa711x_attach);
-       return 0;
-}
+/* ----------------------------------------------------------------------- */
 
-static int saa711x_detach(struct i2c_client *client)
+static int saa711x_remove(struct i2c_client *client)
 {
-       struct saa711x_state *state = i2c_get_clientdata(client);
-       int err;
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
-       err = i2c_detach_client(client);
-       if (err) {
-               return err;
-       }
-
-       kfree(state);
-       kfree(client);
+       v4l2_device_unregister_subdev(sd);
+       kfree(to_state(sd));
        return 0;
 }
 
-/* ----------------------------------------------------------------------- */
-
-/* i2c implementation */
-static struct i2c_driver i2c_driver_saa711x = {
-       .driver = {
-               .name = "saa7115",
-       },
-       .id = I2C_DRIVERID_SAA711X,
-       .attach_adapter = saa711x_probe,
-       .detach_client = saa711x_detach,
-       .command = saa711x_command,
+static const struct i2c_device_id saa7115_id[] = {
+       { "saa7115_auto", 1 }, /* autodetect */
+       { "saa7111", 0 },
+       { "saa7113", 0 },
+       { "saa7114", 0 },
+       { "saa7115", 0 },
+       { "saa7118", 0 },
+       { }
 };
+MODULE_DEVICE_TABLE(i2c, saa7115_id);
 
-
-static int __init saa711x_init_module(void)
-{
-       return i2c_add_driver(&i2c_driver_saa711x);
-}
-
-static void __exit saa711x_cleanup_module(void)
-{
-       i2c_del_driver(&i2c_driver_saa711x);
-}
-
-module_init(saa711x_init_module);
-module_exit(saa711x_cleanup_module);
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+       .name = "saa7115",
+       .probe = saa711x_probe,
+       .remove = saa711x_remove,
+       .id_table = saa7115_id,
+};