#include <linux/mutex.h>
 #include <linux/delay.h>
 
+#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM             0x0
+#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED         0x1
+#define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM              0x2
+#define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM              0x3
+#define ADV7180_STD_NTSC_J                             0x4
+#define ADV7180_STD_NTSC_M                             0x5
+#define ADV7180_STD_PAL60                              0x6
+#define ADV7180_STD_NTSC_443                           0x7
+#define ADV7180_STD_PAL_BG                             0x8
+#define ADV7180_STD_PAL_N                              0x9
+#define ADV7180_STD_PAL_M                              0xa
+#define ADV7180_STD_PAL_M_PED                          0xb
+#define ADV7180_STD_PAL_COMB_N                         0xc
+#define ADV7180_STD_PAL_COMB_N_PED                     0xd
+#define ADV7180_STD_PAL_SECAM                          0xe
+#define ADV7180_STD_PAL_SECAM_PED                      0xf
+
 #define ADV7180_REG_INPUT_CONTROL                      0x0000
-#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM   0x00
-#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
-#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM    0x20
-#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM    0x30
-#define ADV7180_INPUT_CONTROL_NTSC_J                   0x40
-#define ADV7180_INPUT_CONTROL_NTSC_M                   0x50
-#define ADV7180_INPUT_CONTROL_PAL60                    0x60
-#define ADV7180_INPUT_CONTROL_NTSC_443                 0x70
-#define ADV7180_INPUT_CONTROL_PAL_BG                   0x80
-#define ADV7180_INPUT_CONTROL_PAL_N                    0x90
-#define ADV7180_INPUT_CONTROL_PAL_M                    0xa0
-#define ADV7180_INPUT_CONTROL_PAL_M_PED                        0xb0
-#define ADV7180_INPUT_CONTROL_PAL_COMB_N               0xc0
-#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED           0xd0
-#define ADV7180_INPUT_CONTROL_PAL_SECAM                        0xe0
-#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED            0xf0
 #define ADV7180_INPUT_CONTROL_INSEL_MASK               0x0f
 
 #define ADV7180_REG_EXTENDED_OUTPUT_CONTROL            0x0004
 #define ADV7180_REG_NTSC_V_BIT_END     0x00E6
 #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND    0x4F
 
+#define ADV7180_INPUT_CVBS_AIN1 0x00
+#define ADV7180_INPUT_CVBS_AIN2 0x01
+#define ADV7180_INPUT_CVBS_AIN3 0x02
+#define ADV7180_INPUT_CVBS_AIN4 0x03
+#define ADV7180_INPUT_CVBS_AIN5 0x04
+#define ADV7180_INPUT_CVBS_AIN6 0x05
+#define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06
+#define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07
+#define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08
+#define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09
+#define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a
+
+struct adv7180_state;
+
+#define ADV7180_FLAG_RESET_POWERED     BIT(0)
+
+struct adv7180_chip_info {
+       unsigned int flags;
+       unsigned int valid_input_mask;
+       int (*set_std)(struct adv7180_state *st, unsigned int std);
+       int (*select_input)(struct adv7180_state *st, unsigned int input);
+       int (*init)(struct adv7180_state *state);
+};
+
 struct adv7180_state {
        struct v4l2_ctrl_handler ctrl_hdl;
        struct v4l2_subdev      sd;
 
        struct i2c_client       *client;
        unsigned int            register_page;
+       const struct adv7180_chip_info *chip_info;
 };
 #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,            \
                                            struct adv7180_state,       \
        return i2c_smbus_read_byte_data(state->client, reg & 0xff);
 }
 
+static int adv7180_set_video_standard(struct adv7180_state *state,
+       unsigned int std)
+{
+       return state->chip_info->set_std(state, std);
+}
 
 static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
 {
 static int v4l2_std_to_adv7180(v4l2_std_id std)
 {
        if (std == V4L2_STD_PAL_60)
-               return ADV7180_INPUT_CONTROL_PAL60;
+               return ADV7180_STD_PAL60;
        if (std == V4L2_STD_NTSC_443)
-               return ADV7180_INPUT_CONTROL_NTSC_443;
+               return ADV7180_STD_NTSC_443;
        if (std == V4L2_STD_PAL_N)
-               return ADV7180_INPUT_CONTROL_PAL_N;
+               return ADV7180_STD_PAL_N;
        if (std == V4L2_STD_PAL_M)
-               return ADV7180_INPUT_CONTROL_PAL_M;
+               return ADV7180_STD_PAL_M;
        if (std == V4L2_STD_PAL_Nc)
-               return ADV7180_INPUT_CONTROL_PAL_COMB_N;
+               return ADV7180_STD_PAL_COMB_N;
 
        if (std & V4L2_STD_PAL)
-               return ADV7180_INPUT_CONTROL_PAL_BG;
+               return ADV7180_STD_PAL_BG;
        if (std & V4L2_STD_NTSC)
-               return ADV7180_INPUT_CONTROL_NTSC_M;
+               return ADV7180_STD_NTSC_M;
        if (std & V4L2_STD_SECAM)
-               return ADV7180_INPUT_CONTROL_PAL_SECAM;
+               return ADV7180_STD_PAL_SECAM;
 
        return -EINVAL;
 }
        if (ret)
                return ret;
 
-       /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
-        * all inputs and let the card driver take care of validation
-        */
-       if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
+       if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) {
+               ret = -EINVAL;
                goto out;
+       }
 
-       ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
-       if (ret < 0)
-               goto out;
+       ret = state->chip_info->select_input(state, input);
 
-       ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
-       ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret | input);
-       state->input = input;
+       if (ret == 0)
+               state->input = input;
 out:
        mutex_unlock(&state->mutex);
        return ret;
        int ret;
 
        if (state->autodetect) {
-               ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
-                                   ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
-                                   | state->input);
+               ret = adv7180_set_video_standard(state,
+                       ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
                if (ret < 0)
                        return ret;
 
                if (ret < 0)
                        return ret;
 
-               ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
-                                   ret | state->input);
+               ret = adv7180_set_video_standard(state, ret);
                if (ret < 0)
                        return ret;
        }
        .g_mbus_config = adv7180_g_mbus_config,
 };
 
+
 static const struct v4l2_subdev_core_ops adv7180_core_ops = {
        .s_power = adv7180_s_power,
 };
        return IRQ_HANDLED;
 }
 
-static int init_device(struct adv7180_state *state)
+static int adv7180_init(struct adv7180_state *state)
 {
        int ret;
 
-       mutex_lock(&state->mutex);
-
-       adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
-       usleep_range(2000, 10000);
-
-       ret = adv7180_program_std(state);
-       if (ret)
-               goto out_unlock;
-
        /* ITU-R BT.656-4 compatible */
        ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
                        ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
        if (ret < 0)
-               goto out_unlock;
+               return ret;
 
        /* Manually set V bit end position in NTSC mode */
-       ret = adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
+       return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
                                        ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
+}
+
+static int adv7180_set_std(struct adv7180_state *state, unsigned int std)
+{
+       return adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
+               (std << 4) | state->input);
+}
+
+static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
+{
+       int ret;
+
+       ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
        if (ret < 0)
+               return ret;
+
+       ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
+       ret |= input;
+       return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret);
+}
+
+static const struct adv7180_chip_info adv7180_info = {
+       .flags = ADV7180_FLAG_RESET_POWERED,
+       /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
+        * all inputs and let the card driver take care of validation
+        */
+       .valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) |
+               BIT(ADV7180_INPUT_CVBS_AIN2) |
+               BIT(ADV7180_INPUT_CVBS_AIN3) |
+               BIT(ADV7180_INPUT_CVBS_AIN4) |
+               BIT(ADV7180_INPUT_CVBS_AIN5) |
+               BIT(ADV7180_INPUT_CVBS_AIN6) |
+               BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) |
+               BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) |
+               BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) |
+               BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+               BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6),
+       .init = adv7180_init,
+       .set_std = adv7180_set_std,
+       .select_input = adv7180_select_input,
+};
+
+static int init_device(struct adv7180_state *state)
+{
+       int ret;
+
+       mutex_lock(&state->mutex);
+
+       adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
+       usleep_range(2000, 10000);
+
+       ret = state->chip_info->init(state);
+       if (ret)
                goto out_unlock;
 
-       /* read current norm */
-       __adv7180_status(state, NULL, &state->curr_norm);
+       ret = adv7180_program_std(state);
+       if (ret)
+               goto out_unlock;
 
        /* register for interrupts */
        if (state->irq > 0) {
                return -ENOMEM;
 
        state->client = client;
+       state->chip_info = (struct adv7180_chip_info *)id->driver_data;
 
        state->irq = client->irq;
        mutex_init(&state->mutex);
        state->autodetect = true;
-       state->powered = true;
+       if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
+               state->powered = true;
+       else
+               state->powered = false;
        state->input = 0;
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
 }
 
 static const struct i2c_device_id adv7180_id[] = {
-       {KBUILD_MODNAME, 0},
+       { "adv7180", (kernel_ulong_t)&adv7180_info },
        {},
 };
+MODULE_DEVICE_TABLE(i2c, adv7180_id);
 
 #ifdef CONFIG_PM_SLEEP
 static int adv7180_suspend(struct device *dev)
 #define ADV7180_PM_OPS NULL
 #endif
 
-MODULE_DEVICE_TABLE(i2c, adv7180_id);
-
 static struct i2c_driver adv7180_driver = {
        .driver = {
                   .owner = THIS_MODULE,