include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / drivers / media / video / adv7180.c
index 1b3cbd0..23e610f 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
 #include <linux/i2c-id.h>
+#include <linux/slab.h>
 #include <media/v4l2-ioctl.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
+#include <linux/mutex.h>
 
 #define DRIVER_NAME "adv7180"
 
-#define ADV7180_INPUT_CONTROL_REG      0x00
-#define ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM      0x00
-#define ADV7180_AUTODETECT_ENABLE_REG  0x07
-#define ADV7180_AUTODETECT_DEFAULT     0x7f
-
-
-#define ADV7180_STATUS1_REG 0x10
-#define ADV7180_STATUS1_AUTOD_MASK 0x70
+#define ADV7180_INPUT_CONTROL_REG                      0x00
+#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_EXTENDED_OUTPUT_CONTROL_REG            0x04
+#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS                0xC5
+
+#define ADV7180_AUTODETECT_ENABLE_REG                  0x07
+#define ADV7180_AUTODETECT_DEFAULT                     0x7f
+
+#define ADV7180_ADI_CTRL_REG                           0x0e
+#define ADV7180_ADI_CTRL_IRQ_SPACE                     0x20
+
+#define ADV7180_STATUS1_REG                            0x10
+#define ADV7180_STATUS1_IN_LOCK                0x01
+#define ADV7180_STATUS1_AUTOD_MASK     0x70
 #define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00
 #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
 #define ADV7180_STATUS1_AUTOD_PAL_M    0x20
 #define ADV7180_IDENT_REG 0x11
 #define ADV7180_ID_7180 0x18
 
+#define ADV7180_ICONF1_ADI             0x40
+#define ADV7180_ICONF1_ACTIVE_LOW      0x01
+#define ADV7180_ICONF1_PSYNC_ONLY      0x10
+#define ADV7180_ICONF1_ACTIVE_TO_CLR   0xC0
+
+#define ADV7180_IRQ1_LOCK      0x01
+#define ADV7180_IRQ1_UNLOCK    0x02
+#define ADV7180_ISR1_ADI       0x42
+#define ADV7180_ICR1_ADI       0x43
+#define ADV7180_IMR1_ADI       0x44
+#define ADV7180_IMR2_ADI       0x48
+#define ADV7180_IRQ3_AD_CHANGE 0x08
+#define ADV7180_ISR3_ADI       0x4A
+#define ADV7180_ICR3_ADI       0x4B
+#define ADV7180_IMR3_ADI       0x4C
+#define ADV7180_IMR4_ADI       0x50
 
 struct adv7180_state {
-       struct v4l2_subdev sd;
+       struct v4l2_subdev      sd;
+       struct work_struct      work;
+       struct mutex            mutex; /* mutual excl. when accessing chip */
+       int                     irq;
+       v4l2_std_id             curr_norm;
+       bool                    autodetect;
 };
 
-static v4l2_std_id determine_norm(struct i2c_client *client)
+static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
 {
-       u8 status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
-
        switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
        case ADV7180_STATUS1_AUTOD_NTSM_M_J:
-               return V4L2_STD_NTSC_M_JP;
+               return V4L2_STD_NTSC;
        case ADV7180_STATUS1_AUTOD_NTSC_4_43:
                return V4L2_STD_NTSC_443;
        case ADV7180_STATUS1_AUTOD_PAL_M:
@@ -81,6 +124,53 @@ static v4l2_std_id determine_norm(struct i2c_client *client)
        }
 }
 
+static int v4l2_std_to_adv7180(v4l2_std_id std)
+{
+       if (std == V4L2_STD_PAL_60)
+               return ADV7180_INPUT_CONTROL_PAL60;
+       if (std == V4L2_STD_NTSC_443)
+               return ADV7180_INPUT_CONTROL_NTSC_443;
+       if (std == V4L2_STD_PAL_N)
+               return ADV7180_INPUT_CONTROL_PAL_N;
+       if (std == V4L2_STD_PAL_M)
+               return ADV7180_INPUT_CONTROL_PAL_M;
+       if (std == V4L2_STD_PAL_Nc)
+               return ADV7180_INPUT_CONTROL_PAL_COMB_N;
+
+       if (std & V4L2_STD_PAL)
+               return ADV7180_INPUT_CONTROL_PAL_BG;
+       if (std & V4L2_STD_NTSC)
+               return ADV7180_INPUT_CONTROL_NTSC_M;
+       if (std & V4L2_STD_SECAM)
+               return ADV7180_INPUT_CONTROL_PAL_SECAM;
+
+       return -EINVAL;
+}
+
+static u32 adv7180_status_to_v4l2(u8 status1)
+{
+       if (!(status1 & ADV7180_STATUS1_IN_LOCK))
+               return V4L2_IN_ST_NO_SIGNAL;
+
+       return 0;
+}
+
+static int __adv7180_status(struct i2c_client *client, u32 *status,
+       v4l2_std_id *std)
+{
+       int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
+
+       if (status1 < 0)
+               return status1;
+
+       if (status)
+               *status = adv7180_status_to_v4l2(status1);
+       if (std)
+               *std = adv7180_std_to_v4l2(status1);
+
+       return 0;
+}
+
 static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
 {
        return container_of(sd, struct adv7180_state, sd);
@@ -88,10 +178,31 @@ static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
 
 static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
 {
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct adv7180_state *state = to_state(sd);
+       int err = mutex_lock_interruptible(&state->mutex);
+       if (err)
+               return err;
+
+       /* when we are interrupt driven we know the state */
+       if (!state->autodetect || state->irq > 0)
+               *std = state->curr_norm;
+       else
+               err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std);
+
+       mutex_unlock(&state->mutex);
+       return err;
+}
 
-       *std = determine_norm(client);
-       return 0;
+static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+       struct adv7180_state *state = to_state(sd);
+       int ret = mutex_lock_interruptible(&state->mutex);
+       if (ret)
+               return ret;
+
+       ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL);
+       mutex_unlock(&state->mutex);
+       return ret;
 }
 
 static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
@@ -102,12 +213,51 @@ static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
        return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0);
 }
 
+static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+       struct adv7180_state *state = to_state(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = mutex_lock_interruptible(&state->mutex);
+       if (ret)
+               return ret;
+
+       /* all standards -> autodetect */
+       if (std == V4L2_STD_ALL) {
+               ret = i2c_smbus_write_byte_data(client,
+                       ADV7180_INPUT_CONTROL_REG,
+                       ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+               if (ret < 0)
+                       goto out;
+
+               __adv7180_status(client, NULL, &state->curr_norm);
+               state->autodetect = true;
+       } else {
+               ret = v4l2_std_to_adv7180(std);
+               if (ret < 0)
+                       goto out;
+
+               ret = i2c_smbus_write_byte_data(client,
+                       ADV7180_INPUT_CONTROL_REG, ret);
+               if (ret < 0)
+                       goto out;
+
+               state->curr_norm = std;
+               state->autodetect = false;
+       }
+       ret = 0;
+out:
+       mutex_unlock(&state->mutex);
+       return ret;
+}
+
 static const struct v4l2_subdev_video_ops adv7180_video_ops = {
        .querystd = adv7180_querystd,
+       .g_input_status = adv7180_g_input_status,
 };
 
 static const struct v4l2_subdev_core_ops adv7180_core_ops = {
        .g_chip_ident = adv7180_g_chip_ident,
+       .s_std = adv7180_s_std,
 };
 
 static const struct v4l2_subdev_ops adv7180_ops = {
@@ -115,12 +265,45 @@ static const struct v4l2_subdev_ops adv7180_ops = {
        .video = &adv7180_video_ops,
 };
 
+static void adv7180_work(struct work_struct *work)
+{
+       struct adv7180_state *state = container_of(work, struct adv7180_state,
+               work);
+       struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
+       u8 isr3;
+
+       mutex_lock(&state->mutex);
+       i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+               ADV7180_ADI_CTRL_IRQ_SPACE);
+       isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI);
+       /* clear */
+       i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3);
+       i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0);
+
+       if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
+               __adv7180_status(client, NULL, &state->curr_norm);
+       mutex_unlock(&state->mutex);
+
+       enable_irq(state->irq);
+}
+
+static irqreturn_t adv7180_irq(int irq, void *devid)
+{
+       struct adv7180_state *state = devid;
+
+       schedule_work(&state->work);
+
+       disable_irq_nosync(state->irq);
+
+       return IRQ_HANDLED;
+}
+
 /*
  * Generic i2c probe
  * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
  */
 
-static int adv7180_probe(struct i2c_client *client,
+static __devinit int adv7180_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
        struct adv7180_state *state;
@@ -135,32 +318,111 @@ static int adv7180_probe(struct i2c_client *client,
                        client->addr << 1, client->adapter->name);
 
        state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL);
-       if (state == NULL)
-               return -ENOMEM;
+       if (state == NULL) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       state->irq = client->irq;
+       INIT_WORK(&state->work, adv7180_work);
+       mutex_init(&state->mutex);
+       state->autodetect = true;
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
 
        /* Initialize adv7180 */
-       /* enable autodetection */
+       /* Enable autodetection */
        ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
-               ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM);
-       if (ret > 0)
-               ret = i2c_smbus_write_byte_data(client,
-                       ADV7180_AUTODETECT_ENABLE_REG,
-                       ADV7180_AUTODETECT_DEFAULT);
-       if (ret < 0) {
-               printk(KERN_ERR DRIVER_NAME
-                       ": Failed to communicate to chip: %d\n", ret);
-               return ret;
+               ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+       if (ret < 0)
+               goto err_unreg_subdev;
+
+       ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG,
+               ADV7180_AUTODETECT_DEFAULT);
+       if (ret < 0)
+               goto err_unreg_subdev;
+
+       /* ITU-R BT.656-4 compatible */
+       ret = i2c_smbus_write_byte_data(client,
+               ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
+               ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
+       if (ret < 0)
+               goto err_unreg_subdev;
+
+       /* read current norm */
+       __adv7180_status(client, NULL, &state->curr_norm);
+
+       /* register for interrupts */
+       if (state->irq > 0) {
+               ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME,
+                       state);
+               if (ret)
+                       goto err_unreg_subdev;
+
+               ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+                       ADV7180_ADI_CTRL_IRQ_SPACE);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               /* config the Interrupt pin to be active low */
+               ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
+                       ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               /* enable AD change interrupts interrupts */
+               ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
+                       ADV7180_IRQ3_AD_CHANGE);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+                       0);
+               if (ret < 0)
+                       goto err_unreg_subdev;
        }
 
        return 0;
+
+err_unreg_subdev:
+       mutex_destroy(&state->mutex);
+       v4l2_device_unregister_subdev(sd);
+       kfree(state);
+err:
+       printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret);
+       return ret;
 }
 
-static int adv7180_remove(struct i2c_client *client)
+static __devexit int adv7180_remove(struct i2c_client *client)
 {
        struct v4l2_subdev *sd = i2c_get_clientdata(client);
+       struct adv7180_state *state = to_state(sd);
+
+       if (state->irq > 0) {
+               free_irq(client->irq, state);
+               if (cancel_work_sync(&state->work)) {
+                       /*
+                        * Work was pending, therefore we need to enable
+                        * IRQ here to balance the disable_irq() done in the
+                        * interrupt handler.
+                        */
+                       enable_irq(state->irq);
+               }
+       }
 
+       mutex_destroy(&state->mutex);
        v4l2_device_unregister_subdev(sd);
        kfree(to_state(sd));
        return 0;
@@ -179,7 +441,7 @@ static struct i2c_driver adv7180_driver = {
                .name   = DRIVER_NAME,
        },
        .probe          = adv7180_probe,
-       .remove         = adv7180_remove,
+       .remove         = __devexit_p(adv7180_remove),
        .id_table       = adv7180_id,
 };