summaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c/adv7180.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/adv7180.c')
-rw-r--r--drivers/media/i2c/adv7180.c118
1 files changed, 70 insertions, 48 deletions
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index d7d99f1c69e4..5e638b159452 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -123,11 +123,11 @@
struct adv7180_state {
struct v4l2_ctrl_handler ctrl_hdl;
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;
+ bool powered;
u8 input;
};
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
@@ -312,6 +312,37 @@ out:
return ret;
}
+static int adv7180_set_power(struct adv7180_state *state,
+ struct i2c_client *client, bool on)
+{
+ u8 val;
+
+ if (on)
+ val = ADV7180_PWR_MAN_ON;
+ else
+ val = ADV7180_PWR_MAN_OFF;
+
+ return i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, val);
+}
+
+static int adv7180_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct adv7180_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = mutex_lock_interruptible(&state->mutex);
+ if (ret)
+ return ret;
+
+ ret = adv7180_set_power(state, client, on);
+ if (ret == 0)
+ state->powered = on;
+
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
@@ -442,6 +473,7 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
.s_std = adv7180_s_std,
+ .s_power = adv7180_s_power,
};
static const struct v4l2_subdev_ops adv7180_ops = {
@@ -449,10 +481,9 @@ static const struct v4l2_subdev_ops adv7180_ops = {
.video = &adv7180_video_ops,
};
-static void adv7180_work(struct work_struct *work)
+static irqreturn_t adv7180_irq(int irq, void *devid)
{
- struct adv7180_state *state = container_of(work, struct adv7180_state,
- work);
+ struct adv7180_state *state = devid;
struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
u8 isr3;
@@ -468,17 +499,6 @@ static void adv7180_work(struct work_struct *work)
__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;
}
@@ -533,48 +553,52 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
/* register for interrupts */
if (state->irq > 0) {
- ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME,
- state);
+ ret = request_threaded_irq(state->irq, NULL, adv7180_irq,
+ IRQF_ONESHOT, KBUILD_MODNAME, state);
if (ret)
return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
ADV7180_ADI_CTRL_IRQ_SPACE);
if (ret < 0)
- return ret;
+ goto err;
/* 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)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
if (ret < 0)
- return ret;
+ goto err;
/* enable AD change interrupts interrupts */
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
ADV7180_IRQ3_AD_CHANGE);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
0);
if (ret < 0)
- return ret;
+ goto err;
}
return 0;
+
+err:
+ free_irq(state->irq, state);
+ return ret;
}
static int adv7180_probe(struct i2c_client *client,
@@ -598,9 +622,9 @@ static int adv7180_probe(struct i2c_client *client,
}
state->irq = client->irq;
- INIT_WORK(&state->work, adv7180_work);
mutex_init(&state->mutex);
state->autodetect = true;
+ state->powered = true;
state->input = 0;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
@@ -611,15 +635,21 @@ static int adv7180_probe(struct i2c_client *client,
ret = init_device(client, state);
if (ret)
goto err_free_ctrl;
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret)
+ goto err_free_irq;
+
return 0;
+err_free_irq:
+ if (state->irq > 0)
+ free_irq(client->irq, state);
err_free_ctrl:
adv7180_exit_controls(state);
err_unreg_subdev:
mutex_destroy(&state->mutex);
- v4l2_device_unregister_subdev(sd);
err:
- printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret);
return ret;
}
@@ -628,20 +658,14 @@ static 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) {
+ v4l2_async_unregister_subdev(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);
+ adv7180_exit_controls(state);
+ mutex_destroy(&state->mutex);
return 0;
}
@@ -654,13 +678,10 @@ static const struct i2c_device_id adv7180_id[] = {
static int adv7180_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
- int ret;
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7180_state *state = to_state(sd);
- ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
- ADV7180_PWR_MAN_OFF);
- if (ret < 0)
- return ret;
- return 0;
+ return adv7180_set_power(state, client, false);
}
static int adv7180_resume(struct device *dev)
@@ -670,10 +691,11 @@ static int adv7180_resume(struct device *dev)
struct adv7180_state *state = to_state(sd);
int ret;
- ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
- ADV7180_PWR_MAN_ON);
- if (ret < 0)
- return ret;
+ if (state->powered) {
+ ret = adv7180_set_power(state, client, true);
+ if (ret)
+ return ret;
+ }
ret = init_device(client, state);
if (ret < 0)
return ret;