diff options
Diffstat (limited to 'drivers/media/i2c/adv7180.c')
-rw-r--r-- | drivers/media/i2c/adv7180.c | 160 |
1 files changed, 121 insertions, 39 deletions
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index ff57c1dcb8af..b77b0a4dbf68 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -26,8 +26,9 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/of.h> -#include <media/v4l2-ioctl.h> #include <linux/videodev2.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <linux/mutex.h> @@ -192,8 +193,8 @@ struct adv7180_state { struct mutex mutex; /* mutual excl. when accessing chip */ int irq; v4l2_std_id curr_norm; - bool autodetect; bool powered; + bool streaming; u8 input; struct i2c_client *client; @@ -338,12 +339,26 @@ static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) 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(state, NULL, std); + if (state->streaming) { + err = -EBUSY; + goto unlock; + } + + err = adv7180_set_video_standard(state, + ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM); + if (err) + goto unlock; + + msleep(100); + __adv7180_status(state, NULL, std); + + err = v4l2_std_to_adv7180(state->curr_norm); + if (err < 0) + goto unlock; + err = adv7180_set_video_standard(state, err); + +unlock: mutex_unlock(&state->mutex); return err; } @@ -387,23 +402,13 @@ static int adv7180_program_std(struct adv7180_state *state) { int ret; - if (state->autodetect) { - ret = adv7180_set_video_standard(state, - ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM); - if (ret < 0) - return ret; - - __adv7180_status(state, NULL, &state->curr_norm); - } else { - ret = v4l2_std_to_adv7180(state->curr_norm); - if (ret < 0) - return ret; - - ret = adv7180_set_video_standard(state, ret); - if (ret < 0) - return ret; - } + ret = v4l2_std_to_adv7180(state->curr_norm); + if (ret < 0) + return ret; + ret = adv7180_set_video_standard(state, ret); + if (ret < 0) + return ret; return 0; } @@ -415,18 +420,12 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) if (ret) return ret; - /* all standards -> autodetect */ - if (std == V4L2_STD_ALL) { - state->autodetect = true; - } else { - /* Make sure we can support this std */ - ret = v4l2_std_to_adv7180(std); - if (ret < 0) - goto out; + /* Make sure we can support this std */ + ret = v4l2_std_to_adv7180(std); + if (ret < 0) + goto out; - state->curr_norm = std; - state->autodetect = false; - } + state->curr_norm = std; ret = adv7180_program_std(state); out: @@ -434,6 +433,15 @@ out: return ret; } +static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + struct adv7180_state *state = to_state(sd); + + *norm = state->curr_norm; + + return 0; +} + static int adv7180_set_power(struct adv7180_state *state, bool on) { u8 val; @@ -717,17 +725,77 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd, return 0; } +static int adv7180_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *cropcap) +{ + struct adv7180_state *state = to_state(sd); + + if (state->curr_norm & V4L2_STD_525_60) { + cropcap->pixelaspect.numerator = 11; + cropcap->pixelaspect.denominator = 10; + } else { + cropcap->pixelaspect.numerator = 54; + cropcap->pixelaspect.denominator = 59; + } + + return 0; +} + +static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + *norm = V4L2_STD_ALL; + return 0; +} + +static int adv7180_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct adv7180_state *state = to_state(sd); + int ret; + + /* It's always safe to stop streaming, no need to take the lock */ + if (!enable) { + state->streaming = enable; + return 0; + } + + /* Must wait until querystd released the lock */ + ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + state->streaming = enable; + mutex_unlock(&state->mutex); + return 0; +} + +static int adv7180_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + static const struct v4l2_subdev_video_ops adv7180_video_ops = { .s_std = adv7180_s_std, + .g_std = adv7180_g_std, .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, .g_mbus_config = adv7180_g_mbus_config, + .cropcap = adv7180_cropcap, + .g_tvnorms = adv7180_g_tvnorms, + .s_stream = adv7180_s_stream, }; - static const struct v4l2_subdev_core_ops adv7180_core_ops = { .s_power = adv7180_s_power, + .subscribe_event = adv7180_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { @@ -752,8 +820,14 @@ static irqreturn_t adv7180_irq(int irq, void *devid) /* clear */ adv7180_write(state, ADV7180_REG_ICR3, isr3); - if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) - __adv7180_status(state, NULL, &state->curr_norm); + if (isr3 & ADV7180_IRQ3_AD_CHANGE) { + static const struct v4l2_event src_ch = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + v4l2_subdev_notify_event(&state->sd, &src_ch); + } mutex_unlock(&state->mutex); return IRQ_HANDLED; @@ -1198,7 +1272,7 @@ static int adv7180_probe(struct i2c_client *client, state->irq = client->irq; mutex_init(&state->mutex); - state->autodetect = true; + state->curr_norm = V4L2_STD_NTSC; if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED) state->powered = true; else @@ -1206,7 +1280,7 @@ static int adv7180_probe(struct i2c_client *client, state->input = 0; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; ret = adv7180_init_controls(state); if (ret) @@ -1328,6 +1402,14 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); #ifdef CONFIG_OF static const struct of_device_id adv7180_of_id[] = { { .compatible = "adi,adv7180", }, + { .compatible = "adi,adv7182", }, + { .compatible = "adi,adv7280", }, + { .compatible = "adi,adv7280-m", }, + { .compatible = "adi,adv7281", }, + { .compatible = "adi,adv7281-m", }, + { .compatible = "adi,adv7281-ma", }, + { .compatible = "adi,adv7282", }, + { .compatible = "adi,adv7282-m", }, { }, }; |