aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c
diff options
context:
space:
mode:
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.c27
-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/tvp5150.c25
-rw-r--r--drivers/media/i2c/vs6624.c2
40 files changed, 5836 insertions, 249 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 6e313d5243a0..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;
}
/*
@@ -1496,7 +1504,6 @@ MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
static const struct dev_pm_ops et8ek8_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(et8ek8_suspend, et8ek8_resume)
};
-MODULE_DEVICE_TABLE(of, et8ek8_of_table);
static struct i2c_driver et8ek8_i2c_driver = {
.driver = {
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/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 9da4bf4f2c7a..7b79a7498751 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -659,7 +659,7 @@ static int tvp5150_set_vbi(struct v4l2_subdev *sd,
struct tvp5150 *decoder = to_tvp5150(sd);
v4l2_std_id std = decoder->norm;
u8 reg;
- int pos=0;
+ int pos = 0;
if (std == V4L2_STD_ALL) {
dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n");
@@ -669,33 +669,30 @@ static int tvp5150_set_vbi(struct v4l2_subdev *sd,
line += 3;
}
- if (line<6||line>27)
+ if (line < 6 || line > 27)
return 0;
- while (regs->reg != (u16)-1 ) {
+ while (regs->reg != (u16)-1) {
if ((type & regs->type.vbi_type) &&
- (line>=regs->type.ini_line) &&
- (line<=regs->type.end_line)) {
- type=regs->type.vbi_type;
+ (line >= regs->type.ini_line) &&
+ (line <= regs->type.end_line))
break;
- }
regs++;
pos++;
}
+
if (regs->reg == (u16)-1)
return 0;
- type=pos | (flags & 0xf0);
- reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
+ type = pos | (flags & 0xf0);
+ reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
- if (fields&1) {
+ if (fields & 1)
tvp5150_write(sd, reg, type);
- }
- if (fields&2) {
- tvp5150_write(sd, reg+1, type);
- }
+ if (fields & 2)
+ tvp5150_write(sd, reg + 1, type);
return type;
}
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,