From 31b9cb6ecd0128a165fe2b437ef63a6fa744de7b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Apr 2018 06:12:17 -0400 Subject: media: ov7740: Fix number of controls hint The driver has 12 controls, not 2. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7740.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 01f5787..c9b8bec 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -953,7 +953,7 @@ static int ov7740_init_controls(struct ov7740 *ov7740) struct v4l2_ctrl_handler *ctrl_hdlr = &ov7740->ctrl_handler; int ret; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 2); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); if (ret < 0) return ret; -- cgit v1.1 From bad04a55ab1fadfeb83999d338e7fe511d2edfd9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Apr 2018 06:14:26 -0400 Subject: media: ov7740: Check for possible NULL return value in control creation Check that creating the control actually succeeded before accessing it. A failure would lead to NULL pointer reference. Fix this. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7740.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index c9b8bec..cbfa5a3 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -980,21 +980,26 @@ static int ov7740_init_controls(struct ov7740 *ov7740) V4L2_CID_HFLIP, 0, 1, 1, 0); ov7740->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + ov7740->gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_GAIN, 0, 1023, 1, 500); + if (ov7740->gain) + ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + ov7740->auto_gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + ov7740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_EXPOSURE, 0, 65535, 1, 500); + if (ov7740->exposure) + ov7740->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + ov7740->auto_exposure = v4l2_ctrl_new_std_menu(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); - ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; - ov7740->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; - v4l2_ctrl_auto_cluster(3, &ov7740->auto_wb, 0, false); v4l2_ctrl_auto_cluster(2, &ov7740->auto_gain, 0, true); v4l2_ctrl_auto_cluster(2, &ov7740->auto_exposure, -- cgit v1.1 From 80a1f95b8a6fe14b0c4b1f688b3a5d7170936e2c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Apr 2018 06:16:39 -0400 Subject: media: ov7740: Fix control handler error at the end of control init Check that no error happened during adding controls to the driver's control handler. Print an error message and bail out if there was one. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7740.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index cbfa5a3..3dad33c 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1006,6 +1006,13 @@ static int ov7740_init_controls(struct ov7740 *ov7740) V4L2_EXPOSURE_MANUAL, false); v4l2_ctrl_cluster(2, &ov7740->hflip); + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "controls initialisation failed (%d)\n", + ret); + goto error; + } + ret = v4l2_ctrl_handler_setup(ctrl_hdlr); if (ret) { dev_err(&client->dev, "%s control init failed (%d)\n", -- cgit v1.1 From 65b0ae5e3ee5fccb2311652d91249a1fff3522d8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Apr 2018 06:18:11 -0400 Subject: media: ov7740: Set subdev HAS_EVENT flag The driver has event support implemented but fails to set the flag enabling event support. Set the flag to enable control events. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7740.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 3dad33c..605f3e2 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1086,7 +1086,7 @@ static int ov7740_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov7740_subdev_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) -- cgit v1.1 From 5cd770f68a2081849a4c1f140dd4eff8387a3da6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Apr 2018 08:27:50 -0400 Subject: media: tda1997x: Use bitwise or for setting subdev flags Assigning subdev flags in probe() after v4l2_i2c_subdev_init() clears the I2C flag set by that function. Fix this by using bitwise or instead. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tda1997x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 3021913..1c5b5f7 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2567,7 +2567,7 @@ static int tda1997x_probe(struct i2c_client *client, snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", id->name, i2c_adapter_id(client->adapter), client->addr); - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.function = MEDIA_ENT_F_DTV_DECODER; sd->entity.ops = &tda1997x_media_ops; -- cgit v1.1 From 5cebaac609744414463d1ecc28fdecd26c1b9bc1 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Fri, 6 Apr 2018 18:52:31 -0400 Subject: media: video-i2c: add video-i2c driver There are several thermal sensors that only have a low-speed bus interface but output valid video data. This patchset enables support for the AMG88xx "Grid-Eye" sensor family. Signed-off-by: Matt Ranostay Acked-by: Sakari Ailus [hans.verkuil@cisco.com: split up int ret = ...->xfer(); line] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/video-i2c.c | 564 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 578 insertions(+) create mode 100644 drivers/media/i2c/video-i2c.c (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 541f0d28..faaaceb 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -974,6 +974,19 @@ config VIDEO_M52790 To compile this driver as a module, choose M here: the module will be called m52790. + +config VIDEO_I2C + tristate "I2C transport video support" + depends on VIDEO_V4L2 && I2C + select VIDEOBUF2_VMALLOC + ---help--- + Enable the I2C transport video support which supports the + following: + * Panasonic AMG88xx Grid-Eye Sensors + + To compile this driver as a module, choose M here: the + module will be called video-i2c + endmenu menu "Sensors used on soc_camera driver" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index ea34aee..84cc472 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -96,6 +96,7 @@ obj-$(CONFIG_VIDEO_LM3646) += lm3646.o obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o +obj-$(CONFIG_VIDEO_I2C) += video-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c new file mode 100644 index 0000000..971eb46 --- /dev/null +++ b/drivers/media/i2c/video-i2c.c @@ -0,0 +1,564 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * video-i2c.c - Support for I2C transport video devices + * + * Copyright (C) 2018 Matt Ranostay + * + * Supported: + * - Panasonic AMG88xx Grid-Eye Sensors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VIDEO_I2C_DRIVER "video-i2c" + +struct video_i2c_chip; + +struct video_i2c_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct video_i2c_data { + struct i2c_client *client; + const struct video_i2c_chip *chip; + struct mutex lock; + spinlock_t slock; + unsigned int sequence; + struct mutex queue_lock; + + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct vb2_queue vb_vidq; + + struct task_struct *kthread_vid_cap; + struct list_head vid_cap_active; +}; + +const static struct v4l2_fmtdesc amg88xx_format = { + .pixelformat = V4L2_PIX_FMT_Y12, +}; + +const static struct v4l2_frmsize_discrete amg88xx_size = { + .width = 8, + .height = 8, +}; + +struct video_i2c_chip { + /* video dimensions */ + const struct v4l2_fmtdesc *format; + const struct v4l2_frmsize_discrete *size; + + /* max frames per second */ + unsigned int max_fps; + + /* pixel buffer size */ + unsigned int buffer_size; + + /* pixel size in bits */ + unsigned int bpp; + + /* xfer function */ + int (*xfer)(struct video_i2c_data *data, char *buf); +}; + +static int amg88xx_xfer(struct video_i2c_data *data, char *buf) +{ + struct i2c_client *client = data->client; + struct i2c_msg msg[2]; + u8 reg = 0x80; + int ret; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = (char *)® + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = data->chip->buffer_size; + msg[1].buf = (char *)buf; + + ret = i2c_transfer(client->adapter, msg, 2); + + return (ret == 2) ? 0 : -EIO; +} + +#define AMG88XX 0 + +static const struct video_i2c_chip video_i2c_chip[] = { + [AMG88XX] = { + .size = &amg88xx_size, + .format = &amg88xx_format, + .max_fps = 10, + .buffer_size = 128, + .bpp = 16, + .xfer = &amg88xx_xfer, + }, +}; + +static const struct v4l2_file_operations video_i2c_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static int queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct video_i2c_data *data = vb2_get_drv_priv(vq); + unsigned int size = data->chip->buffer_size; + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int buffer_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct video_i2c_data *data = vb2_get_drv_priv(vb->vb2_queue); + unsigned int size = data->chip->buffer_size; + + if (vb2_plane_size(vb, 0) < size) + return -EINVAL; + + vbuf->field = V4L2_FIELD_NONE; + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void buffer_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct video_i2c_data *data = vb2_get_drv_priv(vb->vb2_queue); + struct video_i2c_buffer *buf = + container_of(vbuf, struct video_i2c_buffer, vb); + + spin_lock(&data->slock); + list_add_tail(&buf->list, &data->vid_cap_active); + spin_unlock(&data->slock); +} + +static int video_i2c_thread_vid_cap(void *priv) +{ + struct video_i2c_data *data = priv; + unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps); + + set_freezable(); + + do { + unsigned long start_jiffies = jiffies; + struct video_i2c_buffer *vid_cap_buf = NULL; + int schedule_delay; + + try_to_freeze(); + + spin_lock(&data->slock); + + if (!list_empty(&data->vid_cap_active)) { + vid_cap_buf = list_last_entry(&data->vid_cap_active, + struct video_i2c_buffer, list); + list_del(&vid_cap_buf->list); + } + + spin_unlock(&data->slock); + + if (vid_cap_buf) { + struct vb2_buffer *vb2_buf = &vid_cap_buf->vb.vb2_buf; + void *vbuf = vb2_plane_vaddr(vb2_buf, 0); + int ret; + + ret = data->chip->xfer(data, vbuf); + vb2_buf->timestamp = ktime_get_ns(); + vid_cap_buf->vb.sequence = data->sequence++; + vb2_buffer_done(vb2_buf, ret ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + } + + schedule_delay = delay - (jiffies - start_jiffies); + + if (time_after(jiffies, start_jiffies + delay)) + schedule_delay = delay; + + schedule_timeout_interruptible(schedule_delay); + } while (!kthread_should_stop()); + + return 0; +} + +static void video_i2c_del_list(struct vb2_queue *vq, enum vb2_buffer_state state) +{ + struct video_i2c_data *data = vb2_get_drv_priv(vq); + struct video_i2c_buffer *buf, *tmp; + + spin_lock(&data->slock); + + list_for_each_entry_safe(buf, tmp, &data->vid_cap_active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + spin_unlock(&data->slock); +} + +static int start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct video_i2c_data *data = vb2_get_drv_priv(vq); + + if (data->kthread_vid_cap) + return 0; + + data->sequence = 0; + data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data, + "%s-vid-cap", data->v4l2_dev.name); + if (!IS_ERR(data->kthread_vid_cap)) + return 0; + + video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED); + + return PTR_ERR(data->kthread_vid_cap); +} + +static void stop_streaming(struct vb2_queue *vq) +{ + struct video_i2c_data *data = vb2_get_drv_priv(vq); + + if (data->kthread_vid_cap == NULL) + return; + + kthread_stop(data->kthread_vid_cap); + data->kthread_vid_cap = NULL; + + video_i2c_del_list(vq, VB2_BUF_STATE_ERROR); +} + +static struct vb2_ops video_i2c_video_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int video_i2c_querycap(struct file *file, void *priv, + struct v4l2_capability *vcap) +{ + struct video_i2c_data *data = video_drvdata(file); + struct i2c_client *client = data->client; + + strlcpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver)); + strlcpy(vcap->card, data->vdev.name, sizeof(vcap->card)); + + sprintf(vcap->bus_info, "I2C:%d-%d", client->adapter->nr, client->addr); + + return 0; +} + +static int video_i2c_g_input(struct file *file, void *fh, unsigned int *inp) +{ + *inp = 0; + + return 0; +} + +static int video_i2c_s_input(struct file *file, void *fh, unsigned int inp) +{ + return (inp > 0) ? -EINVAL : 0; +} + +static int video_i2c_enum_input(struct file *file, void *fh, + struct v4l2_input *vin) +{ + if (vin->index > 0) + return -EINVAL; + + strlcpy(vin->name, "Camera", sizeof(vin->name)); + + vin->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int video_i2c_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + struct video_i2c_data *data = video_drvdata(file); + enum v4l2_buf_type type = fmt->type; + + if (fmt->index > 0) + return -EINVAL; + + *fmt = *data->chip->format; + fmt->type = type; + + return 0; +} + +static int video_i2c_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + const struct video_i2c_data *data = video_drvdata(file); + const struct v4l2_frmsize_discrete *size = data->chip->size; + + /* currently only one frame size is allowed */ + if (fsize->index > 0) + return -EINVAL; + + if (fsize->pixel_format != data->chip->format->pixelformat) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = size->width; + fsize->discrete.height = size->height; + + return 0; +} + +static int video_i2c_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fe) +{ + const struct video_i2c_data *data = video_drvdata(file); + const struct v4l2_frmsize_discrete *size = data->chip->size; + + if (fe->index > 0) + return -EINVAL; + + if (fe->width != size->width || fe->height != size->height) + return -EINVAL; + + fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fe->discrete.numerator = 1; + fe->discrete.denominator = data->chip->max_fps; + + return 0; +} + +static int video_i2c_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + const struct video_i2c_data *data = video_drvdata(file); + const struct v4l2_frmsize_discrete *size = data->chip->size; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + unsigned int bpp = data->chip->bpp / 8; + + pix->width = size->width; + pix->height = size->height; + pix->pixelformat = data->chip->format->pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width * bpp; + pix->sizeimage = pix->bytesperline * pix->height; + pix->colorspace = V4L2_COLORSPACE_RAW; + + return 0; +} + +static int video_i2c_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + struct video_i2c_data *data = video_drvdata(file); + + if (vb2_is_busy(&data->vb_vidq)) + return -EBUSY; + + return video_i2c_try_fmt_vid_cap(file, fh, fmt); +} + +static int video_i2c_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct video_i2c_data *data = video_drvdata(filp); + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + parm->parm.capture.readbuffers = 1; + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe.numerator = 1; + parm->parm.capture.timeperframe.denominator = data->chip->max_fps; + + return 0; +} + +static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = { + .vidioc_querycap = video_i2c_querycap, + .vidioc_g_input = video_i2c_g_input, + .vidioc_s_input = video_i2c_s_input, + .vidioc_enum_input = video_i2c_enum_input, + .vidioc_enum_fmt_vid_cap = video_i2c_enum_fmt_vid_cap, + .vidioc_enum_framesizes = video_i2c_enum_framesizes, + .vidioc_enum_frameintervals = video_i2c_enum_frameintervals, + .vidioc_g_fmt_vid_cap = video_i2c_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = video_i2c_s_fmt_vid_cap, + .vidioc_g_parm = video_i2c_g_parm, + .vidioc_s_parm = video_i2c_g_parm, + .vidioc_try_fmt_vid_cap = video_i2c_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static void video_i2c_release(struct video_device *vdev) +{ + kfree(video_get_drvdata(vdev)); +} + +static int video_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct video_i2c_data *data; + struct v4l2_device *v4l2_dev; + struct vb2_queue *queue; + int ret = -ENODEV; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (dev_fwnode(&client->dev)) + data->chip = device_get_match_data(&client->dev); + else if (id) + data->chip = &video_i2c_chip[id->driver_data]; + else + goto error_free_device; + + data->client = client; + v4l2_dev = &data->v4l2_dev; + strlcpy(v4l2_dev->name, VIDEO_I2C_DRIVER, sizeof(v4l2_dev->name)); + + ret = v4l2_device_register(&client->dev, v4l2_dev); + if (ret < 0) + goto error_free_device; + + mutex_init(&data->lock); + mutex_init(&data->queue_lock); + + queue = &data->vb_vidq; + queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + queue->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR | VB2_READ; + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + queue->drv_priv = data; + queue->buf_struct_size = sizeof(struct video_i2c_buffer); + queue->min_buffers_needed = 1; + queue->ops = &video_i2c_video_qops; + queue->mem_ops = &vb2_vmalloc_memops; + + ret = vb2_queue_init(queue); + if (ret < 0) + goto error_unregister_device; + + data->vdev.queue = queue; + data->vdev.queue->lock = &data->queue_lock; + + snprintf(data->vdev.name, sizeof(data->vdev.name), + "I2C %d-%d Transport Video", + client->adapter->nr, client->addr); + + data->vdev.v4l2_dev = v4l2_dev; + data->vdev.fops = &video_i2c_fops; + data->vdev.lock = &data->lock; + data->vdev.ioctl_ops = &video_i2c_ioctl_ops; + data->vdev.release = video_i2c_release; + data->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + + spin_lock_init(&data->slock); + INIT_LIST_HEAD(&data->vid_cap_active); + + video_set_drvdata(&data->vdev, data); + i2c_set_clientdata(client, data); + + ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + goto error_unregister_device; + + return 0; + +error_unregister_device: + v4l2_device_unregister(v4l2_dev); + mutex_destroy(&data->lock); + mutex_destroy(&data->queue_lock); + +error_free_device: + kfree(data); + + return ret; +} + +static int video_i2c_remove(struct i2c_client *client) +{ + struct video_i2c_data *data = i2c_get_clientdata(client); + + video_unregister_device(&data->vdev); + v4l2_device_unregister(&data->v4l2_dev); + + mutex_destroy(&data->lock); + mutex_destroy(&data->queue_lock); + + return 0; +} + +static const struct i2c_device_id video_i2c_id_table[] = { + { "amg88xx", AMG88XX }, + {} +}; +MODULE_DEVICE_TABLE(i2c, video_i2c_id_table); + +static const struct of_device_id video_i2c_of_match[] = { + { .compatible = "panasonic,amg88xx", .data = &video_i2c_chip[AMG88XX] }, + {} +}; +MODULE_DEVICE_TABLE(of, video_i2c_of_match); + +static struct i2c_driver video_i2c_driver = { + .driver = { + .name = VIDEO_I2C_DRIVER, + .of_match_table = video_i2c_of_match, + }, + .probe = video_i2c_probe, + .remove = video_i2c_remove, + .id_table = video_i2c_id_table, +}; + +module_i2c_driver(video_i2c_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("I2C transport video support"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 7bc8a0de6945ba9f17b07404b1261defab27f5b5 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Thu, 3 May 2018 06:38:40 -0400 Subject: media: rc: probe zilog transmitter when zilog receiver is found Both Hauppauge WinTV 44981 (bt878) and the HVR 1110 (saa7134) have a Zilog Z8F0811. The transmitter was not probed. Receive and transmit tested on both cards. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ir-kbd-i2c.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index a7e23bc..a14a74e 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -739,6 +739,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) struct rc_dev *rc = NULL; struct i2c_adapter *adap = client->adapter; unsigned short addr = client->addr; + bool probe_tx = (id->driver_data & FLAG_TX) != 0; int err; if ((id->driver_data & FLAG_HDPVR) && !enable_hdpvr) { @@ -800,6 +801,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) rc_proto = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_RC6_6A_32; ir_codes = RC_MAP_HAUPPAUGE; + probe_tx = true; break; } @@ -892,7 +894,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) INIT_DELAYED_WORK(&ir->work, ir_work); - if (id->driver_data & FLAG_TX) { + if (probe_tx) { ir->tx_c = i2c_new_dummy(client->adapter, 0x70); if (!ir->tx_c) { dev_err(&client->dev, "failed to setup tx i2c address"); -- cgit v1.1 From 1b3d5f2ae882cfca37c4422dfd72fd455d01166a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 May 2018 10:18:05 -0400 Subject: media: video-i2c: get rid of two gcc warnings After adding this driver, gcc complains with: drivers/media/i2c/video-i2c.c:55:1: warning: 'static' is not at beginning of declaration [-Wold-style-declaration] const static struct v4l2_fmtdesc amg88xx_format = { ^~~~~ drivers/media/i2c/video-i2c.c:59:1: warning: 'static' is not at beginning of declaration [-Wold-style-declaration] const static struct v4l2_frmsize_discrete amg88xx_size = { ^~~~~ Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/video-i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 971eb46..0b347cc 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -52,11 +52,11 @@ struct video_i2c_data { struct list_head vid_cap_active; }; -const static struct v4l2_fmtdesc amg88xx_format = { +static const struct v4l2_fmtdesc amg88xx_format = { .pixelformat = V4L2_PIX_FMT_Y12, }; -const static struct v4l2_frmsize_discrete amg88xx_size = { +static const struct v4l2_frmsize_discrete amg88xx_size = { .width = 8, .height = 8, }; -- cgit v1.1 From 949abce61c94c3e71f3d608eba240408aff1e3df Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 24 Mar 2018 10:01:33 -0400 Subject: media: ov5695: Remove owner assignment from i2c_driver Structure i2c_driver does not need to set the owner field, as this will be populated by the driver core. Generated by scripts/coccinelle/api/platform_no_drv_owner.cocci. Cc: Shunqian Zheng Signed-off-by: Fabio Estevam Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5695.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index 9be38a0..9a80dec 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -1385,7 +1385,6 @@ MODULE_DEVICE_TABLE(of, ov5695_of_match); static struct i2c_driver ov5695_i2c_driver = { .driver = { .name = "ov5695", - .owner = THIS_MODULE, .pm = &ov5695_pm_ops, .of_match_table = of_match_ptr(ov5695_of_match), }, -- cgit v1.1 From 0872f4f4f13c116844fc7308cd21431c6d187665 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 24 Mar 2018 10:01:34 -0400 Subject: media: ov13858: Remove owner assignment from i2c_driver Structure i2c_driver does not need to set the owner field, as this will be populated by the driver core. Generated by scripts/coccinelle/api/platform_no_drv_owner.cocci. Cc: Sakari Ailus Signed-off-by: Fabio Estevam Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov13858.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 3dbcae2..a66f620 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1796,7 +1796,6 @@ MODULE_DEVICE_TABLE(acpi, ov13858_acpi_ids); static struct i2c_driver ov13858_i2c_driver = { .driver = { .name = "ov13858", - .owner = THIS_MODULE, .pm = &ov13858_pm_ops, .acpi_match_table = ACPI_PTR(ov13858_acpi_ids), }, -- cgit v1.1 From bb7a3681b2cf0cb31df12c41d75eaf2e56340bb9 Mon Sep 17 00:00:00 2001 From: Nasser Afshin Date: Mon, 2 Apr 2018 18:23:20 -0400 Subject: media: i2c: tvp5150: Use parentheses for sizeof This patch resolves a checkpatch.pl warning: WARNING: sizeof *cap should be sizeof(*cap) Signed-off-by: Nasser Afshin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 2476d81..8322190 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -623,7 +623,7 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, int line, i; dev_dbg_lvl(sd->dev, 1, debug, "g_sliced_vbi_cap\n"); - memset(cap, 0, sizeof *cap); + memset(cap, 0, sizeof(*cap)); for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) { const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i]; -- cgit v1.1 From 4efcd5ed94e76a58b654d31d183505e784c57fa7 Mon Sep 17 00:00:00 2001 From: Nasser Afshin Date: Mon, 2 Apr 2018 18:23:17 -0400 Subject: media: i2c: tvp5150: Add a space after commas This patch resolves checkpatch.pl errors: ERROR: space required after that ',' (ctx:VxV) Signed-off-by: Nasser Afshin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 134 ++++++++++++++++++++++---------------------- 1 file changed, 67 insertions(+), 67 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 8322190..d1e93c1 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -319,136 +319,136 @@ struct i2c_reg_value { /* Default values as sugested at TVP5150AM1 datasheet */ static const struct i2c_reg_value tvp5150_init_default[] = { { /* 0x00 */ - TVP5150_VD_IN_SRC_SEL_1,0x00 + TVP5150_VD_IN_SRC_SEL_1, 0x00 }, { /* 0x01 */ - TVP5150_ANAL_CHL_CTL,0x15 + TVP5150_ANAL_CHL_CTL, 0x15 }, { /* 0x02 */ - TVP5150_OP_MODE_CTL,0x00 + TVP5150_OP_MODE_CTL, 0x00 }, { /* 0x03 */ - TVP5150_MISC_CTL,0x01 + TVP5150_MISC_CTL, 0x01 }, { /* 0x06 */ - TVP5150_COLOR_KIL_THSH_CTL,0x10 + TVP5150_COLOR_KIL_THSH_CTL, 0x10 }, { /* 0x07 */ - TVP5150_LUMA_PROC_CTL_1,0x60 + TVP5150_LUMA_PROC_CTL_1, 0x60 }, { /* 0x08 */ - TVP5150_LUMA_PROC_CTL_2,0x00 + TVP5150_LUMA_PROC_CTL_2, 0x00 }, { /* 0x09 */ - TVP5150_BRIGHT_CTL,0x80 + TVP5150_BRIGHT_CTL, 0x80 }, { /* 0x0a */ - TVP5150_SATURATION_CTL,0x80 + TVP5150_SATURATION_CTL, 0x80 }, { /* 0x0b */ - TVP5150_HUE_CTL,0x00 + TVP5150_HUE_CTL, 0x00 }, { /* 0x0c */ - TVP5150_CONTRAST_CTL,0x80 + TVP5150_CONTRAST_CTL, 0x80 }, { /* 0x0d */ - TVP5150_DATA_RATE_SEL,0x47 + TVP5150_DATA_RATE_SEL, 0x47 }, { /* 0x0e */ - TVP5150_LUMA_PROC_CTL_3,0x00 + TVP5150_LUMA_PROC_CTL_3, 0x00 }, { /* 0x0f */ - TVP5150_CONF_SHARED_PIN,0x08 + TVP5150_CONF_SHARED_PIN, 0x08 }, { /* 0x11 */ - TVP5150_ACT_VD_CROP_ST_MSB,0x00 + TVP5150_ACT_VD_CROP_ST_MSB, 0x00 }, { /* 0x12 */ - TVP5150_ACT_VD_CROP_ST_LSB,0x00 + TVP5150_ACT_VD_CROP_ST_LSB, 0x00 }, { /* 0x13 */ - TVP5150_ACT_VD_CROP_STP_MSB,0x00 + TVP5150_ACT_VD_CROP_STP_MSB, 0x00 }, { /* 0x14 */ - TVP5150_ACT_VD_CROP_STP_LSB,0x00 + TVP5150_ACT_VD_CROP_STP_LSB, 0x00 }, { /* 0x15 */ - TVP5150_GENLOCK,0x01 + TVP5150_GENLOCK, 0x01 }, { /* 0x16 */ - TVP5150_HORIZ_SYNC_START,0x80 + TVP5150_HORIZ_SYNC_START, 0x80 }, { /* 0x18 */ - TVP5150_VERT_BLANKING_START,0x00 + TVP5150_VERT_BLANKING_START, 0x00 }, { /* 0x19 */ - TVP5150_VERT_BLANKING_STOP,0x00 + TVP5150_VERT_BLANKING_STOP, 0x00 }, { /* 0x1a */ - TVP5150_CHROMA_PROC_CTL_1,0x0c + TVP5150_CHROMA_PROC_CTL_1, 0x0c }, { /* 0x1b */ - TVP5150_CHROMA_PROC_CTL_2,0x14 + TVP5150_CHROMA_PROC_CTL_2, 0x14 }, { /* 0x1c */ - TVP5150_INT_RESET_REG_B,0x00 + TVP5150_INT_RESET_REG_B, 0x00 }, { /* 0x1d */ - TVP5150_INT_ENABLE_REG_B,0x00 + TVP5150_INT_ENABLE_REG_B, 0x00 }, { /* 0x1e */ - TVP5150_INTT_CONFIG_REG_B,0x00 + TVP5150_INTT_CONFIG_REG_B, 0x00 }, { /* 0x28 */ - TVP5150_VIDEO_STD,0x00 + TVP5150_VIDEO_STD, 0x00 }, { /* 0x2e */ - TVP5150_MACROVISION_ON_CTR,0x0f + TVP5150_MACROVISION_ON_CTR, 0x0f }, { /* 0x2f */ - TVP5150_MACROVISION_OFF_CTR,0x01 + TVP5150_MACROVISION_OFF_CTR, 0x01 }, { /* 0xbb */ - TVP5150_TELETEXT_FIL_ENA,0x00 + TVP5150_TELETEXT_FIL_ENA, 0x00 }, { /* 0xc0 */ - TVP5150_INT_STATUS_REG_A,0x00 + TVP5150_INT_STATUS_REG_A, 0x00 }, { /* 0xc1 */ - TVP5150_INT_ENABLE_REG_A,0x00 + TVP5150_INT_ENABLE_REG_A, 0x00 }, { /* 0xc2 */ - TVP5150_INT_CONF,0x04 + TVP5150_INT_CONF, 0x04 }, { /* 0xc8 */ - TVP5150_FIFO_INT_THRESHOLD,0x80 + TVP5150_FIFO_INT_THRESHOLD, 0x80 }, { /* 0xc9 */ - TVP5150_FIFO_RESET,0x00 + TVP5150_FIFO_RESET, 0x00 }, { /* 0xca */ - TVP5150_LINE_NUMBER_INT,0x00 + TVP5150_LINE_NUMBER_INT, 0x00 }, { /* 0xcb */ - TVP5150_PIX_ALIGN_REG_LOW,0x4e + TVP5150_PIX_ALIGN_REG_LOW, 0x4e }, { /* 0xcc */ - TVP5150_PIX_ALIGN_REG_HIGH,0x00 + TVP5150_PIX_ALIGN_REG_HIGH, 0x00 }, { /* 0xcd */ - TVP5150_FIFO_OUT_CTRL,0x01 + TVP5150_FIFO_OUT_CTRL, 0x01 }, { /* 0xcf */ - TVP5150_FULL_FIELD_ENA,0x00 + TVP5150_FULL_FIELD_ENA, 0x00 }, { /* 0xd0 */ - TVP5150_LINE_MODE_INI,0x00 + TVP5150_LINE_MODE_INI, 0x00 }, { /* 0xfc */ - TVP5150_FULL_FIELD_MODE_REG,0x7f + TVP5150_FULL_FIELD_MODE_REG, 0x7f }, { /* end of data */ - 0xff,0xff + 0xff, 0xff } }; @@ -456,27 +456,27 @@ static const struct i2c_reg_value tvp5150_init_default[] = { static const struct i2c_reg_value tvp5150_init_enable[] = { { TVP5150_CONF_SHARED_PIN, 2 - },{ /* Automatic offset and AGC enabled */ + }, { /* Automatic offset and AGC enabled */ TVP5150_ANAL_CHL_CTL, 0x15 - },{ /* Activate YCrCb output 0x9 or 0xd ? */ + }, { /* Activate YCrCb output 0x9 or 0xd ? */ TVP5150_MISC_CTL, TVP5150_MISC_CTL_GPCL | TVP5150_MISC_CTL_INTREQ_OE | TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE | TVP5150_MISC_CTL_VBLANK | TVP5150_MISC_CTL_CLOCK_OE, - },{ /* Activates video std autodetection for all standards */ + }, { /* Activates video std autodetection for all standards */ TVP5150_AUTOSW_MSK, 0x0 - },{ /* Default format: 0x47. For 4:2:2: 0x40 */ + }, { /* Default format: 0x47. For 4:2:2: 0x40 */ TVP5150_DATA_RATE_SEL, 0x47 - },{ + }, { TVP5150_CHROMA_PROC_CTL_1, 0x0c - },{ + }, { TVP5150_CHROMA_PROC_CTL_2, 0x54 - },{ /* Non documented, but initialized on WinTV USB2 */ + }, { /* Non documented, but initialized on WinTV USB2 */ 0x27, 0x20 - },{ - 0xff,0xff + }, { + 0xff, 0xff } }; @@ -506,72 +506,72 @@ static struct i2c_vbi_ram_value vbi_ram_default[] = yet supported are placed under #if 0 */ #if 0 [0] = {0x010, /* Teletext, SECAM, WST System A */ - {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, + {V4L2_SLICED_TELETEXT_SECAM, 6, 23, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } }, #endif [1] = {0x030, /* Teletext, PAL, WST System B */ - {V4L2_SLICED_TELETEXT_B,6,22,1}, + {V4L2_SLICED_TELETEXT_B, 6, 22, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } }, #if 0 [2] = {0x050, /* Teletext, PAL, WST System C */ - {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, + {V4L2_SLICED_TELETEXT_PAL_C, 6, 22, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, [3] = {0x070, /* Teletext, NTSC, WST System B */ - {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, + {V4L2_SLICED_TELETEXT_NTSC_B, 10, 21, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, [4] = {0x090, /* Tetetext, NTSC NABTS System C */ - {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, + {V4L2_SLICED_TELETEXT_NTSC_C, 10, 21, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } }, [5] = {0x0b0, /* Teletext, NTSC-J, NABTS System D */ - {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, + {V4L2_SLICED_TELETEXT_NTSC_D, 10, 21, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, [6] = {0x0d0, /* Closed Caption, PAL/SECAM */ - {V4L2_SLICED_CAPTION_625,22,22,1}, + {V4L2_SLICED_CAPTION_625, 22, 22, 1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, #endif [7] = {0x0f0, /* Closed Caption, NTSC */ - {V4L2_SLICED_CAPTION_525,21,21,1}, + {V4L2_SLICED_CAPTION_525, 21, 21, 1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, [8] = {0x110, /* Wide Screen Signal, PAL/SECAM */ - {V4L2_SLICED_WSS_625,23,23,1}, + {V4L2_SLICED_WSS_625, 23, 23, 1}, { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } }, #if 0 [9] = {0x130, /* Wide Screen Signal, NTSC C */ - {V4L2_SLICED_WSS_525,20,20,1}, + {V4L2_SLICED_WSS_525, 20, 20, 1}, { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } }, [10] = {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ - {V4l2_SLICED_VITC_625,6,22,0}, + {V4l2_SLICED_VITC_625, 6, 22, 0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, [11] = {0x170, /* Vertical Interval Timecode (VITC), NTSC */ - {V4l2_SLICED_VITC_525,10,20,0}, + {V4l2_SLICED_VITC_525, 10, 20, 0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, #endif [12] = {0x190, /* Video Program System (VPS), PAL */ - {V4L2_SLICED_VPS,16,16,0}, + {V4L2_SLICED_VPS, 16, 16, 0}, { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 } }, @@ -655,7 +655,7 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, * MSB = field2 */ static int tvp5150_set_vbi(struct v4l2_subdev *sd, - unsigned int type,u8 flags, int line, + unsigned int type, u8 flags, int line, const int fields) { struct tvp5150 *decoder = to_tvp5150(sd); -- cgit v1.1 From 9bfd8f88dceb30cd5053fd8cbcca231eef2388a8 Mon Sep 17 00:00:00 2001 From: Nasser Afshin Date: Mon, 2 Apr 2018 18:23:18 -0400 Subject: media: i2c: tvp5150: Use the correct comment style This patch resolves checkpatch.pl warnings: WARNING: Block comments use * on subsequent lines WARNING: Block comments use a trailing */ on a separate line Signed-off-by: Nasser Afshin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index d1e93c1..6723ef5 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -502,8 +502,10 @@ struct i2c_vbi_ram_value { static struct i2c_vbi_ram_value vbi_ram_default[] = { - /* FIXME: Current api doesn't handle all VBI types, those not - yet supported are placed under #if 0 */ + /* + * FIXME: Current api doesn't handle all VBI types, those not + * yet supported are placed under #if 0 + */ #if 0 [0] = {0x010, /* Teletext, SECAM, WST System A */ {V4L2_SLICED_TELETEXT_SECAM, 6, 23, 1}, @@ -1101,11 +1103,14 @@ static int tvp5150_s_routing(struct v4l2_subdev *sd, static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) { - /* this is for capturing 36 raw vbi lines - if there's a way to cut off the beginning 2 vbi lines - with the tvp5150 then the vbi line count could be lowered - to 17 lines/field again, although I couldn't find a register - which could do that cropping */ + /* + * this is for capturing 36 raw vbi lines + * if there's a way to cut off the beginning 2 vbi lines + * with the tvp5150 then the vbi line count could be lowered + * to 17 lines/field again, although I couldn't find a register + * which could do that cropping + */ + if (fmt->sample_format == V4L2_PIX_FMT_GREY) tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); if (fmt->count[0] == 18 && fmt->count[1] == 18) { -- cgit v1.1 From 65dc760bb4de0824fad971d265dc9e12ef9e673e Mon Sep 17 00:00:00 2001 From: Nasser Afshin Date: Mon, 2 Apr 2018 18:23:19 -0400 Subject: media: i2c: tvp5150: Fix open brace placement codding style This patch resolves the following checkpatch.pl error: ERROR: that open brace { should be on the previous line Signed-off-by: Nasser Afshin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 6723ef5..d528fdd 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -500,8 +500,8 @@ struct i2c_vbi_ram_value { * and so on. There are 16 possible locations from 0 to 15. */ -static struct i2c_vbi_ram_value vbi_ram_default[] = -{ +static struct i2c_vbi_ram_value vbi_ram_default[] = { + /* * FIXME: Current api doesn't handle all VBI types, those not * yet supported are placed under #if 0 -- cgit v1.1 From 50a0efae2758687737349707e335ccf2de9007d2 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 8 May 2018 02:14:15 -0400 Subject: media: i2c: tda1997: Fix an error handling path 'tda1997x_probe()' If 'media_entity_pads_init()' fails, we must free the resources allocated by 'v4l2_ctrl_handler_init()', as already done in the previous error handling path. 'goto' the right label to fix it. Fixes: 9ac0038db9a7 ("media: i2c: Add TDA1997x HDMI receiver driver") Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tda1997x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 1c5b5f7..b697c6f 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2721,7 +2721,7 @@ static int tda1997x_probe(struct i2c_client *client, state->pads); if (ret) { v4l_err(client, "failed entity_init: %d", ret); - goto err_free_mutex; + goto err_free_handler; } ret = v4l2_async_register_subdev(sd); -- cgit v1.1 From ce96bcf5b4dbd91795870b81d5f80d5257e9cb4c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 24 Apr 2018 06:25:47 -0400 Subject: media: ov5640: Use dev_fwnode() to obtain device's fwnode Use dev_fwnode() on the device instead of getting an fwnode handle of the device's OF node. The result is the same on OF-based systems and looks better, too. Signed-off-by: Sakari Ailus Reviewed-by: Maxime Ripard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 852026b..7acd3b4 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2536,8 +2536,8 @@ static int ov5640_probe(struct i2c_client *client, sensor->ae_target = 52; - endpoint = fwnode_graph_get_next_endpoint( - of_fwnode_handle(client->dev.of_node), NULL); + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), + NULL); if (!endpoint) { dev_err(dev, "endpoint node not found\n"); return -EINVAL; -- cgit v1.1 From 706487807377ae276c3d0f45347b3702d791da12 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Thu, 8 Feb 2018 04:41:59 -0500 Subject: media: ov5645: Fix write_reg return code I2C transfer functions return number of successful operations (on success). Do not return the received positive return code but instead return 0 on success. The users of write_reg function already use this logic. Signed-off-by: Todor Tomov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5645.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 4e3142a..b3f7625 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -600,11 +600,13 @@ static int ov5645_write_reg(struct ov5645 *ov5645, u16 reg, u8 val) regbuf[2] = val; ret = i2c_master_send(ov5645->i2c_client, regbuf, 3); - if (ret < 0) + if (ret < 0) { dev_err(ov5645->dev, "%s: write reg error %d: reg=%x, val=%x\n", __func__, ret, reg, val); + return ret; + } - return ret; + return 0; } static int ov5645_read_reg(struct ov5645 *ov5645, u16 reg, u8 *val) -- cgit v1.1 From 2aae393955d2c923e85ee162f4b7509d09519374 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 10 Feb 2018 10:28:37 -0500 Subject: media: ov2640: make set_fmt() work in power-down mode The set_fmt() subdev pad operation for this driver currently does not only do the driver internal format selection but also do the actual register setup. This doesn't work if the device power control via GPIO lines is enabled. Because the set_fmt() can be called when the device is placed into power down mode. First of all, this fix adds flag to keep track of whether the device starts streaming or not. Then, the set_fmt() postpones applying the actual register setup at this time. Instead the setup will be applied when the streaming is started. Cc: Mauro Carvalho Chehab Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2640.c | 71 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 14 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 4c3b927..68a356d 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -307,6 +307,9 @@ struct ov2640_priv { struct gpio_desc *resetb_gpio; struct gpio_desc *pwdn_gpio; + + struct mutex lock; /* lock to protect streaming */ + bool streaming; }; /* @@ -798,16 +801,13 @@ static const struct ov2640_win_size *ov2640_select_win(u32 width, u32 height) static int ov2640_set_params(struct i2c_client *client, const struct ov2640_win_size *win, u32 code) { - struct ov2640_priv *priv = to_ov2640(client); const struct regval_list *selected_cfmt_regs; u8 val; int ret; - /* select win */ - priv->win = win; + if (!win) + return -EINVAL; - /* select format */ - priv->cfmt_code = 0; switch (code) { case MEDIA_BUS_FMT_RGB565_2X8_BE: dev_dbg(&client->dev, "%s: Selected cfmt RGB565 BE", __func__); @@ -846,13 +846,13 @@ static int ov2640_set_params(struct i2c_client *client, goto err; /* select preamble */ - dev_dbg(&client->dev, "%s: Set size to %s", __func__, priv->win->name); + dev_dbg(&client->dev, "%s: Set size to %s", __func__, win->name); ret = ov2640_write_array(client, ov2640_size_change_preamble_regs); if (ret < 0) goto err; /* set size win */ - ret = ov2640_write_array(client, priv->win->regs); + ret = ov2640_write_array(client, win->regs); if (ret < 0) goto err; @@ -872,14 +872,11 @@ static int ov2640_set_params(struct i2c_client *client, if (ret < 0) goto err; - priv->cfmt_code = code; - return 0; err: dev_err(&client->dev, "%s: Error %d", __func__, ret); ov2640_reset(client); - priv->win = NULL; return ret; } @@ -915,11 +912,15 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); const struct ov2640_win_size *win; + int ret = 0; if (format->pad) return -EINVAL; + mutex_lock(&priv->lock); + /* select suitable win */ win = ov2640_select_win(mf->width, mf->height); mf->width = win->width; @@ -941,10 +942,24 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, break; } - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov2640_set_params(client, win, mf->code); - cfg->try_fmt = *mf; - return 0; + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + struct ov2640_priv *priv = to_ov2640(client); + + if (priv->streaming) { + ret = -EBUSY; + goto out; + } + /* select win */ + priv->win = win; + /* select format */ + priv->cfmt_code = mf->code; + } else { + cfg->try_fmt = *mf; + } +out: + mutex_unlock(&priv->lock); + + return ret; } static int ov2640_enum_mbus_code(struct v4l2_subdev *sd, @@ -979,6 +994,26 @@ static int ov2640_get_selection(struct v4l2_subdev *sd, } } +static int ov2640_s_stream(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); + int ret = 0; + + mutex_lock(&priv->lock); + if (priv->streaming == !on) { + if (on) { + ret = ov2640_set_params(client, priv->win, + priv->cfmt_code); + } + } + if (!ret) + priv->streaming = on; + mutex_unlock(&priv->lock); + + return ret; +} + static int ov2640_video_probe(struct i2c_client *client) { struct ov2640_priv *priv = to_ov2640(client); @@ -1040,9 +1075,14 @@ static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { .set_fmt = ov2640_set_fmt, }; +static const struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { + .s_stream = ov2640_s_stream, +}; + static const struct v4l2_subdev_ops ov2640_subdev_ops = { .core = &ov2640_subdev_core_ops, .pad = &ov2640_subdev_pad_ops, + .video = &ov2640_subdev_video_ops, }; static int ov2640_probe_dt(struct i2c_client *client, @@ -1116,6 +1156,7 @@ static int ov2640_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mutex_init(&priv->lock); v4l2_ctrl_handler_init(&priv->hdl, 2); v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); @@ -1150,6 +1191,7 @@ err_videoprobe: media_entity_cleanup(&priv->subdev.entity); err_hdl: v4l2_ctrl_handler_free(&priv->hdl); + mutex_destroy(&priv->lock); err_clk: clk_disable_unprepare(priv->clk); return ret; @@ -1161,6 +1203,7 @@ static int ov2640_remove(struct i2c_client *client) v4l2_async_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); + mutex_destroy(&priv->lock); media_entity_cleanup(&priv->subdev.entity); v4l2_device_unregister_subdev(&priv->subdev); clk_disable_unprepare(priv->clk); -- cgit v1.1 From deb9eec69662f14818e68c6a4e65c6c71a1789c2 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 10 Feb 2018 10:28:38 -0500 Subject: media: ov2640: make s_ctrl() work in power-down mode The s_ctrl() operation can be called when the device is placed into power down mode. Then, applying controls to H/W should be postponed at this time. Instead the controls will be restored when the streaming is started. Cc: Mauro Carvalho Chehab Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2640.c | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 68a356d..beb7220 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -308,8 +308,9 @@ struct ov2640_priv { struct gpio_desc *resetb_gpio; struct gpio_desc *pwdn_gpio; - struct mutex lock; /* lock to protect streaming */ + struct mutex lock; /* lock to protect streaming and power_count */ bool streaming; + int power_count; }; /* @@ -712,9 +713,20 @@ static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl) struct v4l2_subdev *sd = &container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); u8 val; int ret; + /* v4l2_ctrl_lock() locks our own mutex */ + + /* + * If the device is not powered up by the host driver, do not apply any + * controls to H/W at this time. Instead the controls will be restored + * when the streaming is started. + */ + if (!priv->power_count) + return 0; + ret = i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS); if (ret < 0) return ret; @@ -766,12 +778,9 @@ static int ov2640_s_register(struct v4l2_subdev *sd, } #endif -static int ov2640_s_power(struct v4l2_subdev *sd, int on) +static void ov2640_set_power(struct ov2640_priv *priv, int on) { #ifdef CONFIG_GPIOLIB - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov2640_priv *priv = to_ov2640(client); - if (priv->pwdn_gpio) gpiod_direction_output(priv->pwdn_gpio, !on); if (on && priv->resetb_gpio) { @@ -781,6 +790,25 @@ static int ov2640_s_power(struct v4l2_subdev *sd, int on) gpiod_set_value(priv->resetb_gpio, 0); } #endif +} + +static int ov2640_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); + + mutex_lock(&priv->lock); + + /* + * If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (priv->power_count == !on) + ov2640_set_power(priv, on); + priv->power_count += on ? 1 : -1; + WARN_ON(priv->power_count < 0); + mutex_unlock(&priv->lock); + return 0; } @@ -1005,6 +1033,8 @@ static int ov2640_s_stream(struct v4l2_subdev *sd, int on) if (on) { ret = ov2640_set_params(client, priv->win, priv->cfmt_code); + if (!ret) + ret = __v4l2_ctrl_handler_setup(&priv->hdl); } } if (!ret) @@ -1049,8 +1079,6 @@ static int ov2640_video_probe(struct i2c_client *client) "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", devname, pid, ver, midh, midl); - ret = v4l2_ctrl_handler_setup(&priv->hdl); - done: ov2640_s_power(&priv->subdev, 0); return ret; @@ -1158,6 +1186,7 @@ static int ov2640_probe(struct i2c_client *client, priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; mutex_init(&priv->lock); v4l2_ctrl_handler_init(&priv->hdl, 2); + priv->hdl.lock = &priv->lock; v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, -- cgit v1.1 From 1068fecacf4304ff5c41139c4a84a19bdb08ef90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Myl=C3=A8ne=20Josserand?= Date: Mon, 16 Apr 2018 08:36:51 -0400 Subject: media: ov5640: Add light frequency control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the light frequency control to be able to set the frequency to manual (50Hz or 60Hz) or auto. [Sakari Ailus: Rename "ctl" as "ctrl" as agreed.] [mchehab+samsung@kernel.org: fixed two coding style warnings] Signed-off-by: Mylène Josserand Signed-off-by: Maxime Ripard Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 7acd3b4..60b7da7 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -187,6 +187,7 @@ struct ov5640_ctrls { struct v4l2_ctrl *gain; }; struct v4l2_ctrl *brightness; + struct v4l2_ctrl *light_freq; struct v4l2_ctrl *saturation; struct v4l2_ctrl *contrast; struct v4l2_ctrl *hue; @@ -2155,6 +2156,21 @@ static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value) 0xa4, value ? 0xa4 : 0); } +static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value) +{ + int ret; + + ret = ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL01, BIT(7), + (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) ? + 0 : BIT(7)); + if (ret) + return ret; + + return ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL00, BIT(2), + (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ? + BIT(2) : 0); +} + static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); @@ -2223,6 +2239,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TEST_PATTERN: ret = ov5640_set_ctrl_test_pattern(sensor, ctrl->val); break; + case V4L2_CID_POWER_LINE_FREQUENCY: + ret = ov5640_set_ctrl_light_freq(sensor, ctrl->val); + break; default: ret = -EINVAL; break; @@ -2285,6 +2304,12 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, test_pattern_menu); + ctrls->light_freq = + v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + if (hdl->error) { ret = hdl->error; goto free_ctrls; -- cgit v1.1 From bf4a4b518c205bba1da25df5902974a402bbfb34 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 08:36:52 -0400 Subject: media: ov5640: Don't force the auto exposure state at start time The sensor needs to have the auto exposure stopped while changing mode. However, when the new mode is set, the driver will force the auto exposure on, disregarding whether the control has been changed or not. Bypass the controls code entirely to do that, and only use the control value cached when restoring the auto exposure mode. Signed-off-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 60b7da7..93a7b28 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -1569,7 +1569,8 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor, * change mode directly */ static int ov5640_set_mode_direct(struct ov5640_dev *sensor, - const struct ov5640_mode_info *mode) + const struct ov5640_mode_info *mode, + s32 exposure) { int ret; @@ -1585,7 +1586,8 @@ static int ov5640_set_mode_direct(struct ov5640_dev *sensor, ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1); if (ret) return ret; - return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_AUTO); + + return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, exposure); } static int ov5640_set_mode(struct ov5640_dev *sensor, @@ -1593,6 +1595,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, { const struct ov5640_mode_info *mode = sensor->current_mode; enum ov5640_downsize_mode dn_mode, orig_dn_mode; + s32 exposure; int ret; dn_mode = mode->dn_mode; @@ -1602,7 +1605,9 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 0); if (ret) return ret; - ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_MANUAL); + + exposure = sensor->ctrls.auto_exp->val; + ret = ov5640_set_exposure(sensor, V4L2_EXPOSURE_MANUAL); if (ret) return ret; @@ -1618,7 +1623,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, * change inside subsampling or scaling * download firmware directly */ - ret = ov5640_set_mode_direct(sensor, mode); + ret = ov5640_set_mode_direct(sensor, mode, exposure); } if (ret < 0) -- cgit v1.1 From 8f57c2f86b7da2611041060e1b2466058d21580e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 08:36:53 -0400 Subject: media: ov5640: Init properly the SCLK dividers The SCLK and SCLK2X dividers are fixed in stone in the initialization array. Let's make explicit what we're doing and move that away from the huge array to the initialization code. Signed-off-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 93a7b28..d85f9ae 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -91,6 +91,9 @@ #define OV5640_REG_SDE_CTRL5 0x5585 #define OV5640_REG_AVG_READOUT 0x56a1 +#define OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT 1 +#define OV5640_SCLK_ROOT_DIVIDER_DEFAULT 2 + enum ov5640_mode_id { OV5640_MODE_QCIF_176_144 = 0, OV5640_MODE_QVGA_320_240, @@ -249,7 +252,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0}, {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, - {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0}, + {0x3037, 0x13, 0, 0}, {0x3630, 0x36, 0, 0}, {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0}, {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, @@ -1660,6 +1663,12 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor) if (ret < 0) return ret; + ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f, + (ilog2(OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT) << 2) | + ilog2(OV5640_SCLK_ROOT_DIVIDER_DEFAULT)); + if (ret) + return ret; + /* now restore the last capture mode */ ret = ov5640_set_mode(sensor, &ov5640_mode_init_data); if (ret < 0) -- cgit v1.1 From dba13a0b08abcb51c26fb0fa0da136826f9e64c6 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 08:36:54 -0400 Subject: media: ov5640: Change horizontal and vertical resolutions name The current width and height parameters in the struct ov5640_mode_info are actually the active horizontal and vertical resolutions. Since we're going to add a few other parameters, let's pick a better, more precise name for these values. Signed-off-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index d85f9ae..e31ce54 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -168,8 +168,8 @@ struct reg_value { struct ov5640_mode_info { enum ov5640_mode_id id; enum ov5640_downsize_mode dn_mode; - u32 width; - u32 height; + u32 hact; + u32 vact; const struct reg_value *reg_data; u32 reg_data_size; }; @@ -1394,10 +1394,10 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, if (!mode->reg_data) continue; - if ((nearest && mode->width <= width && - mode->height <= height) || - (!nearest && mode->width == width && - mode->height == height)) + if ((nearest && mode->hact <= width && + mode->vact <= height) || + (!nearest && mode->hact == width && + mode->vact == height)) break; } @@ -1886,8 +1886,8 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd, mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true); if (!mode) return -EINVAL; - fmt->width = mode->width; - fmt->height = mode->height; + fmt->width = mode->hact; + fmt->height = mode->vact; if (new_mode) *new_mode = mode; @@ -2354,10 +2354,10 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd, return -EINVAL; fse->min_width = - ov5640_mode_data[0][fse->index].width; + ov5640_mode_data[0][fse->index].hact; fse->max_width = fse->min_width; fse->min_height = - ov5640_mode_data[0][fse->index].height; + ov5640_mode_data[0][fse->index].vact; fse->max_height = fse->min_height; return 0; @@ -2421,14 +2421,14 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, mode = sensor->current_mode; frame_rate = ov5640_try_frame_interval(sensor, &fi->interval, - mode->width, mode->height); + mode->hact, mode->vact); if (frame_rate < 0) frame_rate = OV5640_15_FPS; sensor->current_fr = frame_rate; sensor->frame_interval = fi->interval; - sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->width, - mode->height, true); + sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->hact, + mode->vact, true); sensor->pending_mode_change = true; out: mutex_unlock(&sensor->lock); -- cgit v1.1 From 476dec012f4c6545b0b7599cd9adba2ed819ad3b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 08:36:55 -0400 Subject: media: ov5640: Add horizontal and vertical totals All the initialization arrays are changing the horizontal and vertical totals for some value. In order to clean up the driver, and since we're going to need that value later on, let's introduce in the ov5640_mode_info structure the horizontal and vertical total sizes, and move these out of the bytes array. Signed-off-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 156 ++++++++++++++++++++++++++++----------------- 1 file changed, 97 insertions(+), 59 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index e31ce54..9fff1da 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -169,7 +169,9 @@ struct ov5640_mode_info { enum ov5640_mode_id id; enum ov5640_downsize_mode dn_mode; u32 hact; + u32 htot; u32 vact; + u32 vtot; const struct reg_value *reg_data; u32 reg_data_size; }; @@ -270,8 +272,8 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xe0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -344,8 +346,8 @@ static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xe0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -365,8 +367,8 @@ static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xe0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -386,8 +388,8 @@ static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xe0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -409,8 +411,8 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xe0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -431,8 +433,8 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xf0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -452,8 +454,8 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xf0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -473,8 +475,8 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0x90, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -494,8 +496,8 @@ static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0x90, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -515,8 +517,8 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xe0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -536,8 +538,8 @@ static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xe0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -557,8 +559,8 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0x40, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -578,8 +580,8 @@ static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0x40, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -600,8 +602,8 @@ static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, - {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xd0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, @@ -622,8 +624,8 @@ static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, - {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0xd0, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, @@ -644,8 +646,8 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, - {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, - {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0x98, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -661,8 +663,7 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, - {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0}, - {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, @@ -681,8 +682,8 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, - {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, - {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0x98, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -698,8 +699,7 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, - {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0}, - {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, @@ -717,8 +717,8 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, - {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, - {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x380b, 0x98, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -732,66 +732,84 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { /* power-on sensor init reg table */ static const struct ov5640_mode_info ov5640_mode_init_data = { - 0, SUBSAMPLING, 640, 480, ov5640_init_setting_30fps_VGA, + 0, SUBSAMPLING, 640, 1896, 480, 984, + ov5640_init_setting_30fps_VGA, ARRAY_SIZE(ov5640_init_setting_30fps_VGA), }; static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = { { - {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 144, + {OV5640_MODE_QCIF_176_144, SUBSAMPLING, + 176, 1896, 144, 984, ov5640_setting_15fps_QCIF_176_144, ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)}, - {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 240, + {OV5640_MODE_QVGA_320_240, SUBSAMPLING, + 320, 1896, 240, 984, ov5640_setting_15fps_QVGA_320_240, ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)}, - {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 480, + {OV5640_MODE_VGA_640_480, SUBSAMPLING, + 640, 1896, 480, 1080, ov5640_setting_15fps_VGA_640_480, ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)}, - {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 480, + {OV5640_MODE_NTSC_720_480, SUBSAMPLING, + 720, 1896, 480, 984, ov5640_setting_15fps_NTSC_720_480, ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)}, - {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 576, + {OV5640_MODE_PAL_720_576, SUBSAMPLING, + 720, 1896, 576, 984, ov5640_setting_15fps_PAL_720_576, ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)}, - {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 768, + {OV5640_MODE_XGA_1024_768, SUBSAMPLING, + 1024, 1896, 768, 1080, ov5640_setting_15fps_XGA_1024_768, ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)}, - {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 720, + {OV5640_MODE_720P_1280_720, SUBSAMPLING, + 1280, 1892, 720, 740, ov5640_setting_15fps_720P_1280_720, ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)}, - {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 1080, + {OV5640_MODE_1080P_1920_1080, SCALING, + 1920, 2500, 1080, 1120, ov5640_setting_15fps_1080P_1920_1080, ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)}, - {OV5640_MODE_QSXGA_2592_1944, SCALING, 2592, 1944, + {OV5640_MODE_QSXGA_2592_1944, SCALING, + 2592, 2844, 1944, 1968, ov5640_setting_15fps_QSXGA_2592_1944, ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, }, { - {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 144, + {OV5640_MODE_QCIF_176_144, SUBSAMPLING, + 176, 1896, 144, 984, ov5640_setting_30fps_QCIF_176_144, ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)}, - {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 240, + {OV5640_MODE_QVGA_320_240, SUBSAMPLING, + 320, 1896, 240, 984, ov5640_setting_30fps_QVGA_320_240, ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)}, - {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 480, + {OV5640_MODE_VGA_640_480, SUBSAMPLING, + 640, 1896, 480, 1080, ov5640_setting_30fps_VGA_640_480, ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, - {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 480, + {OV5640_MODE_NTSC_720_480, SUBSAMPLING, + 720, 1896, 480, 984, ov5640_setting_30fps_NTSC_720_480, ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, - {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 576, + {OV5640_MODE_PAL_720_576, SUBSAMPLING, + 720, 1896, 576, 984, ov5640_setting_30fps_PAL_720_576, ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)}, - {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 768, + {OV5640_MODE_XGA_1024_768, SUBSAMPLING, + 1024, 1896, 768, 1080, ov5640_setting_30fps_XGA_1024_768, ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)}, - {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 720, + {OV5640_MODE_720P_1280_720, SUBSAMPLING, + 1280, 1892, 720, 740, ov5640_setting_30fps_720P_1280_720, ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, - {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 1080, + {OV5640_MODE_1080P_1920_1080, SCALING, + 1920, 2500, 1080, 1120, ov5640_setting_30fps_1080P_1920_1080, ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)}, - {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, NULL, 0}, + {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0}, }, }; @@ -1381,6 +1399,22 @@ static int ov5640_set_virtual_channel(struct ov5640_dev *sensor) return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp); } +static int ov5640_set_timings(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + int ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot); + if (ret < 0) + return ret; + + return 0; +} + static const struct ov5640_mode_info * ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, int width, int height, bool nearest) @@ -1632,6 +1666,10 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, if (ret < 0) return ret; + ret = ov5640_set_timings(sensor, mode); + if (ret < 0) + return ret; + ret = ov5640_set_ae_target(sensor, sensor->ae_target); if (ret < 0) return ret; -- cgit v1.1 From 86633417997fdb4142d69298bfe182d56c29d7be Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 08:36:56 -0400 Subject: media: ov5640: Program the visible resolution The active frame size is set in the initialization arrays, but the value itself is also available in the struct ov5640_mode_info. Let's move these values out of the big bytes arrays, and program it with the value of the mode that we are given. Signed-off-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 58 +++++++++++----------------------------------- 1 file changed, 14 insertions(+), 44 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 9fff1da..f6e40cc 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -60,6 +60,8 @@ #define OV5640_REG_AEC_PK_MANUAL 0x3503 #define OV5640_REG_AEC_PK_REAL_GAIN 0x350a #define OV5640_REG_AEC_PK_VTS 0x350c +#define OV5640_REG_TIMING_DVPHO 0x3808 +#define OV5640_REG_TIMING_DVPVO 0x380a #define OV5640_REG_TIMING_HTS 0x380c #define OV5640_REG_TIMING_VTS 0x380e #define OV5640_REG_TIMING_TC_REG21 0x3821 @@ -271,8 +273,6 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -345,8 +345,6 @@ static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -366,8 +364,6 @@ static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -387,8 +383,6 @@ static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -399,8 +393,7 @@ static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, - {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, - {0x380b, 0x00, 0, 0}, {0x3035, 0x12, 0, 0}, + {0x3035, 0x12, 0, 0}, }; static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { @@ -410,8 +403,6 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -421,8 +412,7 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3808, 0x04, 0, 0}, - {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { @@ -432,8 +422,6 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0xf0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -453,8 +441,6 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0xf0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -474,8 +460,6 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0x90, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -495,8 +479,6 @@ static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0x90, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -516,8 +498,6 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -537,8 +517,6 @@ static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -558,8 +536,6 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0x40, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -579,8 +555,6 @@ static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0x40, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -601,8 +575,6 @@ static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, - {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0xd0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -623,8 +595,6 @@ static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, - {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0xd0, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, @@ -645,8 +615,6 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, - {0x380b, 0x98, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, @@ -661,8 +629,7 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, - {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, - {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, + {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, @@ -681,8 +648,6 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, - {0x380b, 0x98, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, @@ -697,8 +662,7 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, - {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, - {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, + {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, @@ -716,8 +680,6 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, - {0x380b, 0x98, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, @@ -1404,6 +1366,14 @@ static int ov5640_set_timings(struct ov5640_dev *sensor, { int ret; + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact); + if (ret < 0) + return ret; + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot); if (ret < 0) return ret; -- cgit v1.1 From d30bb512da3d8e44db086494b4db4d62bbaba677 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Apr 2018 12:20:46 -0400 Subject: media: Add a driver for the ov7251 camera sensor The ov7251 sensor is a 1/7.5-Inch B&W VGA (640x480) CMOS Digital Image Sensor from Omnivision. The driver supports the following modes: - 640x480 30fps - 640x480 60fps - 640x480 90fps Output format is 10bit B&W RAW - MEDIA_BUS_FMT_Y10_1X10. The driver supports configuration via user controls for: - exposure and gain; - horizontal and vertical flip; - test pattern. [Sakari Ailus: Wrap a line over 80 characters, fix trivial sparse warning] Signed-off-by: Todor Tomov Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov7251.c | 1503 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1516 insertions(+) create mode 100644 drivers/media/i2c/ov7251.c (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index faaaceb..b95b447 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -688,6 +688,18 @@ config VIDEO_OV5695 To compile this driver as a module, choose M here: the module will be called ov5695. +config VIDEO_OV7251 + tristate "OmniVision OV7251 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + This is a Video4Linux2 sensor-level driver for the OmniVision + OV7251 camera. + + To compile this driver as a module, choose M here: the + module will be called ov7251. + config VIDEO_OV772X tristate "OmniVision OV772x sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 84cc472..ff6e291 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_VIDEO_OV5647) += ov5647.o obj-$(CONFIG_VIDEO_OV5670) += ov5670.o obj-$(CONFIG_VIDEO_OV5695) += ov5695.o obj-$(CONFIG_VIDEO_OV6650) += ov6650.o +obj-$(CONFIG_VIDEO_OV7251) += ov7251.o obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV772X) += ov772x.o diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c new file mode 100644 index 0000000..d3ebb75 --- /dev/null +++ b/drivers/media/i2c/ov7251.c @@ -0,0 +1,1503 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the OV7251 camera sensor. + * + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OV7251_SC_MODE_SELECT 0x0100 +#define OV7251_SC_MODE_SELECT_SW_STANDBY 0x0 +#define OV7251_SC_MODE_SELECT_STREAMING 0x1 + +#define OV7251_CHIP_ID_HIGH 0x300a +#define OV7251_CHIP_ID_HIGH_BYTE 0x77 +#define OV7251_CHIP_ID_LOW 0x300b +#define OV7251_CHIP_ID_LOW_BYTE 0x50 +#define OV7251_SC_GP_IO_IN1 0x3029 +#define OV7251_AEC_EXPO_0 0x3500 +#define OV7251_AEC_EXPO_1 0x3501 +#define OV7251_AEC_EXPO_2 0x3502 +#define OV7251_AEC_AGC_ADJ_0 0x350a +#define OV7251_AEC_AGC_ADJ_1 0x350b +#define OV7251_TIMING_FORMAT1 0x3820 +#define OV7251_TIMING_FORMAT1_VFLIP BIT(2) +#define OV7251_TIMING_FORMAT2 0x3821 +#define OV7251_TIMING_FORMAT2_MIRROR BIT(2) +#define OV7251_PRE_ISP_00 0x5e00 +#define OV7251_PRE_ISP_00_TEST_PATTERN BIT(7) + +struct reg_value { + u16 reg; + u8 val; +}; + +struct ov7251_mode_info { + u32 width; + u32 height; + const struct reg_value *data; + u32 data_size; + u32 pixel_clock; + u32 link_freq; + u16 exposure_max; + u16 exposure_def; + struct v4l2_fract timeperframe; +}; + +struct ov7251 { + struct i2c_client *i2c_client; + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_fwnode_endpoint ep; + struct v4l2_mbus_framefmt fmt; + struct v4l2_rect crop; + struct clk *xclk; + u32 xclk_freq; + + struct regulator *io_regulator; + struct regulator *core_regulator; + struct regulator *analog_regulator; + + const struct ov7251_mode_info *current_mode; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixel_clock; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + + /* Cached register values */ + u8 aec_pk_manual; + u8 pre_isp_00; + u8 timing_format1; + u8 timing_format2; + + struct mutex lock; /* lock to protect power state, ctrls and mode */ + bool power_on; + + struct gpio_desc *enable_gpio; +}; + +static inline struct ov7251 *to_ov7251(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov7251, sd); +} + +static const struct reg_value ov7251_global_init_setting[] = { + { 0x0103, 0x01 }, + { 0x303b, 0x02 }, +}; + +static const struct reg_value ov7251_setting_vga_30fps[] = { + { 0x3005, 0x00 }, + { 0x3012, 0xc0 }, + { 0x3013, 0xd2 }, + { 0x3014, 0x04 }, + { 0x3016, 0xf0 }, + { 0x3017, 0xf0 }, + { 0x3018, 0xf0 }, + { 0x301a, 0xf0 }, + { 0x301b, 0xf0 }, + { 0x301c, 0xf0 }, + { 0x3023, 0x05 }, + { 0x3037, 0xf0 }, + { 0x3098, 0x04 }, /* pll2 pre divider */ + { 0x3099, 0x28 }, /* pll2 multiplier */ + { 0x309a, 0x05 }, /* pll2 sys divider */ + { 0x309b, 0x04 }, /* pll2 adc divider */ + { 0x309d, 0x00 }, /* pll2 divider */ + { 0x30b0, 0x0a }, /* pll1 pix divider */ + { 0x30b1, 0x01 }, /* pll1 divider */ + { 0x30b3, 0x64 }, /* pll1 multiplier */ + { 0x30b4, 0x03 }, /* pll1 pre divider */ + { 0x30b5, 0x05 }, /* pll1 mipi divider */ + { 0x3106, 0xda }, + { 0x3503, 0x07 }, + { 0x3509, 0x10 }, + { 0x3600, 0x1c }, + { 0x3602, 0x62 }, + { 0x3620, 0xb7 }, + { 0x3622, 0x04 }, + { 0x3626, 0x21 }, + { 0x3627, 0x30 }, + { 0x3630, 0x44 }, + { 0x3631, 0x35 }, + { 0x3634, 0x60 }, + { 0x3636, 0x00 }, + { 0x3662, 0x01 }, + { 0x3663, 0x70 }, + { 0x3664, 0x50 }, + { 0x3666, 0x0a }, + { 0x3669, 0x1a }, + { 0x366a, 0x00 }, + { 0x366b, 0x50 }, + { 0x3673, 0x01 }, + { 0x3674, 0xff }, + { 0x3675, 0x03 }, + { 0x3705, 0xc1 }, + { 0x3709, 0x40 }, + { 0x373c, 0x08 }, + { 0x3742, 0x00 }, + { 0x3757, 0xb3 }, + { 0x3788, 0x00 }, + { 0x37a8, 0x01 }, + { 0x37a9, 0xc0 }, + { 0x3800, 0x00 }, + { 0x3801, 0x04 }, + { 0x3802, 0x00 }, + { 0x3803, 0x04 }, + { 0x3804, 0x02 }, + { 0x3805, 0x8b }, + { 0x3806, 0x01 }, + { 0x3807, 0xeb }, + { 0x3808, 0x02 }, /* width high */ + { 0x3809, 0x80 }, /* width low */ + { 0x380a, 0x01 }, /* height high */ + { 0x380b, 0xe0 }, /* height low */ + { 0x380c, 0x03 }, /* total horiz timing high */ + { 0x380d, 0xa0 }, /* total horiz timing low */ + { 0x380e, 0x06 }, /* total vertical timing high */ + { 0x380f, 0xbc }, /* total vertical timing low */ + { 0x3810, 0x00 }, + { 0x3811, 0x04 }, + { 0x3812, 0x00 }, + { 0x3813, 0x05 }, + { 0x3814, 0x11 }, + { 0x3815, 0x11 }, + { 0x3820, 0x40 }, + { 0x3821, 0x00 }, + { 0x382f, 0x0e }, + { 0x3832, 0x00 }, + { 0x3833, 0x05 }, + { 0x3834, 0x00 }, + { 0x3835, 0x0c }, + { 0x3837, 0x00 }, + { 0x3b80, 0x00 }, + { 0x3b81, 0xa5 }, + { 0x3b82, 0x10 }, + { 0x3b83, 0x00 }, + { 0x3b84, 0x08 }, + { 0x3b85, 0x00 }, + { 0x3b86, 0x01 }, + { 0x3b87, 0x00 }, + { 0x3b88, 0x00 }, + { 0x3b89, 0x00 }, + { 0x3b8a, 0x00 }, + { 0x3b8b, 0x05 }, + { 0x3b8c, 0x00 }, + { 0x3b8d, 0x00 }, + { 0x3b8e, 0x00 }, + { 0x3b8f, 0x1a }, + { 0x3b94, 0x05 }, + { 0x3b95, 0xf2 }, + { 0x3b96, 0x40 }, + { 0x3c00, 0x89 }, + { 0x3c01, 0x63 }, + { 0x3c02, 0x01 }, + { 0x3c03, 0x00 }, + { 0x3c04, 0x00 }, + { 0x3c05, 0x03 }, + { 0x3c06, 0x00 }, + { 0x3c07, 0x06 }, + { 0x3c0c, 0x01 }, + { 0x3c0d, 0xd0 }, + { 0x3c0e, 0x02 }, + { 0x3c0f, 0x0a }, + { 0x4001, 0x42 }, + { 0x4004, 0x04 }, + { 0x4005, 0x00 }, + { 0x404e, 0x01 }, + { 0x4300, 0xff }, + { 0x4301, 0x00 }, + { 0x4315, 0x00 }, + { 0x4501, 0x48 }, + { 0x4600, 0x00 }, + { 0x4601, 0x4e }, + { 0x4801, 0x0f }, + { 0x4806, 0x0f }, + { 0x4819, 0xaa }, + { 0x4823, 0x3e }, + { 0x4837, 0x19 }, + { 0x4a0d, 0x00 }, + { 0x4a47, 0x7f }, + { 0x4a49, 0xf0 }, + { 0x4a4b, 0x30 }, + { 0x5000, 0x85 }, + { 0x5001, 0x80 }, +}; + +static const struct reg_value ov7251_setting_vga_60fps[] = { + { 0x3005, 0x00 }, + { 0x3012, 0xc0 }, + { 0x3013, 0xd2 }, + { 0x3014, 0x04 }, + { 0x3016, 0x10 }, + { 0x3017, 0x00 }, + { 0x3018, 0x00 }, + { 0x301a, 0x00 }, + { 0x301b, 0x00 }, + { 0x301c, 0x00 }, + { 0x3023, 0x05 }, + { 0x3037, 0xf0 }, + { 0x3098, 0x04 }, /* pll2 pre divider */ + { 0x3099, 0x28 }, /* pll2 multiplier */ + { 0x309a, 0x05 }, /* pll2 sys divider */ + { 0x309b, 0x04 }, /* pll2 adc divider */ + { 0x309d, 0x00 }, /* pll2 divider */ + { 0x30b0, 0x0a }, /* pll1 pix divider */ + { 0x30b1, 0x01 }, /* pll1 divider */ + { 0x30b3, 0x64 }, /* pll1 multiplier */ + { 0x30b4, 0x03 }, /* pll1 pre divider */ + { 0x30b5, 0x05 }, /* pll1 mipi divider */ + { 0x3106, 0xda }, + { 0x3503, 0x07 }, + { 0x3509, 0x10 }, + { 0x3600, 0x1c }, + { 0x3602, 0x62 }, + { 0x3620, 0xb7 }, + { 0x3622, 0x04 }, + { 0x3626, 0x21 }, + { 0x3627, 0x30 }, + { 0x3630, 0x44 }, + { 0x3631, 0x35 }, + { 0x3634, 0x60 }, + { 0x3636, 0x00 }, + { 0x3662, 0x01 }, + { 0x3663, 0x70 }, + { 0x3664, 0x50 }, + { 0x3666, 0x0a }, + { 0x3669, 0x1a }, + { 0x366a, 0x00 }, + { 0x366b, 0x50 }, + { 0x3673, 0x01 }, + { 0x3674, 0xff }, + { 0x3675, 0x03 }, + { 0x3705, 0xc1 }, + { 0x3709, 0x40 }, + { 0x373c, 0x08 }, + { 0x3742, 0x00 }, + { 0x3757, 0xb3 }, + { 0x3788, 0x00 }, + { 0x37a8, 0x01 }, + { 0x37a9, 0xc0 }, + { 0x3800, 0x00 }, + { 0x3801, 0x04 }, + { 0x3802, 0x00 }, + { 0x3803, 0x04 }, + { 0x3804, 0x02 }, + { 0x3805, 0x8b }, + { 0x3806, 0x01 }, + { 0x3807, 0xeb }, + { 0x3808, 0x02 }, /* width high */ + { 0x3809, 0x80 }, /* width low */ + { 0x380a, 0x01 }, /* height high */ + { 0x380b, 0xe0 }, /* height low */ + { 0x380c, 0x03 }, /* total horiz timing high */ + { 0x380d, 0xa0 }, /* total horiz timing low */ + { 0x380e, 0x03 }, /* total vertical timing high */ + { 0x380f, 0x5c }, /* total vertical timing low */ + { 0x3810, 0x00 }, + { 0x3811, 0x04 }, + { 0x3812, 0x00 }, + { 0x3813, 0x05 }, + { 0x3814, 0x11 }, + { 0x3815, 0x11 }, + { 0x3820, 0x40 }, + { 0x3821, 0x00 }, + { 0x382f, 0x0e }, + { 0x3832, 0x00 }, + { 0x3833, 0x05 }, + { 0x3834, 0x00 }, + { 0x3835, 0x0c }, + { 0x3837, 0x00 }, + { 0x3b80, 0x00 }, + { 0x3b81, 0xa5 }, + { 0x3b82, 0x10 }, + { 0x3b83, 0x00 }, + { 0x3b84, 0x08 }, + { 0x3b85, 0x00 }, + { 0x3b86, 0x01 }, + { 0x3b87, 0x00 }, + { 0x3b88, 0x00 }, + { 0x3b89, 0x00 }, + { 0x3b8a, 0x00 }, + { 0x3b8b, 0x05 }, + { 0x3b8c, 0x00 }, + { 0x3b8d, 0x00 }, + { 0x3b8e, 0x00 }, + { 0x3b8f, 0x1a }, + { 0x3b94, 0x05 }, + { 0x3b95, 0xf2 }, + { 0x3b96, 0x40 }, + { 0x3c00, 0x89 }, + { 0x3c01, 0x63 }, + { 0x3c02, 0x01 }, + { 0x3c03, 0x00 }, + { 0x3c04, 0x00 }, + { 0x3c05, 0x03 }, + { 0x3c06, 0x00 }, + { 0x3c07, 0x06 }, + { 0x3c0c, 0x01 }, + { 0x3c0d, 0xd0 }, + { 0x3c0e, 0x02 }, + { 0x3c0f, 0x0a }, + { 0x4001, 0x42 }, + { 0x4004, 0x04 }, + { 0x4005, 0x00 }, + { 0x404e, 0x01 }, + { 0x4300, 0xff }, + { 0x4301, 0x00 }, + { 0x4315, 0x00 }, + { 0x4501, 0x48 }, + { 0x4600, 0x00 }, + { 0x4601, 0x4e }, + { 0x4801, 0x0f }, + { 0x4806, 0x0f }, + { 0x4819, 0xaa }, + { 0x4823, 0x3e }, + { 0x4837, 0x19 }, + { 0x4a0d, 0x00 }, + { 0x4a47, 0x7f }, + { 0x4a49, 0xf0 }, + { 0x4a4b, 0x30 }, + { 0x5000, 0x85 }, + { 0x5001, 0x80 }, +}; + +static const struct reg_value ov7251_setting_vga_90fps[] = { + { 0x3005, 0x00 }, + { 0x3012, 0xc0 }, + { 0x3013, 0xd2 }, + { 0x3014, 0x04 }, + { 0x3016, 0x10 }, + { 0x3017, 0x00 }, + { 0x3018, 0x00 }, + { 0x301a, 0x00 }, + { 0x301b, 0x00 }, + { 0x301c, 0x00 }, + { 0x3023, 0x05 }, + { 0x3037, 0xf0 }, + { 0x3098, 0x04 }, /* pll2 pre divider */ + { 0x3099, 0x28 }, /* pll2 multiplier */ + { 0x309a, 0x05 }, /* pll2 sys divider */ + { 0x309b, 0x04 }, /* pll2 adc divider */ + { 0x309d, 0x00 }, /* pll2 divider */ + { 0x30b0, 0x0a }, /* pll1 pix divider */ + { 0x30b1, 0x01 }, /* pll1 divider */ + { 0x30b3, 0x64 }, /* pll1 multiplier */ + { 0x30b4, 0x03 }, /* pll1 pre divider */ + { 0x30b5, 0x05 }, /* pll1 mipi divider */ + { 0x3106, 0xda }, + { 0x3503, 0x07 }, + { 0x3509, 0x10 }, + { 0x3600, 0x1c }, + { 0x3602, 0x62 }, + { 0x3620, 0xb7 }, + { 0x3622, 0x04 }, + { 0x3626, 0x21 }, + { 0x3627, 0x30 }, + { 0x3630, 0x44 }, + { 0x3631, 0x35 }, + { 0x3634, 0x60 }, + { 0x3636, 0x00 }, + { 0x3662, 0x01 }, + { 0x3663, 0x70 }, + { 0x3664, 0x50 }, + { 0x3666, 0x0a }, + { 0x3669, 0x1a }, + { 0x366a, 0x00 }, + { 0x366b, 0x50 }, + { 0x3673, 0x01 }, + { 0x3674, 0xff }, + { 0x3675, 0x03 }, + { 0x3705, 0xc1 }, + { 0x3709, 0x40 }, + { 0x373c, 0x08 }, + { 0x3742, 0x00 }, + { 0x3757, 0xb3 }, + { 0x3788, 0x00 }, + { 0x37a8, 0x01 }, + { 0x37a9, 0xc0 }, + { 0x3800, 0x00 }, + { 0x3801, 0x04 }, + { 0x3802, 0x00 }, + { 0x3803, 0x04 }, + { 0x3804, 0x02 }, + { 0x3805, 0x8b }, + { 0x3806, 0x01 }, + { 0x3807, 0xeb }, + { 0x3808, 0x02 }, /* width high */ + { 0x3809, 0x80 }, /* width low */ + { 0x380a, 0x01 }, /* height high */ + { 0x380b, 0xe0 }, /* height low */ + { 0x380c, 0x03 }, /* total horiz timing high */ + { 0x380d, 0xa0 }, /* total horiz timing low */ + { 0x380e, 0x02 }, /* total vertical timing high */ + { 0x380f, 0x3c }, /* total vertical timing low */ + { 0x3810, 0x00 }, + { 0x3811, 0x04 }, + { 0x3812, 0x00 }, + { 0x3813, 0x05 }, + { 0x3814, 0x11 }, + { 0x3815, 0x11 }, + { 0x3820, 0x40 }, + { 0x3821, 0x00 }, + { 0x382f, 0x0e }, + { 0x3832, 0x00 }, + { 0x3833, 0x05 }, + { 0x3834, 0x00 }, + { 0x3835, 0x0c }, + { 0x3837, 0x00 }, + { 0x3b80, 0x00 }, + { 0x3b81, 0xa5 }, + { 0x3b82, 0x10 }, + { 0x3b83, 0x00 }, + { 0x3b84, 0x08 }, + { 0x3b85, 0x00 }, + { 0x3b86, 0x01 }, + { 0x3b87, 0x00 }, + { 0x3b88, 0x00 }, + { 0x3b89, 0x00 }, + { 0x3b8a, 0x00 }, + { 0x3b8b, 0x05 }, + { 0x3b8c, 0x00 }, + { 0x3b8d, 0x00 }, + { 0x3b8e, 0x00 }, + { 0x3b8f, 0x1a }, + { 0x3b94, 0x05 }, + { 0x3b95, 0xf2 }, + { 0x3b96, 0x40 }, + { 0x3c00, 0x89 }, + { 0x3c01, 0x63 }, + { 0x3c02, 0x01 }, + { 0x3c03, 0x00 }, + { 0x3c04, 0x00 }, + { 0x3c05, 0x03 }, + { 0x3c06, 0x00 }, + { 0x3c07, 0x06 }, + { 0x3c0c, 0x01 }, + { 0x3c0d, 0xd0 }, + { 0x3c0e, 0x02 }, + { 0x3c0f, 0x0a }, + { 0x4001, 0x42 }, + { 0x4004, 0x04 }, + { 0x4005, 0x00 }, + { 0x404e, 0x01 }, + { 0x4300, 0xff }, + { 0x4301, 0x00 }, + { 0x4315, 0x00 }, + { 0x4501, 0x48 }, + { 0x4600, 0x00 }, + { 0x4601, 0x4e }, + { 0x4801, 0x0f }, + { 0x4806, 0x0f }, + { 0x4819, 0xaa }, + { 0x4823, 0x3e }, + { 0x4837, 0x19 }, + { 0x4a0d, 0x00 }, + { 0x4a47, 0x7f }, + { 0x4a49, 0xf0 }, + { 0x4a4b, 0x30 }, + { 0x5000, 0x85 }, + { 0x5001, 0x80 }, +}; + +static const s64 link_freq[] = { + 240000000, +}; + +static const struct ov7251_mode_info ov7251_mode_info_data[] = { + { + .width = 640, + .height = 480, + .data = ov7251_setting_vga_30fps, + .data_size = ARRAY_SIZE(ov7251_setting_vga_30fps), + .pixel_clock = 48000000, + .link_freq = 0, /* an index in link_freq[] */ + .exposure_max = 1704, + .exposure_def = 504, + .timeperframe = { + .numerator = 100, + .denominator = 3000 + } + }, + { + .width = 640, + .height = 480, + .data = ov7251_setting_vga_60fps, + .data_size = ARRAY_SIZE(ov7251_setting_vga_60fps), + .pixel_clock = 48000000, + .link_freq = 0, /* an index in link_freq[] */ + .exposure_max = 840, + .exposure_def = 504, + .timeperframe = { + .numerator = 100, + .denominator = 6014 + } + }, + { + .width = 640, + .height = 480, + .data = ov7251_setting_vga_90fps, + .data_size = ARRAY_SIZE(ov7251_setting_vga_90fps), + .pixel_clock = 48000000, + .link_freq = 0, /* an index in link_freq[] */ + .exposure_max = 552, + .exposure_def = 504, + .timeperframe = { + .numerator = 100, + .denominator = 9043 + } + }, +}; + +static int ov7251_regulators_enable(struct ov7251 *ov7251) +{ + int ret; + + /* OV7251 power up sequence requires core regulator + * to be enabled not earlier than io regulator + */ + + ret = regulator_enable(ov7251->io_regulator); + if (ret < 0) { + dev_err(ov7251->dev, "set io voltage failed\n"); + return ret; + } + + ret = regulator_enable(ov7251->analog_regulator); + if (ret) { + dev_err(ov7251->dev, "set analog voltage failed\n"); + goto err_disable_io; + } + + ret = regulator_enable(ov7251->core_regulator); + if (ret) { + dev_err(ov7251->dev, "set core voltage failed\n"); + goto err_disable_analog; + } + + return 0; + +err_disable_analog: + regulator_disable(ov7251->analog_regulator); + +err_disable_io: + regulator_disable(ov7251->io_regulator); + + return ret; +} + +static void ov7251_regulators_disable(struct ov7251 *ov7251) +{ + int ret; + + ret = regulator_disable(ov7251->core_regulator); + if (ret < 0) + dev_err(ov7251->dev, "core regulator disable failed\n"); + + ret = regulator_disable(ov7251->analog_regulator); + if (ret < 0) + dev_err(ov7251->dev, "analog regulator disable failed\n"); + + ret = regulator_disable(ov7251->io_regulator); + if (ret < 0) + dev_err(ov7251->dev, "io regulator disable failed\n"); +} + +static int ov7251_write_reg(struct ov7251 *ov7251, u16 reg, u8 val) +{ + u8 regbuf[3]; + int ret; + + regbuf[0] = reg >> 8; + regbuf[1] = reg & 0xff; + regbuf[2] = val; + + ret = i2c_master_send(ov7251->i2c_client, regbuf, 3); + if (ret < 0) { + dev_err(ov7251->dev, "%s: write reg error %d: reg=%x, val=%x\n", + __func__, ret, reg, val); + return ret; + } + + return 0; +} + +static int ov7251_write_seq_regs(struct ov7251 *ov7251, u16 reg, u8 *val, + u8 num) +{ + u8 regbuf[5]; + u8 nregbuf = sizeof(reg) + num * sizeof(*val); + int ret = 0; + + if (nregbuf > sizeof(regbuf)) + return -EINVAL; + + regbuf[0] = reg >> 8; + regbuf[1] = reg & 0xff; + + memcpy(regbuf + 2, val, num); + + ret = i2c_master_send(ov7251->i2c_client, regbuf, nregbuf); + if (ret < 0) { + dev_err(ov7251->dev, + "%s: write seq regs error %d: first reg=%x\n", + __func__, ret, reg); + return ret; + } + + return 0; +} + +static int ov7251_read_reg(struct ov7251 *ov7251, u16 reg, u8 *val) +{ + u8 regbuf[2]; + int ret; + + regbuf[0] = reg >> 8; + regbuf[1] = reg & 0xff; + + ret = i2c_master_send(ov7251->i2c_client, regbuf, 2); + if (ret < 0) { + dev_err(ov7251->dev, "%s: write reg error %d: reg=%x\n", + __func__, ret, reg); + return ret; + } + + ret = i2c_master_recv(ov7251->i2c_client, val, 1); + if (ret < 0) { + dev_err(ov7251->dev, "%s: read reg error %d: reg=%x\n", + __func__, ret, reg); + return ret; + } + + return 0; +} + +static int ov7251_set_exposure(struct ov7251 *ov7251, s32 exposure) +{ + u16 reg; + u8 val[3]; + + reg = OV7251_AEC_EXPO_0; + val[0] = (exposure & 0xf000) >> 12; /* goes to OV7251_AEC_EXPO_0 */ + val[1] = (exposure & 0x0ff0) >> 4; /* goes to OV7251_AEC_EXPO_1 */ + val[2] = (exposure & 0x000f) << 4; /* goes to OV7251_AEC_EXPO_2 */ + + return ov7251_write_seq_regs(ov7251, reg, val, 3); +} + +static int ov7251_set_gain(struct ov7251 *ov7251, s32 gain) +{ + u16 reg; + u8 val[2]; + + reg = OV7251_AEC_AGC_ADJ_0; + val[0] = (gain & 0x0300) >> 8; /* goes to OV7251_AEC_AGC_ADJ_0 */ + val[1] = gain & 0xff; /* goes to OV7251_AEC_AGC_ADJ_1 */ + + return ov7251_write_seq_regs(ov7251, reg, val, 2); +} + +static int ov7251_set_register_array(struct ov7251 *ov7251, + const struct reg_value *settings, + unsigned int num_settings) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_settings; ++i, ++settings) { + ret = ov7251_write_reg(ov7251, settings->reg, settings->val); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ov7251_set_power_on(struct ov7251 *ov7251) +{ + int ret; + u32 wait_us; + + ret = ov7251_regulators_enable(ov7251); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(ov7251->xclk); + if (ret < 0) { + dev_err(ov7251->dev, "clk prepare enable failed\n"); + ov7251_regulators_disable(ov7251); + return ret; + } + + gpiod_set_value_cansleep(ov7251->enable_gpio, 1); + + /* wait at least 65536 external clock cycles */ + wait_us = DIV_ROUND_UP(65536 * 1000, + DIV_ROUND_UP(ov7251->xclk_freq, 1000)); + usleep_range(wait_us, wait_us + 1000); + + return 0; +} + +static void ov7251_set_power_off(struct ov7251 *ov7251) +{ + clk_disable_unprepare(ov7251->xclk); + gpiod_set_value_cansleep(ov7251->enable_gpio, 0); + ov7251_regulators_disable(ov7251); +} + +static int ov7251_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + int ret = 0; + + mutex_lock(&ov7251->lock); + + /* If the power state is not modified - no work to do. */ + if (ov7251->power_on == !!on) + goto exit; + + if (on) { + ret = ov7251_set_power_on(ov7251); + if (ret < 0) + goto exit; + + ret = ov7251_set_register_array(ov7251, + ov7251_global_init_setting, + ARRAY_SIZE(ov7251_global_init_setting)); + if (ret < 0) { + dev_err(ov7251->dev, "could not set init registers\n"); + ov7251_set_power_off(ov7251); + goto exit; + } + + ov7251->power_on = true; + } else { + ov7251_set_power_off(ov7251); + ov7251->power_on = false; + } + +exit: + mutex_unlock(&ov7251->lock); + + return ret; +} + +static int ov7251_set_hflip(struct ov7251 *ov7251, s32 value) +{ + u8 val = ov7251->timing_format2; + int ret; + + if (value) + val |= OV7251_TIMING_FORMAT2_MIRROR; + else + val &= ~OV7251_TIMING_FORMAT2_MIRROR; + + ret = ov7251_write_reg(ov7251, OV7251_TIMING_FORMAT2, val); + if (!ret) + ov7251->timing_format2 = val; + + return ret; +} + +static int ov7251_set_vflip(struct ov7251 *ov7251, s32 value) +{ + u8 val = ov7251->timing_format1; + int ret; + + if (value) + val |= OV7251_TIMING_FORMAT1_VFLIP; + else + val &= ~OV7251_TIMING_FORMAT1_VFLIP; + + ret = ov7251_write_reg(ov7251, OV7251_TIMING_FORMAT1, val); + if (!ret) + ov7251->timing_format1 = val; + + return ret; +} + +static int ov7251_set_test_pattern(struct ov7251 *ov7251, s32 value) +{ + u8 val = ov7251->pre_isp_00; + int ret; + + if (value) + val |= OV7251_PRE_ISP_00_TEST_PATTERN; + else + val &= ~OV7251_PRE_ISP_00_TEST_PATTERN; + + ret = ov7251_write_reg(ov7251, OV7251_PRE_ISP_00, val); + if (!ret) + ov7251->pre_isp_00 = val; + + return ret; +} + +static const char * const ov7251_test_pattern_menu[] = { + "Disabled", + "Vertical Pattern Bars", +}; + +static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov7251 *ov7251 = container_of(ctrl->handler, + struct ov7251, ctrls); + int ret; + + /* v4l2_ctrl_lock() locks our mutex */ + + if (!ov7251->power_on) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = ov7251_set_exposure(ov7251, ctrl->val); + break; + case V4L2_CID_GAIN: + ret = ov7251_set_gain(ov7251, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov7251_set_test_pattern(ov7251, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = ov7251_set_hflip(ov7251, ctrl->val); + break; + case V4L2_CID_VFLIP: + ret = ov7251_set_vflip(ov7251, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops ov7251_ctrl_ops = { + .s_ctrl = ov7251_s_ctrl, +}; + +static int ov7251_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_Y10_1X10; + + return 0; +} + +static int ov7251_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->code != MEDIA_BUS_FMT_Y10_1X10) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(ov7251_mode_info_data)) + return -EINVAL; + + fse->min_width = ov7251_mode_info_data[fse->index].width; + fse->max_width = ov7251_mode_info_data[fse->index].width; + fse->min_height = ov7251_mode_info_data[fse->index].height; + fse->max_height = ov7251_mode_info_data[fse->index].height; + + return 0; +} + +static int ov7251_enum_frame_ival(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + unsigned int index = fie->index; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ov7251_mode_info_data); i++) { + if (fie->width != ov7251_mode_info_data[i].width || + fie->height != ov7251_mode_info_data[i].height) + continue; + + if (index-- == 0) { + fie->interval = ov7251_mode_info_data[i].timeperframe; + return 0; + } + } + + return -EINVAL; +} + +static struct v4l2_mbus_framefmt * +__ov7251_get_pad_format(struct ov7251 *ov7251, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&ov7251->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &ov7251->fmt; + default: + return NULL; + } +} + +static int ov7251_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + + mutex_lock(&ov7251->lock); + format->format = *__ov7251_get_pad_format(ov7251, cfg, format->pad, + format->which); + mutex_unlock(&ov7251->lock); + + return 0; +} + +static struct v4l2_rect * +__ov7251_get_pad_crop(struct ov7251 *ov7251, struct v4l2_subdev_pad_config *cfg, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&ov7251->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &ov7251->crop; + default: + return NULL; + } +} + +static inline u32 avg_fps(const struct v4l2_fract *t) +{ + return (t->denominator + (t->numerator >> 1)) / t->numerator; +} + +static const struct ov7251_mode_info * +ov7251_find_mode_by_ival(struct ov7251 *ov7251, struct v4l2_fract *timeperframe) +{ + const struct ov7251_mode_info *mode = ov7251->current_mode; + unsigned int fps_req = avg_fps(timeperframe); + unsigned int max_dist_match = (unsigned int) -1; + unsigned int i, n = 0; + + for (i = 0; i < ARRAY_SIZE(ov7251_mode_info_data); i++) { + unsigned int dist; + unsigned int fps_tmp; + + if (mode->width != ov7251_mode_info_data[i].width || + mode->height != ov7251_mode_info_data[i].height) + continue; + + fps_tmp = avg_fps(&ov7251_mode_info_data[i].timeperframe); + + dist = abs(fps_req - fps_tmp); + + if (dist < max_dist_match) { + n = i; + max_dist_match = dist; + } + } + + return &ov7251_mode_info_data[n]; +} + +static int ov7251_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + const struct ov7251_mode_info *new_mode; + int ret = 0; + + mutex_lock(&ov7251->lock); + + __crop = __ov7251_get_pad_crop(ov7251, cfg, format->pad, format->which); + + new_mode = v4l2_find_nearest_size(ov7251_mode_info_data, + ARRAY_SIZE(ov7251_mode_info_data), + width, height, + format->format.width, format->format.height); + + __crop->width = new_mode->width; + __crop->height = new_mode->height; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock, + new_mode->pixel_clock); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq, + new_mode->link_freq); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_modify_range(ov7251->exposure, + 1, new_mode->exposure_max, + 1, new_mode->exposure_def); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->exposure, + new_mode->exposure_def); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->gain, 16); + if (ret < 0) + goto exit; + + ov7251->current_mode = new_mode; + } + + __format = __ov7251_get_pad_format(ov7251, cfg, format->pad, + format->which); + __format->width = __crop->width; + __format->height = __crop->height; + __format->code = MEDIA_BUS_FMT_Y10_1X10; + __format->field = V4L2_FIELD_NONE; + __format->colorspace = V4L2_COLORSPACE_SRGB; + __format->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(__format->colorspace); + __format->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + __format->colorspace, __format->ycbcr_enc); + __format->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(__format->colorspace); + + format->format = *__format; + +exit: + mutex_unlock(&ov7251->lock); + + return ret; +} + +static int ov7251_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format fmt = { + .which = cfg ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .width = 640, + .height = 480 + } + }; + + ov7251_set_format(subdev, cfg, &fmt); + + return 0; +} + +static int ov7251_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + mutex_lock(&ov7251->lock); + sel->r = *__ov7251_get_pad_crop(ov7251, cfg, sel->pad, + sel->which); + mutex_unlock(&ov7251->lock); + + return 0; +} + +static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct ov7251 *ov7251 = to_ov7251(subdev); + int ret; + + mutex_lock(&ov7251->lock); + + if (enable) { + ret = ov7251_set_register_array(ov7251, + ov7251->current_mode->data, + ov7251->current_mode->data_size); + if (ret < 0) { + dev_err(ov7251->dev, "could not set mode %dx%d\n", + ov7251->current_mode->width, + ov7251->current_mode->height); + goto exit; + } + ret = __v4l2_ctrl_handler_setup(&ov7251->ctrls); + if (ret < 0) { + dev_err(ov7251->dev, "could not sync v4l2 controls\n"); + goto exit; + } + ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT, + OV7251_SC_MODE_SELECT_STREAMING); + } else { + ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT, + OV7251_SC_MODE_SELECT_SW_STANDBY); + } + +exit: + mutex_unlock(&ov7251->lock); + + return ret; +} + +static int ov7251_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov7251 *ov7251 = to_ov7251(subdev); + + mutex_lock(&ov7251->lock); + fi->interval = ov7251->current_mode->timeperframe; + mutex_unlock(&ov7251->lock); + + return 0; +} + +static int ov7251_set_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov7251 *ov7251 = to_ov7251(subdev); + const struct ov7251_mode_info *new_mode; + int ret = 0; + + mutex_lock(&ov7251->lock); + new_mode = ov7251_find_mode_by_ival(ov7251, &fi->interval); + + if (new_mode != ov7251->current_mode) { + ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock, + new_mode->pixel_clock); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq, + new_mode->link_freq); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_modify_range(ov7251->exposure, + 1, new_mode->exposure_max, + 1, new_mode->exposure_def); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->exposure, + new_mode->exposure_def); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->gain, 16); + if (ret < 0) + goto exit; + + ov7251->current_mode = new_mode; + } + + fi->interval = ov7251->current_mode->timeperframe; + +exit: + mutex_unlock(&ov7251->lock); + + return ret; +} + +static const struct v4l2_subdev_core_ops ov7251_core_ops = { + .s_power = ov7251_s_power, +}; + +static const struct v4l2_subdev_video_ops ov7251_video_ops = { + .s_stream = ov7251_s_stream, + .g_frame_interval = ov7251_get_frame_interval, + .s_frame_interval = ov7251_set_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov7251_subdev_pad_ops = { + .init_cfg = ov7251_entity_init_cfg, + .enum_mbus_code = ov7251_enum_mbus_code, + .enum_frame_size = ov7251_enum_frame_size, + .enum_frame_interval = ov7251_enum_frame_ival, + .get_fmt = ov7251_get_format, + .set_fmt = ov7251_set_format, + .get_selection = ov7251_get_selection, +}; + +static const struct v4l2_subdev_ops ov7251_subdev_ops = { + .core = &ov7251_core_ops, + .video = &ov7251_video_ops, + .pad = &ov7251_subdev_pad_ops, +}; + +static int ov7251_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct fwnode_handle *endpoint; + struct ov7251 *ov7251; + u8 chip_id_high, chip_id_low, chip_rev; + int ret; + + ov7251 = devm_kzalloc(dev, sizeof(struct ov7251), GFP_KERNEL); + if (!ov7251) + return -ENOMEM; + + ov7251->i2c_client = client; + ov7251->dev = dev; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(endpoint, &ov7251->ep); + fwnode_handle_put(endpoint); + if (ret < 0) { + dev_err(dev, "parsing endpoint node failed\n"); + return ret; + } + + if (ov7251->ep.bus_type != V4L2_MBUS_CSI2) { + dev_err(dev, "invalid bus type (%u), must be CSI2 (%u)\n", + ov7251->ep.bus_type, V4L2_MBUS_CSI2); + return -EINVAL; + } + + /* get system clock (xclk) */ + ov7251->xclk = devm_clk_get(dev, "xclk"); + if (IS_ERR(ov7251->xclk)) { + dev_err(dev, "could not get xclk"); + return PTR_ERR(ov7251->xclk); + } + + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &ov7251->xclk_freq); + if (ret) { + dev_err(dev, "could not get xclk frequency\n"); + return ret; + } + + /* external clock must be 24MHz, allow 1% tolerance */ + if (ov7251->xclk_freq < 23760000 || ov7251->xclk_freq > 24240000) { + dev_err(dev, "external clock frequency %u is not supported\n", + ov7251->xclk_freq); + return -EINVAL; + } + + ret = clk_set_rate(ov7251->xclk, ov7251->xclk_freq); + if (ret) { + dev_err(dev, "could not set xclk frequency\n"); + return ret; + } + + ov7251->io_regulator = devm_regulator_get(dev, "vdddo"); + if (IS_ERR(ov7251->io_regulator)) { + dev_err(dev, "cannot get io regulator\n"); + return PTR_ERR(ov7251->io_regulator); + } + + ov7251->core_regulator = devm_regulator_get(dev, "vddd"); + if (IS_ERR(ov7251->core_regulator)) { + dev_err(dev, "cannot get core regulator\n"); + return PTR_ERR(ov7251->core_regulator); + } + + ov7251->analog_regulator = devm_regulator_get(dev, "vdda"); + if (IS_ERR(ov7251->analog_regulator)) { + dev_err(dev, "cannot get analog regulator\n"); + return PTR_ERR(ov7251->analog_regulator); + } + + ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(ov7251->enable_gpio)) { + dev_err(dev, "cannot get enable gpio\n"); + return PTR_ERR(ov7251->enable_gpio); + } + + mutex_init(&ov7251->lock); + + v4l2_ctrl_handler_init(&ov7251->ctrls, 7); + ov7251->ctrls.lock = &ov7251->lock; + + v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 32, 1, 32); + ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_GAIN, 16, 1023, 1, 16); + v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov7251_test_pattern_menu) - 1, + 0, 0, ov7251_test_pattern_menu); + ov7251->pixel_clock = v4l2_ctrl_new_std(&ov7251->ctrls, + &ov7251_ctrl_ops, + V4L2_CID_PIXEL_RATE, + 1, INT_MAX, 1, 1); + ov7251->link_freq = v4l2_ctrl_new_int_menu(&ov7251->ctrls, + &ov7251_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq) - 1, + 0, link_freq); + if (ov7251->link_freq) + ov7251->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ov7251->sd.ctrl_handler = &ov7251->ctrls; + + if (ov7251->ctrls.error) { + dev_err(dev, "%s: control initialization error %d\n", + __func__, ov7251->ctrls.error); + ret = ov7251->ctrls.error; + goto free_ctrl; + } + + v4l2_i2c_subdev_init(&ov7251->sd, client, &ov7251_subdev_ops); + ov7251->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov7251->pad.flags = MEDIA_PAD_FL_SOURCE; + ov7251->sd.dev = &client->dev; + ov7251->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&ov7251->sd.entity, 1, &ov7251->pad); + if (ret < 0) { + dev_err(dev, "could not register media entity\n"); + goto free_ctrl; + } + + ret = ov7251_s_power(&ov7251->sd, true); + if (ret < 0) { + dev_err(dev, "could not power up OV7251\n"); + goto free_entity; + } + + ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_HIGH, &chip_id_high); + if (ret < 0 || chip_id_high != OV7251_CHIP_ID_HIGH_BYTE) { + dev_err(dev, "could not read ID high\n"); + ret = -ENODEV; + goto power_down; + } + ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_LOW, &chip_id_low); + if (ret < 0 || chip_id_low != OV7251_CHIP_ID_LOW_BYTE) { + dev_err(dev, "could not read ID low\n"); + ret = -ENODEV; + goto power_down; + } + + ret = ov7251_read_reg(ov7251, OV7251_SC_GP_IO_IN1, &chip_rev); + if (ret < 0) { + dev_err(dev, "could not read revision\n"); + ret = -ENODEV; + goto power_down; + } + chip_rev >>= 4; + + dev_info(dev, "OV7251 revision %x (%s) detected at address 0x%02x\n", + chip_rev, + chip_rev == 0x4 ? "1A / 1B" : + chip_rev == 0x5 ? "1C / 1D" : + chip_rev == 0x6 ? "1E" : + chip_rev == 0x7 ? "1F" : "unknown", + client->addr); + + ret = ov7251_read_reg(ov7251, OV7251_PRE_ISP_00, + &ov7251->pre_isp_00); + if (ret < 0) { + dev_err(dev, "could not read test pattern value\n"); + ret = -ENODEV; + goto power_down; + } + + ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT1, + &ov7251->timing_format1); + if (ret < 0) { + dev_err(dev, "could not read vflip value\n"); + ret = -ENODEV; + goto power_down; + } + + ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT2, + &ov7251->timing_format2); + if (ret < 0) { + dev_err(dev, "could not read hflip value\n"); + ret = -ENODEV; + goto power_down; + } + + ov7251_s_power(&ov7251->sd, false); + + ret = v4l2_async_register_subdev(&ov7251->sd); + if (ret < 0) { + dev_err(dev, "could not register v4l2 device\n"); + goto free_entity; + } + + ov7251_entity_init_cfg(&ov7251->sd, NULL); + + return 0; + +power_down: + ov7251_s_power(&ov7251->sd, false); +free_entity: + media_entity_cleanup(&ov7251->sd.entity); +free_ctrl: + v4l2_ctrl_handler_free(&ov7251->ctrls); + mutex_destroy(&ov7251->lock); + + return ret; +} + +static int ov7251_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov7251 *ov7251 = to_ov7251(sd); + + v4l2_async_unregister_subdev(&ov7251->sd); + media_entity_cleanup(&ov7251->sd.entity); + v4l2_ctrl_handler_free(&ov7251->ctrls); + mutex_destroy(&ov7251->lock); + + return 0; +} + +static const struct of_device_id ov7251_of_match[] = { + { .compatible = "ovti,ov7251" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov7251_of_match); + +static struct i2c_driver ov7251_i2c_driver = { + .driver = { + .of_match_table = ov7251_of_match, + .name = "ov7251", + }, + .probe_new = ov7251_probe, + .remove = ov7251_remove, +}; + +module_i2c_driver(ov7251_i2c_driver); + +MODULE_DESCRIPTION("Omnivision OV7251 Camera Driver"); +MODULE_AUTHOR("Todor Tomov "); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 7a2148dfda8001c983f0effd9afd8a7fa58e99c4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 25 Apr 2018 11:04:21 -0400 Subject: media: smiapp: fix timeout checking in smiapp_read_nvm The current code decrements the timeout counter i and the end of each loop i is incremented, so the check for timeout will always be false and hence the timeout mechanism is just a dead code path. Potentially, if the RD_READY bit is not set, we could end up in an infinite loop. Fix this so the timeout starts from 1000 and decrements to zero, if at the end of the loop i is zero we have a timeout condition. Detected by CoverityScan, CID#1324008 ("Logically dead code") Fixes: ccfc97bdb5ae ("[media] smiapp: Add driver") Signed-off-by: Colin Ian King Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 3b7ace3..e1f8208 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1001,7 +1001,7 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor, if (rval) goto out; - for (i = 0; i < 1000; i++) { + for (i = 1000; i > 0; i--) { rval = smiapp_read( sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); @@ -1012,11 +1012,10 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor, if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) break; - if (--i == 0) { - rval = -ETIMEDOUT; - goto out; - } - + } + if (!i) { + rval = -ETIMEDOUT; + goto out; } for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { -- cgit v1.1 From 4c858e962ef9c648e5f00bd29c5c14aa7485ce45 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Tue, 24 Apr 2018 04:24:06 -0400 Subject: media: imx274: document reset delays more clearly Document the unit to avoid having to look through the code to compute it. Also clarify that these are min and max values. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index daec33f..5e425db 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -87,7 +87,7 @@ #define IMX274_SHR_LIMIT_CONST (4) /* - * Constants for sensor reset delay + * Min and max sensor reset delay (microseconds) */ #define IMX274_RESET_DELAY1 (2000) #define IMX274_RESET_DELAY2 (2200) -- cgit v1.1 From 013433919ae8b7cc9566c125845bbce2e09bbaf0 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Tue, 24 Apr 2018 04:24:07 -0400 Subject: media: imx274: fix typo in comment interal->interval Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 5e425db..dfd04ed 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -971,7 +971,7 @@ static int imx274_s_frame_interval(struct v4l2_subdev *sd, if (!ret) { /* * exposure time range is decided by frame interval - * need to update it after frame interal changes + * need to update it after frame interval changes */ min = IMX274_MIN_EXPOSURE_TIME; max = fi->interval.numerator * 1000000 -- cgit v1.1 From cf90870454990c54a754c5b2e9ca2e4277ab9b2d Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Tue, 24 Apr 2018 04:24:08 -0400 Subject: media: imx274: slightly simplify code imx274_s_frame_interval() already has a direct pointer to the v4l2 exposure control, so reuse it to simplify code. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index dfd04ed..c5d00ad 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -984,7 +984,7 @@ static int imx274_s_frame_interval(struct v4l2_subdev *sd, } /* update exposure time accordingly */ - imx274_set_exposure(imx274, imx274->ctrls.exposure->val); + imx274_set_exposure(imx274, ctrl->val); dev_dbg(&imx274->client->dev, "set frame interval to %uus\n", fi->interval.numerator * 1000000 -- cgit v1.1 From f067ddad003b67be8acb7fd739172dcfb4817a2a Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Tue, 24 Apr 2018 04:24:09 -0400 Subject: media: imx274: remove unused data from struct imx274_frmfmt struct imx274_frmfmt is instantiated only in the imx274_formats[] array, where imx274_formats[N].mode always equals N (via enum imx274_mode). So .mode carries no information, and unsurprisingly it is never used. mbus_code is never used because the 12 bit modes are not implemented. The colorspace member is also never used, which is normal since the imx274 sensor can output only one colorspace. Let's get rid of all of them. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index c5d00ad..9203d37 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -156,10 +156,7 @@ enum imx274_mode { * imx274 format related structure */ struct imx274_frmfmt { - u32 mbus_code; - enum v4l2_colorspace colorspace; struct v4l2_frmsize_discrete size; - enum imx274_mode mode; }; /* @@ -501,12 +498,9 @@ static const struct reg_8 *mode_table[] = { * imx274 format related structure */ static const struct imx274_frmfmt imx274_formats[] = { - {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {3840, 2160}, - IMX274_MODE_3840X2160}, - {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {1920, 1080}, - IMX274_MODE_1920X1080}, - {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {1280, 720}, - IMX274_MODE_1280X720}, + { {3840, 2160} }, + { {1920, 1080} }, + { {1280, 720} }, }; /* @@ -890,9 +884,8 @@ static int imx274_set_fmt(struct v4l2_subdev *sd, int index; dev_dbg(&client->dev, - "%s: width = %d height = %d code = %d mbus_code = %d\n", - __func__, fmt->width, fmt->height, fmt->code, - imx274_formats[imx274->mode_index].mbus_code); + "%s: width = %d height = %d code = %d\n", + __func__, fmt->width, fmt->height, fmt->code); mutex_lock(&imx274->lock); -- cgit v1.1 From 4eb7846d3ead1c2f461811e41e63462016e19ae7 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Tue, 24 Apr 2018 04:24:10 -0400 Subject: media: imx274: rename and reorder register address definitions Most registers are defined using the name used in the datasheet. E.g. the defines for the HMAX register are IMX274_HMAX_REG_*. Rename the SHR and VMAX register accordingly. Also move them close to related registers: SHR close to SVR, VMAX close to HMAX. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 9203d37..62a0d7a 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -107,15 +107,15 @@ /* * IMX274 register definitions */ -#define IMX274_FRAME_LENGTH_ADDR_1 0x30FA /* VMAX, MSB */ -#define IMX274_FRAME_LENGTH_ADDR_2 0x30F9 /* VMAX */ -#define IMX274_FRAME_LENGTH_ADDR_3 0x30F8 /* VMAX, LSB */ +#define IMX274_SHR_REG_MSB 0x300D /* SHR */ +#define IMX274_SHR_REG_LSB 0x300C /* SHR */ #define IMX274_SVR_REG_MSB 0x300F /* SVR */ #define IMX274_SVR_REG_LSB 0x300E /* SVR */ +#define IMX274_VMAX_REG_1 0x30FA /* VMAX, MSB */ +#define IMX274_VMAX_REG_2 0x30F9 /* VMAX */ +#define IMX274_VMAX_REG_3 0x30F8 /* VMAX, LSB */ #define IMX274_HMAX_REG_MSB 0x30F7 /* HMAX */ #define IMX274_HMAX_REG_LSB 0x30F6 /* HMAX */ -#define IMX274_COARSE_TIME_ADDR_MSB 0x300D /* SHR */ -#define IMX274_COARSE_TIME_ADDR_LSB 0x300C /* SHR */ #define IMX274_ANALOG_GAIN_ADDR_LSB 0x300A /* ANALOG GAIN LSB */ #define IMX274_ANALOG_GAIN_ADDR_MSB 0x300B /* ANALOG GAIN MSB */ #define IMX274_DIGITAL_GAIN_REG 0x3012 /* Digital Gain */ @@ -1126,15 +1126,15 @@ static int imx274_get_frame_length(struct stimx274 *priv, u32 *val) svr = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0]; /* vmax */ - err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_3, ®_val[0]); + err = imx274_read_reg(priv, IMX274_VMAX_REG_3, ®_val[0]); if (err) goto fail; - err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_2, ®_val[1]); + err = imx274_read_reg(priv, IMX274_VMAX_REG_2, ®_val[1]); if (err) goto fail; - err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_1, ®_val[2]); + err = imx274_read_reg(priv, IMX274_VMAX_REG_1, ®_val[2]); if (err) goto fail; @@ -1293,10 +1293,10 @@ fail: static inline void imx274_calculate_coarse_time_regs(struct reg_8 regs[2], u32 coarse_time) { - regs->addr = IMX274_COARSE_TIME_ADDR_MSB; + regs->addr = IMX274_SHR_REG_MSB; regs->val = (coarse_time >> IMX274_SHIFT_8_BITS) & IMX274_MASK_LSB_8_BITS; - (regs + 1)->addr = IMX274_COARSE_TIME_ADDR_LSB; + (regs + 1)->addr = IMX274_SHR_REG_LSB; (regs + 1)->val = (coarse_time) & IMX274_MASK_LSB_8_BITS; } @@ -1464,13 +1464,13 @@ static int imx274_set_test_pattern(struct stimx274 *priv, int val) static inline void imx274_calculate_frame_length_regs(struct reg_8 regs[3], u32 frame_length) { - regs->addr = IMX274_FRAME_LENGTH_ADDR_1; + regs->addr = IMX274_VMAX_REG_1; regs->val = (frame_length >> IMX274_SHIFT_16_BITS) & IMX274_MASK_LSB_4_BITS; - (regs + 1)->addr = IMX274_FRAME_LENGTH_ADDR_2; + (regs + 1)->addr = IMX274_VMAX_REG_2; (regs + 1)->val = (frame_length >> IMX274_SHIFT_8_BITS) & IMX274_MASK_LSB_8_BITS; - (regs + 2)->addr = IMX274_FRAME_LENGTH_ADDR_3; + (regs + 2)->addr = IMX274_VMAX_REG_3; (regs + 2)->val = (frame_length) & IMX274_MASK_LSB_8_BITS; } -- cgit v1.1 From 8ed8bba70b4355b1ba029b151ade84475dd12991 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Tue, 24 Apr 2018 04:24:11 -0400 Subject: media: imx274: remove non-indexed pointers from mode_table mode_table[] has 3 members that are accessed based on their index, which makes worth using an array. The other members are always accessed with a constant index. This added indirection gives no improvement and only makes code more verbose. Remove these pointers from the array and access them directly. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 62a0d7a..63fb94e 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -144,12 +144,6 @@ enum imx274_mode { IMX274_MODE_3840X2160, IMX274_MODE_1920X1080, IMX274_MODE_1280X720, - - IMX274_MODE_START_STREAM_1, - IMX274_MODE_START_STREAM_2, - IMX274_MODE_START_STREAM_3, - IMX274_MODE_START_STREAM_4, - IMX274_MODE_STOP_STREAM }; /* @@ -486,12 +480,6 @@ static const struct reg_8 *mode_table[] = { [IMX274_MODE_3840X2160] = imx274_mode1_3840x2160_raw10, [IMX274_MODE_1920X1080] = imx274_mode3_1920x1080_raw10, [IMX274_MODE_1280X720] = imx274_mode5_1280x720_raw10, - - [IMX274_MODE_START_STREAM_1] = imx274_start_1, - [IMX274_MODE_START_STREAM_2] = imx274_start_2, - [IMX274_MODE_START_STREAM_3] = imx274_start_3, - [IMX274_MODE_START_STREAM_4] = imx274_start_4, - [IMX274_MODE_STOP_STREAM] = imx274_stop, }; /* @@ -731,11 +719,11 @@ static int imx274_mode_regs(struct stimx274 *priv, int mode) { int err = 0; - err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_1]); + err = imx274_write_table(priv, imx274_start_1); if (err) return err; - err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_2]); + err = imx274_write_table(priv, imx274_start_2); if (err) return err; @@ -760,7 +748,7 @@ static int imx274_start_stream(struct stimx274 *priv) * give it 1 extra ms for margin */ msleep_range(11); - err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_3]); + err = imx274_write_table(priv, imx274_start_3); if (err) return err; @@ -770,7 +758,7 @@ static int imx274_start_stream(struct stimx274 *priv) * give it 1 extra ms for margin */ msleep_range(8); - err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_4]); + err = imx274_write_table(priv, imx274_start_4); if (err) return err; @@ -1081,8 +1069,7 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) goto fail; } else { /* stop stream */ - ret = imx274_write_table(imx274, - mode_table[IMX274_MODE_STOP_STREAM]); + ret = imx274_write_table(imx274, imx274_stop); if (ret) goto fail; } @@ -1779,7 +1766,7 @@ static int imx274_remove(struct i2c_client *client) struct stimx274 *imx274 = to_imx274(sd); /* stop stream */ - imx274_write_table(imx274, mode_table[IMX274_MODE_STOP_STREAM]); + imx274_write_table(imx274, imx274_stop); v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&imx274->ctrls.handler); -- cgit v1.1 From 50eea4ab9169f9ca4f24f01612005497f708b667 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 11 May 2018 10:04:34 -0400 Subject: media: i2c: adv748x: Fix pixel rate values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pixel rate, as reported by the V4L2_CID_PIXEL_RATE control, must include both horizontal and vertical blanking. Both the AFE and HDMI receiver program it incorrectly: - The HDMI receiver goes to the trouble of removing blanking to compute the rate of active pixels. This is easy to fix by removing the computation and returning the incoming pixel clock rate directly. - The AFE performs similar calculation, while it should simply return the fixed pixel rate for analog sources, mandated by the ADV748x to be 14.3180180 MHz. [Niklas: Update AFE fixed pixel rate] Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-afe.c | 12 ++++++------ drivers/media/i2c/adv748x/adv748x-hdmi.c | 8 +------- 2 files changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 61514ba..edd25e8 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -321,17 +321,17 @@ static const struct v4l2_subdev_video_ops adv748x_afe_video_ops = { static int adv748x_afe_propagate_pixelrate(struct adv748x_afe *afe) { struct v4l2_subdev *tx; - unsigned int width, height, fps; tx = adv748x_get_remote_sd(&afe->pads[ADV748X_AFE_SOURCE]); if (!tx) return -ENOLINK; - width = 720; - height = afe->curr_norm & V4L2_STD_525_60 ? 480 : 576; - fps = afe->curr_norm & V4L2_STD_525_60 ? 30 : 25; - - return adv748x_csi2_set_pixelrate(tx, width * height * fps); + /* + * The ADV748x ADC sampling frequency is twice the externally supplied + * clock whose frequency is required to be 28.63636 MHz. It oversamples + * with a factor of 4 resulting in a pixel rate of 14.3180180 MHz. + */ + return adv748x_csi2_set_pixelrate(tx, 14318180); } static int adv748x_afe_enum_mbus_code(struct v4l2_subdev *sd, diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index 10d229a..aecc2a8 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -402,8 +402,6 @@ static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi) { struct v4l2_subdev *tx; struct v4l2_dv_timings timings; - struct v4l2_bt_timings *bt = &timings.bt; - unsigned int fps; tx = adv748x_get_remote_sd(&hdmi->pads[ADV748X_HDMI_SOURCE]); if (!tx) @@ -411,11 +409,7 @@ static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi) adv748x_hdmi_query_dv_timings(&hdmi->sd, &timings); - fps = DIV_ROUND_CLOSEST_ULL(bt->pixelclock, - V4L2_DV_BT_FRAME_WIDTH(bt) * - V4L2_DV_BT_FRAME_HEIGHT(bt)); - - return adv748x_csi2_set_pixelrate(tx, bt->width * bt->height * fps); + return adv748x_csi2_set_pixelrate(tx, timings.bt.pixelclock); } static int adv748x_hdmi_enum_mbus_code(struct v4l2_subdev *sd, -- cgit v1.1 From 3c785e48786f9f7ef19a69433ba4c6ce83c949fa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 15 May 2018 04:50:05 -0400 Subject: media: adv7511: fix clearing of the CEC receive buffer The CEC receive buffer was not always cleared correctly. The datasheet was a bit confusing since sometimes it mentioned that the bit in CEC register 0x4a had to be toggled, and sometimes it suggested it was a 'Clear-on-write' bit. But it really needs to be toggled. The patch also enables/disables the CEC irqs after the other irq are enabled/disabled instead of doing it before. It may not matter, but it feels more logical to do it in that order, and the implementation that we (Cisco) have used until now and that is known to be reliable also did it in that order. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7511.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index d23505a..d4b191c 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -732,8 +732,8 @@ static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) /* power up cec section */ adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x01); /* legacy mode and clear all rx buffers */ + adv7511_cec_write(sd, 0x4a, 0x00); adv7511_cec_write(sd, 0x4a, 0x07); - adv7511_cec_write(sd, 0x4a, 0); adv7511_cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */ /* enabled irqs: */ /* tx: ready */ @@ -917,9 +917,6 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) else if (adv7511_have_hotplug(sd)) irqs |= MASK_ADV7511_EDID_RDY_INT; - adv7511_wr_and_or(sd, 0x95, 0xc0, - (state->cec_enabled_adap && enable) ? 0x39 : 0x00); - /* * This i2c write can fail (approx. 1 in 1000 writes). But it * is essential that this register is correct, so retry it @@ -933,9 +930,11 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) irqs_rd = adv7511_rd(sd, 0x94); } while (retries-- && irqs_rd != irqs); - if (irqs_rd == irqs) - return; - v4l2_err(sd, "Could not set interrupts: hw failure?\n"); + if (irqs_rd != irqs) + v4l2_err(sd, "Could not set interrupts: hw failure?\n"); + + adv7511_wr_and_or(sd, 0x95, 0xc0, + (state->cec_enabled_adap && enable) ? 0x39 : 0x00); } /* Interrupt handler */ @@ -982,8 +981,8 @@ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) for (i = 0; i < msg.len; i++) msg.msg[i] = adv7511_cec_read(sd, i + 0x15); - adv7511_cec_write(sd, 0x4a, 1); /* toggle to re-enable rx 1 */ - adv7511_cec_write(sd, 0x4a, 0); + adv7511_cec_write(sd, 0x4a, 0); /* toggle to re-enable rx 1 */ + adv7511_cec_write(sd, 0x4a, 1); cec_received_msg(state->cec_adap, &msg); } } @@ -1778,6 +1777,7 @@ static void adv7511_init_setup(struct v4l2_subdev *sd) /* legacy mode */ adv7511_cec_write(sd, 0x4a, 0x00); + adv7511_cec_write(sd, 0x4a, 0x07); if (cec_clk % 750000 != 0) v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n", -- cgit v1.1 From e4802cb00bfe3d2ae4fbdec48fe4eda10f3b5486 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Wed, 2 May 2018 11:46:08 -0400 Subject: media: imx258: Add imx258 camera sensor driver Add a V4L2 sub-device driver for the Sony IMX258 image sensor. This is a camera sensor using the I2C bus for control and the CSI-2 bus for data. Signed-off-by: Jason Chen Signed-off-by: Andy Yeh Signed-off-by: Alan Chiang Reviewed-by: Tomasz Figa Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx258.c | 1320 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1332 insertions(+) create mode 100644 drivers/media/i2c/imx258.c (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index b95b447..341452f 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -575,6 +575,17 @@ config VIDEO_APTINA_PLL config VIDEO_SMIAPP_PLL tristate +config VIDEO_IMX258 + tristate "Sony IMX258 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the Sony + IMX258 camera. + + To compile this driver as a module, choose M here: the + module will be called imx258. + config VIDEO_IMX274 tristate "Sony IMX274 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index ff6e291..d679d57 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_VIDEO_I2C) += video-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o +obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c new file mode 100644 index 0000000..fad3012 --- /dev/null +++ b/drivers/media/i2c/imx258.c @@ -0,0 +1,1320 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX258_REG_VALUE_08BIT 1 +#define IMX258_REG_VALUE_16BIT 2 + +#define IMX258_REG_MODE_SELECT 0x0100 +#define IMX258_MODE_STANDBY 0x00 +#define IMX258_MODE_STREAMING 0x01 + +/* Chip ID */ +#define IMX258_REG_CHIP_ID 0x0016 +#define IMX258_CHIP_ID 0x0258 + +/* V_TIMING internal */ +#define IMX258_VTS_30FPS 0x0c98 +#define IMX258_VTS_30FPS_2K 0x0638 +#define IMX258_VTS_30FPS_VGA 0x034c +#define IMX258_VTS_MAX 0xffff + +/*Frame Length Line*/ +#define IMX258_FLL_MIN 0x08a6 +#define IMX258_FLL_MAX 0xffff +#define IMX258_FLL_STEP 1 +#define IMX258_FLL_DEFAULT 0x0c98 + +/* HBLANK control - read only */ +#define IMX258_PPL_DEFAULT 5352 + +/* Exposure control */ +#define IMX258_REG_EXPOSURE 0x0202 +#define IMX258_EXPOSURE_MIN 4 +#define IMX258_EXPOSURE_STEP 1 +#define IMX258_EXPOSURE_DEFAULT 0x640 +#define IMX258_EXPOSURE_MAX 65535 + +/* Analog gain control */ +#define IMX258_REG_ANALOG_GAIN 0x0204 +#define IMX258_ANA_GAIN_MIN 0 +#define IMX258_ANA_GAIN_MAX 0x1fff +#define IMX258_ANA_GAIN_STEP 1 +#define IMX258_ANA_GAIN_DEFAULT 0x0 + +/* Digital gain control */ +#define IMX258_REG_GR_DIGITAL_GAIN 0x020e +#define IMX258_REG_R_DIGITAL_GAIN 0x0210 +#define IMX258_REG_B_DIGITAL_GAIN 0x0212 +#define IMX258_REG_GB_DIGITAL_GAIN 0x0214 +#define IMX258_DGTL_GAIN_MIN 0 +#define IMX258_DGTL_GAIN_MAX 4096 /* Max = 0xFFF */ +#define IMX258_DGTL_GAIN_DEFAULT 1024 +#define IMX258_DGTL_GAIN_STEP 1 + +/* Test Pattern Control */ +#define IMX258_REG_TEST_PATTERN 0x0600 +#define IMX258_TEST_PATTERN_DISABLE 0 +#define IMX258_TEST_PATTERN_SOLID_COLOR 1 +#define IMX258_TEST_PATTERN_COLOR_BARS 2 +#define IMX258_TEST_PATTERN_GREY_COLOR 3 +#define IMX258_TEST_PATTERN_PN9 4 + +/* Orientation */ +#define REG_MIRROR_FLIP_CONTROL 0x0101 +#define REG_CONFIG_MIRROR_FLIP 0x03 +#define REG_CONFIG_FLIP_TEST_PATTERN 0x02 + +struct imx258_reg { + u16 address; + u8 val; +}; + +struct imx258_reg_list { + u32 num_of_regs; + const struct imx258_reg *regs; +}; + +/* Link frequency config */ +struct imx258_link_freq_config { + u32 pixels_per_line; + + /* PLL registers for this link frequency */ + struct imx258_reg_list reg_list; +}; + +/* Mode : resolution and related config&values */ +struct imx258_mode { + /* Frame width */ + u32 width; + /* Frame height */ + u32 height; + + /* V-timing */ + u32 vts_def; + u32 vts_min; + + /* Index of Link frequency config to be used */ + u32 link_freq_index; + /* Default register values */ + struct imx258_reg_list reg_list; +}; + +/* 4208x3118 needs 1267Mbps/lane, 4 lanes */ +static const struct imx258_reg mipi_data_rate_1267mbps[] = { + { 0x0301, 0x05 }, + { 0x0303, 0x02 }, + { 0x0305, 0x03 }, + { 0x0306, 0x00 }, + { 0x0307, 0xC6 }, + { 0x0309, 0x0A }, + { 0x030B, 0x01 }, + { 0x030D, 0x02 }, + { 0x030E, 0x00 }, + { 0x030F, 0xD8 }, + { 0x0310, 0x00 }, + { 0x0820, 0x13 }, + { 0x0821, 0x4C }, + { 0x0822, 0xCC }, + { 0x0823, 0xCC }, +}; + +static const struct imx258_reg mipi_data_rate_640mbps[] = { + { 0x0301, 0x05 }, + { 0x0303, 0x02 }, + { 0x0305, 0x03 }, + { 0x0306, 0x00 }, + { 0x0307, 0x64 }, + { 0x0309, 0x0A }, + { 0x030B, 0x01 }, + { 0x030D, 0x02 }, + { 0x030E, 0x00 }, + { 0x030F, 0xD8 }, + { 0x0310, 0x00 }, + { 0x0820, 0x0A }, + { 0x0821, 0x00 }, + { 0x0822, 0x00 }, + { 0x0823, 0x00 }, +}; + +static const struct imx258_reg mode_4208x3118_regs[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 0x3051, 0x00 }, + { 0x3052, 0x00 }, + { 0x4E21, 0x14 }, + { 0x6B11, 0xCF }, + { 0x7FF0, 0x08 }, + { 0x7FF1, 0x0F }, + { 0x7FF2, 0x08 }, + { 0x7FF3, 0x1B }, + { 0x7FF4, 0x23 }, + { 0x7FF5, 0x60 }, + { 0x7FF6, 0x00 }, + { 0x7FF7, 0x01 }, + { 0x7FF8, 0x00 }, + { 0x7FF9, 0x78 }, + { 0x7FFA, 0x00 }, + { 0x7FFB, 0x00 }, + { 0x7FFC, 0x00 }, + { 0x7FFD, 0x00 }, + { 0x7FFE, 0x00 }, + { 0x7FFF, 0x03 }, + { 0x7F76, 0x03 }, + { 0x7F77, 0xFE }, + { 0x7FA8, 0x03 }, + { 0x7FA9, 0xFE }, + { 0x7B24, 0x81 }, + { 0x7B25, 0x00 }, + { 0x6564, 0x07 }, + { 0x6B0D, 0x41 }, + { 0x653D, 0x04 }, + { 0x6B05, 0x8C }, + { 0x6B06, 0xF9 }, + { 0x6B08, 0x65 }, + { 0x6B09, 0xFC }, + { 0x6B0A, 0xCF }, + { 0x6B0B, 0xD2 }, + { 0x6700, 0x0E }, + { 0x6707, 0x0E }, + { 0x9104, 0x00 }, + { 0x4648, 0x7F }, + { 0x7420, 0x00 }, + { 0x7421, 0x1C }, + { 0x7422, 0x00 }, + { 0x7423, 0xD7 }, + { 0x5F04, 0x00 }, + { 0x5F05, 0xED }, + { 0x0112, 0x0A }, + { 0x0113, 0x0A }, + { 0x0114, 0x03 }, + { 0x0342, 0x14 }, + { 0x0343, 0xE8 }, + { 0x0340, 0x0C }, + { 0x0341, 0x50 }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x10 }, + { 0x0349, 0x6F }, + { 0x034A, 0x0C }, + { 0x034B, 0x2E }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0401, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x10 }, + { 0x0408, 0x00 }, + { 0x0409, 0x00 }, + { 0x040A, 0x00 }, + { 0x040B, 0x00 }, + { 0x040C, 0x10 }, + { 0x040D, 0x70 }, + { 0x040E, 0x0C }, + { 0x040F, 0x30 }, + { 0x3038, 0x00 }, + { 0x303A, 0x00 }, + { 0x303B, 0x10 }, + { 0x300D, 0x00 }, + { 0x034C, 0x10 }, + { 0x034D, 0x70 }, + { 0x034E, 0x0C }, + { 0x034F, 0x30 }, + { 0x0350, 0x01 }, + { 0x0202, 0x0C }, + { 0x0203, 0x46 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x020E, 0x01 }, + { 0x020F, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x7BCD, 0x00 }, + { 0x94DC, 0x20 }, + { 0x94DD, 0x20 }, + { 0x94DE, 0x20 }, + { 0x95DC, 0x20 }, + { 0x95DD, 0x20 }, + { 0x95DE, 0x20 }, + { 0x7FB0, 0x00 }, + { 0x9010, 0x3E }, + { 0x9419, 0x50 }, + { 0x941B, 0x50 }, + { 0x9519, 0x50 }, + { 0x951B, 0x50 }, + { 0x3030, 0x00 }, + { 0x3032, 0x00 }, + { 0x0220, 0x00 }, +}; + +static const struct imx258_reg mode_2104_1560_regs[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 0x3051, 0x00 }, + { 0x3052, 0x00 }, + { 0x4E21, 0x14 }, + { 0x6B11, 0xCF }, + { 0x7FF0, 0x08 }, + { 0x7FF1, 0x0F }, + { 0x7FF2, 0x08 }, + { 0x7FF3, 0x1B }, + { 0x7FF4, 0x23 }, + { 0x7FF5, 0x60 }, + { 0x7FF6, 0x00 }, + { 0x7FF7, 0x01 }, + { 0x7FF8, 0x00 }, + { 0x7FF9, 0x78 }, + { 0x7FFA, 0x00 }, + { 0x7FFB, 0x00 }, + { 0x7FFC, 0x00 }, + { 0x7FFD, 0x00 }, + { 0x7FFE, 0x00 }, + { 0x7FFF, 0x03 }, + { 0x7F76, 0x03 }, + { 0x7F77, 0xFE }, + { 0x7FA8, 0x03 }, + { 0x7FA9, 0xFE }, + { 0x7B24, 0x81 }, + { 0x7B25, 0x00 }, + { 0x6564, 0x07 }, + { 0x6B0D, 0x41 }, + { 0x653D, 0x04 }, + { 0x6B05, 0x8C }, + { 0x6B06, 0xF9 }, + { 0x6B08, 0x65 }, + { 0x6B09, 0xFC }, + { 0x6B0A, 0xCF }, + { 0x6B0B, 0xD2 }, + { 0x6700, 0x0E }, + { 0x6707, 0x0E }, + { 0x9104, 0x00 }, + { 0x4648, 0x7F }, + { 0x7420, 0x00 }, + { 0x7421, 0x1C }, + { 0x7422, 0x00 }, + { 0x7423, 0xD7 }, + { 0x5F04, 0x00 }, + { 0x5F05, 0xED }, + { 0x0112, 0x0A }, + { 0x0113, 0x0A }, + { 0x0114, 0x03 }, + { 0x0342, 0x14 }, + { 0x0343, 0xE8 }, + { 0x0340, 0x06 }, + { 0x0341, 0x38 }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x10 }, + { 0x0349, 0x6F }, + { 0x034A, 0x0C }, + { 0x034B, 0x2E }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x12 }, + { 0x0401, 0x01 }, + { 0x0404, 0x00 }, + { 0x0405, 0x20 }, + { 0x0408, 0x00 }, + { 0x0409, 0x02 }, + { 0x040A, 0x00 }, + { 0x040B, 0x00 }, + { 0x040C, 0x10 }, + { 0x040D, 0x6A }, + { 0x040E, 0x06 }, + { 0x040F, 0x18 }, + { 0x3038, 0x00 }, + { 0x303A, 0x00 }, + { 0x303B, 0x10 }, + { 0x300D, 0x00 }, + { 0x034C, 0x08 }, + { 0x034D, 0x38 }, + { 0x034E, 0x06 }, + { 0x034F, 0x18 }, + { 0x0350, 0x01 }, + { 0x0202, 0x06 }, + { 0x0203, 0x2E }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x020E, 0x01 }, + { 0x020F, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x7BCD, 0x01 }, + { 0x94DC, 0x20 }, + { 0x94DD, 0x20 }, + { 0x94DE, 0x20 }, + { 0x95DC, 0x20 }, + { 0x95DD, 0x20 }, + { 0x95DE, 0x20 }, + { 0x7FB0, 0x00 }, + { 0x9010, 0x3E }, + { 0x9419, 0x50 }, + { 0x941B, 0x50 }, + { 0x9519, 0x50 }, + { 0x951B, 0x50 }, + { 0x3030, 0x00 }, + { 0x3032, 0x00 }, + { 0x0220, 0x00 }, +}; + +static const struct imx258_reg mode_1048_780_regs[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 0x3051, 0x00 }, + { 0x3052, 0x00 }, + { 0x4E21, 0x14 }, + { 0x6B11, 0xCF }, + { 0x7FF0, 0x08 }, + { 0x7FF1, 0x0F }, + { 0x7FF2, 0x08 }, + { 0x7FF3, 0x1B }, + { 0x7FF4, 0x23 }, + { 0x7FF5, 0x60 }, + { 0x7FF6, 0x00 }, + { 0x7FF7, 0x01 }, + { 0x7FF8, 0x00 }, + { 0x7FF9, 0x78 }, + { 0x7FFA, 0x00 }, + { 0x7FFB, 0x00 }, + { 0x7FFC, 0x00 }, + { 0x7FFD, 0x00 }, + { 0x7FFE, 0x00 }, + { 0x7FFF, 0x03 }, + { 0x7F76, 0x03 }, + { 0x7F77, 0xFE }, + { 0x7FA8, 0x03 }, + { 0x7FA9, 0xFE }, + { 0x7B24, 0x81 }, + { 0x7B25, 0x00 }, + { 0x6564, 0x07 }, + { 0x6B0D, 0x41 }, + { 0x653D, 0x04 }, + { 0x6B05, 0x8C }, + { 0x6B06, 0xF9 }, + { 0x6B08, 0x65 }, + { 0x6B09, 0xFC }, + { 0x6B0A, 0xCF }, + { 0x6B0B, 0xD2 }, + { 0x6700, 0x0E }, + { 0x6707, 0x0E }, + { 0x9104, 0x00 }, + { 0x4648, 0x7F }, + { 0x7420, 0x00 }, + { 0x7421, 0x1C }, + { 0x7422, 0x00 }, + { 0x7423, 0xD7 }, + { 0x5F04, 0x00 }, + { 0x5F05, 0xED }, + { 0x0112, 0x0A }, + { 0x0113, 0x0A }, + { 0x0114, 0x03 }, + { 0x0342, 0x14 }, + { 0x0343, 0xE8 }, + { 0x0340, 0x03 }, + { 0x0341, 0x4C }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x10 }, + { 0x0349, 0x6F }, + { 0x034A, 0x0C }, + { 0x034B, 0x2E }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x14 }, + { 0x0401, 0x01 }, + { 0x0404, 0x00 }, + { 0x0405, 0x40 }, + { 0x0408, 0x00 }, + { 0x0409, 0x06 }, + { 0x040A, 0x00 }, + { 0x040B, 0x00 }, + { 0x040C, 0x10 }, + { 0x040D, 0x64 }, + { 0x040E, 0x03 }, + { 0x040F, 0x0C }, + { 0x3038, 0x00 }, + { 0x303A, 0x00 }, + { 0x303B, 0x10 }, + { 0x300D, 0x00 }, + { 0x034C, 0x04 }, + { 0x034D, 0x18 }, + { 0x034E, 0x03 }, + { 0x034F, 0x0C }, + { 0x0350, 0x01 }, + { 0x0202, 0x03 }, + { 0x0203, 0x42 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x020E, 0x01 }, + { 0x020F, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x7BCD, 0x00 }, + { 0x94DC, 0x20 }, + { 0x94DD, 0x20 }, + { 0x94DE, 0x20 }, + { 0x95DC, 0x20 }, + { 0x95DD, 0x20 }, + { 0x95DE, 0x20 }, + { 0x7FB0, 0x00 }, + { 0x9010, 0x3E }, + { 0x9419, 0x50 }, + { 0x941B, 0x50 }, + { 0x9519, 0x50 }, + { 0x951B, 0x50 }, + { 0x3030, 0x00 }, + { 0x3032, 0x00 }, + { 0x0220, 0x00 }, +}; + +static const char * const imx258_test_pattern_menu[] = { + "Disabled", + "Color Bars", + "Solid Color", + "Grey Color Bars", + "PN9" +}; + +static const int imx258_test_pattern_val[] = { + IMX258_TEST_PATTERN_DISABLE, + IMX258_TEST_PATTERN_COLOR_BARS, + IMX258_TEST_PATTERN_SOLID_COLOR, + IMX258_TEST_PATTERN_GREY_COLOR, + IMX258_TEST_PATTERN_PN9, +}; + +/* Configurations for supported link frequencies */ +#define IMX258_LINK_FREQ_634MHZ 633600000ULL +#define IMX258_LINK_FREQ_320MHZ 320000000ULL + +enum { + IMX258_LINK_FREQ_1267MBPS, + IMX258_LINK_FREQ_640MBPS, +}; + +/* + * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample + * data rate => double data rate; number of lanes => 4; bits per pixel => 10 + */ +static u64 link_freq_to_pixel_rate(u64 f) +{ + f *= 2 * 4; + do_div(f, 10); + + return f; +} + +/* Menu items for LINK_FREQ V4L2 control */ +static const s64 link_freq_menu_items[] = { + IMX258_LINK_FREQ_634MHZ, + IMX258_LINK_FREQ_320MHZ, +}; + +/* Link frequency configs */ +static const struct imx258_link_freq_config link_freq_configs[] = { + [IMX258_LINK_FREQ_1267MBPS] = { + .pixels_per_line = IMX258_PPL_DEFAULT, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_1267mbps), + .regs = mipi_data_rate_1267mbps, + } + }, + [IMX258_LINK_FREQ_640MBPS] = { + .pixels_per_line = IMX258_PPL_DEFAULT, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_640mbps), + .regs = mipi_data_rate_640mbps, + } + }, +}; + +/* Mode configs */ +static const struct imx258_mode supported_modes[] = { + { + .width = 4208, + .height = 3118, + .vts_def = IMX258_VTS_30FPS, + .vts_min = IMX258_VTS_30FPS, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_4208x3118_regs), + .regs = mode_4208x3118_regs, + }, + .link_freq_index = IMX258_LINK_FREQ_1267MBPS, + }, + { + .width = 2104, + .height = 1560, + .vts_def = IMX258_VTS_30FPS_2K, + .vts_min = IMX258_VTS_30FPS_2K, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2104_1560_regs), + .regs = mode_2104_1560_regs, + }, + .link_freq_index = IMX258_LINK_FREQ_640MBPS, + }, + { + .width = 1048, + .height = 780, + .vts_def = IMX258_VTS_30FPS_VGA, + .vts_min = IMX258_VTS_30FPS_VGA, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1048_780_regs), + .regs = mode_1048_780_regs, + }, + .link_freq_index = IMX258_LINK_FREQ_640MBPS, + }, +}; + +struct imx258 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct imx258_mode *cur_mode; + + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx258, sd); +} + +/* Read registers up to 2 at a time */ +static int imx258_read_reg(struct imx258 *imx258, u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2] = { reg >> 8, reg & 0xff }; + u8 data_buf[4] = { 0, }; + int ret; + + if (len > 4) + return -EINVAL; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +/* Write registers up to 2 at a time */ +static int imx258_write_reg(struct imx258 *imx258, u16 reg, u32 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/* Write a list of registers */ +static int imx258_write_regs(struct imx258 *imx258, + const struct imx258_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = imx258_write_reg(imx258, regs[i].address, 1, + regs[i].val); + if (ret) { + dev_err_ratelimited( + &client->dev, + "Failed to write reg 0x%4.4x. error = %d\n", + regs[i].address, ret); + + return ret; + } + } + + return 0; +} + +/* Open sub-device */ +static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + + /* Initialize try_fmt */ + try_fmt->width = supported_modes[0].width; + try_fmt->height = supported_modes[0].height; + try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + return 0; +} + +static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val) +{ + int ret; + + ret = imx258_write_reg(imx258, IMX258_REG_GR_DIGITAL_GAIN, + IMX258_REG_VALUE_16BIT, + val); + if (ret) + return ret; + ret = imx258_write_reg(imx258, IMX258_REG_GB_DIGITAL_GAIN, + IMX258_REG_VALUE_16BIT, + val); + if (ret) + return ret; + ret = imx258_write_reg(imx258, IMX258_REG_R_DIGITAL_GAIN, + IMX258_REG_VALUE_16BIT, + val); + if (ret) + return ret; + ret = imx258_write_reg(imx258, IMX258_REG_B_DIGITAL_GAIN, + IMX258_REG_VALUE_16BIT, + val); + if (ret) + return ret; + return 0; +} + +static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx258 *imx258 = + container_of(ctrl->handler, struct imx258, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + int ret = 0; + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (pm_runtime_get_if_in_use(&client->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = imx258_write_reg(imx258, IMX258_REG_ANALOG_GAIN, + IMX258_REG_VALUE_16BIT, + ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = imx258_write_reg(imx258, IMX258_REG_EXPOSURE, + IMX258_REG_VALUE_16BIT, + ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = imx258_update_digital_gain(imx258, IMX258_REG_VALUE_16BIT, + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN, + IMX258_REG_VALUE_16BIT, + imx258_test_pattern_val[ctrl->val]); + + ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, + IMX258_REG_VALUE_08BIT, + ctrl->val == imx258_test_pattern_val + [IMX258_TEST_PATTERN_DISABLE] ? + REG_CONFIG_MIRROR_FLIP : + REG_CONFIG_FLIP_TEST_PATTERN); + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx258_ctrl_ops = { + .s_ctrl = imx258_set_ctrl, +}; + +static int imx258_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + /* Only one bayer order(GRBG) is supported */ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int imx258_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void imx258_update_pad_format(const struct imx258_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.field = V4L2_FIELD_NONE; +} + +static int __imx258_get_pad_format(struct imx258 *imx258, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, cfg, + fmt->pad); + else + imx258_update_pad_format(imx258->cur_mode, fmt); + + return 0; +} + +static int imx258_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx258 *imx258 = to_imx258(sd); + int ret; + + mutex_lock(&imx258->mutex); + ret = __imx258_get_pad_format(imx258, cfg, fmt); + mutex_unlock(&imx258->mutex); + + return ret; +} + +static int imx258_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx258 *imx258 = to_imx258(sd); + const struct imx258_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + s32 vblank_def; + s32 vblank_min; + s64 h_blank; + s64 pixel_rate; + s64 link_freq; + + mutex_lock(&imx258->mutex); + + /* Only one raw bayer(GBRG) order is supported */ + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, height, + fmt->format.width, fmt->format.height); + imx258_update_pad_format(mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *framefmt = fmt->format; + } else { + imx258->cur_mode = mode; + __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index); + + link_freq = link_freq_menu_items[mode->link_freq_index]; + pixel_rate = link_freq_to_pixel_rate(link_freq); + __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate); + /* Update limits and set FPS to default */ + vblank_def = imx258->cur_mode->vts_def - + imx258->cur_mode->height; + vblank_min = imx258->cur_mode->vts_min - + imx258->cur_mode->height; + __v4l2_ctrl_modify_range( + imx258->vblank, vblank_min, + IMX258_VTS_MAX - imx258->cur_mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(imx258->vblank, vblank_def); + h_blank = + link_freq_configs[mode->link_freq_index].pixels_per_line + - imx258->cur_mode->width; + __v4l2_ctrl_modify_range(imx258->hblank, h_blank, + h_blank, 1, h_blank); + } + + mutex_unlock(&imx258->mutex); + + return 0; +} + +/* Start streaming */ +static int imx258_start_streaming(struct imx258 *imx258) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + const struct imx258_reg_list *reg_list; + int ret, link_freq_index; + + /* Setup PLL */ + link_freq_index = imx258->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "%s failed to set plls\n", __func__); + return ret; + } + + /* Apply default values of current mode */ + reg_list = &imx258->cur_mode->reg_list; + ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + return ret; + } + + /* Set Orientation be 180 degree */ + ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, + IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP); + if (ret) { + dev_err(&client->dev, "%s failed to set orientation\n", + __func__); + return ret; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler); + if (ret) + return ret; + + /* set stream on register */ + return imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, + IMX258_REG_VALUE_08BIT, + IMX258_MODE_STREAMING); +} + +/* Stop streaming */ +static int imx258_stop_streaming(struct imx258 *imx258) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + int ret; + + /* set stream off register */ + ret = imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, + IMX258_REG_VALUE_08BIT, IMX258_MODE_STANDBY); + if (ret) + dev_err(&client->dev, "%s failed to set stream\n", __func__); + + /* + * Return success even if it was an error, as there is nothing the + * caller can do about it. + */ + return 0; +} + +static int imx258_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx258 *imx258 = to_imx258(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&imx258->mutex); + if (imx258->streaming == enable) { + mutex_unlock(&imx258->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto err_unlock; + } + + /* + * Apply default & customized values + * and then start streaming. + */ + ret = imx258_start_streaming(imx258); + if (ret) + goto err_rpm_put; + } else { + imx258_stop_streaming(imx258); + pm_runtime_put(&client->dev); + } + + imx258->streaming = enable; + mutex_unlock(&imx258->mutex); + + return ret; + +err_rpm_put: + pm_runtime_put(&client->dev); +err_unlock: + mutex_unlock(&imx258->mutex); + + return ret; +} + +static int __maybe_unused imx258_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx258 *imx258 = to_imx258(sd); + + if (imx258->streaming) + imx258_stop_streaming(imx258); + + return 0; +} + +static int __maybe_unused imx258_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx258 *imx258 = to_imx258(sd); + int ret; + + if (imx258->streaming) { + ret = imx258_start_streaming(imx258); + if (ret) + goto error; + } + + return 0; + +error: + imx258_stop_streaming(imx258); + imx258->streaming = 0; + return ret; +} + +/* Verify chip ID */ +static int imx258_identify_module(struct imx258 *imx258) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + int ret; + u32 val; + + ret = imx258_read_reg(imx258, IMX258_REG_CHIP_ID, + IMX258_REG_VALUE_16BIT, &val); + if (ret) { + dev_err(&client->dev, "failed to read chip id %x\n", + IMX258_CHIP_ID); + return ret; + } + + if (val != IMX258_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + IMX258_CHIP_ID, val); + return -EIO; + } + + return 0; +} + +static const struct v4l2_subdev_video_ops imx258_video_ops = { + .s_stream = imx258_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx258_pad_ops = { + .enum_mbus_code = imx258_enum_mbus_code, + .get_fmt = imx258_get_pad_format, + .set_fmt = imx258_set_pad_format, + .enum_frame_size = imx258_enum_frame_size, +}; + +static const struct v4l2_subdev_ops imx258_subdev_ops = { + .video = &imx258_video_ops, + .pad = &imx258_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx258_internal_ops = { + .open = imx258_open, +}; + +/* Initialize control handlers */ +static int imx258_init_controls(struct imx258 *imx258) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max; + s64 vblank_def; + s64 vblank_min; + s64 pixel_rate_min; + s64 pixel_rate_max; + int ret; + + ctrl_hdlr = &imx258->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + mutex_init(&imx258->mutex); + ctrl_hdlr->lock = &imx258->mutex; + imx258->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &imx258_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, + link_freq_menu_items); + + if (imx258->link_freq) + imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); + pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]); + /* By default, PIXEL_RATE is read only */ + imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_PIXEL_RATE, + pixel_rate_min, pixel_rate_max, + 1, pixel_rate_max); + + + vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height; + vblank_min = imx258->cur_mode->vts_min - imx258->cur_mode->height; + imx258->vblank = v4l2_ctrl_new_std( + ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_VBLANK, + vblank_min, + IMX258_VTS_MAX - imx258->cur_mode->height, 1, + vblank_def); + + if (imx258->vblank) + imx258->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx258->hblank = v4l2_ctrl_new_std( + ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_HBLANK, + IMX258_PPL_DEFAULT - imx258->cur_mode->width, + IMX258_PPL_DEFAULT - imx258->cur_mode->width, + 1, + IMX258_PPL_DEFAULT - imx258->cur_mode->width); + + if (imx258->hblank) + imx258->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + exposure_max = imx258->cur_mode->vts_def - 8; + imx258->exposure = v4l2_ctrl_new_std( + ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_EXPOSURE, IMX258_EXPOSURE_MIN, + IMX258_EXPOSURE_MAX, IMX258_EXPOSURE_STEP, + IMX258_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX258_ANA_GAIN_MIN, IMX258_ANA_GAIN_MAX, + IMX258_ANA_GAIN_STEP, IMX258_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX258_DGTL_GAIN_MIN, IMX258_DGTL_GAIN_MAX, + IMX258_DGTL_GAIN_STEP, + IMX258_DGTL_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx258_test_pattern_menu) - 1, + 0, 0, imx258_test_pattern_menu); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + goto error; + } + + imx258->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&imx258->mutex); + + return ret; +} + +static void imx258_free_controls(struct imx258 *imx258) +{ + v4l2_ctrl_handler_free(imx258->sd.ctrl_handler); + mutex_destroy(&imx258->mutex); +} + +static int imx258_probe(struct i2c_client *client) +{ + struct imx258 *imx258; + int ret; + u32 val = 0; + + device_property_read_u32(&client->dev, "clock-frequency", &val); + if (val != 19200000) + return -EINVAL; + + imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL); + if (!imx258) + return -ENOMEM; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); + + /* Check module identity */ + ret = imx258_identify_module(imx258); + if (ret) + return ret; + + /* Set default mode to max resolution */ + imx258->cur_mode = &supported_modes[0]; + + ret = imx258_init_controls(imx258); + if (ret) + return ret; + + /* Initialize subdev */ + imx258->sd.internal_ops = &imx258_internal_ops; + imx258->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx258->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + imx258->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&imx258->sd.entity, 1, &imx258->pad); + if (ret) + goto error_handler_free; + + ret = v4l2_async_register_subdev_sensor_common(&imx258->sd); + if (ret < 0) + goto error_media_entity; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&imx258->sd.entity); + +error_handler_free: + imx258_free_controls(imx258); + + return ret; +} + +static int imx258_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx258 *imx258 = to_imx258(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + imx258_free_controls(imx258); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static const struct dev_pm_ops imx258_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx258_suspend, imx258_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id imx258_acpi_ids[] = { + { "SONY258A" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(acpi, imx258_acpi_ids); +#endif + +static struct i2c_driver imx258_i2c_driver = { + .driver = { + .name = "imx258", + .pm = &imx258_pm_ops, + .acpi_match_table = ACPI_PTR(imx258_acpi_ids), + }, + .probe_new = imx258_probe, + .remove = imx258_remove, +}; + +module_i2c_driver(imx258_i2c_driver); + +MODULE_AUTHOR("Yeh, Andy "); +MODULE_AUTHOR("Chiang, Alan "); +MODULE_AUTHOR("Chen, Jason "); +MODULE_DESCRIPTION("Sony IMX258 sensor driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 66a1e187a88fcceb84a390e6ea0c35e9f7a7f252 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 28 May 2018 13:56:37 -0400 Subject: media: imx258: get rid of an unused var drivers/media/i2c/imx258.c: In function 'imx258_init_controls': drivers/media/i2c/imx258.c:1117:6: warning: variable 'exposure_max' set but not used [-Wunused-but-set-variable] s64 exposure_max; ^~~~~~~~~~~~ Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx258.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index fad3012..f3b1247 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -1114,7 +1114,6 @@ static int imx258_init_controls(struct imx258 *imx258) { struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); struct v4l2_ctrl_handler *ctrl_hdlr; - s64 exposure_max; s64 vblank_def; s64 vblank_min; s64 pixel_rate_min; @@ -1168,7 +1167,6 @@ static int imx258_init_controls(struct imx258 *imx258) if (imx258->hblank) imx258->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - exposure_max = imx258->cur_mode->vts_def - 8; imx258->exposure = v4l2_ctrl_new_std( ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_EXPOSURE, IMX258_EXPOSURE_MIN, -- cgit v1.1 From a024ee14cd36dba55fd77322f5d48ee0e55418ae Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:17 -0400 Subject: media: ov772x: correct setting of banding filter The banding filter ON/OFF is controlled via bit 5 of COM8 register. It is attempted to be enabled in ov772x_set_params() by the following line. ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1); But this unexpectedly results disabling the banding filter, because the mask and set bits are exclusive. On the other hand, ov772x_s_ctrl() correctly sets the bit by: ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); Cc: Laurent Pinchart Cc: Hans Verkuil Acked-by: Jacopo Mondi Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index b62860c..e255070 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1035,7 +1035,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, /* Set COM8. */ if (priv->band_filter) { - ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1); + ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); if (!ret) ret = ov772x_mask_set(client, BDBASE, 0xff, 256 - priv->band_filter); -- cgit v1.1 From 00f6f92dbbeb3b98d38b26449be0df46b2e6d6a4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 May 2018 07:33:14 -0400 Subject: media: adv7511: fix incorrect clear of CEC receive interrupt If a CEC message was received and the RX interrupt was set, but not yet processed, and a new transmit was issues, then the transmit code would inadvertently clear the RX interrupt and after that no new messages would ever be received. Instead it should only clear TX interrupts since register 0x97 is a clear-on-write register. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7511.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/i2c') diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index d4b191c..5731751 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -831,8 +831,8 @@ static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, */ adv7511_cec_write_and_or(sd, 0x12, ~0x70, max(1, attempts - 1) << 4); - /* blocking, clear cec tx irq status */ - adv7511_wr_and_or(sd, 0x97, 0xc7, 0x38); + /* clear cec tx irq status */ + adv7511_wr(sd, 0x97, 0x38); /* write data */ for (i = 0; i < len; i++) -- cgit v1.1