aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-09-07 12:53:14 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-09-07 12:53:14 -0700
commitc0da4fa0d1a54495d6055c009ac46b76d1da2c86 (patch)
tree81b00d651c7fd8adf91984c69331074af2a71c32 /drivers/media/i2c
parentMerge tag 'sound-4.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound (diff)
parentmedia: leds: as3645a: add V4L2_FLASH_LED_CLASS dependency (diff)
downloadlinux-dev-c0da4fa0d1a54495d6055c009ac46b76d1da2c86.tar.xz
linux-dev-c0da4fa0d1a54495d6055c009ac46b76d1da2c86.zip
Merge tag 'media/v4.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: "Brazil's Independence Day pull request :-) This is one of the biggest media pull requests, with 625 patches affecting almost all parts of media (RC, DVB, V4L2, CEC, docs). This contains: - A lot of new drivers: * DVB frontends: mxl5xx, stv0910, stv6111; * camera flash: as3645a led driver; * HDMI receiver: adv748X; * camera sensor: Omnivision 6650 5M driver (ov6650); * HDMI CEC: ao-cec meson driver; * V4L2: Qualcom camss driver; * Remote controller: gpio-ir-tx, pwm-ir-tx and zx-irdec drivers. - The DDbridge DVB driver got a massive update, with makes it in sync with modern hardware from that vendor; - There's an important milestone on this series: the DVB documentation was written in 2003, but only started to be updated in 2007. It also used to contain several gaps from the time it was kept out of tree, mentioning error codes and device nodes that never existed upstream. On this series, it received a massive update: all non-deprecated digital TV APIs are now in sync with the current implementation; - Some DVB APIs that aren't used by any upstream driver got removed; - Other parts of the media documentation algo got updated, fixing some bugs on its PDF output and making it compatible with Sphinx version 1.6. As the number of hacks required to build PDF output reduced, I hope we'll have less troubles as newer versions of our documentation toolchain are released (famous last words); - As usual, lots of driver cleanups and improvements" * tag 'media/v4.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (624 commits) media: leds: as3645a: add V4L2_FLASH_LED_CLASS dependency media: get rid of removed DMX_GET_CAPS and DMX_SET_SOURCE leftovers media: Revert "[media] v4l: async: make v4l2 coexist with devicetree nodes in a dt overlay" media: staging: atomisp: sh_css_calloc shall return a pointer to the allocated space media: Revert "[media] lirc_dev: remove superfluous get/put_device() calls" media: add qcom_camss.rst to v4l-drivers rst file media: dvb headers: make checkpatch happier media: dvb uapi: move frontend legacy API to another part of the book media: pixfmt-srggb12p.rst: better format the table for PDF output media: docs-rst: media: Don't use \small for V4L2_PIX_FMT_SRGGB10 documentation media: index.rst: don't write "Contents:" on PDF output media: pixfmt*.rst: replace a two dots by a comma media: vidioc-g-fmt.rst: adjust table format media: vivid.rst: add a blank line to correct ReST format media: v4l2 uapi book: get rid of driver programming's chapter media: format.rst: use the right markup for important notes media: docs-rst: cardlists: change their format to flat-tables media: em28xx-cardlist.rst: update to reflect last changes media: v4l2-event.rst: adjust table to fit on PDF output media: docs: don't show ToC for each part on PDF output ...
Diffstat (limited to 'drivers/media/i2c')
-rw-r--r--drivers/media/i2c/Kconfig36
-rw-r--r--drivers/media/i2c/Makefile3
-rw-r--r--drivers/media/i2c/ad9389b.c2
-rw-r--r--drivers/media/i2c/adv7180.c2
-rw-r--r--drivers/media/i2c/adv748x/Makefile7
-rw-r--r--drivers/media/i2c/adv748x/adv748x-afe.c552
-rw-r--r--drivers/media/i2c/adv748x/adv748x-core.c833
-rw-r--r--drivers/media/i2c/adv748x/adv748x-csi2.c326
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c768
-rw-r--r--drivers/media/i2c/adv748x/adv748x.h425
-rw-r--r--drivers/media/i2c/adv7511.c5
-rw-r--r--drivers/media/i2c/adv7604.c7
-rw-r--r--drivers/media/i2c/adv7842.c5
-rw-r--r--drivers/media/i2c/dw9714.c26
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_driver.c26
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c59
-rw-r--r--drivers/media/i2c/m5mols/m5mols_core.c2
-rw-r--r--drivers/media/i2c/max2175.c2
-rw-r--r--drivers/media/i2c/mt9m111.c6
-rw-r--r--drivers/media/i2c/mt9t001.c8
-rw-r--r--drivers/media/i2c/ov13858.c101
-rw-r--r--drivers/media/i2c/ov5640.c3
-rw-r--r--drivers/media/i2c/ov5645.c49
-rw-r--r--drivers/media/i2c/ov5670.c2601
-rw-r--r--drivers/media/i2c/ov6650.c (renamed from drivers/media/i2c/soc_camera/ov6650.c)77
-rw-r--r--drivers/media/i2c/ov7670.c6
-rw-r--r--drivers/media/i2c/ov9650.c67
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c3
-rw-r--r--drivers/media/i2c/s5k5baf.c9
-rw-r--r--drivers/media/i2c/saa7127.c2
-rw-r--r--drivers/media/i2c/saa717x.c2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-core.c16
-rw-r--r--drivers/media/i2c/smiapp/smiapp-quirk.c8
-rw-r--r--drivers/media/i2c/soc_camera/Kconfig6
-rw-r--r--drivers/media/i2c/soc_camera/Makefile1
-rw-r--r--drivers/media/i2c/soc_camera/mt9t031.c2
-rw-r--r--drivers/media/i2c/tc358743.c2
-rw-r--r--drivers/media/i2c/ths8200.c2
-rw-r--r--drivers/media/i2c/vs6624.c2
39 files changed, 5825 insertions, 234 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 121b3b5394cb..94153895fcd4 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -204,6 +204,18 @@ config VIDEO_ADV7183
To compile this driver as a module, choose M here: the
module will be called adv7183.
+config VIDEO_ADV748X
+ tristate "Analog Devices ADV748x decoder"
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on OF
+ select REGMAP_I2C
+ ---help---
+ V4L2 subdevice driver for the Analog Devices
+ ADV7481 and ADV7482 HDMI/Analog video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv748x.
+
config VIDEO_ADV7604
tristate "Analog Devices ADV7604 decoder"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
@@ -593,6 +605,30 @@ config VIDEO_OV5647
To compile this driver as a module, choose M here: the
module will be called ov5647.
+config VIDEO_OV6650
+ tristate "OmniVision OV6650 sensor support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV6650 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov6650.
+
+config VIDEO_OV5670
+ tristate "OmniVision OV5670 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ depends on MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV5670 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov5670.
+
config VIDEO_OV7640
tristate "OmniVision OV7640 sensor support"
depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 2c0868fa6034..c843c181dfb9 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o
+obj-$(CONFIG_VIDEO_ADV748X) += adv748x/
obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o
obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o
obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o
@@ -62,6 +63,8 @@ obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
+obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
+obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c
index 50f354144ee7..a056d6cdaaaa 100644
--- a/drivers/media/i2c/ad9389b.c
+++ b/drivers/media/i2c/ad9389b.c
@@ -1208,7 +1208,7 @@ static int ad9389b_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static struct i2c_device_id ad9389b_id[] = {
+static const struct i2c_device_id ad9389b_id[] = {
{ "ad9389b", 0 },
{ "ad9889b", 0 },
{ }
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index 78de7ddf5081..3df28f2f9b38 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -1402,6 +1402,8 @@ static int adv7180_remove(struct i2c_client *client)
static const struct i2c_device_id adv7180_id[] = {
{ "adv7180", (kernel_ulong_t)&adv7180_info },
+ { "adv7180cp", (kernel_ulong_t)&adv7180_info },
+ { "adv7180st", (kernel_ulong_t)&adv7180_info },
{ "adv7182", (kernel_ulong_t)&adv7182_info },
{ "adv7280", (kernel_ulong_t)&adv7280_info },
{ "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
diff --git a/drivers/media/i2c/adv748x/Makefile b/drivers/media/i2c/adv748x/Makefile
new file mode 100644
index 000000000000..c0711e076f1d
--- /dev/null
+++ b/drivers/media/i2c/adv748x/Makefile
@@ -0,0 +1,7 @@
+adv748x-objs := \
+ adv748x-afe.o \
+ adv748x-core.o \
+ adv748x-csi2.o \
+ adv748x-hdmi.o
+
+obj-$(CONFIG_VIDEO_ADV748X) += adv748x.o
diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c
new file mode 100644
index 000000000000..b33ccfc08708
--- /dev/null
+++ b/drivers/media/i2c/adv748x/adv748x-afe.c
@@ -0,0 +1,552 @@
+/*
+ * Driver for Analog Devices ADV748X 8 channel analog front end (AFE) receiver
+ * with standard definition processor (SDP)
+ *
+ * Copyright (C) 2017 Renesas Electronics Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/v4l2-dv-timings.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+
+#include "adv748x.h"
+
+/* -----------------------------------------------------------------------------
+ * SDP
+ */
+
+#define ADV748X_AFE_STD_AD_PAL_BG_NTSC_J_SECAM 0x0
+#define ADV748X_AFE_STD_AD_PAL_BG_NTSC_J_SECAM_PED 0x1
+#define ADV748X_AFE_STD_AD_PAL_N_NTSC_J_SECAM 0x2
+#define ADV748X_AFE_STD_AD_PAL_N_NTSC_M_SECAM 0x3
+#define ADV748X_AFE_STD_NTSC_J 0x4
+#define ADV748X_AFE_STD_NTSC_M 0x5
+#define ADV748X_AFE_STD_PAL60 0x6
+#define ADV748X_AFE_STD_NTSC_443 0x7
+#define ADV748X_AFE_STD_PAL_BG 0x8
+#define ADV748X_AFE_STD_PAL_N 0x9
+#define ADV748X_AFE_STD_PAL_M 0xa
+#define ADV748X_AFE_STD_PAL_M_PED 0xb
+#define ADV748X_AFE_STD_PAL_COMB_N 0xc
+#define ADV748X_AFE_STD_PAL_COMB_N_PED 0xd
+#define ADV748X_AFE_STD_PAL_SECAM 0xe
+#define ADV748X_AFE_STD_PAL_SECAM_PED 0xf
+
+static int adv748x_afe_read_ro_map(struct adv748x_state *state, u8 reg)
+{
+ int ret;
+
+ /* Select SDP Read-Only Main Map */
+ ret = sdp_write(state, ADV748X_SDP_MAP_SEL,
+ ADV748X_SDP_MAP_SEL_RO_MAIN);
+ if (ret < 0)
+ return ret;
+
+ return sdp_read(state, reg);
+}
+
+static int adv748x_afe_status(struct adv748x_afe *afe, u32 *signal,
+ v4l2_std_id *std)
+{
+ struct adv748x_state *state = adv748x_afe_to_state(afe);
+ int info;
+
+ /* Read status from reg 0x10 of SDP RO Map */
+ info = adv748x_afe_read_ro_map(state, ADV748X_SDP_RO_10);
+ if (info < 0)
+ return info;
+
+ if (signal)
+ *signal = info & ADV748X_SDP_RO_10_IN_LOCK ?
+ 0 : V4L2_IN_ST_NO_SIGNAL;
+
+ if (!std)
+ return 0;
+
+ /* Standard not valid if there is no signal */
+ if (!(info & ADV748X_SDP_RO_10_IN_LOCK)) {
+ *std = V4L2_STD_UNKNOWN;
+ return 0;
+ }
+
+ switch (info & 0x70) {
+ case 0x00:
+ *std = V4L2_STD_NTSC;
+ break;
+ case 0x10:
+ *std = V4L2_STD_NTSC_443;
+ break;
+ case 0x20:
+ *std = V4L2_STD_PAL_M;
+ break;
+ case 0x30:
+ *std = V4L2_STD_PAL_60;
+ break;
+ case 0x40:
+ *std = V4L2_STD_PAL;
+ break;
+ case 0x50:
+ *std = V4L2_STD_SECAM;
+ break;
+ case 0x60:
+ *std = V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
+ break;
+ case 0x70:
+ *std = V4L2_STD_SECAM;
+ break;
+ default:
+ *std = V4L2_STD_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static void adv748x_afe_fill_format(struct adv748x_afe *afe,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ memset(fmt, 0, sizeof(*fmt));
+
+ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->field = V4L2_FIELD_ALTERNATE;
+
+ fmt->width = 720;
+ fmt->height = afe->curr_norm & V4L2_STD_525_60 ? 480 : 576;
+
+ /* Field height */
+ fmt->height /= 2;
+}
+
+static int adv748x_afe_std(v4l2_std_id std)
+{
+ if (std == V4L2_STD_PAL_60)
+ return ADV748X_AFE_STD_PAL60;
+ if (std == V4L2_STD_NTSC_443)
+ return ADV748X_AFE_STD_NTSC_443;
+ if (std == V4L2_STD_PAL_N)
+ return ADV748X_AFE_STD_PAL_N;
+ if (std == V4L2_STD_PAL_M)
+ return ADV748X_AFE_STD_PAL_M;
+ if (std == V4L2_STD_PAL_Nc)
+ return ADV748X_AFE_STD_PAL_COMB_N;
+ if (std & V4L2_STD_NTSC)
+ return ADV748X_AFE_STD_NTSC_M;
+ if (std & V4L2_STD_PAL)
+ return ADV748X_AFE_STD_PAL_BG;
+ if (std & V4L2_STD_SECAM)
+ return ADV748X_AFE_STD_PAL_SECAM;
+
+ return -EINVAL;
+}
+
+static void adv748x_afe_set_video_standard(struct adv748x_state *state,
+ int sdpstd)
+{
+ sdp_clrset(state, ADV748X_SDP_VID_SEL, ADV748X_SDP_VID_SEL_MASK,
+ (sdpstd & 0xf) << ADV748X_SDP_VID_SEL_SHIFT);
+}
+
+static int adv748x_afe_s_input(struct adv748x_afe *afe, unsigned int input)
+{
+ struct adv748x_state *state = adv748x_afe_to_state(afe);
+
+ return sdp_write(state, ADV748X_SDP_INSEL, input);
+}
+
+static int adv748x_afe_g_pixelaspect(struct v4l2_subdev *sd,
+ struct v4l2_fract *aspect)
+{
+ struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
+
+ if (afe->curr_norm & V4L2_STD_525_60) {
+ aspect->numerator = 11;
+ aspect->denominator = 10;
+ } else {
+ aspect->numerator = 54;
+ aspect->denominator = 59;
+ }
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_video_ops
+ */
+
+static int adv748x_afe_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+ struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
+
+ *norm = afe->curr_norm;
+
+ return 0;
+}
+
+static int adv748x_afe_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
+ struct adv748x_state *state = adv748x_afe_to_state(afe);
+ int afe_std = adv748x_afe_std(std);
+
+ if (afe_std < 0)
+ return afe_std;
+
+ mutex_lock(&state->mutex);
+
+ adv748x_afe_set_video_standard(state, afe_std);
+ afe->curr_norm = std;
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static int adv748x_afe_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
+ struct adv748x_state *state = adv748x_afe_to_state(afe);
+ int ret;
+
+ mutex_lock(&state->mutex);
+
+ if (afe->streaming) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ /* Set auto detect mode */
+ adv748x_afe_set_video_standard(state,
+ ADV748X_AFE_STD_AD_PAL_BG_NTSC_J_SECAM);
+
+ msleep(100);
+
+ /* Read detected standard */
+ ret = adv748x_afe_status(afe, NULL, std);
+
+ /* Restore original state */
+ adv748x_afe_set_video_standard(state, afe->curr_norm);
+
+unlock:
+ mutex_unlock(&state->mutex);
+
+ return ret;
+}
+
+static int adv748x_afe_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+ *norm = V4L2_STD_ALL;
+
+ return 0;
+}
+
+static int adv748x_afe_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
+ struct adv748x_state *state = adv748x_afe_to_state(afe);
+ int ret;
+
+ mutex_lock(&state->mutex);
+
+ ret = adv748x_afe_status(afe, status, NULL);
+
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
+static int adv748x_afe_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
+ struct adv748x_state *state = adv748x_afe_to_state(afe);
+ int ret, signal = V4L2_IN_ST_NO_SIGNAL;
+
+ mutex_lock(&state->mutex);
+
+ if (enable) {
+ ret = adv748x_afe_s_input(afe, afe->input);
+ if (ret)
+ goto unlock;
+ }
+
+ ret = adv748x_txb_power(state, enable);
+ if (ret)
+ goto unlock;
+
+ afe->streaming = enable;
+
+ adv748x_afe_status(afe, &signal, NULL);
+ if (signal != V4L2_IN_ST_NO_SIGNAL)
+ adv_dbg(state, "Detected SDP signal\n");
+ else
+ adv_dbg(state, "Couldn't detect SDP video signal\n");
+
+unlock:
+ mutex_unlock(&state->mutex);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops adv748x_afe_video_ops = {
+ .g_std = adv748x_afe_g_std,
+ .s_std = adv748x_afe_s_std,
+ .querystd = adv748x_afe_querystd,
+ .g_tvnorms = adv748x_afe_g_tvnorms,
+ .g_input_status = adv748x_afe_g_input_status,
+ .s_stream = adv748x_afe_s_stream,
+ .g_pixelaspect = adv748x_afe_g_pixelaspect,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_pad_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);
+}
+
+static int adv748x_afe_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_UYVY8_2X8;
+
+ return 0;
+}
+
+static int adv748x_afe_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
+ struct v4l2_mbus_framefmt *mbusformat;
+
+ /* It makes no sense to get the format of the analog sink pads */
+ if (sdformat->pad != ADV748X_AFE_SOURCE)
+ return -EINVAL;
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad);
+ sdformat->format = *mbusformat;
+ } else {
+ adv748x_afe_fill_format(afe, &sdformat->format);
+ adv748x_afe_propagate_pixelrate(afe);
+ }
+
+ return 0;
+}
+
+static int adv748x_afe_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct v4l2_mbus_framefmt *mbusformat;
+
+ /* It makes no sense to get the format of the analog sink pads */
+ if (sdformat->pad != ADV748X_AFE_SOURCE)
+ return -EINVAL;
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ return adv748x_afe_get_format(sd, cfg, sdformat);
+
+ mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad);
+ *mbusformat = sdformat->format;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops adv748x_afe_pad_ops = {
+ .enum_mbus_code = adv748x_afe_enum_mbus_code,
+ .set_fmt = adv748x_afe_set_format,
+ .get_fmt = adv748x_afe_get_format,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_ops
+ */
+
+static const struct v4l2_subdev_ops adv748x_afe_ops = {
+ .video = &adv748x_afe_video_ops,
+ .pad = &adv748x_afe_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+static const char * const afe_ctrl_frp_menu[] = {
+ "Disabled",
+ "Solid Blue",
+ "Color Bars",
+ "Grey Ramp",
+ "Cb Ramp",
+ "Cr Ramp",
+ "Boundary"
+};
+
+static int adv748x_afe_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct adv748x_afe *afe = adv748x_ctrl_to_afe(ctrl);
+ struct adv748x_state *state = adv748x_afe_to_state(afe);
+ bool enable;
+ int ret;
+
+ ret = sdp_write(state, 0x0e, 0x00);
+ if (ret < 0)
+ return ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ret = sdp_write(state, ADV748X_SDP_BRI, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ /* Hue is inverted according to HSL chart */
+ ret = sdp_write(state, ADV748X_SDP_HUE, -ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ ret = sdp_write(state, ADV748X_SDP_CON, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ ret = sdp_write(state, ADV748X_SDP_SD_SAT_U, ctrl->val);
+ if (ret)
+ break;
+ ret = sdp_write(state, ADV748X_SDP_SD_SAT_V, ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ enable = !!ctrl->val;
+
+ /* Enable/Disable Color bar test patterns */
+ ret = sdp_clrset(state, ADV748X_SDP_DEF, ADV748X_SDP_DEF_VAL_EN,
+ enable);
+ if (ret)
+ break;
+ ret = sdp_clrset(state, ADV748X_SDP_FRP, ADV748X_SDP_FRP_MASK,
+ enable ? ctrl->val - 1 : 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops adv748x_afe_ctrl_ops = {
+ .s_ctrl = adv748x_afe_s_ctrl,
+};
+
+static int adv748x_afe_init_controls(struct adv748x_afe *afe)
+{
+ struct adv748x_state *state = adv748x_afe_to_state(afe);
+
+ v4l2_ctrl_handler_init(&afe->ctrl_hdl, 5);
+
+ /* Use our mutex for the controls */
+ afe->ctrl_hdl.lock = &state->mutex;
+
+ v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, ADV748X_SDP_BRI_MIN,
+ ADV748X_SDP_BRI_MAX, 1, ADV748X_SDP_BRI_DEF);
+ v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
+ V4L2_CID_CONTRAST, ADV748X_SDP_CON_MIN,
+ ADV748X_SDP_CON_MAX, 1, ADV748X_SDP_CON_DEF);
+ v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
+ V4L2_CID_SATURATION, ADV748X_SDP_SAT_MIN,
+ ADV748X_SDP_SAT_MAX, 1, ADV748X_SDP_SAT_DEF);
+ v4l2_ctrl_new_std(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
+ V4L2_CID_HUE, ADV748X_SDP_HUE_MIN,
+ ADV748X_SDP_HUE_MAX, 1, ADV748X_SDP_HUE_DEF);
+
+ v4l2_ctrl_new_std_menu_items(&afe->ctrl_hdl, &adv748x_afe_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(afe_ctrl_frp_menu) - 1,
+ 0, 0, afe_ctrl_frp_menu);
+
+ afe->sd.ctrl_handler = &afe->ctrl_hdl;
+ if (afe->ctrl_hdl.error) {
+ v4l2_ctrl_handler_free(&afe->ctrl_hdl);
+ return afe->ctrl_hdl.error;
+ }
+
+ return v4l2_ctrl_handler_setup(&afe->ctrl_hdl);
+}
+
+int adv748x_afe_init(struct adv748x_afe *afe)
+{
+ struct adv748x_state *state = adv748x_afe_to_state(afe);
+ int ret;
+ unsigned int i;
+
+ afe->input = 0;
+ afe->streaming = false;
+ afe->curr_norm = V4L2_STD_NTSC_M;
+
+ adv748x_subdev_init(&afe->sd, state, &adv748x_afe_ops,
+ MEDIA_ENT_F_ATV_DECODER, "afe");
+
+ /* Identify the first connector found as a default input if set */
+ for (i = ADV748X_PORT_AIN0; i <= ADV748X_PORT_AIN7; i++) {
+ /* Inputs and ports are 1-indexed to match the data sheet */
+ if (state->endpoints[i]) {
+ afe->input = i;
+ break;
+ }
+ }
+
+ adv748x_afe_s_input(afe, afe->input);
+
+ adv_dbg(state, "AFE Default input set to %d\n", afe->input);
+
+ /* Entity pads and sinks are 0-indexed to match the pads */
+ for (i = ADV748X_AFE_SINK_AIN0; i <= ADV748X_AFE_SINK_AIN7; i++)
+ afe->pads[i].flags = MEDIA_PAD_FL_SINK;
+
+ afe->pads[ADV748X_AFE_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&afe->sd.entity, ADV748X_AFE_NR_PADS,
+ afe->pads);
+ if (ret)
+ return ret;
+
+ ret = adv748x_afe_init_controls(afe);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ media_entity_cleanup(&afe->sd.entity);
+
+ return ret;
+}
+
+void adv748x_afe_cleanup(struct adv748x_afe *afe)
+{
+ v4l2_device_unregister_subdev(&afe->sd);
+ media_entity_cleanup(&afe->sd.entity);
+ v4l2_ctrl_handler_free(&afe->ctrl_hdl);
+}
diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c
new file mode 100644
index 000000000000..5ee14f2c2747
--- /dev/null
+++ b/drivers/media/i2c/adv748x/adv748x-core.c
@@ -0,0 +1,833 @@
+/*
+ * Driver for Analog Devices ADV748X HDMI receiver with AFE
+ *
+ * Copyright (C) 2017 Renesas Electronics Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Authors:
+ * Koji Matsuoka <koji.matsuoka.xm@renesas.com>
+ * Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ * Kieran Bingham <kieran.bingham@ideasonboard.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/v4l2-dv-timings.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+
+#include "adv748x.h"
+
+/* -----------------------------------------------------------------------------
+ * Register manipulation
+ */
+
+static const struct regmap_config adv748x_regmap_cnf[] = {
+ {
+ .name = "io",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "dpll",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "cp",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "hdmi",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "edid",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "repeater",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "infoframe",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "cec",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "sdp",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+
+ {
+ .name = "txb",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+ {
+ .name = "txa",
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ },
+};
+
+static int adv748x_configure_regmap(struct adv748x_state *state, int region)
+{
+ int err;
+
+ if (!state->i2c_clients[region])
+ return -ENODEV;
+
+ state->regmap[region] =
+ devm_regmap_init_i2c(state->i2c_clients[region],
+ &adv748x_regmap_cnf[region]);
+
+ if (IS_ERR(state->regmap[region])) {
+ err = PTR_ERR(state->regmap[region]);
+ adv_err(state,
+ "Error initializing regmap %d with error %d\n",
+ region, err);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Default addresses for the I2C pages */
+static int adv748x_i2c_addresses[ADV748X_PAGE_MAX] = {
+ ADV748X_I2C_IO,
+ ADV748X_I2C_DPLL,
+ ADV748X_I2C_CP,
+ ADV748X_I2C_HDMI,
+ ADV748X_I2C_EDID,
+ ADV748X_I2C_REPEATER,
+ ADV748X_I2C_INFOFRAME,
+ ADV748X_I2C_CEC,
+ ADV748X_I2C_SDP,
+ ADV748X_I2C_TXB,
+ ADV748X_I2C_TXA,
+};
+
+static int adv748x_read_check(struct adv748x_state *state,
+ int client_page, u8 reg)
+{
+ struct i2c_client *client = state->i2c_clients[client_page];
+ int err;
+ unsigned int val;
+
+ err = regmap_read(state->regmap[client_page], reg, &val);
+
+ if (err) {
+ adv_err(state, "error reading %02x, %02x\n",
+ client->addr, reg);
+ return err;
+ }
+
+ return val;
+}
+
+int adv748x_read(struct adv748x_state *state, u8 page, u8 reg)
+{
+ return adv748x_read_check(state, page, reg);
+}
+
+int adv748x_write(struct adv748x_state *state, u8 page, u8 reg, u8 value)
+{
+ return regmap_write(state->regmap[page], reg, value);
+}
+
+/* adv748x_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX
+ * size to one or more registers.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int adv748x_write_block(struct adv748x_state *state, int client_page,
+ unsigned int init_reg, const void *val,
+ size_t val_len)
+{
+ struct regmap *regmap = state->regmap[client_page];
+
+ if (val_len > I2C_SMBUS_BLOCK_MAX)
+ val_len = I2C_SMBUS_BLOCK_MAX;
+
+ return regmap_raw_write(regmap, init_reg, val, val_len);
+}
+
+static struct i2c_client *adv748x_dummy_client(struct adv748x_state *state,
+ u8 addr, u8 io_reg)
+{
+ struct i2c_client *client = state->client;
+
+ if (addr)
+ io_write(state, io_reg, addr << 1);
+
+ return i2c_new_dummy(client->adapter, io_read(state, io_reg) >> 1);
+}
+
+static void adv748x_unregister_clients(struct adv748x_state *state)
+{
+ unsigned int i;
+
+ for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) {
+ if (state->i2c_clients[i])
+ i2c_unregister_device(state->i2c_clients[i]);
+ }
+}
+
+static int adv748x_initialise_clients(struct adv748x_state *state)
+{
+ int i;
+ int ret;
+
+ for (i = ADV748X_PAGE_DPLL; i < ADV748X_PAGE_MAX; ++i) {
+ state->i2c_clients[i] =
+ adv748x_dummy_client(state, adv748x_i2c_addresses[i],
+ ADV748X_IO_SLAVE_ADDR_BASE + i);
+ if (state->i2c_clients[i] == NULL) {
+ adv_err(state, "failed to create i2c client %u\n", i);
+ return -ENOMEM;
+ }
+
+ ret = adv748x_configure_regmap(state, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * struct adv748x_reg_value - Register write instruction
+ * @page: Regmap page identifier
+ * @reg: I2C register
+ * @value: value to write to @page at @reg
+ */
+struct adv748x_reg_value {
+ u8 page;
+ u8 reg;
+ u8 value;
+};
+
+static int adv748x_write_regs(struct adv748x_state *state,
+ const struct adv748x_reg_value *regs)
+{
+ int ret;
+
+ while (regs->page != ADV748X_PAGE_EOR) {
+ if (regs->page == ADV748X_PAGE_WAIT) {
+ msleep(regs->value);
+ } else {
+ ret = adv748x_write(state, regs->page, regs->reg,
+ regs->value);
+ if (ret < 0) {
+ adv_err(state,
+ "Error regs page: 0x%02x reg: 0x%02x\n",
+ regs->page, regs->reg);
+ return ret;
+ }
+ }
+ regs++;
+ }
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * TXA and TXB
+ */
+
+static const struct adv748x_reg_value adv748x_power_up_txa_4lane[] = {
+
+ {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */
+ {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */
+
+ {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+ {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */
+ {ADV748X_PAGE_TXA, 0x00, 0x24 },/* Power-up CSI-TX */
+ {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
+ {ADV748X_PAGE_TXA, 0xc1, 0x2b}, /* ADI Required Write */
+ {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
+ {ADV748X_PAGE_TXA, 0x31, 0x80}, /* ADI Required Write */
+
+ {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
+};
+
+static const struct adv748x_reg_value adv748x_power_down_txa_4lane[] = {
+
+ {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0x1e, 0x00}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */
+ {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+ {ADV748X_PAGE_TXA, 0xc1, 0x3b}, /* ADI Required Write */
+
+ {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
+};
+
+static const struct adv748x_reg_value adv748x_power_up_txb_1lane[] = {
+
+ {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */
+ {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */
+
+ {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */
+ {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */
+ {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+ {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */
+ {ADV748X_PAGE_TXB, 0x00, 0x21 },/* Power-up CSI-TX */
+ {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
+ {ADV748X_PAGE_TXB, 0xc1, 0x2b}, /* ADI Required Write */
+ {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
+ {ADV748X_PAGE_TXB, 0x31, 0x80}, /* ADI Required Write */
+
+ {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
+};
+
+static const struct adv748x_reg_value adv748x_power_down_txb_1lane[] = {
+
+ {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */
+ {ADV748X_PAGE_TXB, 0x1e, 0x00}, /* ADI Required Write */
+ {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 4-lane MIPI */
+ {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+ {ADV748X_PAGE_TXB, 0xc1, 0x3b}, /* ADI Required Write */
+
+ {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
+};
+
+int adv748x_txa_power(struct adv748x_state *state, bool on)
+{
+ int val;
+
+ val = txa_read(state, ADV748X_CSI_FS_AS_LS);
+ if (val < 0)
+ return val;
+
+ /*
+ * This test against BIT(6) is not documented by the datasheet, but was
+ * specified in the downstream driver.
+ * Track with a WARN_ONCE to determine if it is ever set by HW.
+ */
+ WARN_ONCE((on && val & ADV748X_CSI_FS_AS_LS_UNKNOWN),
+ "Enabling with unknown bit set");
+
+ if (on)
+ return adv748x_write_regs(state, adv748x_power_up_txa_4lane);
+
+ return adv748x_write_regs(state, adv748x_power_down_txa_4lane);
+}
+
+int adv748x_txb_power(struct adv748x_state *state, bool on)
+{
+ int val;
+
+ val = txb_read(state, ADV748X_CSI_FS_AS_LS);
+ if (val < 0)
+ return val;
+
+ /*
+ * This test against BIT(6) is not documented by the datasheet, but was
+ * specified in the downstream driver.
+ * Track with a WARN_ONCE to determine if it is ever set by HW.
+ */
+ WARN_ONCE((on && val & ADV748X_CSI_FS_AS_LS_UNKNOWN),
+ "Enabling with unknown bit set");
+
+ if (on)
+ return adv748x_write_regs(state, adv748x_power_up_txb_1lane);
+
+ return adv748x_write_regs(state, adv748x_power_down_txb_1lane);
+}
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+
+static const struct media_entity_operations adv748x_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* -----------------------------------------------------------------------------
+ * HW setup
+ */
+
+static const struct adv748x_reg_value adv748x_sw_reset[] = {
+
+ {ADV748X_PAGE_IO, 0xff, 0xff}, /* SW reset */
+ {ADV748X_PAGE_WAIT, 0x00, 0x05},/* delay 5 */
+ {ADV748X_PAGE_IO, 0x01, 0x76}, /* ADI Required Write */
+ {ADV748X_PAGE_IO, 0xf2, 0x01}, /* Enable I2C Read Auto-Increment */
+ {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
+};
+
+static const struct adv748x_reg_value adv748x_set_slave_address[] = {
+ {ADV748X_PAGE_IO, 0xf3, ADV748X_I2C_DPLL << 1},
+ {ADV748X_PAGE_IO, 0xf4, ADV748X_I2C_CP << 1},
+ {ADV748X_PAGE_IO, 0xf5, ADV748X_I2C_HDMI << 1},
+ {ADV748X_PAGE_IO, 0xf6, ADV748X_I2C_EDID << 1},
+ {ADV748X_PAGE_IO, 0xf7, ADV748X_I2C_REPEATER << 1},
+ {ADV748X_PAGE_IO, 0xf8, ADV748X_I2C_INFOFRAME << 1},
+ {ADV748X_PAGE_IO, 0xfa, ADV748X_I2C_CEC << 1},
+ {ADV748X_PAGE_IO, 0xfb, ADV748X_I2C_SDP << 1},
+ {ADV748X_PAGE_IO, 0xfc, ADV748X_I2C_TXB << 1},
+ {ADV748X_PAGE_IO, 0xfd, ADV748X_I2C_TXA << 1},
+ {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
+};
+
+/* Supported Formats For Script Below */
+/* - 01-29 HDMI to MIPI TxA CSI 4-Lane - RGB888: */
+static const struct adv748x_reg_value adv748x_init_txa_4lane[] = {
+ /* Disable chip powerdown & Enable HDMI Rx block */
+ {ADV748X_PAGE_IO, 0x00, 0x40},
+
+ {ADV748X_PAGE_REPEATER, 0x40, 0x83}, /* Enable HDCP 1.1 */
+
+ {ADV748X_PAGE_HDMI, 0x00, 0x08},/* Foreground Channel = A */
+ {ADV748X_PAGE_HDMI, 0x98, 0xff},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x99, 0xa3},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x9a, 0x00},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x9b, 0x0a},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x9d, 0x40},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0xcb, 0x09},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x3d, 0x10},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x3e, 0x7b},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x3f, 0x5e},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x4e, 0xfe},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x4f, 0x18},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x57, 0xa3},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x58, 0x04},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0x85, 0x10},/* ADI Required Write */
+
+ {ADV748X_PAGE_HDMI, 0x83, 0x00},/* Enable All Terminations */
+ {ADV748X_PAGE_HDMI, 0xa3, 0x01},/* ADI Required Write */
+ {ADV748X_PAGE_HDMI, 0xbe, 0x00},/* ADI Required Write */
+
+ {ADV748X_PAGE_HDMI, 0x6c, 0x01},/* HPA Manual Enable */
+ {ADV748X_PAGE_HDMI, 0xf8, 0x01},/* HPA Asserted */
+ {ADV748X_PAGE_HDMI, 0x0f, 0x00},/* Audio Mute Speed Set to Fastest */
+ /* (Smallest Step Size) */
+
+ {ADV748X_PAGE_IO, 0x04, 0x02}, /* RGB Out of CP */
+ {ADV748X_PAGE_IO, 0x12, 0xf0}, /* CSC Depends on ip Packets, SDR 444 */
+ {ADV748X_PAGE_IO, 0x17, 0x80}, /* Luma & Chroma can reach 254d */
+ {ADV748X_PAGE_IO, 0x03, 0x86}, /* CP-Insert_AV_Code */
+
+ {ADV748X_PAGE_CP, 0x7c, 0x00}, /* ADI Required Write */
+
+ {ADV748X_PAGE_IO, 0x0c, 0xe0}, /* Enable LLC_DLL & Double LLC Timing */
+ {ADV748X_PAGE_IO, 0x0e, 0xdd}, /* LLC/PIX/SPI PINS TRISTATED AUD */
+ /* Outputs Enabled */
+ {ADV748X_PAGE_IO, 0x10, 0xa0}, /* Enable 4-lane CSI Tx & Pixel Port */
+
+ {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */
+ {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */
+ {ADV748X_PAGE_TXA, 0xdb, 0x10}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0xd6, 0x07}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0xc4, 0x0a}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0x71, 0x33}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0x72, 0x11}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */
+
+ {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */
+ {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+ {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */
+ {ADV748X_PAGE_TXA, 0x00, 0x24 },/* Power-up CSI-TX */
+ {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
+ {ADV748X_PAGE_TXA, 0xc1, 0x2b}, /* ADI Required Write */
+ {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
+ {ADV748X_PAGE_TXA, 0x31, 0x80}, /* ADI Required Write */
+
+ {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
+};
+
+/* 02-01 Analog CVBS to MIPI TX-B CSI 1-Lane - */
+/* Autodetect CVBS Single Ended In Ain 1 - MIPI Out */
+static const struct adv748x_reg_value adv748x_init_txb_1lane[] = {
+
+ {ADV748X_PAGE_IO, 0x00, 0x30}, /* Disable chip powerdown Rx */
+ {ADV748X_PAGE_IO, 0xf2, 0x01}, /* Enable I2C Read Auto-Increment */
+
+ {ADV748X_PAGE_IO, 0x0e, 0xff}, /* LLC/PIX/AUD/SPI PINS TRISTATED */
+
+ {ADV748X_PAGE_SDP, 0x0f, 0x00}, /* Exit Power Down Mode */
+ {ADV748X_PAGE_SDP, 0x52, 0xcd}, /* ADI Required Write */
+
+ {ADV748X_PAGE_SDP, 0x0e, 0x80}, /* ADI Required Write */
+ {ADV748X_PAGE_SDP, 0x9c, 0x00}, /* ADI Required Write */
+ {ADV748X_PAGE_SDP, 0x9c, 0xff}, /* ADI Required Write */
+ {ADV748X_PAGE_SDP, 0x0e, 0x00}, /* ADI Required Write */
+
+ /* ADI recommended writes for improved video quality */
+ {ADV748X_PAGE_SDP, 0x80, 0x51}, /* ADI Required Write */
+ {ADV748X_PAGE_SDP, 0x81, 0x51}, /* ADI Required Write */
+ {ADV748X_PAGE_SDP, 0x82, 0x68}, /* ADI Required Write */
+
+ {ADV748X_PAGE_SDP, 0x03, 0x42}, /* Tri-S Output , PwrDwn 656 pads */
+ {ADV748X_PAGE_SDP, 0x04, 0xb5}, /* ITU-R BT.656-4 compatible */
+ {ADV748X_PAGE_SDP, 0x13, 0x00}, /* ADI Required Write */
+
+ {ADV748X_PAGE_SDP, 0x17, 0x41}, /* Select SH1 */
+ {ADV748X_PAGE_SDP, 0x31, 0x12}, /* ADI Required Write */
+ {ADV748X_PAGE_SDP, 0xe6, 0x4f}, /* V bit end pos manually in NTSC */
+
+ /* Enable 1-Lane MIPI Tx, */
+ /* enable pixel output and route SD through Pixel port */
+ {ADV748X_PAGE_IO, 0x10, 0x70},
+
+ {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */
+ {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */
+ {ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */
+ {ADV748X_PAGE_TXB, 0xc4, 0x0a}, /* ADI Required Write */
+ {ADV748X_PAGE_TXB, 0x71, 0x33}, /* ADI Required Write */
+ {ADV748X_PAGE_TXB, 0x72, 0x11}, /* ADI Required Write */
+ {ADV748X_PAGE_TXB, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */
+ {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */
+ {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */
+ {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+
+ {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */
+ {ADV748X_PAGE_TXB, 0x00, 0x21 },/* Power-up CSI-TX */
+ {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
+ {ADV748X_PAGE_TXB, 0xc1, 0x2b}, /* ADI Required Write */
+ {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
+ {ADV748X_PAGE_TXB, 0x31, 0x80}, /* ADI Required Write */
+
+ {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
+};
+
+static int adv748x_reset(struct adv748x_state *state)
+{
+ int ret;
+
+ ret = adv748x_write_regs(state, adv748x_sw_reset);
+ if (ret < 0)
+ return ret;
+
+ ret = adv748x_write_regs(state, adv748x_set_slave_address);
+ if (ret < 0)
+ return ret;
+
+ /* Init and power down TXA */
+ ret = adv748x_write_regs(state, adv748x_init_txa_4lane);
+ if (ret)
+ return ret;
+
+ adv748x_txa_power(state, 0);
+
+ /* Init and power down TXB */
+ ret = adv748x_write_regs(state, adv748x_init_txb_1lane);
+ if (ret)
+ return ret;
+
+ adv748x_txb_power(state, 0);
+
+ /* Disable chip powerdown & Enable HDMI Rx block */
+ io_write(state, ADV748X_IO_PD, ADV748X_IO_PD_RX_EN);
+
+ /* Enable 4-lane CSI Tx & Pixel Port */
+ io_write(state, ADV748X_IO_10, ADV748X_IO_10_CSI4_EN |
+ ADV748X_IO_10_CSI1_EN |
+ ADV748X_IO_10_PIX_OUT_EN);
+
+ /* Use vid_std and v_freq as freerun resolution for CP */
+ cp_clrset(state, ADV748X_CP_CLMP_POS, ADV748X_CP_CLMP_POS_DIS_AUTO,
+ ADV748X_CP_CLMP_POS_DIS_AUTO);
+
+ return 0;
+}
+
+static int adv748x_identify_chip(struct adv748x_state *state)
+{
+ int msb, lsb;
+
+ lsb = io_read(state, ADV748X_IO_CHIP_REV_ID_1);
+ msb = io_read(state, ADV748X_IO_CHIP_REV_ID_2);
+
+ if (lsb < 0 || msb < 0) {
+ adv_err(state, "Failed to read chip revision\n");
+ return -EIO;
+ }
+
+ adv_info(state, "chip found @ 0x%02x revision %02x%02x\n",
+ state->client->addr << 1, lsb, msb);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * i2c driver
+ */
+
+void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state,
+ const struct v4l2_subdev_ops *ops, u32 function,
+ const char *ident)
+{
+ v4l2_subdev_init(sd, ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ /* the owner is the same as the i2c_client's driver owner */
+ sd->owner = state->dev->driver->owner;
+ sd->dev = state->dev;
+
+ v4l2_set_subdevdata(sd, state);
+
+ /* initialize name */
+ snprintf(sd->name, sizeof(sd->name), "%s %d-%04x %s",
+ state->dev->driver->name,
+ i2c_adapter_id(state->client->adapter),
+ state->client->addr, ident);
+
+ sd->entity.function = function;
+ sd->entity.ops = &adv748x_media_ops;
+}
+
+static int adv748x_parse_dt(struct adv748x_state *state)
+{
+ struct device_node *ep_np = NULL;
+ struct of_endpoint ep;
+ bool found = false;
+
+ for_each_endpoint_of_node(state->dev->of_node, ep_np) {
+ of_graph_parse_endpoint(ep_np, &ep);
+ adv_info(state, "Endpoint %s on port %d",
+ of_node_full_name(ep.local_node),
+ ep.port);
+
+ if (ep.port >= ADV748X_PORT_MAX) {
+ adv_err(state, "Invalid endpoint %s on port %d",
+ of_node_full_name(ep.local_node),
+ ep.port);
+
+ continue;
+ }
+
+ if (state->endpoints[ep.port]) {
+ adv_err(state,
+ "Multiple port endpoints are not supported");
+ continue;
+ }
+
+ of_node_get(ep_np);
+ state->endpoints[ep.port] = ep_np;
+
+ found = true;
+ }
+
+ return found ? 0 : -ENODEV;
+}
+
+static void adv748x_dt_cleanup(struct adv748x_state *state)
+{
+ unsigned int i;
+
+ for (i = 0; i < ADV748X_PORT_MAX; i++)
+ of_node_put(state->endpoints[i]);
+}
+
+static int adv748x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv748x_state *state;
+ int ret;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ state = kzalloc(sizeof(struct adv748x_state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ mutex_init(&state->mutex);
+
+ state->dev = &client->dev;
+ state->client = client;
+ state->i2c_clients[ADV748X_PAGE_IO] = client;
+ i2c_set_clientdata(client, state);
+
+ /* Discover and process ports declared by the Device tree endpoints */
+ ret = adv748x_parse_dt(state);
+ if (ret) {
+ adv_err(state, "Failed to parse device tree");
+ goto err_free_mutex;
+ }
+
+ /* Configure IO Regmap region */
+ ret = adv748x_configure_regmap(state, ADV748X_PAGE_IO);
+ if (ret) {
+ adv_err(state, "Error configuring IO regmap region");
+ goto err_cleanup_dt;
+ }
+
+ ret = adv748x_identify_chip(state);
+ if (ret) {
+ adv_err(state, "Failed to identify chip");
+ goto err_cleanup_clients;
+ }
+
+ /* Configure remaining pages as I2C clients with regmap access */
+ ret = adv748x_initialise_clients(state);
+ if (ret) {
+ adv_err(state, "Failed to setup client regmap pages");
+ goto err_cleanup_clients;
+ }
+
+ /* SW reset ADV748X to its default values */
+ ret = adv748x_reset(state);
+ if (ret) {
+ adv_err(state, "Failed to reset hardware");
+ goto err_cleanup_clients;
+ }
+
+ /* Initialise HDMI */
+ ret = adv748x_hdmi_init(&state->hdmi);
+ if (ret) {
+ adv_err(state, "Failed to probe HDMI");
+ goto err_cleanup_clients;
+ }
+
+ /* Initialise AFE */
+ ret = adv748x_afe_init(&state->afe);
+ if (ret) {
+ adv_err(state, "Failed to probe AFE");
+ goto err_cleanup_hdmi;
+ }
+
+ /* Initialise TXA */
+ ret = adv748x_csi2_init(state, &state->txa);
+ if (ret) {
+ adv_err(state, "Failed to probe TXA");
+ goto err_cleanup_afe;
+ }
+
+ /* Initialise TXB */
+ ret = adv748x_csi2_init(state, &state->txb);
+ if (ret) {
+ adv_err(state, "Failed to probe TXB");
+ goto err_cleanup_txa;
+ }
+
+ return 0;
+
+err_cleanup_txa:
+ adv748x_csi2_cleanup(&state->txa);
+err_cleanup_afe:
+ adv748x_afe_cleanup(&state->afe);
+err_cleanup_hdmi:
+ adv748x_hdmi_cleanup(&state->hdmi);
+err_cleanup_clients:
+ adv748x_unregister_clients(state);
+err_cleanup_dt:
+ adv748x_dt_cleanup(state);
+err_free_mutex:
+ mutex_destroy(&state->mutex);
+ kfree(state);
+
+ return ret;
+}
+
+static int adv748x_remove(struct i2c_client *client)
+{
+ struct adv748x_state *state = i2c_get_clientdata(client);
+
+ adv748x_afe_cleanup(&state->afe);
+ adv748x_hdmi_cleanup(&state->hdmi);
+
+ adv748x_csi2_cleanup(&state->txa);
+ adv748x_csi2_cleanup(&state->txb);
+
+ adv748x_unregister_clients(state);
+ adv748x_dt_cleanup(state);
+ mutex_destroy(&state->mutex);
+
+ kfree(state);
+
+ return 0;
+}
+
+static const struct i2c_device_id adv748x_id[] = {
+ { "adv7481", 0 },
+ { "adv7482", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, adv748x_id);
+
+static const struct of_device_id adv748x_of_table[] = {
+ { .compatible = "adi,adv7481", },
+ { .compatible = "adi,adv7482", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adv748x_of_table);
+
+static struct i2c_driver adv748x_driver = {
+ .driver = {
+ .name = "adv748x",
+ .of_match_table = adv748x_of_table,
+ },
+ .probe = adv748x_probe,
+ .remove = adv748x_remove,
+ .id_table = adv748x_id,
+};
+
+module_i2c_driver(adv748x_driver);
+
+MODULE_AUTHOR("Kieran Bingham <kieran.bingham@ideasonboard.com>");
+MODULE_DESCRIPTION("ADV748X video decoder");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c
new file mode 100644
index 000000000000..979825d4a419
--- /dev/null
+++ b/drivers/media/i2c/adv748x/adv748x-csi2.c
@@ -0,0 +1,326 @@
+/*
+ * Driver for Analog Devices ADV748X CSI-2 Transmitter
+ *
+ * Copyright (C) 2017 Renesas Electronics Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#include "adv748x.h"
+
+static bool is_txa(struct adv748x_csi2 *tx)
+{
+ return tx == &tx->state->txa;
+}
+
+static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx,
+ unsigned int vc)
+{
+ return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT);
+}
+
+/**
+ * adv748x_csi2_register_link : Register and link internal entities
+ *
+ * @tx: CSI2 private entity
+ * @v4l2_dev: Video registration device
+ * @src: Source subdevice to establish link
+ * @src_pad: Pad number of source to link to this @tx
+ *
+ * Ensure that the subdevice is registered against the v4l2_device, and link the
+ * source pad to the sink pad of the CSI2 bus entity.
+ */
+static int adv748x_csi2_register_link(struct adv748x_csi2 *tx,
+ struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *src,
+ unsigned int src_pad)
+{
+ int enabled = MEDIA_LNK_FL_ENABLED;
+ int ret;
+
+ /*
+ * Dynamic linking of the AFE is not supported.
+ * Register the links as immutable.
+ */
+ enabled |= MEDIA_LNK_FL_IMMUTABLE;
+
+ if (!src->v4l2_dev) {
+ ret = v4l2_device_register_subdev(v4l2_dev, src);
+ if (ret)
+ return ret;
+ }
+
+ return media_create_pad_link(&src->entity, src_pad,
+ &tx->sd.entity, ADV748X_CSI2_SINK,
+ enabled);
+}
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_internal_ops
+ *
+ * We use the internal registered operation to be able to ensure that our
+ * incremental subdevices (not connected in the forward path) can be registered
+ * against the resulting video path and media device.
+ */
+
+static int adv748x_csi2_registered(struct v4l2_subdev *sd)
+{
+ struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
+ struct adv748x_state *state = tx->state;
+
+ adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB",
+ sd->name);
+
+ /*
+ * The adv748x hardware allows the AFE to route through the TXA, however
+ * this is not currently supported in this driver.
+ *
+ * Link HDMI->TXA, and AFE->TXB directly.
+ */
+ if (is_txa(tx)) {
+ return adv748x_csi2_register_link(tx, sd->v4l2_dev,
+ &state->hdmi.sd,
+ ADV748X_HDMI_SOURCE);
+ } else {
+ return adv748x_csi2_register_link(tx, sd->v4l2_dev,
+ &state->afe.sd,
+ ADV748X_AFE_SOURCE);
+ }
+}
+
+static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = {
+ .registered = adv748x_csi2_registered,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_video_ops
+ */
+
+static int adv748x_csi2_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
+ struct v4l2_subdev *src;
+
+ src = adv748x_get_remote_sd(&tx->pads[ADV748X_CSI2_SINK]);
+ if (!src)
+ return -EPIPE;
+
+ return v4l2_subdev_call(src, video, s_stream, enable);
+}
+
+static const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = {
+ .s_stream = adv748x_csi2_s_stream,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_pad_ops
+ *
+ * The CSI2 bus pads are ignorant to the data sizes or formats.
+ * But we must support setting the pad formats for format propagation.
+ */
+
+static struct v4l2_mbus_framefmt *
+adv748x_csi2_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, u32 which)
+{
+ struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
+
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(sd, cfg, pad);
+
+ return &tx->format;
+}
+
+static int adv748x_csi2_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
+ struct adv748x_state *state = tx->state;
+ struct v4l2_mbus_framefmt *mbusformat;
+
+ mbusformat = adv748x_csi2_get_pad_format(sd, cfg, sdformat->pad,
+ sdformat->which);
+ if (!mbusformat)
+ return -EINVAL;
+
+ mutex_lock(&state->mutex);
+
+ sdformat->format = *mbusformat;
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static int adv748x_csi2_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
+ struct adv748x_state *state = tx->state;
+ struct v4l2_mbus_framefmt *mbusformat;
+ int ret = 0;
+
+ mbusformat = adv748x_csi2_get_pad_format(sd, cfg, sdformat->pad,
+ sdformat->which);
+ if (!mbusformat)
+ return -EINVAL;
+
+ mutex_lock(&state->mutex);
+
+ if (sdformat->pad == ADV748X_CSI2_SOURCE) {
+ const struct v4l2_mbus_framefmt *sink_fmt;
+
+ sink_fmt = adv748x_csi2_get_pad_format(sd, cfg,
+ ADV748X_CSI2_SINK,
+ sdformat->which);
+
+ if (!sink_fmt) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ sdformat->format = *sink_fmt;
+ }
+
+ *mbusformat = sdformat->format;
+
+unlock:
+ mutex_unlock(&state->mutex);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = {
+ .get_fmt = adv748x_csi2_get_format,
+ .set_fmt = adv748x_csi2_set_format,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_ops
+ */
+
+static const struct v4l2_subdev_ops adv748x_csi2_ops = {
+ .video = &adv748x_csi2_video_ops,
+ .pad = &adv748x_csi2_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Subdev module and controls
+ */
+
+int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate)
+{
+ struct v4l2_ctrl *ctrl;
+
+ ctrl = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_PIXEL_RATE);
+ if (!ctrl)
+ return -EINVAL;
+
+ return v4l2_ctrl_s_ctrl_int64(ctrl, rate);
+}
+
+static int adv748x_csi2_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_PIXEL_RATE:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ctrl_ops adv748x_csi2_ctrl_ops = {
+ .s_ctrl = adv748x_csi2_s_ctrl,
+};
+
+static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx)
+{
+
+ v4l2_ctrl_handler_init(&tx->ctrl_hdl, 1);
+
+ v4l2_ctrl_new_std(&tx->ctrl_hdl, &adv748x_csi2_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
+
+ tx->sd.ctrl_handler = &tx->ctrl_hdl;
+ if (tx->ctrl_hdl.error) {
+ v4l2_ctrl_handler_free(&tx->ctrl_hdl);
+ return tx->ctrl_hdl.error;
+ }
+
+ return v4l2_ctrl_handler_setup(&tx->ctrl_hdl);
+}
+
+int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
+{
+ struct device_node *ep;
+ int ret;
+
+ /* We can not use container_of to get back to the state with two TXs */
+ tx->state = state;
+ tx->page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB;
+
+ ep = state->endpoints[is_txa(tx) ? ADV748X_PORT_TXA : ADV748X_PORT_TXB];
+ if (!ep) {
+ adv_err(state, "No endpoint found for %s\n",
+ is_txa(tx) ? "txa" : "txb");
+ return -ENODEV;
+ }
+
+ /* Initialise the virtual channel */
+ adv748x_csi2_set_virtual_channel(tx, 0);
+
+ adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops,
+ MEDIA_ENT_F_UNKNOWN,
+ is_txa(tx) ? "txa" : "txb");
+
+ /* Ensure that matching is based upon the endpoint fwnodes */
+ tx->sd.fwnode = of_fwnode_handle(ep);
+
+ /* Register internal ops for incremental subdev registration */
+ tx->sd.internal_ops = &adv748x_csi2_internal_ops;
+
+ tx->pads[ADV748X_CSI2_SINK].flags = MEDIA_PAD_FL_SINK;
+ tx->pads[ADV748X_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&tx->sd.entity, ADV748X_CSI2_NR_PADS,
+ tx->pads);
+ if (ret)
+ return ret;
+
+ ret = adv748x_csi2_init_controls(tx);
+ if (ret)
+ goto err_free_media;
+
+ ret = v4l2_async_register_subdev(&tx->sd);
+ if (ret)
+ goto err_free_ctrl;
+
+ return 0;
+
+err_free_ctrl:
+ v4l2_ctrl_handler_free(&tx->ctrl_hdl);
+err_free_media:
+ media_entity_cleanup(&tx->sd.entity);
+
+ return ret;
+}
+
+void adv748x_csi2_cleanup(struct adv748x_csi2 *tx)
+{
+ v4l2_async_unregister_subdev(&tx->sd);
+ media_entity_cleanup(&tx->sd.entity);
+ v4l2_ctrl_handler_free(&tx->ctrl_hdl);
+}
diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c
new file mode 100644
index 000000000000..4da4253553fc
--- /dev/null
+++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c
@@ -0,0 +1,768 @@
+/*
+ * Driver for Analog Devices ADV748X HDMI receiver and Component Processor (CP)
+ *
+ * Copyright (C) 2017 Renesas Electronics Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+
+#include <uapi/linux/v4l2-dv-timings.h>
+
+#include "adv748x.h"
+
+/* -----------------------------------------------------------------------------
+ * HDMI and CP
+ */
+
+#define ADV748X_HDMI_MIN_WIDTH 640
+#define ADV748X_HDMI_MAX_WIDTH 1920
+#define ADV748X_HDMI_MIN_HEIGHT 480
+#define ADV748X_HDMI_MAX_HEIGHT 1200
+
+/* V4L2_DV_BT_CEA_720X480I59_94 - 0.5 MHz */
+#define ADV748X_HDMI_MIN_PIXELCLOCK 13000000
+/* V4L2_DV_BT_DMT_1600X1200P60 */
+#define ADV748X_HDMI_MAX_PIXELCLOCK 162000000
+
+static const struct v4l2_dv_timings_cap adv748x_hdmi_timings_cap = {
+ .type = V4L2_DV_BT_656_1120,
+ /* keep this initialization for compatibility with GCC < 4.4.6 */
+ .reserved = { 0 },
+
+ V4L2_INIT_BT_TIMINGS(ADV748X_HDMI_MIN_WIDTH, ADV748X_HDMI_MAX_WIDTH,
+ ADV748X_HDMI_MIN_HEIGHT, ADV748X_HDMI_MAX_HEIGHT,
+ ADV748X_HDMI_MIN_PIXELCLOCK,
+ ADV748X_HDMI_MAX_PIXELCLOCK,
+ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT,
+ V4L2_DV_BT_CAP_PROGRESSIVE)
+};
+
+struct adv748x_hdmi_video_standards {
+ struct v4l2_dv_timings timings;
+ u8 vid_std;
+ u8 v_freq;
+};
+
+static const struct adv748x_hdmi_video_standards
+adv748x_hdmi_video_standards[] = {
+ { V4L2_DV_BT_CEA_720X480P59_94, 0x4a, 0x00 },
+ { V4L2_DV_BT_CEA_720X576P50, 0x4b, 0x00 },
+ { V4L2_DV_BT_CEA_1280X720P60, 0x53, 0x00 },
+ { V4L2_DV_BT_CEA_1280X720P50, 0x53, 0x01 },
+ { V4L2_DV_BT_CEA_1280X720P30, 0x53, 0x02 },
+ { V4L2_DV_BT_CEA_1280X720P25, 0x53, 0x03 },
+ { V4L2_DV_BT_CEA_1280X720P24, 0x53, 0x04 },
+ { V4L2_DV_BT_CEA_1920X1080P60, 0x5e, 0x00 },
+ { V4L2_DV_BT_CEA_1920X1080P50, 0x5e, 0x01 },
+ { V4L2_DV_BT_CEA_1920X1080P30, 0x5e, 0x02 },
+ { V4L2_DV_BT_CEA_1920X1080P25, 0x5e, 0x03 },
+ { V4L2_DV_BT_CEA_1920X1080P24, 0x5e, 0x04 },
+ /* SVGA */
+ { V4L2_DV_BT_DMT_800X600P56, 0x80, 0x00 },
+ { V4L2_DV_BT_DMT_800X600P60, 0x81, 0x00 },
+ { V4L2_DV_BT_DMT_800X600P72, 0x82, 0x00 },
+ { V4L2_DV_BT_DMT_800X600P75, 0x83, 0x00 },
+ { V4L2_DV_BT_DMT_800X600P85, 0x84, 0x00 },
+ /* SXGA */
+ { V4L2_DV_BT_DMT_1280X1024P60, 0x85, 0x00 },
+ { V4L2_DV_BT_DMT_1280X1024P75, 0x86, 0x00 },
+ /* VGA */
+ { V4L2_DV_BT_DMT_640X480P60, 0x88, 0x00 },
+ { V4L2_DV_BT_DMT_640X480P72, 0x89, 0x00 },
+ { V4L2_DV_BT_DMT_640X480P75, 0x8a, 0x00 },
+ { V4L2_DV_BT_DMT_640X480P85, 0x8b, 0x00 },
+ /* XGA */
+ { V4L2_DV_BT_DMT_1024X768P60, 0x8c, 0x00 },
+ { V4L2_DV_BT_DMT_1024X768P70, 0x8d, 0x00 },
+ { V4L2_DV_BT_DMT_1024X768P75, 0x8e, 0x00 },
+ { V4L2_DV_BT_DMT_1024X768P85, 0x8f, 0x00 },
+ /* UXGA */
+ { V4L2_DV_BT_DMT_1600X1200P60, 0x96, 0x00 },
+};
+
+static void adv748x_hdmi_fill_format(struct adv748x_hdmi *hdmi,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ memset(fmt, 0, sizeof(*fmt));
+
+ fmt->code = MEDIA_BUS_FMT_RGB888_1X24;
+ fmt->field = hdmi->timings.bt.interlaced ?
+ V4L2_FIELD_ALTERNATE : V4L2_FIELD_NONE;
+
+ /* TODO: The colorspace depends on the AVI InfoFrame contents */
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt->width = hdmi->timings.bt.width;
+ fmt->height = hdmi->timings.bt.height;
+}
+
+static void adv748x_fill_optional_dv_timings(struct v4l2_dv_timings *timings)
+{
+ v4l2_find_dv_timings_cap(timings, &adv748x_hdmi_timings_cap,
+ 250000, NULL, NULL);
+}
+
+static bool adv748x_hdmi_has_signal(struct adv748x_state *state)
+{
+ int val;
+
+ /* Check that VERT_FILTER and DE_REGEN is locked */
+ val = hdmi_read(state, ADV748X_HDMI_LW1);
+ return (val & ADV748X_HDMI_LW1_VERT_FILTER) &&
+ (val & ADV748X_HDMI_LW1_DE_REGEN);
+}
+
+static int adv748x_hdmi_read_pixelclock(struct adv748x_state *state)
+{
+ int a, b;
+
+ a = hdmi_read(state, ADV748X_HDMI_TMDS_1);
+ b = hdmi_read(state, ADV748X_HDMI_TMDS_2);
+ if (a < 0 || b < 0)
+ return -ENODATA;
+
+ /*
+ * The high 9 bits store TMDS frequency measurement in MHz
+ * The low 7 bits of TMDS_2 store the 7-bit TMDS fractional frequency
+ * measurement in 1/128 MHz
+ */
+ return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128;
+}
+
+/*
+ * adv748x_hdmi_set_de_timings: Adjust horizontal picture offset through DE
+ *
+ * HDMI CP uses a Data Enable synchronisation timing reference
+ *
+ * Vary the leading and trailing edge position of the DE signal output by the CP
+ * core. Values are stored as signed-twos-complement in one-pixel-clock units
+ *
+ * The start and end are shifted equally by the 10-bit shift value.
+ */
+static void adv748x_hdmi_set_de_timings(struct adv748x_state *state, int shift)
+{
+ u8 high, low;
+
+ /* POS_HIGH stores bits 8 and 9 of both the start and end */
+ high = ADV748X_CP_DE_POS_HIGH_SET;
+ high |= (shift & 0x300) >> 8;
+ low = shift & 0xff;
+
+ /* The sequence of the writes is important and must be followed */
+ cp_write(state, ADV748X_CP_DE_POS_HIGH, high);
+ cp_write(state, ADV748X_CP_DE_POS_END_LOW, low);
+
+ high |= (shift & 0x300) >> 6;
+
+ cp_write(state, ADV748X_CP_DE_POS_HIGH, high);
+ cp_write(state, ADV748X_CP_DE_POS_START_LOW, low);
+}
+
+static int adv748x_hdmi_set_video_timings(struct adv748x_state *state,
+ const struct v4l2_dv_timings *timings)
+{
+ const struct adv748x_hdmi_video_standards *stds =
+ adv748x_hdmi_video_standards;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(adv748x_hdmi_video_standards); i++) {
+ if (!v4l2_match_dv_timings(timings, &stds[i].timings, 250000,
+ false))
+ continue;
+ }
+
+ if (i >= ARRAY_SIZE(adv748x_hdmi_video_standards))
+ return -EINVAL;
+
+ /*
+ * When setting cp_vid_std to either 720p, 1080i, or 1080p, the video
+ * will get shifted horizontally to the left in active video mode.
+ * The de_h_start and de_h_end controls are used to centre the picture
+ * correctly
+ */
+ switch (stds[i].vid_std) {
+ case 0x53: /* 720p */
+ adv748x_hdmi_set_de_timings(state, -40);
+ break;
+ case 0x54: /* 1080i */
+ case 0x5e: /* 1080p */
+ adv748x_hdmi_set_de_timings(state, -44);
+ break;
+ default:
+ adv748x_hdmi_set_de_timings(state, 0);
+ break;
+ }
+
+ io_write(state, ADV748X_IO_VID_STD, stds[i].vid_std);
+ io_clrset(state, ADV748X_IO_DATAPATH, ADV748X_IO_DATAPATH_VFREQ_M,
+ stds[i].v_freq << ADV748X_IO_DATAPATH_VFREQ_SHIFT);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_video_ops
+ */
+
+static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
+ struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
+ int ret;
+
+ if (!timings)
+ return -EINVAL;
+
+ if (v4l2_match_dv_timings(&hdmi->timings, timings, 0, false))
+ return 0;
+
+ if (!v4l2_valid_dv_timings(timings, &adv748x_hdmi_timings_cap,
+ NULL, NULL))
+ return -ERANGE;
+
+ adv748x_fill_optional_dv_timings(timings);
+
+ mutex_lock(&state->mutex);
+
+ ret = adv748x_hdmi_set_video_timings(state, timings);
+ if (ret)
+ goto error;
+
+ hdmi->timings = *timings;
+
+ cp_clrset(state, ADV748X_CP_VID_ADJ_2, ADV748X_CP_VID_ADJ_2_INTERLACED,
+ timings->bt.interlaced ?
+ ADV748X_CP_VID_ADJ_2_INTERLACED : 0);
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+
+error:
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
+static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
+ struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
+
+ mutex_lock(&state->mutex);
+
+ *timings = hdmi->timings;
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
+ struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
+ struct v4l2_bt_timings *bt = &timings->bt;
+ int pixelclock;
+ int polarity;
+
+ if (!timings)
+ return -EINVAL;
+
+ memset(timings, 0, sizeof(struct v4l2_dv_timings));
+
+ if (!adv748x_hdmi_has_signal(state))
+ return -ENOLINK;
+
+ pixelclock = adv748x_hdmi_read_pixelclock(state);
+ if (pixelclock < 0)
+ return -ENODATA;
+
+ timings->type = V4L2_DV_BT_656_1120;
+
+ bt->pixelclock = pixelclock;
+ bt->interlaced = hdmi_read(state, ADV748X_HDMI_F1H1) &
+ ADV748X_HDMI_F1H1_INTERLACED ?
+ V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
+ bt->width = hdmi_read16(state, ADV748X_HDMI_LW1,
+ ADV748X_HDMI_LW1_WIDTH_MASK);
+ bt->height = hdmi_read16(state, ADV748X_HDMI_F0H1,
+ ADV748X_HDMI_F0H1_HEIGHT_MASK);
+ bt->hfrontporch = hdmi_read16(state, ADV748X_HDMI_HFRONT_PORCH,
+ ADV748X_HDMI_HFRONT_PORCH_MASK);
+ bt->hsync = hdmi_read16(state, ADV748X_HDMI_HSYNC_WIDTH,
+ ADV748X_HDMI_HSYNC_WIDTH_MASK);
+ bt->hbackporch = hdmi_read16(state, ADV748X_HDMI_HBACK_PORCH,
+ ADV748X_HDMI_HBACK_PORCH_MASK);
+ bt->vfrontporch = hdmi_read16(state, ADV748X_HDMI_VFRONT_PORCH,
+ ADV748X_HDMI_VFRONT_PORCH_MASK) / 2;
+ bt->vsync = hdmi_read16(state, ADV748X_HDMI_VSYNC_WIDTH,
+ ADV748X_HDMI_VSYNC_WIDTH_MASK) / 2;
+ bt->vbackporch = hdmi_read16(state, ADV748X_HDMI_VBACK_PORCH,
+ ADV748X_HDMI_VBACK_PORCH_MASK) / 2;
+
+ polarity = hdmi_read(state, 0x05);
+ bt->polarities = (polarity & BIT(4) ? V4L2_DV_VSYNC_POS_POL : 0) |
+ (polarity & BIT(5) ? V4L2_DV_HSYNC_POS_POL : 0);
+
+ if (bt->interlaced == V4L2_DV_INTERLACED) {
+ bt->height += hdmi_read16(state, 0x0b, 0x1fff);
+ bt->il_vfrontporch = hdmi_read16(state, 0x2c, 0x3fff) / 2;
+ bt->il_vsync = hdmi_read16(state, 0x30, 0x3fff) / 2;
+ bt->il_vbackporch = hdmi_read16(state, 0x34, 0x3fff) / 2;
+ }
+
+ adv748x_fill_optional_dv_timings(timings);
+
+ /*
+ * No interrupt handling is implemented yet.
+ * There should be an IRQ when a cable is plugged and the new timings
+ * should be figured out and stored to state.
+ */
+ hdmi->timings = *timings;
+
+ return 0;
+}
+
+static int adv748x_hdmi_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
+ struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
+
+ mutex_lock(&state->mutex);
+
+ *status = adv748x_hdmi_has_signal(state) ? 0 : V4L2_IN_ST_NO_SIGNAL;
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static int adv748x_hdmi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
+ struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
+ int ret;
+
+ mutex_lock(&state->mutex);
+
+ ret = adv748x_txa_power(state, enable);
+ if (ret)
+ goto done;
+
+ if (adv748x_hdmi_has_signal(state))
+ adv_dbg(state, "Detected HDMI signal\n");
+ else
+ adv_dbg(state, "Couldn't detect HDMI video signal\n");
+
+done:
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
+static int adv748x_hdmi_g_pixelaspect(struct v4l2_subdev *sd,
+ struct v4l2_fract *aspect)
+{
+ aspect->numerator = 1;
+ aspect->denominator = 1;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = {
+ .s_dv_timings = adv748x_hdmi_s_dv_timings,
+ .g_dv_timings = adv748x_hdmi_g_dv_timings,
+ .query_dv_timings = adv748x_hdmi_query_dv_timings,
+ .g_input_status = adv748x_hdmi_g_input_status,
+ .s_stream = adv748x_hdmi_s_stream,
+ .g_pixelaspect = adv748x_hdmi_g_pixelaspect,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_pad_ops
+ */
+
+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)
+ return -ENOLINK;
+
+ 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);
+}
+
+static int adv748x_hdmi_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_RGB888_1X24;
+
+ return 0;
+}
+
+static int adv748x_hdmi_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
+ struct v4l2_mbus_framefmt *mbusformat;
+
+ if (sdformat->pad != ADV748X_HDMI_SOURCE)
+ return -EINVAL;
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad);
+ sdformat->format = *mbusformat;
+ } else {
+ adv748x_hdmi_fill_format(hdmi, &sdformat->format);
+ adv748x_hdmi_propagate_pixelrate(hdmi);
+ }
+
+ return 0;
+}
+
+static int adv748x_hdmi_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct v4l2_mbus_framefmt *mbusformat;
+
+ if (sdformat->pad != ADV748X_HDMI_SOURCE)
+ return -EINVAL;
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ return adv748x_hdmi_get_format(sd, cfg, sdformat);
+
+ mbusformat = v4l2_subdev_get_try_format(sd, cfg, sdformat->pad);
+ *mbusformat = sdformat->format;
+
+ return 0;
+}
+
+static int adv748x_hdmi_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+ struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ if (!hdmi->edid.present)
+ return -ENODATA;
+
+ if (edid->start_block == 0 && edid->blocks == 0) {
+ edid->blocks = hdmi->edid.blocks;
+ return 0;
+ }
+
+ if (edid->start_block >= hdmi->edid.blocks)
+ return -EINVAL;
+
+ if (edid->start_block + edid->blocks > hdmi->edid.blocks)
+ edid->blocks = hdmi->edid.blocks - edid->start_block;
+
+ memcpy(edid->edid, hdmi->edid.edid + edid->start_block * 128,
+ edid->blocks * 128);
+
+ return 0;
+}
+
+static inline int adv748x_hdmi_edid_write_block(struct adv748x_hdmi *hdmi,
+ unsigned int total_len, const u8 *val)
+{
+ struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
+ int err = 0;
+ int i = 0;
+ int len = 0;
+
+ adv_dbg(state, "%s: write EDID block (%d byte)\n",
+ __func__, total_len);
+
+ while (!err && i < total_len) {
+ len = (total_len - i) > I2C_SMBUS_BLOCK_MAX ?
+ I2C_SMBUS_BLOCK_MAX :
+ (total_len - i);
+
+ err = adv748x_write_block(state, ADV748X_PAGE_EDID,
+ i, val + i, len);
+ i += len;
+ }
+
+ return err;
+}
+
+static int adv748x_hdmi_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+ struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
+ struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
+ int err;
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ if (edid->start_block != 0)
+ return -EINVAL;
+
+ if (edid->blocks == 0) {
+ hdmi->edid.blocks = 0;
+ hdmi->edid.present = 0;
+
+ /* Fall back to a 16:9 aspect ratio */
+ hdmi->aspect_ratio.numerator = 16;
+ hdmi->aspect_ratio.denominator = 9;
+
+ /* Disable the EDID */
+ repeater_write(state, ADV748X_REPEATER_EDID_SZ,
+ edid->blocks << ADV748X_REPEATER_EDID_SZ_SHIFT);
+
+ repeater_write(state, ADV748X_REPEATER_EDID_CTL, 0);
+
+ return 0;
+ }
+
+ if (edid->blocks > 4) {
+ edid->blocks = 4;
+ return -E2BIG;
+ }
+
+ memcpy(hdmi->edid.edid, edid->edid, 128 * edid->blocks);
+ hdmi->edid.blocks = edid->blocks;
+ hdmi->edid.present = true;
+
+ hdmi->aspect_ratio = v4l2_calc_aspect_ratio(edid->edid[0x15],
+ edid->edid[0x16]);
+
+ err = adv748x_hdmi_edid_write_block(hdmi, 128 * edid->blocks,
+ hdmi->edid.edid);
+ if (err < 0) {
+ v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad);
+ return err;
+ }
+
+ repeater_write(state, ADV748X_REPEATER_EDID_SZ,
+ edid->blocks << ADV748X_REPEATER_EDID_SZ_SHIFT);
+
+ repeater_write(state, ADV748X_REPEATER_EDID_CTL,
+ ADV748X_REPEATER_EDID_CTL_EN);
+
+ return 0;
+}
+
+static bool adv748x_hdmi_check_dv_timings(const struct v4l2_dv_timings *timings,
+ void *hdl)
+{
+ const struct adv748x_hdmi_video_standards *stds =
+ adv748x_hdmi_video_standards;
+ unsigned int i;
+
+ for (i = 0; stds[i].timings.bt.width; i++)
+ if (v4l2_match_dv_timings(timings, &stds[i].timings, 0, false))
+ return true;
+
+ return false;
+}
+
+static int adv748x_hdmi_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
+{
+ return v4l2_enum_dv_timings_cap(timings, &adv748x_hdmi_timings_cap,
+ adv748x_hdmi_check_dv_timings, NULL);
+}
+
+static int adv748x_hdmi_dv_timings_cap(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings_cap *cap)
+{
+ *cap = adv748x_hdmi_timings_cap;
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops adv748x_pad_ops_hdmi = {
+ .enum_mbus_code = adv748x_hdmi_enum_mbus_code,
+ .set_fmt = adv748x_hdmi_set_format,
+ .get_fmt = adv748x_hdmi_get_format,
+ .get_edid = adv748x_hdmi_get_edid,
+ .set_edid = adv748x_hdmi_set_edid,
+ .dv_timings_cap = adv748x_hdmi_dv_timings_cap,
+ .enum_dv_timings = adv748x_hdmi_enum_dv_timings,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_ops
+ */
+
+static const struct v4l2_subdev_ops adv748x_ops_hdmi = {
+ .video = &adv748x_video_ops_hdmi,
+ .pad = &adv748x_pad_ops_hdmi,
+};
+
+/* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+static const char * const hdmi_ctrl_patgen_menu[] = {
+ "Disabled",
+ "Solid Color",
+ "Color Bars",
+ "Ramp Grey",
+ "Ramp Blue",
+ "Ramp Red",
+ "Checkered"
+};
+
+static int adv748x_hdmi_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct adv748x_hdmi *hdmi = adv748x_ctrl_to_hdmi(ctrl);
+ struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
+ int ret;
+ u8 pattern;
+
+ /* Enable video adjustment first */
+ ret = cp_clrset(state, ADV748X_CP_VID_ADJ,
+ ADV748X_CP_VID_ADJ_ENABLE,
+ ADV748X_CP_VID_ADJ_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ret = cp_write(state, ADV748X_CP_BRI, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ ret = cp_write(state, ADV748X_CP_HUE, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ ret = cp_write(state, ADV748X_CP_CON, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ ret = cp_write(state, ADV748X_CP_SAT, ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ pattern = ctrl->val;
+
+ /* Pattern is 0-indexed. Ctrl Menu is 1-indexed */
+ if (pattern) {
+ pattern--;
+ pattern |= ADV748X_CP_PAT_GEN_EN;
+ }
+
+ ret = cp_write(state, ADV748X_CP_PAT_GEN, pattern);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops adv748x_hdmi_ctrl_ops = {
+ .s_ctrl = adv748x_hdmi_s_ctrl,
+};
+
+static int adv748x_hdmi_init_controls(struct adv748x_hdmi *hdmi)
+{
+ struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
+
+ v4l2_ctrl_handler_init(&hdmi->ctrl_hdl, 5);
+
+ /* Use our mutex for the controls */
+ hdmi->ctrl_hdl.lock = &state->mutex;
+
+ v4l2_ctrl_new_std(&hdmi->ctrl_hdl, &adv748x_hdmi_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, ADV748X_CP_BRI_MIN,
+ ADV748X_CP_BRI_MAX, 1, ADV748X_CP_BRI_DEF);
+ v4l2_ctrl_new_std(&hdmi->ctrl_hdl, &adv748x_hdmi_ctrl_ops,
+ V4L2_CID_CONTRAST, ADV748X_CP_CON_MIN,
+ ADV748X_CP_CON_MAX, 1, ADV748X_CP_CON_DEF);
+ v4l2_ctrl_new_std(&hdmi->ctrl_hdl, &adv748x_hdmi_ctrl_ops,
+ V4L2_CID_SATURATION, ADV748X_CP_SAT_MIN,
+ ADV748X_CP_SAT_MAX, 1, ADV748X_CP_SAT_DEF);
+ v4l2_ctrl_new_std(&hdmi->ctrl_hdl, &adv748x_hdmi_ctrl_ops,
+ V4L2_CID_HUE, ADV748X_CP_HUE_MIN,
+ ADV748X_CP_HUE_MAX, 1, ADV748X_CP_HUE_DEF);
+
+ /*
+ * Todo: V4L2_CID_DV_RX_POWER_PRESENT should also be supported when
+ * interrupts are handled correctly
+ */
+
+ v4l2_ctrl_new_std_menu_items(&hdmi->ctrl_hdl, &adv748x_hdmi_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(hdmi_ctrl_patgen_menu) - 1,
+ 0, 0, hdmi_ctrl_patgen_menu);
+
+ hdmi->sd.ctrl_handler = &hdmi->ctrl_hdl;
+ if (hdmi->ctrl_hdl.error) {
+ v4l2_ctrl_handler_free(&hdmi->ctrl_hdl);
+ return hdmi->ctrl_hdl.error;
+ }
+
+ return v4l2_ctrl_handler_setup(&hdmi->ctrl_hdl);
+}
+
+int adv748x_hdmi_init(struct adv748x_hdmi *hdmi)
+{
+ struct adv748x_state *state = adv748x_hdmi_to_state(hdmi);
+ static const struct v4l2_dv_timings cea1280x720 =
+ V4L2_DV_BT_CEA_1280X720P30;
+ int ret;
+
+ hdmi->timings = cea1280x720;
+
+ /* Initialise a default 16:9 aspect ratio */
+ hdmi->aspect_ratio.numerator = 16;
+ hdmi->aspect_ratio.denominator = 9;
+
+ adv748x_subdev_init(&hdmi->sd, state, &adv748x_ops_hdmi,
+ MEDIA_ENT_F_IO_DTV, "hdmi");
+
+ hdmi->pads[ADV748X_HDMI_SINK].flags = MEDIA_PAD_FL_SINK;
+ hdmi->pads[ADV748X_HDMI_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&hdmi->sd.entity,
+ ADV748X_HDMI_NR_PADS, hdmi->pads);
+ if (ret)
+ return ret;
+
+ ret = adv748x_hdmi_init_controls(hdmi);
+ if (ret)
+ goto err_free_media;
+
+ return 0;
+
+err_free_media:
+ media_entity_cleanup(&hdmi->sd.entity);
+
+ return ret;
+}
+
+void adv748x_hdmi_cleanup(struct adv748x_hdmi *hdmi)
+{
+ v4l2_device_unregister_subdev(&hdmi->sd);
+ media_entity_cleanup(&hdmi->sd.entity);
+ v4l2_ctrl_handler_free(&hdmi->ctrl_hdl);
+}
diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h
new file mode 100644
index 000000000000..cc4151b5b31e
--- /dev/null
+++ b/drivers/media/i2c/adv748x/adv748x.h
@@ -0,0 +1,425 @@
+/*
+ * Driver for Analog Devices ADV748X video decoder and HDMI receiver
+ *
+ * Copyright (C) 2017 Renesas Electronics Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Authors:
+ * Koji Matsuoka <koji.matsuoka.xm@renesas.com>
+ * Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ * Kieran Bingham <kieran.bingham@ideasonboard.com>
+ *
+ * The ADV748x range of receivers have the following configurations:
+ *
+ * Analog HDMI MHL 4-Lane 1-Lane
+ * In In CSI CSI
+ * ADV7480 X X X
+ * ADV7481 X X X X X
+ * ADV7482 X X X X
+ */
+
+#include <linux/i2c.h>
+
+#ifndef _ADV748X_H_
+#define _ADV748X_H_
+
+/* I2C slave addresses */
+#define ADV748X_I2C_IO 0x70 /* IO Map */
+#define ADV748X_I2C_DPLL 0x26 /* DPLL Map */
+#define ADV748X_I2C_CP 0x22 /* CP Map */
+#define ADV748X_I2C_HDMI 0x34 /* HDMI Map */
+#define ADV748X_I2C_EDID 0x36 /* EDID Map */
+#define ADV748X_I2C_REPEATER 0x32 /* HDMI RX Repeater Map */
+#define ADV748X_I2C_INFOFRAME 0x31 /* HDMI RX InfoFrame Map */
+#define ADV748X_I2C_CEC 0x41 /* CEC Map */
+#define ADV748X_I2C_SDP 0x79 /* SDP Map */
+#define ADV748X_I2C_TXB 0x48 /* CSI-TXB Map */
+#define ADV748X_I2C_TXA 0x4a /* CSI-TXA Map */
+
+enum adv748x_page {
+ ADV748X_PAGE_IO,
+ ADV748X_PAGE_DPLL,
+ ADV748X_PAGE_CP,
+ ADV748X_PAGE_HDMI,
+ ADV748X_PAGE_EDID,
+ ADV748X_PAGE_REPEATER,
+ ADV748X_PAGE_INFOFRAME,
+ ADV748X_PAGE_CEC,
+ ADV748X_PAGE_SDP,
+ ADV748X_PAGE_TXB,
+ ADV748X_PAGE_TXA,
+ ADV748X_PAGE_MAX,
+
+ /* Fake pages for register sequences */
+ ADV748X_PAGE_WAIT, /* Wait x msec */
+ ADV748X_PAGE_EOR, /* End Mark */
+};
+
+/**
+ * enum adv748x_ports - Device tree port number definitions
+ *
+ * The ADV748X ports define the mapping between subdevices
+ * and the device tree specification
+ */
+enum adv748x_ports {
+ ADV748X_PORT_AIN0 = 0,
+ ADV748X_PORT_AIN1 = 1,
+ ADV748X_PORT_AIN2 = 2,
+ ADV748X_PORT_AIN3 = 3,
+ ADV748X_PORT_AIN4 = 4,
+ ADV748X_PORT_AIN5 = 5,
+ ADV748X_PORT_AIN6 = 6,
+ ADV748X_PORT_AIN7 = 7,
+ ADV748X_PORT_HDMI = 8,
+ ADV748X_PORT_TTL = 9,
+ ADV748X_PORT_TXA = 10,
+ ADV748X_PORT_TXB = 11,
+ ADV748X_PORT_MAX = 12,
+};
+
+enum adv748x_csi2_pads {
+ ADV748X_CSI2_SINK,
+ ADV748X_CSI2_SOURCE,
+ ADV748X_CSI2_NR_PADS,
+};
+
+/* CSI2 transmitters can have 2 internal connections, HDMI/AFE */
+#define ADV748X_CSI2_MAX_SUBDEVS 2
+
+struct adv748x_csi2 {
+ struct adv748x_state *state;
+ struct v4l2_mbus_framefmt format;
+ unsigned int page;
+
+ struct media_pad pads[ADV748X_CSI2_NR_PADS];
+ struct v4l2_ctrl_handler ctrl_hdl;
+ struct v4l2_subdev sd;
+};
+
+#define notifier_to_csi2(n) container_of(n, struct adv748x_csi2, notifier)
+#define adv748x_sd_to_csi2(sd) container_of(sd, struct adv748x_csi2, sd)
+
+enum adv748x_hdmi_pads {
+ ADV748X_HDMI_SINK,
+ ADV748X_HDMI_SOURCE,
+ ADV748X_HDMI_NR_PADS,
+};
+
+struct adv748x_hdmi {
+ struct media_pad pads[ADV748X_HDMI_NR_PADS];
+ struct v4l2_ctrl_handler ctrl_hdl;
+ struct v4l2_subdev sd;
+ struct v4l2_mbus_framefmt format;
+
+ struct v4l2_dv_timings timings;
+ struct v4l2_fract aspect_ratio;
+
+ struct {
+ u8 edid[512];
+ u32 present;
+ unsigned int blocks;
+ } edid;
+};
+
+#define adv748x_ctrl_to_hdmi(ctrl) \
+ container_of(ctrl->handler, struct adv748x_hdmi, ctrl_hdl)
+#define adv748x_sd_to_hdmi(sd) container_of(sd, struct adv748x_hdmi, sd)
+
+enum adv748x_afe_pads {
+ ADV748X_AFE_SINK_AIN0,
+ ADV748X_AFE_SINK_AIN1,
+ ADV748X_AFE_SINK_AIN2,
+ ADV748X_AFE_SINK_AIN3,
+ ADV748X_AFE_SINK_AIN4,
+ ADV748X_AFE_SINK_AIN5,
+ ADV748X_AFE_SINK_AIN6,
+ ADV748X_AFE_SINK_AIN7,
+ ADV748X_AFE_SOURCE,
+ ADV748X_AFE_NR_PADS,
+};
+
+struct adv748x_afe {
+ struct media_pad pads[ADV748X_AFE_NR_PADS];
+ struct v4l2_ctrl_handler ctrl_hdl;
+ struct v4l2_subdev sd;
+ struct v4l2_mbus_framefmt format;
+
+ bool streaming;
+ v4l2_std_id curr_norm;
+ unsigned int input;
+};
+
+#define adv748x_ctrl_to_afe(ctrl) \
+ container_of(ctrl->handler, struct adv748x_afe, ctrl_hdl)
+#define adv748x_sd_to_afe(sd) container_of(sd, struct adv748x_afe, sd)
+
+/**
+ * struct adv748x_state - State of ADV748X
+ * @dev: (OF) device
+ * @client: I2C client
+ * @mutex: protect global state
+ *
+ * @endpoints: parsed device node endpoints for each port
+ *
+ * @i2c_addresses I2C Page addresses
+ * @i2c_clients I2C clients for the page accesses
+ * @regmap regmap configuration pages.
+ *
+ * @hdmi: state of HDMI receiver context
+ * @afe: state of AFE receiver context
+ * @txa: state of TXA transmitter context
+ * @txb: state of TXB transmitter context
+ */
+struct adv748x_state {
+ struct device *dev;
+ struct i2c_client *client;
+ struct mutex mutex;
+
+ struct device_node *endpoints[ADV748X_PORT_MAX];
+
+ struct i2c_client *i2c_clients[ADV748X_PAGE_MAX];
+ struct regmap *regmap[ADV748X_PAGE_MAX];
+
+ struct adv748x_hdmi hdmi;
+ struct adv748x_afe afe;
+ struct adv748x_csi2 txa;
+ struct adv748x_csi2 txb;
+};
+
+#define adv748x_hdmi_to_state(h) container_of(h, struct adv748x_state, hdmi)
+#define adv748x_afe_to_state(a) container_of(a, struct adv748x_state, afe)
+
+#define adv_err(a, fmt, arg...) dev_err(a->dev, fmt, ##arg)
+#define adv_info(a, fmt, arg...) dev_info(a->dev, fmt, ##arg)
+#define adv_dbg(a, fmt, arg...) dev_dbg(a->dev, fmt, ##arg)
+
+/* Register Mappings */
+
+/* IO Map */
+#define ADV748X_IO_PD 0x00 /* power down controls */
+#define ADV748X_IO_PD_RX_EN BIT(6)
+
+#define ADV748X_IO_REG_04 0x04
+#define ADV748X_IO_REG_04_FORCE_FR BIT(0) /* Force CP free-run */
+
+#define ADV748X_IO_DATAPATH 0x03 /* datapath cntrl */
+#define ADV748X_IO_DATAPATH_VFREQ_M 0x70
+#define ADV748X_IO_DATAPATH_VFREQ_SHIFT 4
+
+#define ADV748X_IO_VID_STD 0x05
+
+#define ADV748X_IO_10 0x10 /* io_reg_10 */
+#define ADV748X_IO_10_CSI4_EN BIT(7)
+#define ADV748X_IO_10_CSI1_EN BIT(6)
+#define ADV748X_IO_10_PIX_OUT_EN BIT(5)
+
+#define ADV748X_IO_CHIP_REV_ID_1 0xdf
+#define ADV748X_IO_CHIP_REV_ID_2 0xe0
+
+#define ADV748X_IO_SLAVE_ADDR_BASE 0xf2
+
+/* HDMI RX Map */
+#define ADV748X_HDMI_LW1 0x07 /* line width_1 */
+#define ADV748X_HDMI_LW1_VERT_FILTER BIT(7)
+#define ADV748X_HDMI_LW1_DE_REGEN BIT(5)
+#define ADV748X_HDMI_LW1_WIDTH_MASK 0x1fff
+
+#define ADV748X_HDMI_F0H1 0x09 /* field0 height_1 */
+#define ADV748X_HDMI_F0H1_HEIGHT_MASK 0x1fff
+
+#define ADV748X_HDMI_F1H1 0x0b /* field1 height_1 */
+#define ADV748X_HDMI_F1H1_INTERLACED BIT(5)
+
+#define ADV748X_HDMI_HFRONT_PORCH 0x20 /* hsync_front_porch_1 */
+#define ADV748X_HDMI_HFRONT_PORCH_MASK 0x1fff
+
+#define ADV748X_HDMI_HSYNC_WIDTH 0x22 /* hsync_pulse_width_1 */
+#define ADV748X_HDMI_HSYNC_WIDTH_MASK 0x1fff
+
+#define ADV748X_HDMI_HBACK_PORCH 0x24 /* hsync_back_porch_1 */
+#define ADV748X_HDMI_HBACK_PORCH_MASK 0x1fff
+
+#define ADV748X_HDMI_VFRONT_PORCH 0x2a /* field0_vs_front_porch_1 */
+#define ADV748X_HDMI_VFRONT_PORCH_MASK 0x3fff
+
+#define ADV748X_HDMI_VSYNC_WIDTH 0x2e /* field0_vs_pulse_width_1 */
+#define ADV748X_HDMI_VSYNC_WIDTH_MASK 0x3fff
+
+#define ADV748X_HDMI_VBACK_PORCH 0x32 /* field0_vs_back_porch_1 */
+#define ADV748X_HDMI_VBACK_PORCH_MASK 0x3fff
+
+#define ADV748X_HDMI_TMDS_1 0x51 /* hdmi_reg_51 */
+#define ADV748X_HDMI_TMDS_2 0x52 /* hdmi_reg_52 */
+
+/* HDMI RX Repeater Map */
+#define ADV748X_REPEATER_EDID_SZ 0x70 /* primary_edid_size */
+#define ADV748X_REPEATER_EDID_SZ_SHIFT 4
+
+#define ADV748X_REPEATER_EDID_CTL 0x74 /* hdcp edid controls */
+#define ADV748X_REPEATER_EDID_CTL_EN BIT(0) /* man_edid_a_enable */
+
+/* SDP Main Map */
+#define ADV748X_SDP_INSEL 0x00 /* user_map_rw_reg_00 */
+
+#define ADV748X_SDP_VID_SEL 0x02 /* user_map_rw_reg_02 */
+#define ADV748X_SDP_VID_SEL_MASK 0xf0
+#define ADV748X_SDP_VID_SEL_SHIFT 4
+
+/* Contrast - Unsigned*/
+#define ADV748X_SDP_CON 0x08 /* user_map_rw_reg_08 */
+#define ADV748X_SDP_CON_MIN 0
+#define ADV748X_SDP_CON_DEF 128
+#define ADV748X_SDP_CON_MAX 255
+
+/* Brightness - Signed */
+#define ADV748X_SDP_BRI 0x0a /* user_map_rw_reg_0a */
+#define ADV748X_SDP_BRI_MIN -128
+#define ADV748X_SDP_BRI_DEF 0
+#define ADV748X_SDP_BRI_MAX 127
+
+/* Hue - Signed, inverted*/
+#define ADV748X_SDP_HUE 0x0b /* user_map_rw_reg_0b */
+#define ADV748X_SDP_HUE_MIN -127
+#define ADV748X_SDP_HUE_DEF 0
+#define ADV748X_SDP_HUE_MAX 128
+
+/* Test Patterns / Default Values */
+#define ADV748X_SDP_DEF 0x0c /* user_map_rw_reg_0c */
+#define ADV748X_SDP_DEF_VAL_EN BIT(0) /* Force free run mode */
+#define ADV748X_SDP_DEF_VAL_AUTO_EN BIT(1) /* Free run when no signal */
+
+#define ADV748X_SDP_MAP_SEL 0x0e /* user_map_rw_reg_0e */
+#define ADV748X_SDP_MAP_SEL_RO_MAIN 1
+
+/* Free run pattern select */
+#define ADV748X_SDP_FRP 0x14
+#define ADV748X_SDP_FRP_MASK GENMASK(3, 1)
+
+/* Saturation */
+#define ADV748X_SDP_SD_SAT_U 0xe3 /* user_map_rw_reg_e3 */
+#define ADV748X_SDP_SD_SAT_V 0xe4 /* user_map_rw_reg_e4 */
+#define ADV748X_SDP_SAT_MIN 0
+#define ADV748X_SDP_SAT_DEF 128
+#define ADV748X_SDP_SAT_MAX 255
+
+/* SDP RO Main Map */
+#define ADV748X_SDP_RO_10 0x10
+#define ADV748X_SDP_RO_10_IN_LOCK BIT(0)
+
+/* CP Map */
+#define ADV748X_CP_PAT_GEN 0x37 /* int_pat_gen_1 */
+#define ADV748X_CP_PAT_GEN_EN BIT(7)
+
+/* Contrast Control - Unsigned */
+#define ADV748X_CP_CON 0x3a /* contrast_cntrl */
+#define ADV748X_CP_CON_MIN 0 /* Minimum contrast */
+#define ADV748X_CP_CON_DEF 128 /* Default */
+#define ADV748X_CP_CON_MAX 255 /* Maximum contrast */
+
+/* Saturation Control - Unsigned */
+#define ADV748X_CP_SAT 0x3b /* saturation_cntrl */
+#define ADV748X_CP_SAT_MIN 0 /* Minimum saturation */
+#define ADV748X_CP_SAT_DEF 128 /* Default */
+#define ADV748X_CP_SAT_MAX 255 /* Maximum saturation */
+
+/* Brightness Control - Signed */
+#define ADV748X_CP_BRI 0x3c /* brightness_cntrl */
+#define ADV748X_CP_BRI_MIN -128 /* Luma is -512d */
+#define ADV748X_CP_BRI_DEF 0 /* Luma is 0 */
+#define ADV748X_CP_BRI_MAX 127 /* Luma is 508d */
+
+/* Hue Control */
+#define ADV748X_CP_HUE 0x3d /* hue_cntrl */
+#define ADV748X_CP_HUE_MIN 0 /* -90 degree */
+#define ADV748X_CP_HUE_DEF 0 /* -90 degree */
+#define ADV748X_CP_HUE_MAX 255 /* +90 degree */
+
+#define ADV748X_CP_VID_ADJ 0x3e /* vid_adj_0 */
+#define ADV748X_CP_VID_ADJ_ENABLE BIT(7) /* Enable colour controls */
+
+#define ADV748X_CP_DE_POS_HIGH 0x8b /* de_pos_adj_6 */
+#define ADV748X_CP_DE_POS_HIGH_SET BIT(6)
+#define ADV748X_CP_DE_POS_END_LOW 0x8c /* de_pos_adj_7 */
+#define ADV748X_CP_DE_POS_START_LOW 0x8d /* de_pos_adj_8 */
+
+#define ADV748X_CP_VID_ADJ_2 0x91
+#define ADV748X_CP_VID_ADJ_2_INTERLACED BIT(6)
+#define ADV748X_CP_VID_ADJ_2_INTERLACED_3D BIT(4)
+
+#define ADV748X_CP_CLMP_POS 0xc9 /* clmp_pos_cntrl_4 */
+#define ADV748X_CP_CLMP_POS_DIS_AUTO BIT(0) /* dis_auto_param_buff */
+
+/* CSI : TXA/TXB Maps */
+#define ADV748X_CSI_VC_REF 0x0d /* csi_tx_top_reg_0d */
+#define ADV748X_CSI_VC_REF_SHIFT 6
+
+#define ADV748X_CSI_FS_AS_LS 0x1e /* csi_tx_top_reg_1e */
+#define ADV748X_CSI_FS_AS_LS_UNKNOWN BIT(6) /* Undocumented bit */
+
+/* Register handling */
+
+int adv748x_read(struct adv748x_state *state, u8 addr, u8 reg);
+int adv748x_write(struct adv748x_state *state, u8 page, u8 reg, u8 value);
+int adv748x_write_block(struct adv748x_state *state, int client_page,
+ unsigned int init_reg, const void *val,
+ size_t val_len);
+
+#define io_read(s, r) adv748x_read(s, ADV748X_PAGE_IO, r)
+#define io_write(s, r, v) adv748x_write(s, ADV748X_PAGE_IO, r, v)
+#define io_clrset(s, r, m, v) io_write(s, r, (io_read(s, r) & ~m) | v)
+
+#define hdmi_read(s, r) adv748x_read(s, ADV748X_PAGE_HDMI, r)
+#define hdmi_read16(s, r, m) (((hdmi_read(s, r) << 8) | hdmi_read(s, r+1)) & m)
+#define hdmi_write(s, r, v) adv748x_write(s, ADV748X_PAGE_HDMI, r, v)
+
+#define repeater_read(s, r) adv748x_read(s, ADV748X_PAGE_REPEATER, r)
+#define repeater_write(s, r, v) adv748x_write(s, ADV748X_PAGE_REPEATER, r, v)
+
+#define sdp_read(s, r) adv748x_read(s, ADV748X_PAGE_SDP, r)
+#define sdp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_SDP, r, v)
+#define sdp_clrset(s, r, m, v) sdp_write(s, r, (sdp_read(s, r) & ~m) | v)
+
+#define cp_read(s, r) adv748x_read(s, ADV748X_PAGE_CP, r)
+#define cp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_CP, r, v)
+#define cp_clrset(s, r, m, v) cp_write(s, r, (cp_read(s, r) & ~m) | v)
+
+#define txa_read(s, r) adv748x_read(s, ADV748X_PAGE_TXA, r)
+#define txb_read(s, r) adv748x_read(s, ADV748X_PAGE_TXB, r)
+
+#define tx_read(t, r) adv748x_read(t->state, t->page, r)
+#define tx_write(t, r, v) adv748x_write(t->state, t->page, r, v)
+
+static inline struct v4l2_subdev *adv748x_get_remote_sd(struct media_pad *pad)
+{
+ pad = media_entity_remote_pad(pad);
+ if (!pad)
+ return NULL;
+
+ return media_entity_to_v4l2_subdev(pad->entity);
+}
+
+void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state,
+ const struct v4l2_subdev_ops *ops, u32 function,
+ const char *ident);
+
+int adv748x_register_subdevs(struct adv748x_state *state,
+ struct v4l2_device *v4l2_dev);
+
+int adv748x_txa_power(struct adv748x_state *state, bool on);
+int adv748x_txb_power(struct adv748x_state *state, bool on);
+
+int adv748x_afe_init(struct adv748x_afe *afe);
+void adv748x_afe_cleanup(struct adv748x_afe *afe);
+
+int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx);
+void adv748x_csi2_cleanup(struct adv748x_csi2 *tx);
+int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate);
+
+int adv748x_hdmi_init(struct adv748x_hdmi *hdmi);
+void adv748x_hdmi_cleanup(struct adv748x_hdmi *hdmi);
+
+#endif /* _ADV748X_H_ */
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index ccc478605643..2817bafc67bf 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -1927,8 +1927,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
state->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
- state, dev_name(&client->dev), CEC_CAP_TRANSMIT |
- CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | CEC_CAP_RC,
+ state, dev_name(&client->dev), CEC_CAP_DEFAULTS,
ADV7511_MAX_ADDRS);
err = PTR_ERR_OR_ZERO(state->cec_adap);
if (err) {
@@ -1986,7 +1985,7 @@ static int adv7511_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static struct i2c_device_id adv7511_id[] = {
+static const struct i2c_device_id adv7511_id[] = {
{ "adv7511", 0 },
{ }
};
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 660bacb8f7d9..f289b8aca1da 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -618,7 +618,7 @@ static int adv76xx_read_reg(struct v4l2_subdev *sd, unsigned int reg)
unsigned int val;
int err;
- if (!(BIT(page) & state->info->page_mask))
+ if (page >= ADV76XX_PAGE_MAX || !(BIT(page) & state->info->page_mask))
return -EINVAL;
reg &= 0xff;
@@ -633,7 +633,7 @@ static int adv76xx_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val)
struct adv76xx_state *state = to_state(sd);
unsigned int page = reg >> 8;
- if (!(BIT(page) & state->info->page_mask))
+ if (page >= ADV76XX_PAGE_MAX || !(BIT(page) & state->info->page_mask))
return -EINVAL;
reg &= 0xff;
@@ -3515,8 +3515,7 @@ static int adv76xx_probe(struct i2c_client *client,
#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops,
state, dev_name(&client->dev),
- CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
- CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV76XX_MAX_ADDRS);
+ CEC_CAP_DEFAULTS, ADV76XX_MAX_ADDRS);
err = PTR_ERR_OR_ZERO(state->cec_adap);
if (err)
goto err_entity;
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index 303effda1a2e..65f34e7e146f 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -3568,8 +3568,7 @@ static int adv7842_probe(struct i2c_client *client,
#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
state->cec_adap = cec_allocate_adapter(&adv7842_cec_adap_ops,
state, dev_name(&client->dev),
- CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
- CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV7842_MAX_ADDRS);
+ CEC_CAP_DEFAULTS, ADV7842_MAX_ADDRS);
err = PTR_ERR_OR_ZERO(state->cec_adap);
if (err)
goto err_entity;
@@ -3608,7 +3607,7 @@ static int adv7842_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static struct i2c_device_id adv7842_id[] = {
+static const struct i2c_device_id adv7842_id[] = {
{ "adv7842", 0 },
{ }
};
diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c
index 6a607d7f82de..95af4fc99cd0 100644
--- a/drivers/media/i2c/dw9714.c
+++ b/drivers/media/i2c/dw9714.c
@@ -11,7 +11,6 @@
* GNU General Public License for more details.
*/
-#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
@@ -147,8 +146,7 @@ static int dw9714_init_controls(struct dw9714_device *dev_vcm)
return hdl->error;
}
-static int dw9714_probe(struct i2c_client *client,
- const struct i2c_device_id *devid)
+static int dw9714_probe(struct i2c_client *client)
{
struct dw9714_device *dw9714_dev;
int rval;
@@ -250,20 +248,18 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev)
return 0;
}
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id dw9714_acpi_match[] = {
- {},
-};
-MODULE_DEVICE_TABLE(acpi, dw9714_acpi_match);
-#endif
-
static const struct i2c_device_id dw9714_id_table[] = {
- {DW9714_NAME, 0},
- {}
+ { DW9714_NAME, 0 },
+ { { 0 } }
};
-
MODULE_DEVICE_TABLE(i2c, dw9714_id_table);
+static const struct of_device_id dw9714_of_table[] = {
+ { .compatible = "dongwoon,dw9714" },
+ { { 0 } }
+};
+MODULE_DEVICE_TABLE(of, dw9714_of_table);
+
static const struct dev_pm_ops dw9714_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dw9714_vcm_suspend, dw9714_vcm_resume)
SET_RUNTIME_PM_OPS(dw9714_vcm_suspend, dw9714_vcm_resume, NULL)
@@ -273,9 +269,9 @@ static struct i2c_driver dw9714_i2c_driver = {
.driver = {
.name = DW9714_NAME,
.pm = &dw9714_pm_ops,
- .acpi_match_table = ACPI_PTR(dw9714_acpi_match),
+ .of_match_table = dw9714_of_table,
},
- .probe = dw9714_probe,
+ .probe_new = dw9714_probe,
.remove = dw9714_remove,
.id_table = dw9714_id_table,
};
diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
index f39f5179dd95..c14f0fd6ded3 100644
--- a/drivers/media/i2c/et8ek8/et8ek8_driver.c
+++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
@@ -43,7 +43,7 @@
#define ET8EK8_NAME "et8ek8"
#define ET8EK8_PRIV_MEM_SIZE 128
-#define ET8EK8_MAX_MSG 48
+#define ET8EK8_MAX_MSG 8
struct et8ek8_sensor {
struct v4l2_subdev subdev;
@@ -220,7 +220,8 @@ static void et8ek8_i2c_create_msg(struct i2c_client *client, u16 len, u16 reg,
/*
* A buffered write method that puts the wanted register write
- * commands in a message list and passes the list to the i2c framework
+ * commands in smaller number of message lists and passes the lists to
+ * the i2c framework
*/
static int et8ek8_i2c_buffered_write_regs(struct i2c_client *client,
const struct et8ek8_reg *wnext,
@@ -231,11 +232,7 @@ static int et8ek8_i2c_buffered_write_regs(struct i2c_client *client,
int wcnt = 0;
u16 reg, data_length;
u32 val;
-
- if (WARN_ONCE(cnt > ET8EK8_MAX_MSG,
- ET8EK8_NAME ": %s: too many messages.\n", __func__)) {
- return -EINVAL;
- }
+ int rval;
/* Create new write messages for all writes */
while (wcnt < cnt) {
@@ -249,10 +246,21 @@ static int et8ek8_i2c_buffered_write_regs(struct i2c_client *client,
/* Update write count */
wcnt++;
+
+ if (wcnt < ET8EK8_MAX_MSG)
+ continue;
+
+ rval = i2c_transfer(client->adapter, msg, wcnt);
+ if (rval < 0)
+ return rval;
+
+ cnt -= wcnt;
+ wcnt = 0;
}
- /* Now we send everything ... */
- return i2c_transfer(client->adapter, msg, wcnt);
+ rval = i2c_transfer(client->adapter, msg, wcnt);
+
+ return rval < 0 ? rval : 0;
}
/*
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index cee7fd9cf08b..a374e2a0ac3d 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -59,8 +59,8 @@ module_param(debug, int, 0644); /* debug level (0,1,2) */
/* ----------------------------------------------------------------------- */
-static int get_key_haup_common(struct IR_i2c *ir, enum rc_type *protocol,
- u32 *scancode, u8 *ptoggle, int size)
+static int get_key_haup_common(struct IR_i2c *ir, enum rc_proto *protocol,
+ u32 *scancode, u8 *ptoggle, int size)
{
unsigned char buf[6];
int start, range, toggle, dev, code, ircode, vendor;
@@ -99,7 +99,7 @@ static int get_key_haup_common(struct IR_i2c *ir, enum rc_type *protocol,
dprintk(1, "ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n",
start, range, toggle, dev, code);
- *protocol = RC_TYPE_RC5;
+ *protocol = RC_PROTO_RC5;
*scancode = RC_SCANCODE_RC5(dev, code);
*ptoggle = toggle;
@@ -111,13 +111,13 @@ static int get_key_haup_common(struct IR_i2c *ir, enum rc_type *protocol,
if (vendor == 0x800f) {
*ptoggle = (dev & 0x80) != 0;
- *protocol = RC_TYPE_RC6_MCE;
+ *protocol = RC_PROTO_RC6_MCE;
dev &= 0x7f;
dprintk(1, "ir hauppauge (rc6-mce): t%d vendor=%d dev=%d code=%d\n",
*ptoggle, vendor, dev, code);
} else {
*ptoggle = 0;
- *protocol = RC_TYPE_RC6_6A_32;
+ *protocol = RC_PROTO_RC6_6A_32;
dprintk(1, "ir hauppauge (rc6-6a-32): vendor=%d dev=%d code=%d\n",
vendor, dev, code);
}
@@ -130,13 +130,13 @@ static int get_key_haup_common(struct IR_i2c *ir, enum rc_type *protocol,
return 0;
}
-static int get_key_haup(struct IR_i2c *ir, enum rc_type *protocol,
+static int get_key_haup(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
return get_key_haup_common(ir, protocol, scancode, toggle, 3);
}
-static int get_key_haup_xvr(struct IR_i2c *ir, enum rc_type *protocol,
+static int get_key_haup_xvr(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
int ret;
@@ -155,7 +155,7 @@ static int get_key_haup_xvr(struct IR_i2c *ir, enum rc_type *protocol,
return get_key_haup_common(ir, protocol, scancode, toggle, 6);
}
-static int get_key_pixelview(struct IR_i2c *ir, enum rc_type *protocol,
+static int get_key_pixelview(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
unsigned char b;
@@ -166,13 +166,13 @@ static int get_key_pixelview(struct IR_i2c *ir, enum rc_type *protocol,
return -EIO;
}
- *protocol = RC_TYPE_OTHER;
+ *protocol = RC_PROTO_OTHER;
*scancode = b;
*toggle = 0;
return 1;
}
-static int get_key_fusionhdtv(struct IR_i2c *ir, enum rc_type *protocol,
+static int get_key_fusionhdtv(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
unsigned char buf[4];
@@ -191,13 +191,13 @@ static int get_key_fusionhdtv(struct IR_i2c *ir, enum rc_type *protocol,
if(buf[0] != 0x1 || buf[1] != 0xfe)
return 0;
- *protocol = RC_TYPE_UNKNOWN;
+ *protocol = RC_PROTO_UNKNOWN;
*scancode = buf[2];
*toggle = 0;
return 1;
}
-static int get_key_knc1(struct IR_i2c *ir, enum rc_type *protocol,
+static int get_key_knc1(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
unsigned char b;
@@ -221,13 +221,13 @@ static int get_key_knc1(struct IR_i2c *ir, enum rc_type *protocol,
/* keep old data */
return 1;
- *protocol = RC_TYPE_UNKNOWN;
+ *protocol = RC_PROTO_UNKNOWN;
*scancode = b;
*toggle = 0;
return 1;
}
-static int get_key_avermedia_cardbus(struct IR_i2c *ir, enum rc_type *protocol,
+static int get_key_avermedia_cardbus(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
unsigned char subaddr, key, keygroup;
@@ -262,7 +262,7 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, enum rc_type *protocol,
}
key |= (keygroup & 1) << 6;
- *protocol = RC_TYPE_UNKNOWN;
+ *protocol = RC_PROTO_UNKNOWN;
*scancode = key;
if (ir->c->addr == 0x41) /* AVerMedia EM78P153 */
*scancode |= keygroup << 8;
@@ -274,7 +274,7 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, enum rc_type *protocol,
static int ir_key_poll(struct IR_i2c *ir)
{
- enum rc_type protocol;
+ enum rc_proto protocol;
u32 scancode;
u8 toggle;
int rc;
@@ -315,7 +315,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
char *ir_codes = NULL;
const char *name = NULL;
- u64 rc_type = RC_BIT_UNKNOWN;
+ u64 rc_proto = RC_PROTO_BIT_UNKNOWN;
struct IR_i2c *ir;
struct rc_dev *rc = NULL;
struct i2c_adapter *adap = client->adapter;
@@ -334,7 +334,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
case 0x64:
name = "Pixelview";
ir->get_key = get_key_pixelview;
- rc_type = RC_BIT_OTHER;
+ rc_proto = RC_PROTO_BIT_OTHER;
ir_codes = RC_MAP_EMPTY;
break;
case 0x18:
@@ -342,38 +342,39 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
case 0x1a:
name = "Hauppauge";
ir->get_key = get_key_haup;
- rc_type = RC_BIT_RC5;
+ rc_proto = RC_PROTO_BIT_RC5;
ir_codes = RC_MAP_HAUPPAUGE;
break;
case 0x30:
name = "KNC One";
ir->get_key = get_key_knc1;
- rc_type = RC_BIT_OTHER;
+ rc_proto = RC_PROTO_BIT_OTHER;
ir_codes = RC_MAP_EMPTY;
break;
case 0x6b:
name = "FusionHDTV";
ir->get_key = get_key_fusionhdtv;
- rc_type = RC_BIT_UNKNOWN;
+ rc_proto = RC_PROTO_BIT_UNKNOWN;
ir_codes = RC_MAP_FUSIONHDTV_MCE;
break;
case 0x40:
name = "AVerMedia Cardbus remote";
ir->get_key = get_key_avermedia_cardbus;
- rc_type = RC_BIT_OTHER;
+ rc_proto = RC_PROTO_BIT_OTHER;
ir_codes = RC_MAP_AVERMEDIA_CARDBUS;
break;
case 0x41:
name = "AVerMedia EM78P153";
ir->get_key = get_key_avermedia_cardbus;
- rc_type = RC_BIT_OTHER;
+ rc_proto = RC_PROTO_BIT_OTHER;
/* RM-KV remote, seems to be same as RM-K6 */
ir_codes = RC_MAP_AVERMEDIA_M733A_RM_K6;
break;
case 0x71:
name = "Hauppauge/Zilog Z8";
ir->get_key = get_key_haup_xvr;
- rc_type = RC_BIT_RC5 | RC_BIT_RC6_MCE | RC_BIT_RC6_6A_32;
+ rc_proto = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC6_MCE |
+ RC_PROTO_BIT_RC6_6A_32;
ir_codes = RC_MAP_HAUPPAUGE;
break;
}
@@ -388,7 +389,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
name = init_data->name;
if (init_data->type)
- rc_type = init_data->type;
+ rc_proto = init_data->type;
if (init_data->polling_interval)
ir->polling_interval = init_data->polling_interval;
@@ -431,7 +432,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
ir->rc = rc;
/* Make sure we are all setup before going on */
- if (!name || !ir->get_key || !rc_type || !ir_codes) {
+ if (!name || !ir->get_key || !rc_proto || !ir_codes) {
dprintk(1, ": Unsupported device at address 0x%02x\n",
addr);
err = -ENODEV;
@@ -452,14 +453,14 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
*/
rc->input_id.bustype = BUS_I2C;
rc->input_phys = ir->phys;
- rc->input_name = ir->name;
+ rc->device_name = ir->name;
/*
* Initialize the other fields of rc_dev
*/
rc->map_name = ir->ir_codes;
- rc->allowed_protocols = rc_type;
- rc->enabled_protocols = rc_type;
+ rc->allowed_protocols = rc_proto;
+ rc->enabled_protocols = rc_proto;
if (!rc->driver_name)
rc->driver_name = MODULE_NAME;
diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c
index 9ccb5ee55fa9..463534d44756 100644
--- a/drivers/media/i2c/m5mols/m5mols_core.c
+++ b/drivers/media/i2c/m5mols/m5mols_core.c
@@ -457,7 +457,7 @@ static int m5mols_get_version(struct v4l2_subdev *sd)
v4l2_info(sd, "Manufacturer\t[%s]\n",
is_manufacturer(info, REG_SAMSUNG_ELECTRO) ?
- "Samsung Electro-Machanics" :
+ "Samsung Electro-Mechanics" :
is_manufacturer(info, REG_SAMSUNG_OPTICS) ?
"Samsung Fiber-Optics" :
is_manufacturer(info, REG_SAMSUNG_TECHWIN) ?
diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c
index a4736a8a7792..bf0e821a2b93 100644
--- a/drivers/media/i2c/max2175.c
+++ b/drivers/media/i2c/max2175.c
@@ -1319,7 +1319,7 @@ static int max2175_probe(struct i2c_client *client,
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(&client->dev, "cannot get clock %d\n", ret);
- return -ENODEV;
+ return ret;
}
regmap = devm_regmap_init_i2c(client, &max2175_regmap_config);
diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c
index 72e71b762827..99b992e46702 100644
--- a/drivers/media/i2c/mt9m111.c
+++ b/drivers/media/i2c/mt9m111.c
@@ -835,7 +835,7 @@ static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
.s_ctrl = mt9m111_s_ctrl,
};
-static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
+static const struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
.s_power = mt9m111_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9m111_g_register,
@@ -865,7 +865,7 @@ static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
return 0;
}
-static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
+static const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
.g_mbus_config = mt9m111_g_mbus_config,
};
@@ -877,7 +877,7 @@ static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = {
.set_fmt = mt9m111_set_fmt,
};
-static struct v4l2_subdev_ops mt9m111_subdev_ops = {
+static const struct v4l2_subdev_ops mt9m111_subdev_ops = {
.core = &mt9m111_subdev_core_ops,
.video = &mt9m111_subdev_video_ops,
.pad = &mt9m111_subdev_pad_ops,
diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c
index 842017fa4aab..9d981d9f5686 100644
--- a/drivers/media/i2c/mt9t001.c
+++ b/drivers/media/i2c/mt9t001.c
@@ -822,15 +822,15 @@ static int mt9t001_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
return mt9t001_set_power(subdev, 0);
}
-static struct v4l2_subdev_core_ops mt9t001_subdev_core_ops = {
+static const struct v4l2_subdev_core_ops mt9t001_subdev_core_ops = {
.s_power = mt9t001_set_power,
};
-static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = {
+static const struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = {
.s_stream = mt9t001_s_stream,
};
-static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = {
+static const struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = {
.enum_mbus_code = mt9t001_enum_mbus_code,
.enum_frame_size = mt9t001_enum_frame_size,
.get_fmt = mt9t001_get_format,
@@ -839,7 +839,7 @@ static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = {
.set_selection = mt9t001_set_selection,
};
-static struct v4l2_subdev_ops mt9t001_subdev_ops = {
+static const struct v4l2_subdev_ops mt9t001_subdev_ops = {
.core = &mt9t001_subdev_core_ops,
.video = &mt9t001_subdev_video_ops,
.pad = &mt9t001_subdev_pad_ops,
diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c
index 86550d8ddfee..af7af0d14c69 100644
--- a/drivers/media/i2c/ov13858.c
+++ b/drivers/media/i2c/ov13858.c
@@ -57,16 +57,14 @@
#define OV13858_VTS_30FPS 0x0c8e /* 30 fps */
#define OV13858_VTS_60FPS 0x0648 /* 60 fps */
#define OV13858_VTS_MAX 0x7fff
-#define OV13858_VBLANK_MIN 56
/* HBLANK control - read only */
-#define OV13858_PPL_540MHZ 2244
-#define OV13858_PPL_1080MHZ 4488
+#define OV13858_PPL_270MHZ 2244
+#define OV13858_PPL_540MHZ 4488
/* Exposure control */
#define OV13858_REG_EXPOSURE 0x3500
#define OV13858_EXPOSURE_MIN 4
-#define OV13858_EXPOSURE_MAX (OV13858_VTS_MAX - 8)
#define OV13858_EXPOSURE_STEP 1
#define OV13858_EXPOSURE_DEFAULT 0x640
@@ -78,13 +76,13 @@
#define OV13858_ANA_GAIN_DEFAULT 0x80
/* Digital gain control */
-#define OV13858_REG_DIGITAL_GAIN 0x350a
-#define OV13858_DGTL_GAIN_MASK 0xf3
-#define OV13858_DGTL_GAIN_SHIFT 2
-#define OV13858_DGTL_GAIN_MIN 1
-#define OV13858_DGTL_GAIN_MAX 4
-#define OV13858_DGTL_GAIN_STEP 1
-#define OV13858_DGTL_GAIN_DEFAULT 1
+#define OV13858_REG_B_MWB_GAIN 0x5100
+#define OV13858_REG_G_MWB_GAIN 0x5102
+#define OV13858_REG_R_MWB_GAIN 0x5104
+#define OV13858_DGTL_GAIN_MIN 0
+#define OV13858_DGTL_GAIN_MAX 16384 /* Max = 16 X */
+#define OV13858_DGTL_GAIN_DEFAULT 1024 /* Default gain = 1 X */
+#define OV13858_DGTL_GAIN_STEP 1 /* Each step = 1/1024 */
/* Test Pattern Control */
#define OV13858_REG_TEST_PATTERN 0x4503
@@ -121,7 +119,8 @@ struct ov13858_mode {
u32 height;
/* V-timing */
- u32 vts;
+ u32 vts_def;
+ u32 vts_min;
/* Index of Link frequency config to be used */
u32 link_freq_index;
@@ -944,31 +943,33 @@ static const char * const ov13858_test_pattern_menu[] = {
/* Configurations for supported link frequencies */
#define OV13858_NUM_OF_LINK_FREQS 2
-#define OV13858_LINK_FREQ_1080MBPS 1080000000
-#define OV13858_LINK_FREQ_540MBPS 540000000
+#define OV13858_LINK_FREQ_540MHZ 540000000ULL
+#define OV13858_LINK_FREQ_270MHZ 270000000ULL
#define OV13858_LINK_FREQ_INDEX_0 0
#define OV13858_LINK_FREQ_INDEX_1 1
/* Menu items for LINK_FREQ V4L2 control */
static const s64 link_freq_menu_items[OV13858_NUM_OF_LINK_FREQS] = {
- OV13858_LINK_FREQ_1080MBPS,
- OV13858_LINK_FREQ_540MBPS
+ OV13858_LINK_FREQ_540MHZ,
+ OV13858_LINK_FREQ_270MHZ
};
/* Link frequency configs */
static const struct ov13858_link_freq_config
link_freq_configs[OV13858_NUM_OF_LINK_FREQS] = {
{
- .pixel_rate = 864000000,
- .pixels_per_line = OV13858_PPL_1080MHZ,
+ /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
+ .pixel_rate = (OV13858_LINK_FREQ_540MHZ * 2 * 4) / 10,
+ .pixels_per_line = OV13858_PPL_540MHZ,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mipi_data_rate_1080mbps),
.regs = mipi_data_rate_1080mbps,
}
},
{
- .pixel_rate = 432000000,
- .pixels_per_line = OV13858_PPL_540MHZ,
+ /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
+ .pixel_rate = (OV13858_LINK_FREQ_270MHZ * 2 * 4) / 10,
+ .pixels_per_line = OV13858_PPL_270MHZ,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mipi_data_rate_540mbps),
.regs = mipi_data_rate_540mbps,
@@ -981,7 +982,8 @@ static const struct ov13858_mode supported_modes[] = {
{
.width = 4224,
.height = 3136,
- .vts = OV13858_VTS_30FPS,
+ .vts_def = OV13858_VTS_30FPS,
+ .vts_min = OV13858_VTS_30FPS,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_4224x3136_regs),
.regs = mode_4224x3136_regs,
@@ -991,7 +993,8 @@ static const struct ov13858_mode supported_modes[] = {
{
.width = 2112,
.height = 1568,
- .vts = OV13858_VTS_30FPS,
+ .vts_def = OV13858_VTS_30FPS,
+ .vts_min = 1608,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_2112x1568_regs),
.regs = mode_2112x1568_regs,
@@ -1001,7 +1004,8 @@ static const struct ov13858_mode supported_modes[] = {
{
.width = 2112,
.height = 1188,
- .vts = OV13858_VTS_30FPS,
+ .vts_def = OV13858_VTS_30FPS,
+ .vts_min = 1608,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_2112x1188_regs),
.regs = mode_2112x1188_regs,
@@ -1011,7 +1015,8 @@ static const struct ov13858_mode supported_modes[] = {
{
.width = 1056,
.height = 784,
- .vts = OV13858_VTS_30FPS,
+ .vts_def = OV13858_VTS_30FPS,
+ .vts_min = 804,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_1056x784_regs),
.regs = mode_1056x784_regs,
@@ -1161,21 +1166,21 @@ static int ov13858_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
static int ov13858_update_digital_gain(struct ov13858 *ov13858, u32 d_gain)
{
int ret;
- u32 val;
- if (d_gain == 3)
- return -EINVAL;
+ ret = ov13858_write_reg(ov13858, OV13858_REG_B_MWB_GAIN,
+ OV13858_REG_VALUE_16BIT, d_gain);
+ if (ret)
+ return ret;
- ret = ov13858_read_reg(ov13858, OV13858_REG_DIGITAL_GAIN,
- OV13858_REG_VALUE_08BIT, &val);
+ ret = ov13858_write_reg(ov13858, OV13858_REG_G_MWB_GAIN,
+ OV13858_REG_VALUE_16BIT, d_gain);
if (ret)
return ret;
- val &= OV13858_DGTL_GAIN_MASK;
- val |= (d_gain - 1) << OV13858_DGTL_GAIN_SHIFT;
+ ret = ov13858_write_reg(ov13858, OV13858_REG_R_MWB_GAIN,
+ OV13858_REG_VALUE_16BIT, d_gain);
- return ov13858_write_reg(ov13858, OV13858_REG_DIGITAL_GAIN,
- OV13858_REG_VALUE_08BIT, val);
+ return ret;
}
static int ov13858_enable_test_pattern(struct ov13858 *ov13858, u32 pattern)
@@ -1377,6 +1382,8 @@ ov13858_set_pad_format(struct v4l2_subdev *sd,
struct ov13858 *ov13858 = to_ov13858(sd);
const struct ov13858_mode *mode;
struct v4l2_mbus_framefmt *framefmt;
+ s32 vblank_def;
+ s32 vblank_min;
s64 h_blank;
mutex_lock(&ov13858->mutex);
@@ -1397,10 +1404,15 @@ ov13858_set_pad_format(struct v4l2_subdev *sd,
ov13858->pixel_rate,
link_freq_configs[mode->link_freq_index].pixel_rate);
/* Update limits and set FPS to default */
+ vblank_def = ov13858->cur_mode->vts_def -
+ ov13858->cur_mode->height;
+ vblank_min = ov13858->cur_mode->vts_min -
+ ov13858->cur_mode->height;
__v4l2_ctrl_modify_range(
- ov13858->vblank, OV13858_VBLANK_MIN,
+ ov13858->vblank, vblank_min,
OV13858_VTS_MAX - ov13858->cur_mode->height, 1,
- ov13858->cur_mode->vts - ov13858->cur_mode->height);
+ vblank_def);
+ __v4l2_ctrl_s_ctrl(ov13858->vblank, vblank_def);
h_blank =
link_freq_configs[mode->link_freq_index].pixels_per_line
- ov13858->cur_mode->width;
@@ -1602,6 +1614,9 @@ static int ov13858_init_controls(struct ov13858 *ov13858)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd);
struct v4l2_ctrl_handler *ctrl_hdlr;
+ s64 exposure_max;
+ s64 vblank_def;
+ s64 vblank_min;
int ret;
ctrl_hdlr = &ov13858->ctrl_handler;
@@ -1625,25 +1640,27 @@ static int ov13858_init_controls(struct ov13858 *ov13858)
link_freq_configs[0].pixel_rate, 1,
link_freq_configs[0].pixel_rate);
+ vblank_def = ov13858->cur_mode->vts_def - ov13858->cur_mode->height;
+ vblank_min = ov13858->cur_mode->vts_min - ov13858->cur_mode->height;
ov13858->vblank = v4l2_ctrl_new_std(
ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_VBLANK,
- OV13858_VBLANK_MIN,
+ vblank_min,
OV13858_VTS_MAX - ov13858->cur_mode->height, 1,
- ov13858->cur_mode->vts
- - ov13858->cur_mode->height);
+ vblank_def);
ov13858->hblank = v4l2_ctrl_new_std(
ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_HBLANK,
- OV13858_PPL_1080MHZ - ov13858->cur_mode->width,
- OV13858_PPL_1080MHZ - ov13858->cur_mode->width,
+ OV13858_PPL_540MHZ - ov13858->cur_mode->width,
+ OV13858_PPL_540MHZ - ov13858->cur_mode->width,
1,
- OV13858_PPL_1080MHZ - ov13858->cur_mode->width);
+ OV13858_PPL_540MHZ - ov13858->cur_mode->width);
ov13858->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ exposure_max = ov13858->cur_mode->vts_def - 8;
ov13858->exposure = v4l2_ctrl_new_std(
ctrl_hdlr, &ov13858_ctrl_ops,
V4L2_CID_EXPOSURE, OV13858_EXPOSURE_MIN,
- OV13858_EXPOSURE_MAX, OV13858_EXPOSURE_STEP,
+ exposure_max, OV13858_EXPOSURE_STEP,
OV13858_EXPOSURE_DEFAULT);
v4l2_ctrl_new_std(ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 1f5b483cf334..39a2269c0bee 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1524,8 +1524,7 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor)
static void ov5640_power(struct ov5640_dev *sensor, bool enable)
{
- if (sensor->pwdn_gpio)
- gpiod_set_value(sensor->pwdn_gpio, enable ? 0 : 1);
+ gpiod_set_value(sensor->pwdn_gpio, enable ? 0 : 1);
}
static void ov5640_reset(struct ov5640_dev *sensor)
diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c
index d1e844f7f03f..d28845f7356f 100644
--- a/drivers/media/i2c/ov5645.c
+++ b/drivers/media/i2c/ov5645.c
@@ -80,6 +80,8 @@ struct ov5645_mode_info {
u32 height;
const struct reg_value *data;
u32 data_size;
+ u32 pixel_clock;
+ u32 link_freq;
};
struct ov5645 {
@@ -99,6 +101,8 @@ struct ov5645 {
const struct ov5645_mode_info *current_mode;
struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *pixel_clock;
+ struct v4l2_ctrl *link_freq;
/* Cached register values */
u8 aec_pk_manual;
@@ -505,24 +509,35 @@ static const struct reg_value ov5645_setting_full[] = {
{ 0x4202, 0x00 }
};
+static const s64 link_freq[] = {
+ 222880000,
+ 334320000
+};
+
static const struct ov5645_mode_info ov5645_mode_info_data[] = {
{
.width = 1280,
.height = 960,
.data = ov5645_setting_sxga,
- .data_size = ARRAY_SIZE(ov5645_setting_sxga)
+ .data_size = ARRAY_SIZE(ov5645_setting_sxga),
+ .pixel_clock = 111440000,
+ .link_freq = 0 /* an index in link_freq[] */
},
{
.width = 1920,
.height = 1080,
.data = ov5645_setting_1080p,
- .data_size = ARRAY_SIZE(ov5645_setting_1080p)
+ .data_size = ARRAY_SIZE(ov5645_setting_1080p),
+ .pixel_clock = 167160000,
+ .link_freq = 1 /* an index in link_freq[] */
},
{
.width = 2592,
.height = 1944,
.data = ov5645_setting_full,
- .data_size = ARRAY_SIZE(ov5645_setting_full)
+ .data_size = ARRAY_SIZE(ov5645_setting_full),
+ .pixel_clock = 167160000,
+ .link_freq = 1 /* an index in link_freq[] */
},
};
@@ -969,6 +984,7 @@ static int ov5645_set_format(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *__format;
struct v4l2_rect *__crop;
const struct ov5645_mode_info *new_mode;
+ int ret;
__crop = __ov5645_get_pad_crop(ov5645, cfg, format->pad,
format->which);
@@ -978,8 +994,19 @@ static int ov5645_set_format(struct v4l2_subdev *sd,
__crop->width = new_mode->width;
__crop->height = new_mode->height;
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = v4l2_ctrl_s_ctrl_int64(ov5645->pixel_clock,
+ new_mode->pixel_clock);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_ctrl_s_ctrl(ov5645->link_freq,
+ new_mode->link_freq);
+ if (ret < 0)
+ return ret;
+
ov5645->current_mode = new_mode;
+ }
__format = __ov5645_get_pad_format(ov5645, cfg, format->pad,
format->which);
@@ -1197,7 +1224,7 @@ static int ov5645_probe(struct i2c_client *client,
mutex_init(&ov5645->power_lock);
- v4l2_ctrl_handler_init(&ov5645->ctrls, 7);
+ v4l2_ctrl_handler_init(&ov5645->ctrls, 9);
v4l2_ctrl_new_std(&ov5645->ctrls, &ov5645_ctrl_ops,
V4L2_CID_SATURATION, -4, 4, 1, 0);
v4l2_ctrl_new_std(&ov5645->ctrls, &ov5645_ctrl_ops,
@@ -1215,6 +1242,17 @@ static int ov5645_probe(struct i2c_client *client,
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov5645_test_pattern_menu) - 1,
0, 0, ov5645_test_pattern_menu);
+ ov5645->pixel_clock = v4l2_ctrl_new_std(&ov5645->ctrls,
+ &ov5645_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ 1, INT_MAX, 1, 1);
+ ov5645->link_freq = v4l2_ctrl_new_int_menu(&ov5645->ctrls,
+ &ov5645_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq) - 1,
+ 0, link_freq);
+ if (ov5645->link_freq)
+ ov5645->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
ov5645->sd.ctrl_handler = &ov5645->ctrls;
@@ -1229,6 +1267,7 @@ static int ov5645_probe(struct i2c_client *client,
ov5645->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ov5645->pad.flags = MEDIA_PAD_FL_SOURCE;
ov5645->sd.dev = &client->dev;
+ ov5645->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&ov5645->sd.entity, 1, &ov5645->pad);
if (ret < 0) {
diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c
new file mode 100644
index 000000000000..6f7a1d6d2200
--- /dev/null
+++ b/drivers/media/i2c/ov5670.c
@@ -0,0 +1,2601 @@
+/*
+ * Copyright (c) 2017 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#define OV5670_REG_CHIP_ID 0x300a
+#define OV5670_CHIP_ID 0x005670
+
+#define OV5670_REG_MODE_SELECT 0x0100
+#define OV5670_MODE_STANDBY 0x00
+#define OV5670_MODE_STREAMING 0x01
+
+#define OV5670_REG_SOFTWARE_RST 0x0103
+#define OV5670_SOFTWARE_RST 0x01
+
+/* vertical-timings from sensor */
+#define OV5670_REG_VTS 0x380e
+#define OV5670_VTS_30FPS 0x0808 /* default for 30 fps */
+#define OV5670_VTS_MAX 0xffff
+
+/* horizontal-timings from sensor */
+#define OV5670_REG_HTS 0x380c
+
+/*
+ * Pixels-per-line(PPL) = Time-per-line * pixel-rate
+ * In OV5670, Time-per-line = HTS/SCLK.
+ * HTS is fixed for all resolutions, not recommended to change.
+ */
+#define OV5670_FIXED_PPL 2724 /* Pixels per line */
+
+/* Exposure controls from sensor */
+#define OV5670_REG_EXPOSURE 0x3500
+#define OV5670_EXPOSURE_MIN 4
+#define OV5670_EXPOSURE_STEP 1
+
+/* Analog gain controls from sensor */
+#define OV5670_REG_ANALOG_GAIN 0x3508
+#define ANALOG_GAIN_MIN 0
+#define ANALOG_GAIN_MAX 8191
+#define ANALOG_GAIN_STEP 1
+#define ANALOG_GAIN_DEFAULT 128
+
+/* Digital gain controls from sensor */
+#define OV5670_REG_R_DGTL_GAIN 0x5032
+#define OV5670_REG_G_DGTL_GAIN 0x5034
+#define OV5670_REG_B_DGTL_GAIN 0x5036
+#define OV5670_DGTL_GAIN_MIN 0
+#define OV5670_DGTL_GAIN_MAX 4095
+#define OV5670_DGTL_GAIN_STEP 1
+#define OV5670_DGTL_GAIN_DEFAULT 1024
+
+/* Test Pattern Control */
+#define OV5670_REG_TEST_PATTERN 0x4303
+#define OV5670_TEST_PATTERN_ENABLE BIT(3)
+#define OV5670_REG_TEST_PATTERN_CTRL 0x4320
+
+#define OV5670_REG_VALUE_08BIT 1
+#define OV5670_REG_VALUE_16BIT 2
+#define OV5670_REG_VALUE_24BIT 3
+
+/* Initial number of frames to skip to avoid possible garbage */
+#define OV5670_NUM_OF_SKIP_FRAMES 2
+
+struct ov5670_reg {
+ u16 address;
+ u8 val;
+};
+
+struct ov5670_reg_list {
+ u32 num_of_regs;
+ const struct ov5670_reg *regs;
+};
+
+struct ov5670_link_freq_config {
+ u32 pixel_rate;
+ const struct ov5670_reg_list reg_list;
+};
+
+struct ov5670_mode {
+ /* Frame width in pixels */
+ u32 width;
+
+ /* Frame height in pixels */
+ u32 height;
+
+ /* Default vertical timining size */
+ u32 vts_def;
+
+ /* Min vertical timining size */
+ u32 vts_min;
+
+ /* Link frequency needed for this resolution */
+ u32 link_freq_index;
+
+ /* Sensor register settings for this resolution */
+ const struct ov5670_reg_list reg_list;
+};
+
+static const struct ov5670_reg mipi_data_rate_840mbps[] = {
+ {0x0300, 0x04},
+ {0x0301, 0x00},
+ {0x0302, 0x84},
+ {0x0303, 0x00},
+ {0x0304, 0x03},
+ {0x0305, 0x01},
+ {0x0306, 0x01},
+ {0x030a, 0x00},
+ {0x030b, 0x00},
+ {0x030c, 0x00},
+ {0x030d, 0x26},
+ {0x030e, 0x00},
+ {0x030f, 0x06},
+ {0x0312, 0x01},
+ {0x3031, 0x0a},
+};
+
+static const struct ov5670_reg mode_2592x1944_regs[] = {
+ {0x3000, 0x00},
+ {0x3002, 0x21},
+ {0x3005, 0xf0},
+ {0x3007, 0x00},
+ {0x3015, 0x0f},
+ {0x3018, 0x32},
+ {0x301a, 0xf0},
+ {0x301b, 0xf0},
+ {0x301c, 0xf0},
+ {0x301d, 0xf0},
+ {0x301e, 0xf0},
+ {0x3030, 0x00},
+ {0x3031, 0x0a},
+ {0x303c, 0xff},
+ {0x303e, 0xff},
+ {0x3040, 0xf0},
+ {0x3041, 0x00},
+ {0x3042, 0xf0},
+ {0x3106, 0x11},
+ {0x3500, 0x00},
+ {0x3501, 0x80},
+ {0x3502, 0x00},
+ {0x3503, 0x04},
+ {0x3504, 0x03},
+ {0x3505, 0x83},
+ {0x3508, 0x04},
+ {0x3509, 0x00},
+ {0x350e, 0x04},
+ {0x350f, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0xc8},
+ {0x3610, 0x88},
+ {0x3612, 0x48},
+ {0x3614, 0x5b},
+ {0x3615, 0x96},
+ {0x3621, 0xd0},
+ {0x3622, 0x00},
+ {0x3623, 0x00},
+ {0x3633, 0x13},
+ {0x3634, 0x13},
+ {0x3635, 0x13},
+ {0x3636, 0x13},
+ {0x3645, 0x13},
+ {0x3646, 0x82},
+ {0x3650, 0x00},
+ {0x3652, 0xff},
+ {0x3655, 0x20},
+ {0x3656, 0xff},
+ {0x365a, 0xff},
+ {0x365e, 0xff},
+ {0x3668, 0x00},
+ {0x366a, 0x07},
+ {0x366e, 0x10},
+ {0x366d, 0x00},
+ {0x366f, 0x80},
+ {0x3700, 0x28},
+ {0x3701, 0x10},
+ {0x3702, 0x3a},
+ {0x3703, 0x19},
+ {0x3704, 0x10},
+ {0x3705, 0x00},
+ {0x3706, 0x66},
+ {0x3707, 0x08},
+ {0x3708, 0x34},
+ {0x3709, 0x40},
+ {0x370a, 0x01},
+ {0x370b, 0x1b},
+ {0x3714, 0x24},
+ {0x371a, 0x3e},
+ {0x3733, 0x00},
+ {0x3734, 0x00},
+ {0x373a, 0x05},
+ {0x373b, 0x06},
+ {0x373c, 0x0a},
+ {0x373f, 0xa0},
+ {0x3755, 0x00},
+ {0x3758, 0x00},
+ {0x375b, 0x0e},
+ {0x3766, 0x5f},
+ {0x3768, 0x00},
+ {0x3769, 0x22},
+ {0x3773, 0x08},
+ {0x3774, 0x1f},
+ {0x3776, 0x06},
+ {0x37a0, 0x88},
+ {0x37a1, 0x5c},
+ {0x37a7, 0x88},
+ {0x37a8, 0x70},
+ {0x37aa, 0x88},
+ {0x37ab, 0x48},
+ {0x37b3, 0x66},
+ {0x37c2, 0x04},
+ {0x37c5, 0x00},
+ {0x37c8, 0x00},
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x04},
+ {0x3804, 0x0a},
+ {0x3805, 0x33},
+ {0x3806, 0x07},
+ {0x3807, 0xa3},
+ {0x3808, 0x0a},
+ {0x3809, 0x20},
+ {0x380a, 0x07},
+ {0x380b, 0x98},
+ {0x380c, 0x06},
+ {0x380d, 0x90},
+ {0x380e, 0x08},
+ {0x380f, 0x08},
+ {0x3811, 0x04},
+ {0x3813, 0x02},
+ {0x3814, 0x01},
+ {0x3815, 0x01},
+ {0x3816, 0x00},
+ {0x3817, 0x00},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x3820, 0x84},
+ {0x3821, 0x46},
+ {0x3822, 0x48},
+ {0x3826, 0x00},
+ {0x3827, 0x08},
+ {0x382a, 0x01},
+ {0x382b, 0x01},
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x00},
+ {0x3838, 0x10},
+ {0x3841, 0xff},
+ {0x3846, 0x48},
+ {0x3861, 0x00},
+ {0x3862, 0x04},
+ {0x3863, 0x06},
+ {0x3a11, 0x01},
+ {0x3a12, 0x78},
+ {0x3b00, 0x00},
+ {0x3b02, 0x00},
+ {0x3b03, 0x00},
+ {0x3b04, 0x00},
+ {0x3b05, 0x00},
+ {0x3c00, 0x89},
+ {0x3c01, 0xab},
+ {0x3c02, 0x01},
+ {0x3c03, 0x00},
+ {0x3c04, 0x00},
+ {0x3c05, 0x03},
+ {0x3c06, 0x00},
+ {0x3c07, 0x05},
+ {0x3c0c, 0x00},
+ {0x3c0d, 0x00},
+ {0x3c0e, 0x00},
+ {0x3c0f, 0x00},
+ {0x3c40, 0x00},
+ {0x3c41, 0xa3},
+ {0x3c43, 0x7d},
+ {0x3c45, 0xd7},
+ {0x3c47, 0xfc},
+ {0x3c50, 0x05},
+ {0x3c52, 0xaa},
+ {0x3c54, 0x71},
+ {0x3c56, 0x80},
+ {0x3d85, 0x17},
+ {0x3f03, 0x00},
+ {0x3f0a, 0x00},
+ {0x3f0b, 0x00},
+ {0x4001, 0x60},
+ {0x4009, 0x0d},
+ {0x4020, 0x00},
+ {0x4021, 0x00},
+ {0x4022, 0x00},
+ {0x4023, 0x00},
+ {0x4024, 0x00},
+ {0x4025, 0x00},
+ {0x4026, 0x00},
+ {0x4027, 0x00},
+ {0x4028, 0x00},
+ {0x4029, 0x00},
+ {0x402a, 0x00},
+ {0x402b, 0x00},
+ {0x402c, 0x00},
+ {0x402d, 0x00},
+ {0x402e, 0x00},
+ {0x402f, 0x00},
+ {0x4040, 0x00},
+ {0x4041, 0x03},
+ {0x4042, 0x00},
+ {0x4043, 0x7A},
+ {0x4044, 0x00},
+ {0x4045, 0x7A},
+ {0x4046, 0x00},
+ {0x4047, 0x7A},
+ {0x4048, 0x00},
+ {0x4049, 0x7A},
+ {0x4307, 0x30},
+ {0x4500, 0x58},
+ {0x4501, 0x04},
+ {0x4502, 0x40},
+ {0x4503, 0x10},
+ {0x4508, 0xaa},
+ {0x4509, 0xaa},
+ {0x450a, 0x00},
+ {0x450b, 0x00},
+ {0x4600, 0x01},
+ {0x4601, 0x03},
+ {0x4700, 0xa4},
+ {0x4800, 0x4c},
+ {0x4816, 0x53},
+ {0x481f, 0x40},
+ {0x4837, 0x13},
+ {0x5000, 0x56},
+ {0x5001, 0x01},
+ {0x5002, 0x28},
+ {0x5004, 0x0c},
+ {0x5006, 0x0c},
+ {0x5007, 0xe0},
+ {0x5008, 0x01},
+ {0x5009, 0xb0},
+ {0x5901, 0x00},
+ {0x5a01, 0x00},
+ {0x5a03, 0x00},
+ {0x5a04, 0x0c},
+ {0x5a05, 0xe0},
+ {0x5a06, 0x09},
+ {0x5a07, 0xb0},
+ {0x5a08, 0x06},
+ {0x5e00, 0x00},
+ {0x3734, 0x40},
+ {0x5b00, 0x01},
+ {0x5b01, 0x10},
+ {0x5b02, 0x01},
+ {0x5b03, 0xdb},
+ {0x3d8c, 0x71},
+ {0x3d8d, 0xea},
+ {0x4017, 0x08},
+ {0x3618, 0x2a},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x3503, 0x00}
+};
+
+static const struct ov5670_reg mode_1296x972_regs[] = {
+ {0x3000, 0x00},
+ {0x3002, 0x21},
+ {0x3005, 0xf0},
+ {0x3007, 0x00},
+ {0x3015, 0x0f},
+ {0x3018, 0x32},
+ {0x301a, 0xf0},
+ {0x301b, 0xf0},
+ {0x301c, 0xf0},
+ {0x301d, 0xf0},
+ {0x301e, 0xf0},
+ {0x3030, 0x00},
+ {0x3031, 0x0a},
+ {0x303c, 0xff},
+ {0x303e, 0xff},
+ {0x3040, 0xf0},
+ {0x3041, 0x00},
+ {0x3042, 0xf0},
+ {0x3106, 0x11},
+ {0x3500, 0x00},
+ {0x3501, 0x80},
+ {0x3502, 0x00},
+ {0x3503, 0x04},
+ {0x3504, 0x03},
+ {0x3505, 0x83},
+ {0x3508, 0x07},
+ {0x3509, 0x80},
+ {0x350e, 0x04},
+ {0x350f, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0xc8},
+ {0x3610, 0x88},
+ {0x3612, 0x48},
+ {0x3614, 0x5b},
+ {0x3615, 0x96},
+ {0x3621, 0xd0},
+ {0x3622, 0x00},
+ {0x3623, 0x00},
+ {0x3633, 0x13},
+ {0x3634, 0x13},
+ {0x3635, 0x13},
+ {0x3636, 0x13},
+ {0x3645, 0x13},
+ {0x3646, 0x82},
+ {0x3650, 0x00},
+ {0x3652, 0xff},
+ {0x3655, 0x20},
+ {0x3656, 0xff},
+ {0x365a, 0xff},
+ {0x365e, 0xff},
+ {0x3668, 0x00},
+ {0x366a, 0x07},
+ {0x366e, 0x08},
+ {0x366d, 0x00},
+ {0x366f, 0x80},
+ {0x3700, 0x28},
+ {0x3701, 0x10},
+ {0x3702, 0x3a},
+ {0x3703, 0x19},
+ {0x3704, 0x10},
+ {0x3705, 0x00},
+ {0x3706, 0x66},
+ {0x3707, 0x08},
+ {0x3708, 0x34},
+ {0x3709, 0x40},
+ {0x370a, 0x01},
+ {0x370b, 0x1b},
+ {0x3714, 0x24},
+ {0x371a, 0x3e},
+ {0x3733, 0x00},
+ {0x3734, 0x00},
+ {0x373a, 0x05},
+ {0x373b, 0x06},
+ {0x373c, 0x0a},
+ {0x373f, 0xa0},
+ {0x3755, 0x00},
+ {0x3758, 0x00},
+ {0x375b, 0x0e},
+ {0x3766, 0x5f},
+ {0x3768, 0x00},
+ {0x3769, 0x22},
+ {0x3773, 0x08},
+ {0x3774, 0x1f},
+ {0x3776, 0x06},
+ {0x37a0, 0x88},
+ {0x37a1, 0x5c},
+ {0x37a7, 0x88},
+ {0x37a8, 0x70},
+ {0x37aa, 0x88},
+ {0x37ab, 0x48},
+ {0x37b3, 0x66},
+ {0x37c2, 0x04},
+ {0x37c5, 0x00},
+ {0x37c8, 0x00},
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x04},
+ {0x3804, 0x0a},
+ {0x3805, 0x33},
+ {0x3806, 0x07},
+ {0x3807, 0xa3},
+ {0x3808, 0x05},
+ {0x3809, 0x10},
+ {0x380a, 0x03},
+ {0x380b, 0xcc},
+ {0x380c, 0x06},
+ {0x380d, 0x90},
+ {0x380e, 0x08},
+ {0x380f, 0x08},
+ {0x3811, 0x04},
+ {0x3813, 0x04},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x00},
+ {0x3817, 0x00},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x3820, 0x94},
+ {0x3821, 0x47},
+ {0x3822, 0x48},
+ {0x3826, 0x00},
+ {0x3827, 0x08},
+ {0x382a, 0x03},
+ {0x382b, 0x01},
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x00},
+ {0x3838, 0x10},
+ {0x3841, 0xff},
+ {0x3846, 0x48},
+ {0x3861, 0x00},
+ {0x3862, 0x04},
+ {0x3863, 0x06},
+ {0x3a11, 0x01},
+ {0x3a12, 0x78},
+ {0x3b00, 0x00},
+ {0x3b02, 0x00},
+ {0x3b03, 0x00},
+ {0x3b04, 0x00},
+ {0x3b05, 0x00},
+ {0x3c00, 0x89},
+ {0x3c01, 0xab},
+ {0x3c02, 0x01},
+ {0x3c03, 0x00},
+ {0x3c04, 0x00},
+ {0x3c05, 0x03},
+ {0x3c06, 0x00},
+ {0x3c07, 0x05},
+ {0x3c0c, 0x00},
+ {0x3c0d, 0x00},
+ {0x3c0e, 0x00},
+ {0x3c0f, 0x00},
+ {0x3c40, 0x00},
+ {0x3c41, 0xa3},
+ {0x3c43, 0x7d},
+ {0x3c45, 0xd7},
+ {0x3c47, 0xfc},
+ {0x3c50, 0x05},
+ {0x3c52, 0xaa},
+ {0x3c54, 0x71},
+ {0x3c56, 0x80},
+ {0x3d85, 0x17},
+ {0x3f03, 0x00},
+ {0x3f0a, 0x00},
+ {0x3f0b, 0x00},
+ {0x4001, 0x60},
+ {0x4009, 0x05},
+ {0x4020, 0x00},
+ {0x4021, 0x00},
+ {0x4022, 0x00},
+ {0x4023, 0x00},
+ {0x4024, 0x00},
+ {0x4025, 0x00},
+ {0x4026, 0x00},
+ {0x4027, 0x00},
+ {0x4028, 0x00},
+ {0x4029, 0x00},
+ {0x402a, 0x00},
+ {0x402b, 0x00},
+ {0x402c, 0x00},
+ {0x402d, 0x00},
+ {0x402e, 0x00},
+ {0x402f, 0x00},
+ {0x4040, 0x00},
+ {0x4041, 0x03},
+ {0x4042, 0x00},
+ {0x4043, 0x7A},
+ {0x4044, 0x00},
+ {0x4045, 0x7A},
+ {0x4046, 0x00},
+ {0x4047, 0x7A},
+ {0x4048, 0x00},
+ {0x4049, 0x7A},
+ {0x4307, 0x30},
+ {0x4500, 0x58},
+ {0x4501, 0x04},
+ {0x4502, 0x48},
+ {0x4503, 0x10},
+ {0x4508, 0x55},
+ {0x4509, 0x55},
+ {0x450a, 0x00},
+ {0x450b, 0x00},
+ {0x4600, 0x00},
+ {0x4601, 0x81},
+ {0x4700, 0xa4},
+ {0x4800, 0x4c},
+ {0x4816, 0x53},
+ {0x481f, 0x40},
+ {0x4837, 0x13},
+ {0x5000, 0x56},
+ {0x5001, 0x01},
+ {0x5002, 0x28},
+ {0x5004, 0x0c},
+ {0x5006, 0x0c},
+ {0x5007, 0xe0},
+ {0x5008, 0x01},
+ {0x5009, 0xb0},
+ {0x5901, 0x00},
+ {0x5a01, 0x00},
+ {0x5a03, 0x00},
+ {0x5a04, 0x0c},
+ {0x5a05, 0xe0},
+ {0x5a06, 0x09},
+ {0x5a07, 0xb0},
+ {0x5a08, 0x06},
+ {0x5e00, 0x00},
+ {0x3734, 0x40},
+ {0x5b00, 0x01},
+ {0x5b01, 0x10},
+ {0x5b02, 0x01},
+ {0x5b03, 0xdb},
+ {0x3d8c, 0x71},
+ {0x3d8d, 0xea},
+ {0x4017, 0x10},
+ {0x3618, 0x2a},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x04},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x3503, 0x00}
+};
+
+static const struct ov5670_reg mode_648x486_regs[] = {
+ {0x3000, 0x00},
+ {0x3002, 0x21},
+ {0x3005, 0xf0},
+ {0x3007, 0x00},
+ {0x3015, 0x0f},
+ {0x3018, 0x32},
+ {0x301a, 0xf0},
+ {0x301b, 0xf0},
+ {0x301c, 0xf0},
+ {0x301d, 0xf0},
+ {0x301e, 0xf0},
+ {0x3030, 0x00},
+ {0x3031, 0x0a},
+ {0x303c, 0xff},
+ {0x303e, 0xff},
+ {0x3040, 0xf0},
+ {0x3041, 0x00},
+ {0x3042, 0xf0},
+ {0x3106, 0x11},
+ {0x3500, 0x00},
+ {0x3501, 0x80},
+ {0x3502, 0x00},
+ {0x3503, 0x04},
+ {0x3504, 0x03},
+ {0x3505, 0x83},
+ {0x3508, 0x04},
+ {0x3509, 0x00},
+ {0x350e, 0x04},
+ {0x350f, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0xc8},
+ {0x3610, 0x88},
+ {0x3612, 0x48},
+ {0x3614, 0x5b},
+ {0x3615, 0x96},
+ {0x3621, 0xd0},
+ {0x3622, 0x00},
+ {0x3623, 0x04},
+ {0x3633, 0x13},
+ {0x3634, 0x13},
+ {0x3635, 0x13},
+ {0x3636, 0x13},
+ {0x3645, 0x13},
+ {0x3646, 0x82},
+ {0x3650, 0x00},
+ {0x3652, 0xff},
+ {0x3655, 0x20},
+ {0x3656, 0xff},
+ {0x365a, 0xff},
+ {0x365e, 0xff},
+ {0x3668, 0x00},
+ {0x366a, 0x07},
+ {0x366e, 0x08},
+ {0x366d, 0x00},
+ {0x366f, 0x80},
+ {0x3700, 0x28},
+ {0x3701, 0x10},
+ {0x3702, 0x3a},
+ {0x3703, 0x19},
+ {0x3704, 0x10},
+ {0x3705, 0x00},
+ {0x3706, 0x66},
+ {0x3707, 0x08},
+ {0x3708, 0x34},
+ {0x3709, 0x40},
+ {0x370a, 0x01},
+ {0x370b, 0x1b},
+ {0x3714, 0x24},
+ {0x371a, 0x3e},
+ {0x3733, 0x00},
+ {0x3734, 0x00},
+ {0x373a, 0x05},
+ {0x373b, 0x06},
+ {0x373c, 0x0a},
+ {0x373f, 0xa0},
+ {0x3755, 0x00},
+ {0x3758, 0x00},
+ {0x375b, 0x0e},
+ {0x3766, 0x5f},
+ {0x3768, 0x00},
+ {0x3769, 0x22},
+ {0x3773, 0x08},
+ {0x3774, 0x1f},
+ {0x3776, 0x06},
+ {0x37a0, 0x88},
+ {0x37a1, 0x5c},
+ {0x37a7, 0x88},
+ {0x37a8, 0x70},
+ {0x37aa, 0x88},
+ {0x37ab, 0x48},
+ {0x37b3, 0x66},
+ {0x37c2, 0x04},
+ {0x37c5, 0x00},
+ {0x37c8, 0x00},
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x04},
+ {0x3804, 0x0a},
+ {0x3805, 0x33},
+ {0x3806, 0x07},
+ {0x3807, 0xa3},
+ {0x3808, 0x02},
+ {0x3809, 0x88},
+ {0x380a, 0x01},
+ {0x380b, 0xe6},
+ {0x380c, 0x06},
+ {0x380d, 0x90},
+ {0x380e, 0x08},
+ {0x380f, 0x08},
+ {0x3811, 0x04},
+ {0x3813, 0x02},
+ {0x3814, 0x07},
+ {0x3815, 0x01},
+ {0x3816, 0x00},
+ {0x3817, 0x00},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x3820, 0x94},
+ {0x3821, 0xc6},
+ {0x3822, 0x48},
+ {0x3826, 0x00},
+ {0x3827, 0x08},
+ {0x382a, 0x07},
+ {0x382b, 0x01},
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x00},
+ {0x3838, 0x10},
+ {0x3841, 0xff},
+ {0x3846, 0x48},
+ {0x3861, 0x00},
+ {0x3862, 0x04},
+ {0x3863, 0x06},
+ {0x3a11, 0x01},
+ {0x3a12, 0x78},
+ {0x3b00, 0x00},
+ {0x3b02, 0x00},
+ {0x3b03, 0x00},
+ {0x3b04, 0x00},
+ {0x3b05, 0x00},
+ {0x3c00, 0x89},
+ {0x3c01, 0xab},
+ {0x3c02, 0x01},
+ {0x3c03, 0x00},
+ {0x3c04, 0x00},
+ {0x3c05, 0x03},
+ {0x3c06, 0x00},
+ {0x3c07, 0x05},
+ {0x3c0c, 0x00},
+ {0x3c0d, 0x00},
+ {0x3c0e, 0x00},
+ {0x3c0f, 0x00},
+ {0x3c40, 0x00},
+ {0x3c41, 0xa3},
+ {0x3c43, 0x7d},
+ {0x3c45, 0xd7},
+ {0x3c47, 0xfc},
+ {0x3c50, 0x05},
+ {0x3c52, 0xaa},
+ {0x3c54, 0x71},
+ {0x3c56, 0x80},
+ {0x3d85, 0x17},
+ {0x3f03, 0x00},
+ {0x3f0a, 0x00},
+ {0x3f0b, 0x00},
+ {0x4001, 0x60},
+ {0x4009, 0x05},
+ {0x4020, 0x00},
+ {0x4021, 0x00},
+ {0x4022, 0x00},
+ {0x4023, 0x00},
+ {0x4024, 0x00},
+ {0x4025, 0x00},
+ {0x4026, 0x00},
+ {0x4027, 0x00},
+ {0x4028, 0x00},
+ {0x4029, 0x00},
+ {0x402a, 0x00},
+ {0x402b, 0x00},
+ {0x402c, 0x00},
+ {0x402d, 0x00},
+ {0x402e, 0x00},
+ {0x402f, 0x00},
+ {0x4040, 0x00},
+ {0x4041, 0x03},
+ {0x4042, 0x00},
+ {0x4043, 0x7A},
+ {0x4044, 0x00},
+ {0x4045, 0x7A},
+ {0x4046, 0x00},
+ {0x4047, 0x7A},
+ {0x4048, 0x00},
+ {0x4049, 0x7A},
+ {0x4307, 0x30},
+ {0x4500, 0x58},
+ {0x4501, 0x04},
+ {0x4502, 0x40},
+ {0x4503, 0x10},
+ {0x4508, 0x55},
+ {0x4509, 0x55},
+ {0x450a, 0x02},
+ {0x450b, 0x00},
+ {0x4600, 0x00},
+ {0x4601, 0x40},
+ {0x4700, 0xa4},
+ {0x4800, 0x4c},
+ {0x4816, 0x53},
+ {0x481f, 0x40},
+ {0x4837, 0x13},
+ {0x5000, 0x56},
+ {0x5001, 0x01},
+ {0x5002, 0x28},
+ {0x5004, 0x0c},
+ {0x5006, 0x0c},
+ {0x5007, 0xe0},
+ {0x5008, 0x01},
+ {0x5009, 0xb0},
+ {0x5901, 0x00},
+ {0x5a01, 0x00},
+ {0x5a03, 0x00},
+ {0x5a04, 0x0c},
+ {0x5a05, 0xe0},
+ {0x5a06, 0x09},
+ {0x5a07, 0xb0},
+ {0x5a08, 0x06},
+ {0x5e00, 0x00},
+ {0x3734, 0x40},
+ {0x5b00, 0x01},
+ {0x5b01, 0x10},
+ {0x5b02, 0x01},
+ {0x5b03, 0xdb},
+ {0x3d8c, 0x71},
+ {0x3d8d, 0xea},
+ {0x4017, 0x10},
+ {0x3618, 0x2a},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x3503, 0x00}
+};
+
+static const struct ov5670_reg mode_2560x1440_regs[] = {
+ {0x3000, 0x00},
+ {0x3002, 0x21},
+ {0x3005, 0xf0},
+ {0x3007, 0x00},
+ {0x3015, 0x0f},
+ {0x3018, 0x32},
+ {0x301a, 0xf0},
+ {0x301b, 0xf0},
+ {0x301c, 0xf0},
+ {0x301d, 0xf0},
+ {0x301e, 0xf0},
+ {0x3030, 0x00},
+ {0x3031, 0x0a},
+ {0x303c, 0xff},
+ {0x303e, 0xff},
+ {0x3040, 0xf0},
+ {0x3041, 0x00},
+ {0x3042, 0xf0},
+ {0x3106, 0x11},
+ {0x3500, 0x00},
+ {0x3501, 0x80},
+ {0x3502, 0x00},
+ {0x3503, 0x04},
+ {0x3504, 0x03},
+ {0x3505, 0x83},
+ {0x3508, 0x04},
+ {0x3509, 0x00},
+ {0x350e, 0x04},
+ {0x350f, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0xc8},
+ {0x3610, 0x88},
+ {0x3612, 0x48},
+ {0x3614, 0x5b},
+ {0x3615, 0x96},
+ {0x3621, 0xd0},
+ {0x3622, 0x00},
+ {0x3623, 0x00},
+ {0x3633, 0x13},
+ {0x3634, 0x13},
+ {0x3635, 0x13},
+ {0x3636, 0x13},
+ {0x3645, 0x13},
+ {0x3646, 0x82},
+ {0x3650, 0x00},
+ {0x3652, 0xff},
+ {0x3655, 0x20},
+ {0x3656, 0xff},
+ {0x365a, 0xff},
+ {0x365e, 0xff},
+ {0x3668, 0x00},
+ {0x366a, 0x07},
+ {0x366e, 0x10},
+ {0x366d, 0x00},
+ {0x366f, 0x80},
+ {0x3700, 0x28},
+ {0x3701, 0x10},
+ {0x3702, 0x3a},
+ {0x3703, 0x19},
+ {0x3704, 0x10},
+ {0x3705, 0x00},
+ {0x3706, 0x66},
+ {0x3707, 0x08},
+ {0x3708, 0x34},
+ {0x3709, 0x40},
+ {0x370a, 0x01},
+ {0x370b, 0x1b},
+ {0x3714, 0x24},
+ {0x371a, 0x3e},
+ {0x3733, 0x00},
+ {0x3734, 0x00},
+ {0x373a, 0x05},
+ {0x373b, 0x06},
+ {0x373c, 0x0a},
+ {0x373f, 0xa0},
+ {0x3755, 0x00},
+ {0x3758, 0x00},
+ {0x375b, 0x0e},
+ {0x3766, 0x5f},
+ {0x3768, 0x00},
+ {0x3769, 0x22},
+ {0x3773, 0x08},
+ {0x3774, 0x1f},
+ {0x3776, 0x06},
+ {0x37a0, 0x88},
+ {0x37a1, 0x5c},
+ {0x37a7, 0x88},
+ {0x37a8, 0x70},
+ {0x37aa, 0x88},
+ {0x37ab, 0x48},
+ {0x37b3, 0x66},
+ {0x37c2, 0x04},
+ {0x37c5, 0x00},
+ {0x37c8, 0x00},
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x04},
+ {0x3804, 0x0a},
+ {0x3805, 0x33},
+ {0x3806, 0x07},
+ {0x3807, 0xa3},
+ {0x3808, 0x0a},
+ {0x3809, 0x00},
+ {0x380a, 0x05},
+ {0x380b, 0xa0},
+ {0x380c, 0x06},
+ {0x380d, 0x90},
+ {0x380e, 0x08},
+ {0x380f, 0x08},
+ {0x3811, 0x04},
+ {0x3813, 0x02},
+ {0x3814, 0x01},
+ {0x3815, 0x01},
+ {0x3816, 0x00},
+ {0x3817, 0x00},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x3820, 0x84},
+ {0x3821, 0x46},
+ {0x3822, 0x48},
+ {0x3826, 0x00},
+ {0x3827, 0x08},
+ {0x382a, 0x01},
+ {0x382b, 0x01},
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x00},
+ {0x3838, 0x10},
+ {0x3841, 0xff},
+ {0x3846, 0x48},
+ {0x3861, 0x00},
+ {0x3862, 0x04},
+ {0x3863, 0x06},
+ {0x3a11, 0x01},
+ {0x3a12, 0x78},
+ {0x3b00, 0x00},
+ {0x3b02, 0x00},
+ {0x3b03, 0x00},
+ {0x3b04, 0x00},
+ {0x3b05, 0x00},
+ {0x3c00, 0x89},
+ {0x3c01, 0xab},
+ {0x3c02, 0x01},
+ {0x3c03, 0x00},
+ {0x3c04, 0x00},
+ {0x3c05, 0x03},
+ {0x3c06, 0x00},
+ {0x3c07, 0x05},
+ {0x3c0c, 0x00},
+ {0x3c0d, 0x00},
+ {0x3c0e, 0x00},
+ {0x3c0f, 0x00},
+ {0x3c40, 0x00},
+ {0x3c41, 0xa3},
+ {0x3c43, 0x7d},
+ {0x3c45, 0xd7},
+ {0x3c47, 0xfc},
+ {0x3c50, 0x05},
+ {0x3c52, 0xaa},
+ {0x3c54, 0x71},
+ {0x3c56, 0x80},
+ {0x3d85, 0x17},
+ {0x3f03, 0x00},
+ {0x3f0a, 0x00},
+ {0x3f0b, 0x00},
+ {0x4001, 0x60},
+ {0x4009, 0x0d},
+ {0x4020, 0x00},
+ {0x4021, 0x00},
+ {0x4022, 0x00},
+ {0x4023, 0x00},
+ {0x4024, 0x00},
+ {0x4025, 0x00},
+ {0x4026, 0x00},
+ {0x4027, 0x00},
+ {0x4028, 0x00},
+ {0x4029, 0x00},
+ {0x402a, 0x00},
+ {0x402b, 0x00},
+ {0x402c, 0x00},
+ {0x402d, 0x00},
+ {0x402e, 0x00},
+ {0x402f, 0x00},
+ {0x4040, 0x00},
+ {0x4041, 0x03},
+ {0x4042, 0x00},
+ {0x4043, 0x7A},
+ {0x4044, 0x00},
+ {0x4045, 0x7A},
+ {0x4046, 0x00},
+ {0x4047, 0x7A},
+ {0x4048, 0x00},
+ {0x4049, 0x7A},
+ {0x4307, 0x30},
+ {0x4500, 0x58},
+ {0x4501, 0x04},
+ {0x4502, 0x40},
+ {0x4503, 0x10},
+ {0x4508, 0xaa},
+ {0x4509, 0xaa},
+ {0x450a, 0x00},
+ {0x450b, 0x00},
+ {0x4600, 0x01},
+ {0x4601, 0x00},
+ {0x4700, 0xa4},
+ {0x4800, 0x4c},
+ {0x4816, 0x53},
+ {0x481f, 0x40},
+ {0x4837, 0x13},
+ {0x5000, 0x56},
+ {0x5001, 0x01},
+ {0x5002, 0x28},
+ {0x5004, 0x0c},
+ {0x5006, 0x0c},
+ {0x5007, 0xe0},
+ {0x5008, 0x01},
+ {0x5009, 0xb0},
+ {0x5901, 0x00},
+ {0x5a01, 0x00},
+ {0x5a03, 0x00},
+ {0x5a04, 0x0c},
+ {0x5a05, 0xe0},
+ {0x5a06, 0x09},
+ {0x5a07, 0xb0},
+ {0x5a08, 0x06},
+ {0x5e00, 0x00},
+ {0x3734, 0x40},
+ {0x5b00, 0x01},
+ {0x5b01, 0x10},
+ {0x5b02, 0x01},
+ {0x5b03, 0xdb},
+ {0x3d8c, 0x71},
+ {0x3d8d, 0xea},
+ {0x4017, 0x08},
+ {0x3618, 0x2a},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3}
+};
+
+static const struct ov5670_reg mode_1280x720_regs[] = {
+ {0x3000, 0x00},
+ {0x3002, 0x21},
+ {0x3005, 0xf0},
+ {0x3007, 0x00},
+ {0x3015, 0x0f},
+ {0x3018, 0x32},
+ {0x301a, 0xf0},
+ {0x301b, 0xf0},
+ {0x301c, 0xf0},
+ {0x301d, 0xf0},
+ {0x301e, 0xf0},
+ {0x3030, 0x00},
+ {0x3031, 0x0a},
+ {0x303c, 0xff},
+ {0x303e, 0xff},
+ {0x3040, 0xf0},
+ {0x3041, 0x00},
+ {0x3042, 0xf0},
+ {0x3106, 0x11},
+ {0x3500, 0x00},
+ {0x3501, 0x80},
+ {0x3502, 0x00},
+ {0x3503, 0x04},
+ {0x3504, 0x03},
+ {0x3505, 0x83},
+ {0x3508, 0x04},
+ {0x3509, 0x00},
+ {0x350e, 0x04},
+ {0x350f, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0xc8},
+ {0x3610, 0x88},
+ {0x3612, 0x48},
+ {0x3614, 0x5b},
+ {0x3615, 0x96},
+ {0x3621, 0xd0},
+ {0x3622, 0x00},
+ {0x3623, 0x00},
+ {0x3633, 0x13},
+ {0x3634, 0x13},
+ {0x3635, 0x13},
+ {0x3636, 0x13},
+ {0x3645, 0x13},
+ {0x3646, 0x82},
+ {0x3650, 0x00},
+ {0x3652, 0xff},
+ {0x3655, 0x20},
+ {0x3656, 0xff},
+ {0x365a, 0xff},
+ {0x365e, 0xff},
+ {0x3668, 0x00},
+ {0x366a, 0x07},
+ {0x366e, 0x08},
+ {0x366d, 0x00},
+ {0x366f, 0x80},
+ {0x3700, 0x28},
+ {0x3701, 0x10},
+ {0x3702, 0x3a},
+ {0x3703, 0x19},
+ {0x3704, 0x10},
+ {0x3705, 0x00},
+ {0x3706, 0x66},
+ {0x3707, 0x08},
+ {0x3708, 0x34},
+ {0x3709, 0x40},
+ {0x370a, 0x01},
+ {0x370b, 0x1b},
+ {0x3714, 0x24},
+ {0x371a, 0x3e},
+ {0x3733, 0x00},
+ {0x3734, 0x00},
+ {0x373a, 0x05},
+ {0x373b, 0x06},
+ {0x373c, 0x0a},
+ {0x373f, 0xa0},
+ {0x3755, 0x00},
+ {0x3758, 0x00},
+ {0x375b, 0x0e},
+ {0x3766, 0x5f},
+ {0x3768, 0x00},
+ {0x3769, 0x22},
+ {0x3773, 0x08},
+ {0x3774, 0x1f},
+ {0x3776, 0x06},
+ {0x37a0, 0x88},
+ {0x37a1, 0x5c},
+ {0x37a7, 0x88},
+ {0x37a8, 0x70},
+ {0x37aa, 0x88},
+ {0x37ab, 0x48},
+ {0x37b3, 0x66},
+ {0x37c2, 0x04},
+ {0x37c5, 0x00},
+ {0x37c8, 0x00},
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x04},
+ {0x3804, 0x0a},
+ {0x3805, 0x33},
+ {0x3806, 0x07},
+ {0x3807, 0xa3},
+ {0x3808, 0x05},
+ {0x3809, 0x00},
+ {0x380a, 0x02},
+ {0x380b, 0xd0},
+ {0x380c, 0x06},
+ {0x380d, 0x90},
+ {0x380e, 0x08},
+ {0x380f, 0x08},
+ {0x3811, 0x04},
+ {0x3813, 0x02},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x00},
+ {0x3817, 0x00},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x3820, 0x94},
+ {0x3821, 0x47},
+ {0x3822, 0x48},
+ {0x3826, 0x00},
+ {0x3827, 0x08},
+ {0x382a, 0x03},
+ {0x382b, 0x01},
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x00},
+ {0x3838, 0x10},
+ {0x3841, 0xff},
+ {0x3846, 0x48},
+ {0x3861, 0x00},
+ {0x3862, 0x04},
+ {0x3863, 0x06},
+ {0x3a11, 0x01},
+ {0x3a12, 0x78},
+ {0x3b00, 0x00},
+ {0x3b02, 0x00},
+ {0x3b03, 0x00},
+ {0x3b04, 0x00},
+ {0x3b05, 0x00},
+ {0x3c00, 0x89},
+ {0x3c01, 0xab},
+ {0x3c02, 0x01},
+ {0x3c03, 0x00},
+ {0x3c04, 0x00},
+ {0x3c05, 0x03},
+ {0x3c06, 0x00},
+ {0x3c07, 0x05},
+ {0x3c0c, 0x00},
+ {0x3c0d, 0x00},
+ {0x3c0e, 0x00},
+ {0x3c0f, 0x00},
+ {0x3c40, 0x00},
+ {0x3c41, 0xa3},
+ {0x3c43, 0x7d},
+ {0x3c45, 0xd7},
+ {0x3c47, 0xfc},
+ {0x3c50, 0x05},
+ {0x3c52, 0xaa},
+ {0x3c54, 0x71},
+ {0x3c56, 0x80},
+ {0x3d85, 0x17},
+ {0x3f03, 0x00},
+ {0x3f0a, 0x00},
+ {0x3f0b, 0x00},
+ {0x4001, 0x60},
+ {0x4009, 0x05},
+ {0x4020, 0x00},
+ {0x4021, 0x00},
+ {0x4022, 0x00},
+ {0x4023, 0x00},
+ {0x4024, 0x00},
+ {0x4025, 0x00},
+ {0x4026, 0x00},
+ {0x4027, 0x00},
+ {0x4028, 0x00},
+ {0x4029, 0x00},
+ {0x402a, 0x00},
+ {0x402b, 0x00},
+ {0x402c, 0x00},
+ {0x402d, 0x00},
+ {0x402e, 0x00},
+ {0x402f, 0x00},
+ {0x4040, 0x00},
+ {0x4041, 0x03},
+ {0x4042, 0x00},
+ {0x4043, 0x7A},
+ {0x4044, 0x00},
+ {0x4045, 0x7A},
+ {0x4046, 0x00},
+ {0x4047, 0x7A},
+ {0x4048, 0x00},
+ {0x4049, 0x7A},
+ {0x4307, 0x30},
+ {0x4500, 0x58},
+ {0x4501, 0x04},
+ {0x4502, 0x48},
+ {0x4503, 0x10},
+ {0x4508, 0x55},
+ {0x4509, 0x55},
+ {0x450a, 0x00},
+ {0x450b, 0x00},
+ {0x4600, 0x00},
+ {0x4601, 0x80},
+ {0x4700, 0xa4},
+ {0x4800, 0x4c},
+ {0x4816, 0x53},
+ {0x481f, 0x40},
+ {0x4837, 0x13},
+ {0x5000, 0x56},
+ {0x5001, 0x01},
+ {0x5002, 0x28},
+ {0x5004, 0x0c},
+ {0x5006, 0x0c},
+ {0x5007, 0xe0},
+ {0x5008, 0x01},
+ {0x5009, 0xb0},
+ {0x5901, 0x00},
+ {0x5a01, 0x00},
+ {0x5a03, 0x00},
+ {0x5a04, 0x0c},
+ {0x5a05, 0xe0},
+ {0x5a06, 0x09},
+ {0x5a07, 0xb0},
+ {0x5a08, 0x06},
+ {0x5e00, 0x00},
+ {0x3734, 0x40},
+ {0x5b00, 0x01},
+ {0x5b01, 0x10},
+ {0x5b02, 0x01},
+ {0x5b03, 0xdb},
+ {0x3d8c, 0x71},
+ {0x3d8d, 0xea},
+ {0x4017, 0x10},
+ {0x3618, 0x2a},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x3503, 0x00}
+};
+
+static const struct ov5670_reg mode_640x360_regs[] = {
+ {0x3000, 0x00},
+ {0x3002, 0x21},
+ {0x3005, 0xf0},
+ {0x3007, 0x00},
+ {0x3015, 0x0f},
+ {0x3018, 0x32},
+ {0x301a, 0xf0},
+ {0x301b, 0xf0},
+ {0x301c, 0xf0},
+ {0x301d, 0xf0},
+ {0x301e, 0xf0},
+ {0x3030, 0x00},
+ {0x3031, 0x0a},
+ {0x303c, 0xff},
+ {0x303e, 0xff},
+ {0x3040, 0xf0},
+ {0x3041, 0x00},
+ {0x3042, 0xf0},
+ {0x3106, 0x11},
+ {0x3500, 0x00},
+ {0x3501, 0x80},
+ {0x3502, 0x00},
+ {0x3503, 0x04},
+ {0x3504, 0x03},
+ {0x3505, 0x83},
+ {0x3508, 0x04},
+ {0x3509, 0x00},
+ {0x350e, 0x04},
+ {0x350f, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0xc8},
+ {0x3610, 0x88},
+ {0x3612, 0x48},
+ {0x3614, 0x5b},
+ {0x3615, 0x96},
+ {0x3621, 0xd0},
+ {0x3622, 0x00},
+ {0x3623, 0x04},
+ {0x3633, 0x13},
+ {0x3634, 0x13},
+ {0x3635, 0x13},
+ {0x3636, 0x13},
+ {0x3645, 0x13},
+ {0x3646, 0x82},
+ {0x3650, 0x00},
+ {0x3652, 0xff},
+ {0x3655, 0x20},
+ {0x3656, 0xff},
+ {0x365a, 0xff},
+ {0x365e, 0xff},
+ {0x3668, 0x00},
+ {0x366a, 0x07},
+ {0x366e, 0x08},
+ {0x366d, 0x00},
+ {0x366f, 0x80},
+ {0x3700, 0x28},
+ {0x3701, 0x10},
+ {0x3702, 0x3a},
+ {0x3703, 0x19},
+ {0x3704, 0x10},
+ {0x3705, 0x00},
+ {0x3706, 0x66},
+ {0x3707, 0x08},
+ {0x3708, 0x34},
+ {0x3709, 0x40},
+ {0x370a, 0x01},
+ {0x370b, 0x1b},
+ {0x3714, 0x24},
+ {0x371a, 0x3e},
+ {0x3733, 0x00},
+ {0x3734, 0x00},
+ {0x373a, 0x05},
+ {0x373b, 0x06},
+ {0x373c, 0x0a},
+ {0x373f, 0xa0},
+ {0x3755, 0x00},
+ {0x3758, 0x00},
+ {0x375b, 0x0e},
+ {0x3766, 0x5f},
+ {0x3768, 0x00},
+ {0x3769, 0x22},
+ {0x3773, 0x08},
+ {0x3774, 0x1f},
+ {0x3776, 0x06},
+ {0x37a0, 0x88},
+ {0x37a1, 0x5c},
+ {0x37a7, 0x88},
+ {0x37a8, 0x70},
+ {0x37aa, 0x88},
+ {0x37ab, 0x48},
+ {0x37b3, 0x66},
+ {0x37c2, 0x04},
+ {0x37c5, 0x00},
+ {0x37c8, 0x00},
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x04},
+ {0x3804, 0x0a},
+ {0x3805, 0x33},
+ {0x3806, 0x07},
+ {0x3807, 0xa3},
+ {0x3808, 0x02},
+ {0x3809, 0x80},
+ {0x380a, 0x01},
+ {0x380b, 0x68},
+ {0x380c, 0x06},
+ {0x380d, 0x90},
+ {0x380e, 0x08},
+ {0x380f, 0x08},
+ {0x3811, 0x04},
+ {0x3813, 0x02},
+ {0x3814, 0x07},
+ {0x3815, 0x01},
+ {0x3816, 0x00},
+ {0x3817, 0x00},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x3820, 0x94},
+ {0x3821, 0xc6},
+ {0x3822, 0x48},
+ {0x3826, 0x00},
+ {0x3827, 0x08},
+ {0x382a, 0x07},
+ {0x382b, 0x01},
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x00},
+ {0x3838, 0x10},
+ {0x3841, 0xff},
+ {0x3846, 0x48},
+ {0x3861, 0x00},
+ {0x3862, 0x04},
+ {0x3863, 0x06},
+ {0x3a11, 0x01},
+ {0x3a12, 0x78},
+ {0x3b00, 0x00},
+ {0x3b02, 0x00},
+ {0x3b03, 0x00},
+ {0x3b04, 0x00},
+ {0x3b05, 0x00},
+ {0x3c00, 0x89},
+ {0x3c01, 0xab},
+ {0x3c02, 0x01},
+ {0x3c03, 0x00},
+ {0x3c04, 0x00},
+ {0x3c05, 0x03},
+ {0x3c06, 0x00},
+ {0x3c07, 0x05},
+ {0x3c0c, 0x00},
+ {0x3c0d, 0x00},
+ {0x3c0e, 0x00},
+ {0x3c0f, 0x00},
+ {0x3c40, 0x00},
+ {0x3c41, 0xa3},
+ {0x3c43, 0x7d},
+ {0x3c45, 0xd7},
+ {0x3c47, 0xfc},
+ {0x3c50, 0x05},
+ {0x3c52, 0xaa},
+ {0x3c54, 0x71},
+ {0x3c56, 0x80},
+ {0x3d85, 0x17},
+ {0x3f03, 0x00},
+ {0x3f0a, 0x00},
+ {0x3f0b, 0x00},
+ {0x4001, 0x60},
+ {0x4009, 0x05},
+ {0x4020, 0x00},
+ {0x4021, 0x00},
+ {0x4022, 0x00},
+ {0x4023, 0x00},
+ {0x4024, 0x00},
+ {0x4025, 0x00},
+ {0x4026, 0x00},
+ {0x4027, 0x00},
+ {0x4028, 0x00},
+ {0x4029, 0x00},
+ {0x402a, 0x00},
+ {0x402b, 0x00},
+ {0x402c, 0x00},
+ {0x402d, 0x00},
+ {0x402e, 0x00},
+ {0x402f, 0x00},
+ {0x4040, 0x00},
+ {0x4041, 0x03},
+ {0x4042, 0x00},
+ {0x4043, 0x7A},
+ {0x4044, 0x00},
+ {0x4045, 0x7A},
+ {0x4046, 0x00},
+ {0x4047, 0x7A},
+ {0x4048, 0x00},
+ {0x4049, 0x7A},
+ {0x4307, 0x30},
+ {0x4500, 0x58},
+ {0x4501, 0x04},
+ {0x4502, 0x40},
+ {0x4503, 0x10},
+ {0x4508, 0x55},
+ {0x4509, 0x55},
+ {0x450a, 0x02},
+ {0x450b, 0x00},
+ {0x4600, 0x00},
+ {0x4601, 0x40},
+ {0x4700, 0xa4},
+ {0x4800, 0x4c},
+ {0x4816, 0x53},
+ {0x481f, 0x40},
+ {0x4837, 0x13},
+ {0x5000, 0x56},
+ {0x5001, 0x01},
+ {0x5002, 0x28},
+ {0x5004, 0x0c},
+ {0x5006, 0x0c},
+ {0x5007, 0xe0},
+ {0x5008, 0x01},
+ {0x5009, 0xb0},
+ {0x5901, 0x00},
+ {0x5a01, 0x00},
+ {0x5a03, 0x00},
+ {0x5a04, 0x0c},
+ {0x5a05, 0xe0},
+ {0x5a06, 0x09},
+ {0x5a07, 0xb0},
+ {0x5a08, 0x06},
+ {0x5e00, 0x00},
+ {0x3734, 0x40},
+ {0x5b00, 0x01},
+ {0x5b01, 0x10},
+ {0x5b02, 0x01},
+ {0x5b03, 0xdb},
+ {0x3d8c, 0x71},
+ {0x3d8d, 0xea},
+ {0x4017, 0x10},
+ {0x3618, 0x2a},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x3503, 0x00}
+};
+
+static const char * const ov5670_test_pattern_menu[] = {
+ "Disabled",
+ "Vertical Color Bar Type 1",
+};
+
+/* Supported link frequencies */
+#define OV5670_LINK_FREQ_422MHZ 422400000
+#define OV5670_LINK_FREQ_422MHZ_INDEX 0
+static const struct ov5670_link_freq_config link_freq_configs[] = {
+ {
+ /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
+ .pixel_rate = (OV5670_LINK_FREQ_422MHZ * 2 * 2) / 10,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mipi_data_rate_840mbps),
+ .regs = mipi_data_rate_840mbps,
+ }
+ }
+};
+
+static const s64 link_freq_menu_items[] = {
+ OV5670_LINK_FREQ_422MHZ
+};
+
+/*
+ * OV5670 sensor supports following resolutions with full FOV:
+ * 4:3 ==> {2592x1944, 1296x972, 648x486}
+ * 16:9 ==> {2560x1440, 1280x720, 640x360}
+ */
+static const struct ov5670_mode supported_modes[] = {
+ {
+ .width = 2592,
+ .height = 1944,
+ .vts_def = OV5670_VTS_30FPS,
+ .vts_min = OV5670_VTS_30FPS,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs),
+ .regs = mode_2592x1944_regs,
+ },
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ },
+ {
+ .width = 1296,
+ .height = 972,
+ .vts_def = OV5670_VTS_30FPS,
+ .vts_min = 996,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1296x972_regs),
+ .regs = mode_1296x972_regs,
+ },
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ },
+ {
+ .width = 648,
+ .height = 486,
+ .vts_def = OV5670_VTS_30FPS,
+ .vts_min = 516,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_648x486_regs),
+ .regs = mode_648x486_regs,
+ },
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ },
+ {
+ .width = 2560,
+ .height = 1440,
+ .vts_def = OV5670_VTS_30FPS,
+ .vts_min = OV5670_VTS_30FPS,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2560x1440_regs),
+ .regs = mode_2560x1440_regs,
+ },
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ },
+ {
+ .width = 1280,
+ .height = 720,
+ .vts_def = OV5670_VTS_30FPS,
+ .vts_min = 1020,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1280x720_regs),
+ .regs = mode_1280x720_regs,
+ },
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ },
+ {
+ .width = 640,
+ .height = 360,
+ .vts_def = OV5670_VTS_30FPS,
+ .vts_min = 510,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_640x360_regs),
+ .regs = mode_640x360_regs,
+ },
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ }
+};
+
+struct ov5670 {
+ 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 ov5670_mode *cur_mode;
+
+ /* To serialize asynchronus callbacks */
+ struct mutex mutex;
+
+ /* Streaming on/off */
+ bool streaming;
+};
+
+#define to_ov5670(_sd) container_of(_sd, struct ov5670, sd)
+
+/* Read registers up to 4 at a time */
+static int ov5670_read_reg(struct ov5670 *ov5670, u16 reg, unsigned int len,
+ u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+ struct i2c_msg msgs[2];
+ u8 *data_be_p;
+ u32 data_be = 0;
+ u16 reg_addr_be = cpu_to_be16(reg);
+ int ret;
+
+ if (len > 4)
+ return -EINVAL;
+
+ data_be_p = (u8 *)&data_be;
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *)&reg_addr_be;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_be_p[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+/* Write registers up to 4 at a time */
+static int ov5670_write_reg(struct ov5670 *ov5670, u16 reg, unsigned int len,
+ u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+ int buf_i;
+ int val_i;
+ u8 buf[6];
+ u8 *val_p;
+
+ if (len > 4)
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ val = cpu_to_be32(val);
+ val_p = (u8 *)&val;
+ buf_i = 2;
+ val_i = 4 - len;
+
+ while (val_i < 4)
+ buf[buf_i++] = val_p[val_i++];
+
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+/* Write a list of registers */
+static int ov5670_write_regs(struct ov5670 *ov5670,
+ const struct ov5670_reg *regs, unsigned int len)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < len; i++) {
+ ret = ov5670_write_reg(ov5670, 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;
+}
+
+static int ov5670_write_reg_list(struct ov5670 *ov5670,
+ const struct ov5670_reg_list *r_list)
+{
+ return ov5670_write_regs(ov5670, r_list->regs, r_list->num_of_regs);
+}
+
+/* Open sub-device */
+static int ov5670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ov5670 *ov5670 = to_ov5670(sd);
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(sd, fh->pad, 0);
+
+ mutex_lock(&ov5670->mutex);
+
+ /* Initialize try_fmt */
+ try_fmt->width = ov5670->cur_mode->width;
+ try_fmt->height = ov5670->cur_mode->height;
+ try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ try_fmt->field = V4L2_FIELD_NONE;
+
+ /* No crop or compose */
+ mutex_unlock(&ov5670->mutex);
+
+ return 0;
+}
+
+static int ov5670_update_digital_gain(struct ov5670 *ov5670, u32 d_gain)
+{
+ int ret;
+
+ ret = ov5670_write_reg(ov5670, OV5670_REG_R_DGTL_GAIN,
+ OV5670_REG_VALUE_16BIT, d_gain);
+ if (ret)
+ return ret;
+
+ ret = ov5670_write_reg(ov5670, OV5670_REG_G_DGTL_GAIN,
+ OV5670_REG_VALUE_16BIT, d_gain);
+ if (ret)
+ return ret;
+
+ return ov5670_write_reg(ov5670, OV5670_REG_B_DGTL_GAIN,
+ OV5670_REG_VALUE_16BIT, d_gain);
+}
+
+static int ov5670_enable_test_pattern(struct ov5670 *ov5670, u32 pattern)
+{
+ u32 val;
+ int ret;
+
+ /* Set the bayer order that we support */
+ ret = ov5670_write_reg(ov5670, OV5670_REG_TEST_PATTERN_CTRL,
+ OV5670_REG_VALUE_08BIT, 0);
+ if (ret)
+ return ret;
+
+ ret = ov5670_read_reg(ov5670, OV5670_REG_TEST_PATTERN,
+ OV5670_REG_VALUE_08BIT, &val);
+ if (ret)
+ return ret;
+
+ if (pattern)
+ val |= OV5670_TEST_PATTERN_ENABLE;
+ else
+ val &= ~OV5670_TEST_PATTERN_ENABLE;
+
+ return ov5670_write_reg(ov5670, OV5670_REG_TEST_PATTERN,
+ OV5670_REG_VALUE_08BIT, val);
+}
+
+/* Initialize control handlers */
+static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov5670 *ov5670 = container_of(ctrl->handler,
+ struct ov5670, ctrl_handler);
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+ s64 max;
+ int ret = 0;
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ max = ov5670->cur_mode->height + ctrl->val - 8;
+ __v4l2_ctrl_modify_range(ov5670->exposure,
+ ov5670->exposure->minimum, max,
+ ov5670->exposure->step, max);
+ break;
+ }
+
+ /* V4L2 controls values will be applied only when power is already up */
+ if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = ov5670_write_reg(ov5670, OV5670_REG_ANALOG_GAIN,
+ OV5670_REG_VALUE_16BIT, ctrl->val);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ ret = ov5670_update_digital_gain(ov5670, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ /* 4 least significant bits of expsoure are fractional part */
+ ret = ov5670_write_reg(ov5670, OV5670_REG_EXPOSURE,
+ OV5670_REG_VALUE_24BIT, ctrl->val << 4);
+ break;
+ case V4L2_CID_VBLANK:
+ /* Update VTS that meets expected vertical blanking */
+ ret = ov5670_write_reg(ov5670, OV5670_REG_VTS,
+ OV5670_REG_VALUE_16BIT,
+ ov5670->cur_mode->height + ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov5670_enable_test_pattern(ov5670, ctrl->val);
+ break;
+ default:
+ dev_info(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+ __func__, ctrl->id, ctrl->val);
+ break;
+ }
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5670_ctrl_ops = {
+ .s_ctrl = ov5670_set_ctrl,
+};
+
+/* Initialize control handlers */
+static int ov5670_init_controls(struct ov5670 *ov5670)
+{
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ s64 vblank_max;
+ s64 vblank_def;
+ s64 vblank_min;
+ s64 exposure_max;
+ int ret;
+
+ ctrl_hdlr = &ov5670->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+ if (ret)
+ return ret;
+
+ ctrl_hdlr->lock = &ov5670->mutex;
+ ov5670->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
+ &ov5670_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ov5670->link_freq)
+ ov5670->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /* By default, V4L2_CID_PIXEL_RATE is read only */
+ ov5670->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0,
+ link_freq_configs[0].pixel_rate,
+ 1,
+ link_freq_configs[0].pixel_rate);
+
+ vblank_max = OV5670_VTS_MAX - ov5670->cur_mode->height;
+ vblank_def = ov5670->cur_mode->vts_def - ov5670->cur_mode->height;
+ vblank_min = ov5670->cur_mode->vts_min - ov5670->cur_mode->height;
+ ov5670->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops,
+ V4L2_CID_VBLANK, vblank_min,
+ vblank_max, 1, vblank_def);
+
+ ov5670->hblank = v4l2_ctrl_new_std(
+ ctrl_hdlr, &ov5670_ctrl_ops, V4L2_CID_HBLANK,
+ OV5670_FIXED_PPL - ov5670->cur_mode->width,
+ OV5670_FIXED_PPL - ov5670->cur_mode->width, 1,
+ OV5670_FIXED_PPL - ov5670->cur_mode->width);
+ if (ov5670->hblank)
+ ov5670->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /* Get min, max, step, default from sensor */
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ ANALOG_GAIN_MIN, ANALOG_GAIN_MAX, ANALOG_GAIN_STEP,
+ ANALOG_GAIN_DEFAULT);
+
+ /* Digital gain */
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+ OV5670_DGTL_GAIN_MIN, OV5670_DGTL_GAIN_MAX,
+ OV5670_DGTL_GAIN_STEP, OV5670_DGTL_GAIN_DEFAULT);
+
+ /* Get min, max, step, default from sensor */
+ exposure_max = ov5670->cur_mode->vts_def - 8;
+ ov5670->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OV5670_EXPOSURE_MIN,
+ exposure_max, OV5670_EXPOSURE_STEP,
+ exposure_max);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov5670_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov5670_test_pattern_menu) - 1,
+ 0, 0, ov5670_test_pattern_menu);
+
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ goto error;
+ }
+
+ ov5670->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+error:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
+}
+
+static int ov5670_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 ov5670_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;
+}
+
+/* Calculate resolution distance */
+static int ov5670_get_reso_dist(const struct ov5670_mode *mode,
+ struct v4l2_mbus_framefmt *framefmt)
+{
+ return abs(mode->width - framefmt->width) +
+ abs(mode->height - framefmt->height);
+}
+
+/* Find the closest supported resolution to the requested resolution */
+static const struct ov5670_mode *ov5670_find_best_fit(
+ struct ov5670 *ov5670,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+ int dist;
+ int cur_best_fit = 0;
+ int cur_best_fit_dist = -1;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+ dist = ov5670_get_reso_dist(&supported_modes[i], framefmt);
+ if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+ cur_best_fit_dist = dist;
+ cur_best_fit = i;
+ }
+ }
+
+ return &supported_modes[cur_best_fit];
+}
+
+static void ov5670_update_pad_format(const struct ov5670_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 ov5670_do_get_pad_format(struct ov5670 *ov5670,
+ 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(&ov5670->sd, cfg,
+ fmt->pad);
+ else
+ ov5670_update_pad_format(ov5670->cur_mode, fmt);
+
+ return 0;
+}
+
+static int ov5670_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov5670 *ov5670 = to_ov5670(sd);
+ int ret;
+
+ mutex_lock(&ov5670->mutex);
+ ret = ov5670_do_get_pad_format(ov5670, cfg, fmt);
+ mutex_unlock(&ov5670->mutex);
+
+ return ret;
+}
+
+static int ov5670_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov5670 *ov5670 = to_ov5670(sd);
+ const struct ov5670_mode *mode;
+ s32 vblank_def;
+ s32 h_blank;
+
+ mutex_lock(&ov5670->mutex);
+
+ fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+ mode = ov5670_find_best_fit(ov5670, fmt);
+ ov5670_update_pad_format(mode, fmt);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+ } else {
+ ov5670->cur_mode = mode;
+ __v4l2_ctrl_s_ctrl(ov5670->link_freq, mode->link_freq_index);
+ __v4l2_ctrl_s_ctrl_int64(
+ ov5670->pixel_rate,
+ link_freq_configs[mode->link_freq_index].pixel_rate);
+ /* Update limits and set FPS to default */
+ vblank_def = ov5670->cur_mode->vts_def -
+ ov5670->cur_mode->height;
+ __v4l2_ctrl_modify_range(
+ ov5670->vblank,
+ ov5670->cur_mode->vts_min - ov5670->cur_mode->height,
+ OV5670_VTS_MAX - ov5670->cur_mode->height, 1,
+ vblank_def);
+ __v4l2_ctrl_s_ctrl(ov5670->vblank, vblank_def);
+ h_blank = OV5670_FIXED_PPL - ov5670->cur_mode->width;
+ __v4l2_ctrl_modify_range(ov5670->hblank, h_blank, h_blank, 1,
+ h_blank);
+ }
+
+ mutex_unlock(&ov5670->mutex);
+
+ return 0;
+}
+
+static int ov5670_get_skip_frames(struct v4l2_subdev *sd, u32 *frames)
+{
+ *frames = OV5670_NUM_OF_SKIP_FRAMES;
+
+ return 0;
+}
+
+/* Prepare streaming by writing default values and customized values */
+static int ov5670_start_streaming(struct ov5670 *ov5670)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+ const struct ov5670_reg_list *reg_list;
+ int link_freq_index;
+ int ret;
+
+ /* Get out of from software reset */
+ ret = ov5670_write_reg(ov5670, OV5670_REG_SOFTWARE_RST,
+ OV5670_REG_VALUE_08BIT, OV5670_SOFTWARE_RST);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set powerup registers\n",
+ __func__);
+ return ret;
+ }
+
+ /* Setup PLL */
+ link_freq_index = ov5670->cur_mode->link_freq_index;
+ reg_list = &link_freq_configs[link_freq_index].reg_list;
+ ret = ov5670_write_reg_list(ov5670, reg_list);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set plls\n", __func__);
+ return ret;
+ }
+
+ /* Apply default values of current mode */
+ reg_list = &ov5670->cur_mode->reg_list;
+ ret = ov5670_write_reg_list(ov5670, reg_list);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set mode\n", __func__);
+ return ret;
+ }
+
+ ret = __v4l2_ctrl_handler_setup(ov5670->sd.ctrl_handler);
+ if (ret)
+ return ret;
+
+ /* Write stream on list */
+ ret = ov5670_write_reg(ov5670, OV5670_REG_MODE_SELECT,
+ OV5670_REG_VALUE_08BIT, OV5670_MODE_STREAMING);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set stream\n", __func__);
+ return ret;
+ }
+
+ ov5670->streaming = true;
+
+ return 0;
+}
+
+static int ov5670_stop_streaming(struct ov5670 *ov5670)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+ int ret;
+
+ ret = ov5670_write_reg(ov5670, OV5670_REG_MODE_SELECT,
+ OV5670_REG_VALUE_08BIT, OV5670_MODE_STANDBY);
+ if (ret)
+ dev_err(&client->dev, "%s failed to set stream\n", __func__);
+
+ ov5670->streaming = false;
+
+ /* Return success even if it was an error, as there is nothing the
+ * caller can do about it.
+ */
+ return 0;
+}
+
+static int ov5670_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ov5670 *ov5670 = to_ov5670(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ mutex_lock(&ov5670->mutex);
+ if (ov5670->streaming == enable)
+ goto unlock_and_return;
+
+ if (enable) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&client->dev);
+ goto unlock_and_return;
+ }
+
+ ret = ov5670_start_streaming(ov5670);
+ if (ret)
+ goto error;
+ } else {
+ ret = ov5670_stop_streaming(ov5670);
+ pm_runtime_put(&client->dev);
+ }
+ goto unlock_and_return;
+
+error:
+ pm_runtime_put(&client->dev);
+
+unlock_and_return:
+ mutex_unlock(&ov5670->mutex);
+
+ return ret;
+}
+
+static int __maybe_unused ov5670_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5670 *ov5670 = to_ov5670(sd);
+
+ if (ov5670->streaming)
+ ov5670_stop_streaming(ov5670);
+
+ return 0;
+}
+
+static int __maybe_unused ov5670_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5670 *ov5670 = to_ov5670(sd);
+ int ret;
+
+ if (ov5670->streaming) {
+ ret = ov5670_start_streaming(ov5670);
+ if (ret) {
+ ov5670_stop_streaming(ov5670);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* Verify chip ID */
+static int ov5670_identify_module(struct ov5670 *ov5670)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+ int ret;
+ u32 val;
+
+ ret = ov5670_read_reg(ov5670, OV5670_REG_CHIP_ID,
+ OV5670_REG_VALUE_24BIT, &val);
+ if (ret)
+ return ret;
+
+ if (val != OV5670_CHIP_ID) {
+ dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
+ OV5670_CHIP_ID, val);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops ov5670_video_ops = {
+ .s_stream = ov5670_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov5670_pad_ops = {
+ .enum_mbus_code = ov5670_enum_mbus_code,
+ .get_fmt = ov5670_get_pad_format,
+ .set_fmt = ov5670_set_pad_format,
+ .enum_frame_size = ov5670_enum_frame_size,
+};
+
+static const struct v4l2_subdev_sensor_ops ov5670_sensor_ops = {
+ .g_skip_frames = ov5670_get_skip_frames,
+};
+
+static const struct v4l2_subdev_ops ov5670_subdev_ops = {
+ .video = &ov5670_video_ops,
+ .pad = &ov5670_pad_ops,
+ .sensor = &ov5670_sensor_ops,
+};
+
+static const struct media_entity_operations ov5670_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops ov5670_internal_ops = {
+ .open = ov5670_open,
+};
+
+static int ov5670_probe(struct i2c_client *client)
+{
+ struct ov5670 *ov5670;
+ const char *err_msg;
+ u32 input_clk = 0;
+ int ret;
+
+ device_property_read_u32(&client->dev, "clock-frequency", &input_clk);
+ if (input_clk != 19200000)
+ return -EINVAL;
+
+ ov5670 = devm_kzalloc(&client->dev, sizeof(*ov5670), GFP_KERNEL);
+ if (!ov5670) {
+ ret = -ENOMEM;
+ err_msg = "devm_kzalloc() error";
+ goto error_print;
+ }
+
+ /* Initialize subdev */
+ v4l2_i2c_subdev_init(&ov5670->sd, client, &ov5670_subdev_ops);
+
+ /* Check module identity */
+ ret = ov5670_identify_module(ov5670);
+ if (ret) {
+ err_msg = "ov5670_identify_module() error";
+ goto error_print;
+ }
+
+ mutex_init(&ov5670->mutex);
+
+ /* Set default mode to max resolution */
+ ov5670->cur_mode = &supported_modes[0];
+
+ ret = ov5670_init_controls(ov5670);
+ if (ret) {
+ err_msg = "ov5670_init_controls() error";
+ goto error_mutex_destroy;
+ }
+
+ ov5670->sd.internal_ops = &ov5670_internal_ops;
+ ov5670->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov5670->sd.entity.ops = &ov5670_subdev_entity_ops;
+ ov5670->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ /* Source pad initialization */
+ ov5670->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&ov5670->sd.entity, 1, &ov5670->pad);
+ if (ret) {
+ err_msg = "media_entity_pads_init() error";
+ goto error_handler_free;
+ }
+
+ /* Async register for subdev */
+ ret = v4l2_async_register_subdev(&ov5670->sd);
+ if (ret < 0) {
+ err_msg = "v4l2_async_register_subdev() error";
+ goto error_entity_cleanup;
+ }
+
+ ov5670->streaming = false;
+
+ /*
+ * Device is already turned on by i2c-core with ACPI domain PM.
+ * Enable runtime PM and turn off the device.
+ */
+ pm_runtime_get_noresume(&client->dev);
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_put(&client->dev);
+
+ return 0;
+
+error_entity_cleanup:
+ media_entity_cleanup(&ov5670->sd.entity);
+
+error_handler_free:
+ v4l2_ctrl_handler_free(ov5670->sd.ctrl_handler);
+
+error_mutex_destroy:
+ mutex_destroy(&ov5670->mutex);
+
+error_print:
+ dev_err(&client->dev, "%s: %s %d\n", __func__, err_msg, ret);
+
+ return ret;
+}
+
+static int ov5670_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5670 *ov5670 = to_ov5670(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ mutex_destroy(&ov5670->mutex);
+
+ /*
+ * Disable runtime PM but keep the device turned on.
+ * i2c-core with ACPI domain PM will turn off the device.
+ */
+ pm_runtime_get_sync(&client->dev);
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ov5670_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ov5670_suspend, ov5670_resume)
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ov5670_acpi_ids[] = {
+ {"INT3479"},
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(acpi, ov5670_acpi_ids);
+#endif
+
+static struct i2c_driver ov5670_i2c_driver = {
+ .driver = {
+ .name = "ov5670",
+ .pm = &ov5670_pm_ops,
+ .acpi_match_table = ACPI_PTR(ov5670_acpi_ids),
+ },
+ .probe_new = ov5670_probe,
+ .remove = ov5670_remove,
+};
+
+module_i2c_driver(ov5670_i2c_driver);
+
+MODULE_AUTHOR("Rapolu, Chiranjeevi <chiranjeevi.rapolu@intel.com>");
+MODULE_AUTHOR("Yang, Hyungwoo <hyungwoo.yang@intel.com>");
+MODULE_DESCRIPTION("Omnivision ov5670 sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/ov6650.c
index d2be64d54b22..768f2950ea36 100644
--- a/drivers/media/i2c/soc_camera/ov6650.c
+++ b/drivers/media/i2c/ov6650.c
@@ -1,5 +1,5 @@
/*
- * V4L2 SoC Camera driver for OmniVision OV6650 Camera Sensor
+ * V4L2 subdevice driver for OmniVision OV6650 Camera Sensor
*
* Copyright (C) 2010 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
*
@@ -31,9 +31,9 @@
#include <linux/v4l2-mediabus.h>
#include <linux/module.h>
-#include <media/soc_camera.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
/* Register definitions */
#define REG_GAIN 0x00 /* range 00 - 3F */
@@ -426,10 +426,15 @@ static int ov6650_set_register(struct v4l2_subdev *sd,
static int ov6650_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct ov6650 *priv = to_ov6650(client);
+ int ret = 0;
- return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
+ if (on)
+ ret = v4l2_clk_enable(priv->clk);
+ else
+ v4l2_clk_disable(priv->clk);
+
+ return ret;
}
static int ov6650_get_selection(struct v4l2_subdev *sd,
@@ -471,14 +476,13 @@ static int ov6650_set_selection(struct v4l2_subdev *sd,
sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
- rect.left = ALIGN(rect.left, 2);
- rect.width = ALIGN(rect.width, 2);
- rect.top = ALIGN(rect.top, 2);
- rect.height = ALIGN(rect.height, 2);
- soc_camera_limit_side(&rect.left, &rect.width,
- DEF_HSTRT << 1, 2, W_CIF);
- soc_camera_limit_side(&rect.top, &rect.height,
- DEF_VSTRT << 1, 2, H_CIF);
+ v4l_bound_align_image(&rect.width, 2, W_CIF, 1,
+ &rect.height, 2, H_CIF, 1, 0);
+ v4l_bound_align_image(&rect.left, DEF_HSTRT << 1,
+ (DEF_HSTRT << 1) + W_CIF - (__s32)rect.width, 1,
+ &rect.top, DEF_VSTRT << 1,
+ (DEF_VSTRT << 1) + H_CIF - (__s32)rect.height, 1,
+ 0);
ret = ov6650_reg_write(client, REG_HSTRT, rect.left >> 1);
if (!ret) {
@@ -547,8 +551,6 @@ static u8 to_clkrc(struct v4l2_fract *timeperframe,
static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
- struct soc_camera_sense *sense = icd->sense;
struct ov6650 *priv = to_ov6650(client);
bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
struct v4l2_subdev_selection sel = {
@@ -640,32 +642,10 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
}
priv->half_scale = half_scale;
- if (sense) {
- if (sense->master_clock == 8000000) {
- dev_dbg(&client->dev, "8MHz input clock\n");
- clkrc = CLKRC_6MHz;
- } else if (sense->master_clock == 12000000) {
- dev_dbg(&client->dev, "12MHz input clock\n");
- clkrc = CLKRC_12MHz;
- } else if (sense->master_clock == 16000000) {
- dev_dbg(&client->dev, "16MHz input clock\n");
- clkrc = CLKRC_16MHz;
- } else if (sense->master_clock == 24000000) {
- dev_dbg(&client->dev, "24MHz input clock\n");
- clkrc = CLKRC_24MHz;
- } else {
- dev_err(&client->dev,
- "unsupported input clock, check platform data\n");
- return -EINVAL;
- }
- mclk = sense->master_clock;
- priv->pclk_limit = sense->pixel_clock_max;
- } else {
- clkrc = CLKRC_24MHz;
- mclk = 24000000;
- priv->pclk_limit = 0;
- dev_dbg(&client->dev, "using default 24MHz input clock\n");
- }
+ clkrc = CLKRC_12MHz;
+ mclk = 12000000;
+ priv->pclk_limit = 1334000;
+ dev_dbg(&client->dev, "using 12MHz input clock\n");
clkrc |= to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
@@ -899,8 +879,6 @@ static const struct v4l2_subdev_core_ops ov6650_core_ops = {
static int ov6650_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
cfg->flags = V4L2_MBUS_MASTER |
V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
@@ -908,7 +886,6 @@ static int ov6650_g_mbus_config(struct v4l2_subdev *sd,
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
V4L2_MBUS_DATA_ACTIVE_HIGH;
cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
return 0;
}
@@ -918,25 +895,23 @@ static int ov6650_s_mbus_config(struct v4l2_subdev *sd,
const struct v4l2_mbus_config *cfg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg);
int ret;
- if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ if (cfg->flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
else
ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
if (ret)
return ret;
- if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ if (cfg->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
else
ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
if (ret)
return ret;
- if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ if (cfg->flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
else
ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
@@ -973,14 +948,8 @@ static int ov6650_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov6650 *priv;
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
int ret;
- if (!ssdd) {
- dev_err(&client->dev, "Missing platform_data for driver\n");
- return -EINVAL;
- }
-
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(&client->dev,
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index 7270c68ed18a..e88549f0e704 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -1614,8 +1614,10 @@ static int ov7670_probe(struct i2c_client *client,
info->clk = devm_clk_get(&client->dev, "xclk");
if (IS_ERR(info->clk))
- return -EPROBE_DEFER;
- clk_prepare_enable(info->clk);
+ return PTR_ERR(info->clk);
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ return ret;
ret = ov7670_init_gpio(client, info);
if (ret)
diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
index 2de2fbb13b85..6ffb460e8589 100644
--- a/drivers/media/i2c/ov9650.c
+++ b/drivers/media/i2c/ov9650.c
@@ -484,6 +484,7 @@ static int ov965x_set_default_gamma_curve(struct ov965x *ov965x)
for (i = 0; i < ARRAY_SIZE(gamma_curve); i++) {
int ret = ov965x_write(ov965x->client, addr, gamma_curve[i]);
+
if (ret < 0)
return ret;
addr++;
@@ -503,6 +504,7 @@ static int ov965x_set_color_matrix(struct ov965x *ov965x)
for (i = 0; i < ARRAY_SIZE(mtx); i++) {
int ret = ov965x_write(ov965x->client, addr, mtx[i]);
+
if (ret < 0)
return ret;
addr++;
@@ -611,7 +613,7 @@ static int ov965x_set_banding_filter(struct ov965x *ov965x, int value)
}
if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED)
return 0;
- if (WARN_ON(ov965x->fiv == NULL))
+ if (WARN_ON(!ov965x->fiv))
return -EINVAL;
/* Set minimal exposure time for 50/60 HZ lighting */
if (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
@@ -999,44 +1001,47 @@ static int ov965x_initialize_controls(struct ov965x *ov965x)
/* Auto/manual white balance */
ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
- V4L2_CID_AUTO_WHITE_BALANCE,
- 0, 1, 1, 1);
+ V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 1);
ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
0, 0xff, 1, 0x80);
ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
- 0, 0xff, 1, 0x80);
+ 0, 0xff, 1, 0x80);
/* Auto/manual exposure */
- ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
- V4L2_CID_EXPOSURE_AUTO,
- V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+ ctrls->auto_exp =
+ v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0,
+ V4L2_EXPOSURE_AUTO);
/* Exposure time, in 100 us units. min/max is updated dynamically. */
ctrls->exposure = v4l2_ctrl_new_std(hdl, ops,
- V4L2_CID_EXPOSURE_ABSOLUTE,
- 2, 1500, 1, 500);
+ V4L2_CID_EXPOSURE_ABSOLUTE,
+ 2, 1500, 1, 500);
/* Auto/manual gain */
ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
- 0, 1, 1, 1);
+ 0, 1, 1, 1);
ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
- 16, 64 * (16 + 15), 1, 64 * 16);
+ 16, 64 * (16 + 15), 1, 64 * 16);
ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
- -2, 2, 1, 0);
+ -2, 2, 1, 0);
ctrls->brightness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS,
- -3, 3, 1, 0);
+ -3, 3, 1, 0);
ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS,
- 0, 32, 1, 6);
+ 0, 32, 1, 6);
ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
- ctrls->light_freq = v4l2_ctrl_new_std_menu(hdl, ops,
- V4L2_CID_POWER_LINE_FREQUENCY,
- V4L2_CID_POWER_LINE_FREQUENCY_60HZ, ~0x7,
- V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+ ctrls->light_freq =
+ v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, ~0x7,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
- ARRAY_SIZE(test_pattern_menu) - 1, 0, 0,
- test_pattern_menu);
+ ARRAY_SIZE(test_pattern_menu) - 1, 0, 0,
+ test_pattern_menu);
if (hdl->error) {
ret = hdl->error;
v4l2_ctrl_handler_free(hdl);
@@ -1121,7 +1126,6 @@ static int __ov965x_set_frame_interval(struct ov965x *ov965x,
u64 req_int, err, min_err = ~0ULL;
unsigned int i;
-
if (fi->interval.denominator == 0)
return -EINVAL;
@@ -1165,7 +1169,8 @@ static int ov965x_s_frame_interval(struct v4l2_subdev *sd,
return ret;
}
-static int ov965x_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+static int ov965x_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct ov965x *ov965x = to_ov965x(sd);
@@ -1209,7 +1214,8 @@ static void __ov965x_try_frame_size(struct v4l2_mbus_framefmt *mf,
*size = match;
}
-static int ov965x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+static int ov965x_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
unsigned int index = ARRAY_SIZE(ov965x_formats);
@@ -1231,7 +1237,7 @@ static int ov965x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config
mutex_lock(&ov965x->lock);
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- if (cfg != NULL) {
+ if (cfg) {
mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
*mf = fmt->format;
}
@@ -1362,7 +1368,8 @@ static int ov965x_s_stream(struct v4l2_subdev *sd, int on)
*/
static int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
- struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+ struct v4l2_mbus_framefmt *mf =
+ v4l2_subdev_get_try_format(sd, fh->pad, 0);
ov965x_get_default_format(mf);
return 0;
@@ -1470,7 +1477,7 @@ static int ov965x_probe(struct i2c_client *client,
struct ov965x *ov965x;
int ret;
- if (pdata == NULL) {
+ if (!pdata) {
dev_err(&client->dev, "platform data not specified\n");
return -EINVAL;
}
@@ -1498,13 +1505,13 @@ static int ov965x_probe(struct i2c_client *client,
ret = ov965x_configure_gpios(ov965x, pdata);
if (ret < 0)
- return ret;
+ goto err_mutex;
ov965x->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad);
if (ret < 0)
- return ret;
+ goto err_mutex;
ret = ov965x_initialize_controls(ov965x);
if (ret < 0)
@@ -1530,16 +1537,20 @@ err_ctrls:
v4l2_ctrl_handler_free(sd->ctrl_handler);
err_me:
media_entity_cleanup(&sd->entity);
+err_mutex:
+ mutex_destroy(&ov965x->lock);
return ret;
}
static int ov965x_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov965x *ov965x = to_ov965x(sd);
v4l2_async_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
media_entity_cleanup(&sd->entity);
+ mutex_destroy(&ov965x->lock);
return 0;
}
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index f434fb2ee6fc..cdc4f2392ef9 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -1635,8 +1635,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state)
node_ep = of_graph_get_next_endpoint(node, NULL);
if (!node_ep) {
- dev_warn(dev, "no endpoint defined for node: %s\n",
- node->full_name);
+ dev_warn(dev, "no endpoint defined for node: %pOF\n", node);
return 0;
}
diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
index 962051b9939d..ff46d2c96cea 100644
--- a/drivers/media/i2c/s5k5baf.c
+++ b/drivers/media/i2c/s5k5baf.c
@@ -1374,7 +1374,7 @@ static int s5k5baf_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
- static enum selection_rect rtype;
+ enum selection_rect rtype;
struct s5k5baf *state = to_s5k5baf(sd);
rtype = s5k5baf_get_sel_rect(sel->pad, sel->target);
@@ -1863,8 +1863,7 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev)
node_ep = of_graph_get_next_endpoint(node, NULL);
if (!node_ep) {
- dev_err(dev, "no endpoint defined at node %s\n",
- node->full_name);
+ dev_err(dev, "no endpoint defined at node %pOF\n", node);
return -EINVAL;
}
@@ -1882,8 +1881,8 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev)
case V4L2_MBUS_PARALLEL:
break;
default:
- dev_err(dev, "unsupported bus in endpoint defined at node %s\n",
- node->full_name);
+ dev_err(dev, "unsupported bus in endpoint defined at node %pOF\n",
+ node);
return -EINVAL;
}
diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c
index 99c303002e90..01784d441ae6 100644
--- a/drivers/media/i2c/saa7127.c
+++ b/drivers/media/i2c/saa7127.c
@@ -806,7 +806,7 @@ static int saa7127_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static struct i2c_device_id saa7127_id[] = {
+static const struct i2c_device_id saa7127_id[] = {
{ "saa7127_auto", 0 }, /* auto-detection */
{ "saa7126", SAA7127 },
{ "saa7127", SAA7127 },
diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c
index e1f6bc219c64..102467e00fb3 100644
--- a/drivers/media/i2c/saa717x.c
+++ b/drivers/media/i2c/saa717x.c
@@ -1069,7 +1069,7 @@ static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
struct saa717x_state *decoder = to_state(sd);
v4l2_dbg(1, debug, sd, "decoder set norm ");
- v4l2_dbg(1, debug, sd, "(not yet implementd)\n");
+ v4l2_dbg(1, debug, sd, "(not yet implemented)\n");
decoder->radio = 0;
decoder->std = std;
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index e0b0c032c4ac..700f433261d0 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -841,6 +841,8 @@ static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor)
&client->dev,
compressed_max_bpp - sensor->compressed_min_bpp + 1,
sizeof(*sensor->valid_link_freqs), GFP_KERNEL);
+ if (!sensor->valid_link_freqs)
+ return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) {
const struct smiapp_csi_data_format *f =
@@ -2809,13 +2811,19 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev)
switch (bus_cfg->bus_type) {
case V4L2_MBUS_CSI2:
hwcfg->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2;
+ hwcfg->lanes = bus_cfg->bus.mipi_csi2.num_data_lanes;
+ break;
+ case V4L2_MBUS_CCP2:
+ hwcfg->csi_signalling_mode = (bus_cfg->bus.mipi_csi1.strobe) ?
+ SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE :
+ SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK;
+ hwcfg->lanes = 1;
break;
- /* FIXME: add CCP2 support. */
default:
+ dev_err(dev, "unsupported bus %u\n", bus_cfg->bus_type);
goto out_err;
}
- hwcfg->lanes = bus_cfg->bus.mipi_csi2.num_data_lanes;
dev_dbg(dev, "lanes %u\n", hwcfg->lanes);
/* NVM size is not mandatory */
@@ -2828,8 +2836,8 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev)
goto out_err;
}
- dev_dbg(dev, "nvm %d, clk %d, csi %d\n", hwcfg->nvm_size,
- hwcfg->ext_clk, hwcfg->csi_signalling_mode);
+ dev_dbg(dev, "nvm %d, clk %d, mode %d\n",
+ hwcfg->nvm_size, hwcfg->ext_clk, hwcfg->csi_signalling_mode);
if (!bus_cfg->nr_of_link_frequencies) {
dev_warn(dev, "no link frequencies defined\n");
diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c
index cb128eae9c54..95c0272bb014 100644
--- a/drivers/media/i2c/smiapp/smiapp-quirk.c
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.c
@@ -71,7 +71,7 @@ static int jt8ew9_limits(struct smiapp_sensor *sensor)
static int jt8ew9_post_poweron(struct smiapp_sensor *sensor)
{
- const struct smiapp_reg_8 regs[] = {
+ static const struct smiapp_reg_8 regs[] = {
{ 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */
{ 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */
{ 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */
@@ -115,7 +115,7 @@ const struct smiapp_quirk smiapp_jt8ew9_quirk = {
static int imx125es_post_poweron(struct smiapp_sensor *sensor)
{
/* Taken from v02. No idea what the other two are. */
- const struct smiapp_reg_8 regs[] = {
+ static const struct smiapp_reg_8 regs[] = {
/*
* 0x3302: clk during frame blanking:
* 0x00 - HS mode, 0x01 - LP11
@@ -145,7 +145,7 @@ static int jt8ev1_post_poweron(struct smiapp_sensor *sensor)
{
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
int rval;
- const struct smiapp_reg_8 regs[] = {
+ static const struct smiapp_reg_8 regs[] = {
{ 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */
{ 0x30a3, 0xd0 }, /* FLASH STROBE enable */
{ 0x3237, 0x00 }, /* For control of pulse timing for ADC */
@@ -166,7 +166,7 @@ static int jt8ev1_post_poweron(struct smiapp_sensor *sensor)
{ 0x33cf, 0xec }, /* For Black sun */
{ 0x3328, 0x80 }, /* Ugh. No idea what's this. */
};
- const struct smiapp_reg_8 regs_96[] = {
+ static const struct smiapp_reg_8 regs_96[] = {
{ 0x30ae, 0x00 }, /* For control of ADC clock */
{ 0x30af, 0xd0 },
{ 0x30b0, 0x01 },
diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig
index 96859f37cb1c..72b369895b37 100644
--- a/drivers/media/i2c/soc_camera/Kconfig
+++ b/drivers/media/i2c/soc_camera/Kconfig
@@ -47,12 +47,6 @@ config SOC_CAMERA_OV5642
help
This is a V4L2 camera driver for the OmniVision OV5642 sensor
-config SOC_CAMERA_OV6650
- tristate "ov6650 sensor support"
- depends on SOC_CAMERA && I2C
- ---help---
- This is a V4L2 SoC camera driver for the OmniVision OV6650 sensor
-
config SOC_CAMERA_OV772X
tristate "ov772x camera support"
depends on SOC_CAMERA && I2C
diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile
index 974bdb721dbe..78532a7fb8e2 100644
--- a/drivers/media/i2c/soc_camera/Makefile
+++ b/drivers/media/i2c/soc_camera/Makefile
@@ -4,7 +4,6 @@ obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o
obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o
-obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o
obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o
obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o
diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c
index 714fb3555b34..4802d30e47de 100644
--- a/drivers/media/i2c/soc_camera/mt9t031.c
+++ b/drivers/media/i2c/soc_camera/mt9t031.c
@@ -592,7 +592,7 @@ static const struct dev_pm_ops mt9t031_dev_pm_ops = {
.runtime_resume = mt9t031_runtime_resume,
};
-static struct device_type mt9t031_dev_type = {
+static const struct device_type mt9t031_dev_type = {
.name = "MT9T031",
.pm = &mt9t031_dev_pm_ops,
};
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index 5788af238b86..e6f5c363ccab 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -2013,7 +2013,7 @@ static int tc358743_remove(struct i2c_client *client)
return 0;
}
-static struct i2c_device_id tc358743_id[] = {
+static const struct i2c_device_id tc358743_id[] = {
{"tc358743", 0},
{}
};
diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c
index 42340e364cea..498ad2368cbc 100644
--- a/drivers/media/i2c/ths8200.c
+++ b/drivers/media/i2c/ths8200.c
@@ -483,7 +483,7 @@ static int ths8200_remove(struct i2c_client *client)
return 0;
}
-static struct i2c_device_id ths8200_id[] = {
+static const struct i2c_device_id ths8200_id[] = {
{ "ths8200", 0 },
{},
};
diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c
index f0741ab338df..560738213c00 100644
--- a/drivers/media/i2c/vs6624.c
+++ b/drivers/media/i2c/vs6624.c
@@ -58,7 +58,7 @@ static const struct vs6624_format {
},
};
-static struct v4l2_mbus_framefmt vs6624_default_fmt = {
+static const struct v4l2_mbus_framefmt vs6624_default_fmt = {
.width = VGA_WIDTH,
.height = VGA_HEIGHT,
.code = MEDIA_BUS_FMT_UYVY8_2X8,