summaryrefslogtreecommitdiff
path: root/drivers/media/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c')
-rw-r--r--drivers/media/i2c/Kconfig18
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/adv7180.c5
-rw-r--r--drivers/media/i2c/adv748x/adv748x.h2
-rw-r--r--drivers/media/i2c/adv7604.c5
-rw-r--r--drivers/media/i2c/ar0521.c1061
-rw-r--r--drivers/media/i2c/mt9p031.c93
-rw-r--r--drivers/media/i2c/ov5640.c1650
-rw-r--r--drivers/media/i2c/ov5693.c57
-rw-r--r--drivers/media/i2c/ov7251.c7
-rw-r--r--drivers/media/i2c/st-mipid02.c30
-rw-r--r--drivers/media/i2c/tda1997x.c1
-rw-r--r--drivers/media/i2c/tvp5150.c2
13 files changed, 2398 insertions, 534 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 2b20aa6c37b1..7806d4b81716 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -34,6 +34,19 @@ config VIDEO_APTINA_PLL
config VIDEO_CCS_PLL
tristate
+config VIDEO_AR0521
+ tristate "ON Semiconductor AR0521 sensor support"
+ depends on I2C && VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for the ON Semiconductor
+ AR0521 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ar0521.
+
config VIDEO_HI556
tristate "Hynix Hi-556 sensor support"
depends on I2C && VIDEO_DEV
@@ -75,8 +88,10 @@ config VIDEO_HI847
config VIDEO_IMX208
tristate "Sony IMX208 sensor support"
- depends on I2C && VIDEO_DEV && VIDEO_V4L2_SUBDEV_API
+ depends on I2C && VIDEO_DEV
depends on MEDIA_CAMERA_SUPPORT
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
help
This is a Video4Linux2 sensor driver for the Sony
IMX208 camera.
@@ -1178,6 +1193,7 @@ config VIDEO_ISL7998X
depends on OF_GPIO
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
help
Support for Intersil ISL7998x analog to MIPI-CSI2 or
BT.656 decoder.
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 3e1696963e7f..0a2933103dd9 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o
obj-$(CONFIG_VIDEO_AK7375) += ak7375.o
obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
+obj-$(CONFIG_VIDEO_AR0521) += ar0521.o
obj-$(CONFIG_VIDEO_BT819) += bt819.o
obj-$(CONFIG_VIDEO_BT856) += bt856.o
obj-$(CONFIG_VIDEO_BT866) += bt866.o
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index e3a57c178c6b..5fde5243722d 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -43,6 +43,7 @@
#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f
#define ADV7182_REG_INPUT_VIDSEL 0x0002
+#define ADV7182_REG_INPUT_RESERVED BIT(2)
#define ADV7180_REG_OUTPUT_CONTROL 0x0003
#define ADV7180_REG_EXTENDED_OUTPUT_CONTROL 0x0004
@@ -1060,7 +1061,9 @@ static int adv7182_init(struct adv7180_state *state)
static int adv7182_set_std(struct adv7180_state *state, unsigned int std)
{
- return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, std << 4);
+ /* Failing to set the reserved bit can result in increased video noise */
+ return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL,
+ (std << 4) | ADV7182_REG_INPUT_RESERVED);
}
enum adv7182_input_type {
diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h
index 31bac06d46b5..d75eb3d8be5a 100644
--- a/drivers/media/i2c/adv748x/adv748x.h
+++ b/drivers/media/i2c/adv748x/adv748x.h
@@ -417,7 +417,7 @@ int adv748x_write_block(struct adv748x_state *state, int client_page,
static inline struct v4l2_subdev *adv748x_get_remote_sd(struct media_pad *pad)
{
- pad = media_entity_remote_pad(pad);
+ pad = media_pad_remote_pad_first(pad);
if (!pad)
return NULL;
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index bb0c8fc6d383..497419a5cfdd 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -2505,9 +2505,8 @@ static void adv76xx_log_infoframes(struct v4l2_subdev *sd)
union hdmi_infoframe frame;
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (adv76xx_read_infoframe(sd, i, &frame))
- return;
- hdmi_infoframe_log(KERN_INFO, &client->dev, &frame);
+ if (!adv76xx_read_infoframe(sd, i, &frame))
+ hdmi_infoframe_log(KERN_INFO, &client->dev, &frame);
}
}
diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c
new file mode 100644
index 000000000000..c7bdfc69b9be
--- /dev/null
+++ b/drivers/media/i2c/ar0521.c
@@ -0,0 +1,1061 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Sieć Badawcza Łukasiewicz
+ * - Przemysłowy Instytut Automatyki i Pomiarów PIAP
+ * Written by Krzysztof Hałasa
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* External clock (extclk) frequencies */
+#define AR0521_EXTCLK_MIN (10 * 1000 * 1000)
+#define AR0521_EXTCLK_MAX (48 * 1000 * 1000)
+
+/* PLL and PLL2 */
+#define AR0521_PLL_MIN (320 * 1000 * 1000)
+#define AR0521_PLL_MAX (1280 * 1000 * 1000)
+
+/* Effective pixel clocks, the registers may be DDR */
+#define AR0521_PIXEL_CLOCK_RATE (184 * 1000 * 1000)
+#define AR0521_PIXEL_CLOCK_MIN (168 * 1000 * 1000)
+#define AR0521_PIXEL_CLOCK_MAX (414 * 1000 * 1000)
+
+#define AR0521_WIDTH_MIN 8u
+#define AR0521_WIDTH_MAX 2608u
+#define AR0521_HEIGHT_MIN 8u
+#define AR0521_HEIGHT_MAX 1958u
+
+#define AR0521_WIDTH_BLANKING_MIN 572u
+#define AR0521_HEIGHT_BLANKING_MIN 38u /* must be even */
+#define AR0521_TOTAL_WIDTH_MIN 2968u
+
+/* AR0521 registers */
+#define AR0521_REG_VT_PIX_CLK_DIV 0x0300
+#define AR0521_REG_FRAME_LENGTH_LINES 0x0340
+
+#define AR0521_REG_CHIP_ID 0x3000
+#define AR0521_REG_COARSE_INTEGRATION_TIME 0x3012
+#define AR0521_REG_ROW_SPEED 0x3016
+#define AR0521_REG_EXTRA_DELAY 0x3018
+#define AR0521_REG_RESET 0x301A
+#define AR0521_REG_RESET_DEFAULTS 0x0238
+#define AR0521_REG_RESET_GROUP_PARAM_HOLD 0x8000
+#define AR0521_REG_RESET_STREAM BIT(2)
+#define AR0521_REG_RESET_RESTART BIT(1)
+#define AR0521_REG_RESET_INIT BIT(0)
+
+#define AR0521_REG_GREEN1_GAIN 0x3056
+#define AR0521_REG_BLUE_GAIN 0x3058
+#define AR0521_REG_RED_GAIN 0x305A
+#define AR0521_REG_GREEN2_GAIN 0x305C
+#define AR0521_REG_GLOBAL_GAIN 0x305E
+
+#define AR0521_REG_HISPI_TEST_MODE 0x3066
+#define AR0521_REG_HISPI_TEST_MODE_LP11 0x0004
+
+#define AR0521_REG_TEST_PATTERN_MODE 0x3070
+
+#define AR0521_REG_SERIAL_FORMAT 0x31AE
+#define AR0521_REG_SERIAL_FORMAT_MIPI 0x0200
+
+#define AR0521_REG_HISPI_CONTROL_STATUS 0x31C6
+#define AR0521_REG_HISPI_CONTROL_STATUS_FRAMER_TEST_MODE_ENABLE 0x80
+
+#define be cpu_to_be16
+
+static const char * const ar0521_supply_names[] = {
+ "vdd_io", /* I/O (1.8V) supply */
+ "vdd", /* Core, PLL and MIPI (1.2V) supply */
+ "vaa", /* Analog (2.7V) supply */
+};
+
+struct ar0521_ctrls {
+ struct v4l2_ctrl_handler handler;
+ struct {
+ struct v4l2_ctrl *gain;
+ struct v4l2_ctrl *red_balance;
+ struct v4l2_ctrl *blue_balance;
+ };
+ struct {
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ };
+ struct v4l2_ctrl *pixrate;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *test_pattern;
+};
+
+struct ar0521_dev {
+ struct i2c_client *i2c_client;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct clk *extclk;
+ u32 extclk_freq;
+
+ struct regulator *supplies[ARRAY_SIZE(ar0521_supply_names)];
+ struct gpio_desc *reset_gpio;
+
+ /* lock to protect all members below */
+ struct mutex lock;
+
+ struct v4l2_mbus_framefmt fmt;
+ struct ar0521_ctrls ctrls;
+ unsigned int lane_count;
+ u16 total_width;
+ u16 total_height;
+ u16 pll_pre;
+ u16 pll_mult;
+ u16 pll_pre2;
+ u16 pll_mult2;
+ bool streaming;
+};
+
+static inline struct ar0521_dev *to_ar0521_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ar0521_dev, sd);
+}
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct ar0521_dev,
+ ctrls.handler)->sd;
+}
+
+static u32 div64_round(u64 v, u32 d)
+{
+ return div_u64(v + (d >> 1), d);
+}
+
+static u32 div64_round_up(u64 v, u32 d)
+{
+ return div_u64(v + d - 1, d);
+}
+
+/* Data must be BE16, the first value is the register address */
+static int ar0521_write_regs(struct ar0521_dev *sensor, const __be16 *data,
+ unsigned int count)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ struct i2c_msg msg;
+ int ret;
+
+ msg.addr = client->addr;
+ msg.flags = client->flags;
+ msg.buf = (u8 *)data;
+ msg.len = count * sizeof(*data);
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ if (ret < 0) {
+ v4l2_err(&sensor->sd, "%s: I2C write error\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ar0521_write_reg(struct ar0521_dev *sensor, u16 reg, u16 val)
+{
+ __be16 buf[2] = {be(reg), be(val)};
+
+ return ar0521_write_regs(sensor, buf, 2);
+}
+
+static int ar0521_set_geometry(struct ar0521_dev *sensor)
+{
+ /* All dimensions are unsigned 12-bit integers */
+ u16 x = (AR0521_WIDTH_MAX - sensor->fmt.width) / 2;
+ u16 y = ((AR0521_HEIGHT_MAX - sensor->fmt.height) / 2) & ~1;
+ __be16 regs[] = {
+ be(AR0521_REG_FRAME_LENGTH_LINES),
+ be(sensor->total_height),
+ be(sensor->total_width),
+ be(x),
+ be(y),
+ be(x + sensor->fmt.width - 1),
+ be(y + sensor->fmt.height - 1),
+ be(sensor->fmt.width),
+ be(sensor->fmt.height)
+ };
+
+ return ar0521_write_regs(sensor, regs, ARRAY_SIZE(regs));
+}
+
+static int ar0521_set_gains(struct ar0521_dev *sensor)
+{
+ int green = sensor->ctrls.gain->val;
+ int red = max(green + sensor->ctrls.red_balance->val, 0);
+ int blue = max(green + sensor->ctrls.blue_balance->val, 0);
+ unsigned int gain = min(red, min(green, blue));
+ unsigned int analog = min(gain, 64u); /* range is 0 - 127 */
+ __be16 regs[5];
+
+ red = min(red - analog + 64, 511u);
+ green = min(green - analog + 64, 511u);
+ blue = min(blue - analog + 64, 511u);
+ regs[0] = be(AR0521_REG_GREEN1_GAIN);
+ regs[1] = be(green << 7 | analog);
+ regs[2] = be(blue << 7 | analog);
+ regs[3] = be(red << 7 | analog);
+ regs[4] = be(green << 7 | analog);
+
+ return ar0521_write_regs(sensor, regs, ARRAY_SIZE(regs));
+}
+
+static u32 calc_pll(struct ar0521_dev *sensor, int num, u32 freq, u16 *pre_ptr,
+ u16 *mult_ptr)
+{
+ u16 pre = 1, mult = 1, new_pre;
+ u32 pll = AR0521_PLL_MAX + 1;
+
+ for (new_pre = 1; new_pre < 64; new_pre++) {
+ u32 new_pll;
+ u32 new_mult = div64_round_up((u64)freq * new_pre,
+ sensor->extclk_freq);
+
+ if (new_mult < 32)
+ continue; /* Minimum value */
+ if (new_mult > 254)
+ break; /* Maximum, larger pre won't work either */
+ if (sensor->extclk_freq * (u64)new_mult < AR0521_PLL_MIN *
+ new_pre)
+ continue;
+ if (sensor->extclk_freq * (u64)new_mult > AR0521_PLL_MAX *
+ new_pre)
+ break; /* Larger pre won't work either */
+ new_pll = div64_round_up(sensor->extclk_freq * (u64)new_mult,
+ new_pre);
+ if (new_pll < pll) {
+ pll = new_pll;
+ pre = new_pre;
+ mult = new_mult;
+ }
+ }
+
+ pll = div64_round(sensor->extclk_freq * (u64)mult, pre);
+ *pre_ptr = pre;
+ *mult_ptr = mult;
+ return pll;
+}
+
+#define DIV 4
+static void ar0521_calc_mode(struct ar0521_dev *sensor)
+{
+ unsigned int speed_mod = 4 / sensor->lane_count; /* 1 with 4 DDR lanes */
+ u16 total_width = max(sensor->fmt.width + AR0521_WIDTH_BLANKING_MIN,
+ AR0521_TOTAL_WIDTH_MIN);
+ u16 total_height = sensor->fmt.height + AR0521_HEIGHT_BLANKING_MIN;
+
+ /* Calculate approximate pixel clock first */
+ u64 pix_clk = AR0521_PIXEL_CLOCK_RATE;
+
+ /* PLL1 drives pixel clock - dual rate */
+ pix_clk = calc_pll(sensor, 1, pix_clk * (DIV / 2), &sensor->pll_pre,
+ &sensor->pll_mult);
+ pix_clk = div64_round(pix_clk, (DIV / 2));
+ calc_pll(sensor, 2, pix_clk * (DIV / 2) * speed_mod, &sensor->pll_pre2,
+ &sensor->pll_mult2);
+
+ sensor->total_width = total_width;
+ sensor->total_height = total_height;
+}
+
+static int ar0521_write_mode(struct ar0521_dev *sensor)
+{
+ __be16 pll_regs[] = {
+ be(AR0521_REG_VT_PIX_CLK_DIV),
+ /* 0x300 */ be(4), /* vt_pix_clk_div = number of bits / 2 */
+ /* 0x302 */ be(1), /* vt_sys_clk_div */
+ /* 0x304 */ be((sensor->pll_pre2 << 8) | sensor->pll_pre),
+ /* 0x306 */ be((sensor->pll_mult2 << 8) | sensor->pll_mult),
+ /* 0x308 */ be(8), /* op_pix_clk_div = 2 * vt_pix_clk_div */
+ /* 0x30A */ be(1) /* op_sys_clk_div */
+ };
+ int ret;
+
+ /* Stop streaming for just a moment */
+ ret = ar0521_write_reg(sensor, AR0521_REG_RESET,
+ AR0521_REG_RESET_DEFAULTS);
+ if (ret)
+ return ret;
+
+ ret = ar0521_set_geometry(sensor);
+ if (ret)
+ return ret;
+
+ ret = ar0521_write_regs(sensor, pll_regs, ARRAY_SIZE(pll_regs));
+ if (ret)
+ return ret;
+
+ ret = ar0521_write_reg(sensor, AR0521_REG_COARSE_INTEGRATION_TIME,
+ sensor->ctrls.exposure->val);
+ if (ret)
+ return ret;
+
+ ret = ar0521_write_reg(sensor, AR0521_REG_RESET,
+ AR0521_REG_RESET_DEFAULTS |
+ AR0521_REG_RESET_STREAM);
+ if (ret)
+ return ret;
+
+ ret = ar0521_write_reg(sensor, AR0521_REG_TEST_PATTERN_MODE,
+ sensor->ctrls.test_pattern->val);
+ return ret;
+}
+
+static int ar0521_set_stream(struct ar0521_dev *sensor, bool on)
+{
+ int ret;
+
+ if (on) {
+ ret = pm_runtime_resume_and_get(&sensor->i2c_client->dev);
+ if (ret < 0)
+ return ret;
+
+ ar0521_calc_mode(sensor);
+ ret = ar0521_write_mode(sensor);
+ if (ret)
+ goto err;
+
+ ret = ar0521_set_gains(sensor);
+ if (ret)
+ goto err;
+
+ /* Exit LP-11 mode on clock and data lanes */
+ ret = ar0521_write_reg(sensor, AR0521_REG_HISPI_CONTROL_STATUS,
+ 0);
+ if (ret)
+ goto err;
+
+ /* Start streaming */
+ ret = ar0521_write_reg(sensor, AR0521_REG_RESET,
+ AR0521_REG_RESET_DEFAULTS |
+ AR0521_REG_RESET_STREAM);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ pm_runtime_put(&sensor->i2c_client->dev);
+ return ret;
+
+ } else {
+ /*
+ * Reset gain, the sensor may produce all white pixels without
+ * this
+ */
+ ret = ar0521_write_reg(sensor, AR0521_REG_GLOBAL_GAIN, 0x2000);
+ if (ret)
+ return ret;
+
+ /* Stop streaming */
+ ret = ar0521_write_reg(sensor, AR0521_REG_RESET,
+ AR0521_REG_RESET_DEFAULTS);
+ if (ret)
+ return ret;
+
+ pm_runtime_put(&sensor->i2c_client->dev);
+ return 0;
+ }
+}
+
+static void ar0521_adj_fmt(struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->width = clamp(ALIGN(fmt->width, 4), AR0521_WIDTH_MIN,
+ AR0521_WIDTH_MAX);
+ fmt->height = clamp(ALIGN(fmt->height, 4), AR0521_HEIGHT_MIN,
+ AR0521_HEIGHT_MAX);
+ fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int ar0521_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+ struct v4l2_mbus_framefmt *fmt;
+
+ mutex_lock(&sensor->lock);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, 0
+ /* pad */);
+ else
+ fmt = &sensor->fmt;
+
+ format->format = *fmt;
+
+ mutex_unlock(&sensor->lock);
+ return 0;
+}
+
+static int ar0521_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+ int ret = 0;
+
+ ar0521_adj_fmt(&format->format);
+
+ mutex_lock(&sensor->lock);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_get_try_format(sd, sd_state, 0 /* pad */);
+ *fmt = format->format;
+ } else {
+ sensor->fmt = format->format;
+ ar0521_calc_mode(sensor);
+ }
+
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static int ar0521_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+ int ret;
+
+ /* v4l2_ctrl_lock() locks our own mutex */
+
+ switch (ctrl->id) {
+ case V4L2_CID_HBLANK:
+ case V4L2_CID_VBLANK:
+ sensor->total_width = sensor->fmt.width +
+ sensor->ctrls.hblank->val;
+ sensor->total_height = sensor->fmt.width +
+ sensor->ctrls.vblank->val;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ /* access the sensor only if it's powered up */
+ if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HBLANK:
+ case V4L2_CID_VBLANK:
+ ret = ar0521_set_geometry(sensor);
+ break;
+ case V4L2_CID_GAIN:
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ ret = ar0521_set_gains(sensor);
+ break;
+ case V4L2_CID_EXPOSURE:
+ ret = ar0521_write_reg(sensor,
+ AR0521_REG_COARSE_INTEGRATION_TIME,
+ ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ar0521_write_reg(sensor, AR0521_REG_TEST_PATTERN_MODE,
+ ctrl->val);
+ break;
+ }
+
+ pm_runtime_put(&sensor->i2c_client->dev);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ar0521_ctrl_ops = {
+ .s_ctrl = ar0521_s_ctrl,
+};
+
+static const char * const test_pattern_menu[] = {
+ "Disabled",
+ "Solid color",
+ "Color bars",
+ "Faded color bars"
+};
+
+static int ar0521_init_controls(struct ar0521_dev *sensor)
+{
+ const struct v4l2_ctrl_ops *ops = &ar0521_ctrl_ops;
+ struct ar0521_ctrls *ctrls = &sensor->ctrls;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+ int ret;
+
+ v4l2_ctrl_handler_init(hdl, 32);
+
+ /* We can use our own mutex for the ctrl lock */
+ hdl->lock = &sensor->lock;
+
+ /* Manual gain */
+ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 511, 1, 0);
+ ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
+ -512, 511, 1, 0);
+ ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
+ -512, 511, 1, 0);
+ v4l2_ctrl_cluster(3, &ctrls->gain);
+
+ ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
+ AR0521_WIDTH_BLANKING_MIN, 4094, 1,
+ AR0521_WIDTH_BLANKING_MIN);
+ ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+ AR0521_HEIGHT_BLANKING_MIN, 4094, 2,
+ AR0521_HEIGHT_BLANKING_MIN);
+ v4l2_ctrl_cluster(2, &ctrls->hblank);
+
+ /* Read-only */
+ ctrls->pixrate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
+ AR0521_PIXEL_CLOCK_MIN,
+ AR0521_PIXEL_CLOCK_MAX, 1,
+ AR0521_PIXEL_CLOCK_RATE);
+
+ /* Manual exposure time */
+ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 0,
+ 65535, 1, 360);
+
+ ctrls->test_pattern = v4l2_ctrl_new_std_menu_items(hdl, ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(test_pattern_menu) - 1,
+ 0, 0, test_pattern_menu);
+
+ if (hdl->error) {
+ ret = hdl->error;
+ goto free_ctrls;
+ }
+
+ sensor->sd.ctrl_handler = hdl;
+ return 0;
+
+free_ctrls:
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+}
+
+#define REGS_ENTRY(a) {(a), ARRAY_SIZE(a)}
+#define REGS(...) REGS_ENTRY(((const __be16[]){__VA_ARGS__}))
+
+static const struct initial_reg {
+ const __be16 *data; /* data[0] is register address */
+ unsigned int count;
+} initial_regs[] = {
+ REGS(be(0x0112), be(0x0808)), /* 8-bit/8-bit mode */
+
+ /* PEDESTAL+2 :+2 is a workaround for 10bit mode +0.5 rounding */
+ REGS(be(0x301E), be(0x00AA)),
+
+ /* corrections_recommended_bayer */
+ REGS(be(0x3042),
+ be(0x0004), /* 3042: RNC: enable b/w rnc mode */
+ be(0x4580)), /* 3044: RNC: enable row noise correction */
+
+ REGS(be(0x30D2),
+ be(0x0000), /* 30D2: CRM/CC: enable crm on Visible and CC rows */
+ be(0x0000), /* 30D4: CC: CC enabled with 16 samples per column */
+ /* 30D6: CC: bw mode enabled/12 bit data resolution/bw mode */
+ be(0x2FFF)),
+
+ REGS(be(0x30DA),
+ be(0x0FFF), /* 30DA: CC: column correction clip level 2 is 0 */
+ be(0x0FFF), /* 30DC: CC: column correction clip level 3 is 0 */
+ be(0x0000)), /* 30DE: CC: Group FPN correction */
+
+ /* RNC: rnc scaling factor = * 54 / 64 (32 / 38 * 64 = 53.9) */
+ REGS(be(0x30EE), be(0x1136)),
+ REGS(be(0x30FA), be(0xFD00)), /* GPIO0 = flash, GPIO1 = shutter */
+ REGS(be(0x3120), be(0x0005)), /* p1 dither enabled for 10bit mode */
+ REGS(be(0x3172), be(0x0206)), /* txlo clk divider options */
+ /* FDOC:fdoc settings with fdoc every frame turned of */
+ REGS(be(0x3180), be(0x9434)),
+
+ REGS(be(0x31B0),
+ be(0x008B), /* 31B0: frame_preamble - FIXME check WRT lanes# */
+ be(0x0050)), /* 31B2: line_preamble - FIXME check WRT lanes# */
+
+ /* don't use continuous clock mode while shut down */
+ REGS(be(0x31BC), be(0x068C)),
+ REGS(be(0x31E0), be(0x0781)), /* Fuse/2DDC: enable 2ddc */
+
+ /* analog_setup_recommended_10bit */
+ REGS(be(0x341A), be(0x4735)), /* Samp&Hold pulse in ADC */
+ REGS(be(0x3420), be(0x4735)), /* Samp&Hold pulse in ADC */
+ REGS(be(0x3426), be(0x8A1A)), /* ADC offset distribution pulse */
+ REGS(be(0x342A), be(0x0018)), /* pulse_config */
+
+ /* pixel_timing_recommended */
+ REGS(be(0x3D00),
+ /* 3D00 */ be(0x043E), be(0x4760), be(0xFFFF), be(0xFFFF),
+ /* 3D08 */ be(0x8000), be(0x0510), be(0xAF08), be(0x0252),
+ /* 3D10 */ be(0x486F), be(0x5D5D), be(0x8056), be(0x8313),
+ /* 3D18 */ be(0x0087), be(0x6A48), be(0x6982), be(0x0280),
+ /* 3D20 */ be(0x8359), be(0x8D02), be(0x8020), be(0x4882),
+ /* 3D28 */ be(0x4269), be(0x6A95), be(0x5988), be(0x5A83),
+ /* 3D30 */ be(0x5885), be(0x6280), be(0x6289), be(0x6097),
+ /* 3D38 */ be(0x5782), be(0x605C), be(0xBF18), be(0x0961),
+ /* 3D40 */ be(0x5080), be(0x2090), be(0x4390), be(0x4382),
+ /* 3D48 */ be(0x5F8A), be(0x5D5D), be(0x9C63), be(0x8063),
+ /* 3D50 */ be(0xA960), be(0x9757), be(0x8260), be(0x5CFF),
+ /* 3D58 */ be(0xBF10), be(0x1681), be(0x0802), be(0x8000),
+ /* 3D60 */ be(0x141C), be(0x6000), be(0x6022), be(0x4D80),
+ /* 3D68 */ be(0x5C97), be(0x6A69), be(0xAC6F), be(0x4645),
+ /* 3D70 */ be(0x4400), be(0x0513), be(0x8069), be(0x6AC6),
+ /* 3D78 */ be(0x5F95), be(0x5F70), be(0x8040), be(0x4A81),
+ /* 3D80 */ be(0x0300), be(0xE703), be(0x0088), be(0x4A83),
+ /* 3D88 */ be(0x40FF), be(0xFFFF), be(0xFD70), be(0x8040),
+ /* 3D90 */ be(0x4A85), be(0x4FA8), be(0x4F8C), be(0x0070),
+ /* 3D98 */ be(0xBE47), be(0x8847), be(0xBC78), be(0x6B89),
+ /* 3DA0 */ be(0x6A80), be(0x6986), be(0x6B8E), be(0x6B80),
+ /* 3DA8 */ be(0x6980), be(0x6A88), be(0x7C9F), be(0x866B),
+ /* 3DB0 */ be(0x8765), be(0x46FF), be(0xE365), be(0xA679),
+ /* 3DB8 */ be(0x4A40), be(0x4580), be(0x44BC), be(0x7000),
+ /* 3DC0 */ be(0x8040), be(0x0802), be(0x10EF), be(0x0104),
+ /* 3DC8 */ be(0x3860), be(0x5D5D), be(0x5682), be(0x1300),
+ /* 3DD0 */ be(0x8648), be(0x8202), be(0x8082), be(0x598A),
+ /* 3DD8 */ be(0x0280), be(0x2048), be(0x3060), be(0x8042),
+ /* 3DE0 */ be(0x9259), be(0x865A), be(0x8258), be(0x8562),
+ /* 3DE8 */ be(0x8062), be(0x8560), be(0x9257), be(0x8221),
+ /* 3DF0 */ be(0x10FF), be(0xB757), be(0x9361), be(0x1019),
+ /* 3DF8 */ be(0x8020), be(0x9043), be(0x8E43), be(0x845F),
+ /* 3E00 */ be(0x835D), be(0x805D), be(0x8163), be(0x8063),
+ /* 3E08 */ be(0xA060), be(0x9157), be(0x8260), be(0x5CFF),
+ /* 3E10 */ be(0xFFFF), be(0xFFE5), be(0x1016), be(0x2048),
+ /* 3E18 */ be(0x0802), be(0x1C60), be(0x0014), be(0x0060),
+ /* 3E20 */ be(0x2205), be(0x8120), be(0x908F), be(0x6A80),
+ /* 3E28 */ be(0x6982), be(0x5F9F), be(0x6F46), be(0x4544),
+ /* 3E30 */ be(0x0005), be(0x8013), be(0x8069), be(0x6A80),
+ /* 3E38 */ be(0x7000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E40 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E48 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E50 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E58 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E60 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E68 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E70 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E78 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E80 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E88 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E90 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3E98 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3EA0 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3EA8 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000),
+ /* 3EB0 */ be(0x0000), be(0x0000), be(0x0000)),
+
+ REGS(be(0x3EB6), be(0x004C)), /* ECL */
+
+ REGS(be(0x3EBA),
+ be(0xAAAD), /* 3EBA */
+ be(0x0086)), /* 3EBC: Bias currents for FSC/ECL */
+
+ REGS(be(0x3EC0),
+ be(0x1E00), /* 3EC0: SFbin/SH mode settings */
+ be(0x100A), /* 3EC2: CLK divider for ramp for 10 bit 400MH */
+ /* 3EC4: FSC clamps for HDR mode and adc comp power down co */
+ be(0x3300),
+ be(0xEA44), /* 3EC6: VLN and clk gating controls */
+ be(0x6F6F), /* 3EC8: Txl0 and Txlo1 settings for normal mode */
+ be(0x2F4A), /* 3ECA: CDAC/Txlo2/RSTGHI/RSTGLO settings */
+ be(0x0506), /* 3ECC: RSTDHI/RSTDLO/CDAC/TXHI settings */
+ /* 3ECE: Ramp buffer settings and Booster enable (bits 0-5) */
+ be(0x203B),
+ be(0x13F0), /* 3ED0: TXLO from atest/sf bin settings */
+ be(0xA53D), /* 3ED2: Ramp offset */
+ be(0x862F), /* 3ED4: TXLO open loop/row driver settings */
+ be(0x4081), /* 3ED6: Txlatch fr cfpn rows/vln bias */
+ be(0x8003), /* 3ED8: Ramp step setting for 10 bit 400 Mhz */
+ be(0xA580), /* 3EDA: Ramp Offset */
+ be(0xC000), /* 3EDC: over range for rst and under range for sig */
+ be(0xC103)), /* 3EDE: over range for sig and col dec clk settings */
+
+ /* corrections_recommended_bayer */
+ REGS(be(0x3F00),
+ be(0x0017), /* 3F00: BM_T0 */
+ be(0x02DD), /* 3F02: BM_T1 */
+ /* 3F04: if Ana_gain less than 2, use noise_floor0, multipl */
+ be(0x0020),
+ /* 3F06: if Ana_gain between 4 and 7, use noise_floor2 and */
+ be(0x0040),
+ /* 3F08: if Ana_gain between 4 and 7, use noise_floor2 and */
+ be(0x0070),
+ /* 3F0A: Define noise_floor0(low address) and noise_floor1 */
+ be(0x0101),
+ be(0x0302)), /* 3F0C: Define noise_floor2 and noise_floor3 */
+
+ REGS(be(0x3F10),
+ be(0x0505), /* 3F10: single k factor 0 */
+ be(0x0505), /* 3F12: single k factor 1 */
+ be(0x0505), /* 3F14: single k factor 2 */
+ be(0x01FF), /* 3F16: cross factor 0 */
+ be(0x01FF), /* 3F18: cross factor 1 */
+ be(0x01FF), /* 3F1A: cross factor 2 */
+ be(0x0022)), /* 3F1E */
+
+ /* GTH_THRES_RTN: 4max,4min filtered out of every 46 samples and */
+ REGS(be(0x3F2C), be(0x442E)),
+
+ REGS(be(0x3F3E),
+ be(0x0000), /* 3F3E: Switch ADC from 12 bit to 10 bit mode */
+ be(0x1511), /* 3F40: couple k factor 0 */
+ be(0x1511), /* 3F42: couple k factor 1 */
+ be(0x0707)), /* 3F44: couple k factor 2 */
+};
+
+static int ar0521_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+ int i;
+
+ clk_disable_unprepare(sensor->extclk);
+
+ if (sensor->reset_gpio)
+ gpiod_set_value(sensor->reset_gpio, 1); /* assert RESET signal */
+
+ for (i = ARRAY_SIZE(ar0521_supply_names) - 1; i >= 0; i--) {
+ if (sensor->supplies[i])
+ regulator_disable(sensor->supplies[i]);
+ }
+ return 0;
+}
+
+static int ar0521_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+ unsigned int cnt;
+ int ret;
+
+ for (cnt = 0; cnt < ARRAY_SIZE(ar0521_supply_names); cnt++)
+ if (sensor->supplies[cnt]) {
+ ret = regulator_enable(sensor->supplies[cnt]);
+ if (ret < 0)
+ goto off;
+
+ usleep_range(1000, 1500); /* min 1 ms */
+ }
+
+ ret = clk_prepare_enable(sensor->extclk);
+ if (ret < 0) {
+ v4l2_err(&sensor->sd, "error enabling sensor clock\n");
+ goto off;
+ }
+ usleep_range(1000, 1500); /* min 1 ms */
+
+ if (sensor->reset_gpio)
+ /* deassert RESET signal */
+ gpiod_set_value(sensor->reset_gpio, 0);
+ usleep_range(4500, 5000); /* min 45000 clocks */
+
+ for (cnt = 0; cnt < ARRAY_SIZE(initial_regs); cnt++)
+ if (ar0521_write_regs(sensor, initial_regs[cnt].data,
+ initial_regs[cnt].count))
+ goto off;
+
+ ret = ar0521_write_reg(sensor, AR0521_REG_SERIAL_FORMAT,
+ AR0521_REG_SERIAL_FORMAT_MIPI |
+ sensor->lane_count);
+ if (ret)
+ goto off;
+
+ /* set MIPI test mode - disabled for now */
+ ret = ar0521_write_reg(sensor, AR0521_REG_HISPI_TEST_MODE,
+ ((0x40 << sensor->lane_count) - 0x40) |
+ AR0521_REG_HISPI_TEST_MODE_LP11);
+ if (ret)
+ goto off;
+
+ ret = ar0521_write_reg(sensor, AR0521_REG_ROW_SPEED, 0x110 |
+ 4 / sensor->lane_count);
+ if (ret)
+ goto off;
+
+ return 0;
+off:
+ ar0521_power_off(dev);
+ return ret;
+}
+
+static int ar0521_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+
+ if (code->index)
+ return -EINVAL;
+
+ code->code = sensor->fmt.code;
+ return 0;
+}
+
+static int ar0521_pre_streamon(struct v4l2_subdev *sd, u32 flags)
+{
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+ int ret;
+
+ if (!(flags & V4L2_SUBDEV_PRE_STREAMON_FL_MANUAL_LP))
+ return -EACCES;
+
+ ret = pm_runtime_resume_and_get(&sensor->i2c_client->dev);
+ if (ret < 0)
+ return ret;
+
+ /* Set LP-11 on clock and data lanes */
+ ret = ar0521_write_reg(sensor, AR0521_REG_HISPI_CONTROL_STATUS,
+ AR0521_REG_HISPI_CONTROL_STATUS_FRAMER_TEST_MODE_ENABLE);
+ if (ret)
+ goto err;
+
+ /* Start streaming LP-11 */
+ ret = ar0521_write_reg(sensor, AR0521_REG_RESET,
+ AR0521_REG_RESET_DEFAULTS |
+ AR0521_REG_RESET_STREAM);
+ if (ret)
+ goto err;
+ return 0;
+
+err:
+ pm_runtime_put(&sensor->i2c_client->dev);
+ return ret;
+}
+
+static int ar0521_post_streamoff(struct v4l2_subdev *sd)
+{
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+
+ pm_runtime_put(&sensor->i2c_client->dev);
+ return 0;
+}
+
+static int ar0521_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+ int ret;
+
+ mutex_lock(&sensor->lock);
+
+ ret = ar0521_set_stream(sensor, enable);
+ if (!ret)
+ sensor->streaming = enable;
+
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static const struct v4l2_subdev_core_ops ar0521_core_ops = {
+ .log_status = v4l2_ctrl_subdev_log_status,
+};
+
+static const struct v4l2_subdev_video_ops ar0521_video_ops = {
+ .s_stream = ar0521_s_stream,
+ .pre_streamon = ar0521_pre_streamon,
+ .post_streamoff = ar0521_post_streamoff,
+};
+
+static const struct v4l2_subdev_pad_ops ar0521_pad_ops = {
+ .enum_mbus_code = ar0521_enum_mbus_code,
+ .get_fmt = ar0521_get_fmt,
+ .set_fmt = ar0521_set_fmt,
+};
+
+static const struct v4l2_subdev_ops ar0521_subdev_ops = {
+ .core = &ar0521_core_ops,
+ .video = &ar0521_video_ops,
+ .pad = &ar0521_pad_ops,
+};
+
+static int __maybe_unused ar0521_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+
+ if (sensor->streaming)
+ ar0521_set_stream(sensor, 0);
+
+ return 0;
+}
+
+static int __maybe_unused ar0521_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+
+ if (sensor->streaming)
+ return ar0521_set_stream(sensor, 1);
+
+ return 0;
+}
+
+static int ar0521_probe(struct i2c_client *client)
+{
+ struct v4l2_fwnode_endpoint ep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ struct device *dev = &client->dev;
+ struct fwnode_handle *endpoint;
+ struct ar0521_dev *sensor;
+ unsigned int cnt;
+ int ret;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->i2c_client = client;
+ sensor->fmt.width = AR0521_WIDTH_MAX;
+ sensor->fmt.height = AR0521_HEIGHT_MAX;
+
+ endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!endpoint) {
+ dev_err(dev, "endpoint node not found\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(endpoint, &ep);
+ fwnode_handle_put(endpoint);
+ if (ret) {
+ dev_err(dev, "could not parse endpoint\n");
+ return ret;
+ }
+
+ if (ep.bus_type != V4L2_MBUS_CSI2_DPHY) {
+ dev_err(dev, "invalid bus type, must be MIPI CSI2\n");
+ return -EINVAL;
+ }
+
+ sensor->lane_count = ep.bus.mipi_csi2.num_data_lanes;
+ switch (sensor->lane_count) {
+ case 1:
+ case 2:
+ case 4:
+ break;
+ default:
+ dev_err(dev, "invalid number of MIPI data lanes\n");
+ return -EINVAL;
+ }
+
+ /* Get master clock (extclk) */
+ sensor->extclk = devm_clk_get(dev, "extclk");
+ if (IS_ERR(sensor->extclk)) {
+ dev_err(dev, "failed to get extclk\n");
+ return PTR_ERR(sensor->extclk);
+ }
+
+ sensor->extclk_freq = clk_get_rate(sensor->extclk);
+
+ if (sensor->extclk_freq < AR0521_EXTCLK_MIN ||
+ sensor->extclk_freq > AR0521_EXTCLK_MAX) {
+ dev_err(dev, "extclk frequency out of range: %u Hz\n",
+ sensor->extclk_freq);
+ return -EINVAL;
+ }
+
+ /* Request optional reset pin (usually active low) and assert it */
+ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+
+ v4l2_i2c_subdev_init(&sensor->sd, client, &ar0521_subdev_ops);
+
+ sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
+ if (ret)
+ return ret;
+
+ for (cnt = 0; cnt < ARRAY_SIZE(ar0521_supply_names); cnt++) {
+ struct regulator *supply = devm_regulator_get(dev,
+ ar0521_supply_names[cnt]);
+
+ if (IS_ERR(supply)) {
+ dev_info(dev, "no %s regulator found: %li\n",
+ ar0521_supply_names[cnt], PTR_ERR(supply));
+ return PTR_ERR(supply);
+ }
+ sensor->supplies[cnt] = supply;
+ }
+
+ mutex_init(&sensor->lock);
+
+ ret = ar0521_init_controls(sensor);
+ if (ret)
+ goto entity_cleanup;
+
+ ar0521_adj_fmt(&sensor->fmt);
+
+ ret = v4l2_async_register_subdev(&sensor->sd);
+ if (ret)
+ goto free_ctrls;
+
+ /* Turn on the device and enable runtime PM */
+ ret = ar0521_power_on(&client->dev);
+ if (ret)
+ goto disable;
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_idle(&client->dev);
+ return 0;
+
+disable:
+ v4l2_async_unregister_subdev(&sensor->sd);
+ media_entity_cleanup(&sensor->sd.entity);
+free_ctrls:
+ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+entity_cleanup:
+ media_entity_cleanup(&sensor->sd.entity);
+ mutex_destroy(&sensor->lock);
+ return ret;
+}
+
+static int ar0521_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ar0521_dev *sensor = to_ar0521_dev(sd);
+
+ v4l2_async_unregister_subdev(&sensor->sd);
+ media_entity_cleanup(&sensor->sd.entity);
+ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ ar0521_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ mutex_destroy(&sensor->lock);
+ return 0;
+}
+
+static const struct dev_pm_ops ar0521_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ar0521_suspend, ar0521_resume)
+ SET_RUNTIME_PM_OPS(ar0521_power_off, ar0521_power_on, NULL)
+};
+static const struct of_device_id ar0521_dt_ids[] = {
+ {.compatible = "onnn,ar0521"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, ar0521_dt_ids);
+
+static struct i2c_driver ar0521_i2c_driver = {
+ .driver = {
+ .name = "ar0521",
+ .pm = &ar0521_pm_ops,
+ .of_match_table = ar0521_dt_ids,
+ },
+ .probe_new = ar0521_probe,
+ .remove = ar0521_remove,
+};
+
+module_i2c_driver(ar0521_i2c_driver);
+
+MODULE_DESCRIPTION("AR0521 MIPI Camera subdev driver");
+MODULE_AUTHOR("Krzysztof Hałasa <khalasa@piap.pl>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index cbce8b88dbcf..1fd4dc6e4726 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -623,12 +623,22 @@ static int mt9p031_get_selection(struct v4l2_subdev *subdev,
{
struct mt9p031 *mt9p031 = to_mt9p031(subdev);
- if (sel->target != V4L2_SEL_TGT_CROP)
- return -EINVAL;
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = MT9P031_COLUMN_START_MIN;
+ sel->r.top = MT9P031_ROW_START_MIN;
+ sel->r.width = MT9P031_WINDOW_WIDTH_MAX;
+ sel->r.height = MT9P031_WINDOW_HEIGHT_MAX;
+ return 0;
- sel->r = *__mt9p031_get_pad_crop(mt9p031, sd_state, sel->pad,
- sel->which);
- return 0;
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *__mt9p031_get_pad_crop(mt9p031, sd_state,
+ sel->pad, sel->which);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
}
static int mt9p031_set_selection(struct v4l2_subdev *subdev,
@@ -682,6 +692,37 @@ static int mt9p031_set_selection(struct v4l2_subdev *subdev,
return 0;
}
+static int mt9p031_init_cfg(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+ const int which = sd_state == NULL ? V4L2_SUBDEV_FORMAT_ACTIVE :
+ V4L2_SUBDEV_FORMAT_TRY;
+
+ crop = __mt9p031_get_pad_crop(mt9p031, sd_state, 0, which);
+ v4l2_subdev_get_try_crop(subdev, sd_state, 0);
+ crop->left = MT9P031_COLUMN_START_DEF;
+ crop->top = MT9P031_ROW_START_DEF;
+ crop->width = MT9P031_WINDOW_WIDTH_DEF;
+ crop->height = MT9P031_WINDOW_HEIGHT_DEF;
+
+ format = __mt9p031_get_pad_format(mt9p031, sd_state, 0, which);
+
+ if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
+ format->code = MEDIA_BUS_FMT_Y12_1X12;
+ else
+ format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
+
+ format->width = MT9P031_WINDOW_WIDTH_DEF;
+ format->height = MT9P031_WINDOW_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
/* -----------------------------------------------------------------------------
* V4L2 subdev control operations
*/
@@ -980,28 +1021,6 @@ static int mt9p031_registered(struct v4l2_subdev *subdev)
static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
{
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
- struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *crop;
-
- crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0);
- crop->left = MT9P031_COLUMN_START_DEF;
- crop->top = MT9P031_ROW_START_DEF;
- crop->width = MT9P031_WINDOW_WIDTH_DEF;
- crop->height = MT9P031_WINDOW_HEIGHT_DEF;
-
- format = v4l2_subdev_get_try_format(subdev, fh->state, 0);
-
- if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
- format->code = MEDIA_BUS_FMT_Y12_1X12;
- else
- format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
-
- format->width = MT9P031_WINDOW_WIDTH_DEF;
- format->height = MT9P031_WINDOW_HEIGHT_DEF;
- format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
-
return mt9p031_set_power(subdev, 1);
}
@@ -1019,6 +1038,7 @@ static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
};
static const struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
+ .init_cfg = mt9p031_init_cfg,
.enum_mbus_code = mt9p031_enum_mbus_code,
.enum_frame_size = mt9p031_enum_frame_size,
.get_fmt = mt9p031_get_format,
@@ -1166,20 +1186,9 @@ static int mt9p031_probe(struct i2c_client *client,
mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF;
- mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF;
- mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
- mt9p031->crop.top = MT9P031_ROW_START_DEF;
-
- if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
- mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12;
- else
- mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
-
- mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
- mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
- mt9p031->format.field = V4L2_FIELD_NONE;
- mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
+ ret = mt9p031_init_cfg(&mt9p031->subdev, NULL);
+ if (ret)
+ goto done;
mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset",
GPIOD_OUT_HIGH);
@@ -1214,6 +1223,7 @@ static int mt9p031_remove(struct i2c_client *client)
}
static const struct i2c_device_id mt9p031_id[] = {
+ { "mt9p006", MT9P031_MODEL_COLOR },
{ "mt9p031", MT9P031_MODEL_COLOR },
{ "mt9p031m", MT9P031_MODEL_MONOCHROME },
{ }
@@ -1222,6 +1232,7 @@ MODULE_DEVICE_TABLE(i2c, mt9p031_id);
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id mt9p031_of_match[] = {
+ { .compatible = "aptina,mt9p006", },
{ .compatible = "aptina,mt9p031", },
{ .compatible = "aptina,mt9p031m", },
{ /* sentinel */ },
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index db5a19babe67..502f0b62e950 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -29,8 +29,21 @@
#define OV5640_XCLK_MIN 6000000
#define OV5640_XCLK_MAX 54000000
+#define OV5640_NATIVE_WIDTH 2624
+#define OV5640_NATIVE_HEIGHT 1964
+#define OV5640_PIXEL_ARRAY_TOP 14
+#define OV5640_PIXEL_ARRAY_LEFT 16
+#define OV5640_PIXEL_ARRAY_WIDTH 2592
+#define OV5640_PIXEL_ARRAY_HEIGHT 1944
+
+/* FIXME: not documented. */
+#define OV5640_MIN_VBLANK 24
+#define OV5640_MAX_VTS 3375
+
#define OV5640_DEFAULT_SLAVE_ID 0x3c
+#define OV5640_LINK_RATE_MAX 490000000U
+
#define OV5640_REG_SYS_RESET02 0x3002
#define OV5640_REG_SYS_CLOCK_ENABLE02 0x3006
#define OV5640_REG_SYS_CTRL0 0x3008
@@ -59,10 +72,16 @@
#define OV5640_REG_AEC_PK_MANUAL 0x3503
#define OV5640_REG_AEC_PK_REAL_GAIN 0x350a
#define OV5640_REG_AEC_PK_VTS 0x350c
+#define OV5640_REG_TIMING_HS 0x3800
+#define OV5640_REG_TIMING_VS 0x3802
+#define OV5640_REG_TIMING_HW 0x3804
+#define OV5640_REG_TIMING_VH 0x3806
#define OV5640_REG_TIMING_DVPHO 0x3808
#define OV5640_REG_TIMING_DVPVO 0x380a
#define OV5640_REG_TIMING_HTS 0x380c
#define OV5640_REG_TIMING_VTS 0x380e
+#define OV5640_REG_TIMING_HOFFS 0x3810
+#define OV5640_REG_TIMING_VOFFS 0x3812
#define OV5640_REG_TIMING_TC_REG20 0x3820
#define OV5640_REG_TIMING_TC_REG21 0x3821
#define OV5640_REG_AEC_CTRL00 0x3a00
@@ -88,6 +107,7 @@
#define OV5640_REG_POLARITY_CTRL00 0x4740
#define OV5640_REG_MIPI_CTRL00 0x4800
#define OV5640_REG_DEBUG_MODE 0x4814
+#define OV5640_REG_PCLK_PERIOD 0x4837
#define OV5640_REG_ISP_FORMAT_MUX_CTRL 0x501f
#define OV5640_REG_PRE_ISP_TEST_SET1 0x503d
#define OV5640_REG_SDE_CTRL0 0x5580
@@ -118,6 +138,47 @@ enum ov5640_frame_rate {
OV5640_NUM_FRAMERATES,
};
+enum ov5640_pixel_rate_id {
+ OV5640_PIXEL_RATE_168M,
+ OV5640_PIXEL_RATE_148M,
+ OV5640_PIXEL_RATE_124M,
+ OV5640_PIXEL_RATE_96M,
+ OV5640_PIXEL_RATE_48M,
+ OV5640_NUM_PIXEL_RATES,
+};
+
+/*
+ * The chip manual suggests 24/48/96/192 MHz pixel clocks.
+ *
+ * 192MHz exceeds the sysclk limits; use 168MHz as maximum pixel rate for
+ * full resolution mode @15 FPS.
+ */
+static const u32 ov5640_pixel_rates[] = {
+ [OV5640_PIXEL_RATE_168M] = 168000000,
+ [OV5640_PIXEL_RATE_148M] = 148000000,
+ [OV5640_PIXEL_RATE_124M] = 124000000,
+ [OV5640_PIXEL_RATE_96M] = 96000000,
+ [OV5640_PIXEL_RATE_48M] = 48000000,
+};
+
+/*
+ * MIPI CSI-2 link frequencies.
+ *
+ * Derived from the above defined pixel rate for bpp = (8, 16, 24) and
+ * data_lanes = (1, 2)
+ *
+ * link_freq = (pixel_rate * bpp) / (2 * data_lanes)
+ */
+static const s64 ov5640_csi2_link_freqs[] = {
+ 992000000, 888000000, 768000000, 744000000, 672000000, 672000000,
+ 592000000, 592000000, 576000000, 576000000, 496000000, 496000000,
+ 384000000, 384000000, 384000000, 336000000, 296000000, 288000000,
+ 248000000, 192000000, 192000000, 192000000, 96000000,
+};
+
+/* Link freq for default mode: UYVY 16 bpp, 2 data lanes. */
+#define OV5640_DEFAULT_LINK_FREQ 13
+
enum ov5640_format_mux {
OV5640_FMT_MUX_YUV422 = 0,
OV5640_FMT_MUX_RGB,
@@ -130,20 +191,145 @@ enum ov5640_format_mux {
struct ov5640_pixfmt {
u32 code;
u32 colorspace;
+ u8 bpp;
+ u8 ctrl00;
+ enum ov5640_format_mux mux;
+};
+
+static const struct ov5640_pixfmt ov5640_dvp_formats[] = {
+ {
+ /* YUV422, YUYV */
+ .code = MEDIA_BUS_FMT_JPEG_1X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .bpp = 16,
+ .ctrl00 = 0x30,
+ .mux = OV5640_FMT_MUX_YUV422,
+ }, {
+ /* YUV422, UYVY */
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 16,
+ .ctrl00 = 0x3f,
+ .mux = OV5640_FMT_MUX_YUV422,
+ }, {
+ /* YUV422, YUYV */
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 16,
+ .ctrl00 = 0x30,
+ .mux = OV5640_FMT_MUX_YUV422,
+ }, {
+ /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 16,
+ .ctrl00 = 0x6f,
+ .mux = OV5640_FMT_MUX_RGB,
+ }, {
+ /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 16,
+ .ctrl00 = 0x61,
+ .mux = OV5640_FMT_MUX_RGB,
+ }, {
+ /* Raw, BGBG... / GRGR... */
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 8,
+ .ctrl00 = 0x00,
+ .mux = OV5640_FMT_MUX_RAW_DPC,
+ }, {
+ /* Raw bayer, GBGB... / RGRG... */
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 8,
+ .ctrl00 = 0x01,
+ .mux = OV5640_FMT_MUX_RAW_DPC,
+ }, {
+ /* Raw bayer, GRGR... / BGBG... */
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 8,
+ .ctrl00 = 0x02,
+ .mux = OV5640_FMT_MUX_RAW_DPC,
+ }, {
+ /* Raw bayer, RGRG... / GBGB... */
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 8,
+ .ctrl00 = 0x03,
+ .mux = OV5640_FMT_MUX_RAW_DPC,
+ },
+ { /* sentinel */ }
};
-static const struct ov5640_pixfmt ov5640_formats[] = {
- { MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, },
- { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, },
- { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_COLORSPACE_SRGB, },
- { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, },
- { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_COLORSPACE_SRGB, },
- { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, },
- { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, },
- { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, },
- { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_COLORSPACE_SRGB, },
- { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_COLORSPACE_SRGB, },
- { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB, },
+static const struct ov5640_pixfmt ov5640_csi2_formats[] = {
+ {
+ /* YUV422, YUYV */
+ .code = MEDIA_BUS_FMT_JPEG_1X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .bpp = 16,
+ .ctrl00 = 0x30,
+ .mux = OV5640_FMT_MUX_YUV422,
+ }, {
+ /* YUV422, UYVY */
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 16,
+ .ctrl00 = 0x3f,
+ .mux = OV5640_FMT_MUX_YUV422,
+ }, {
+ /* YUV422, YUYV */
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 16,
+ .ctrl00 = 0x30,
+ .mux = OV5640_FMT_MUX_YUV422,
+ }, {
+ /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
+ .code = MEDIA_BUS_FMT_RGB565_1X16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 16,
+ .ctrl00 = 0x6f,
+ .mux = OV5640_FMT_MUX_RGB,
+ }, {
+ /* BGR888: RGB */
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 24,
+ .ctrl00 = 0x23,
+ .mux = OV5640_FMT_MUX_RGB,
+ }, {
+ /* Raw, BGBG... / GRGR... */
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 8,
+ .ctrl00 = 0x00,
+ .mux = OV5640_FMT_MUX_RAW_DPC,
+ }, {
+ /* Raw bayer, GBGB... / RGRG... */
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 8,
+ .ctrl00 = 0x01,
+ .mux = OV5640_FMT_MUX_RAW_DPC,
+ }, {
+ /* Raw bayer, GRGR... / BGBG... */
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 8,
+ .ctrl00 = 0x02,
+ .mux = OV5640_FMT_MUX_RAW_DPC,
+ }, {
+ /* Raw bayer, RGRG... / GBGB... */
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bpp = 8,
+ .ctrl00 = 0x03,
+ .mux = OV5640_FMT_MUX_RAW_DPC,
+ },
+ { /* sentinel */ }
};
/*
@@ -186,21 +372,42 @@ struct reg_value {
u32 delay_ms;
};
+struct ov5640_timings {
+ /* Analog crop rectangle. */
+ struct v4l2_rect analog_crop;
+ /* Visibile crop: from analog crop top-left corner. */
+ struct v4l2_rect crop;
+ /* Total pixels per line: width + fixed hblank. */
+ u32 htot;
+ /* Default vertical blanking: frame height = height + vblank. */
+ u32 vblank_def;
+};
+
struct ov5640_mode_info {
enum ov5640_mode_id id;
enum ov5640_downsize_mode dn_mode;
- u32 hact;
- u32 htot;
- u32 vact;
- u32 vtot;
+ enum ov5640_pixel_rate_id pixel_rate;
+
+ unsigned int width;
+ unsigned int height;
+
+ struct ov5640_timings dvp_timings;
+ struct ov5640_timings csi2_timings;
+
const struct reg_value *reg_data;
u32 reg_data_size;
+
+ /* Used by s_frame_interval only. */
u32 max_fps;
+ u32 def_fps;
};
struct ov5640_ctrls {
struct v4l2_ctrl_handler handler;
struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
struct {
struct v4l2_ctrl *auto_exp;
struct v4l2_ctrl *exposure;
@@ -249,6 +456,7 @@ struct ov5640_dev {
const struct ov5640_mode_info *last_mode;
enum ov5640_frame_rate current_fr;
struct v4l2_fract frame_interval;
+ s64 current_link_freq;
struct ov5640_ctrls ctrls;
@@ -270,6 +478,40 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
ctrls.handler)->sd;
}
+static inline bool ov5640_is_csi2(const struct ov5640_dev *sensor)
+{
+ return sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY;
+}
+
+static inline const struct ov5640_pixfmt *
+ov5640_formats(struct ov5640_dev *sensor)
+{
+ return ov5640_is_csi2(sensor) ? ov5640_csi2_formats
+ : ov5640_dvp_formats;
+}
+
+static const struct ov5640_pixfmt *
+ov5640_code_to_pixfmt(struct ov5640_dev *sensor, u32 code)
+{
+ const struct ov5640_pixfmt *formats = ov5640_formats(sensor);
+ unsigned int i;
+
+ for (i = 0; formats[i].code; ++i) {
+ if (formats[i].code == code)
+ return &formats[i];
+ }
+
+ return &formats[0];
+}
+
+static u32 ov5640_code_to_bpp(struct ov5640_dev *sensor, u32 code)
+{
+ const struct ov5640_pixfmt *format = ov5640_code_to_pixfmt(sensor,
+ code);
+
+ return format->bpp;
+}
+
/*
* FIXME: all of these register tables are likely filled with
* entries that set the register to their power-on default values,
@@ -278,7 +520,19 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
* over i2c.
*/
/* YUV422 UYVY VGA@30fps */
-static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
+
+static const struct v4l2_mbus_framefmt ov5640_default_fmt = {
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .width = 640,
+ .height = 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB),
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
+ .xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB),
+ .field = V4L2_FIELD_NONE,
+};
+
+static const struct reg_value ov5640_init_setting[] = {
{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
{0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0},
{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
@@ -294,11 +548,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
{0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3815, 0x31, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -362,72 +612,11 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
{0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
};
-static const struct reg_value ov5640_setting_VGA_640_480[] = {
- {0x3c07, 0x08, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_XGA_1024_768[] = {
- {0x3c07, 0x08, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_QVGA_320_240[] = {
- {0x3c07, 0x08, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_QQVGA_160_120[] = {
+static const struct reg_value ov5640_setting_low_res[] = {
{0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3815, 0x31, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -437,72 +626,11 @@ static const struct reg_value ov5640_setting_QQVGA_160_120[] = {
{0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
};
-static const struct reg_value ov5640_setting_QCIF_176_144[] = {
- {0x3c07, 0x08, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_NTSC_720_480[] = {
- {0x3c07, 0x08, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_PAL_720_576[] = {
- {0x3c07, 0x08, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
static const struct reg_value ov5640_setting_720P_1280_720[] = {
{0x3c07, 0x07, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3815, 0x31, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
@@ -517,11 +645,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
{0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x11, 0, 0},
- {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3815, 0x11, 0, 0},
{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -532,9 +656,6 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
- {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
- {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0},
{0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
@@ -548,11 +669,7 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
{0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x11, 0, 0},
- {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
- {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3815, 0x11, 0, 0},
{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -563,67 +680,462 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
};
-/* power-on sensor init reg table */
-static const struct ov5640_mode_info ov5640_mode_init_data = {
- 0, SUBSAMPLING, 640, 1896, 480, 984,
- ov5640_init_setting_30fps_VGA,
- ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
- OV5640_30_FPS,
+static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
+ {
+ /* 160x120 */
+ .id = OV5640_MODE_QQVGA_160_120,
+ .dn_mode = SUBSAMPLING,
+ .pixel_rate = OV5640_PIXEL_RATE_48M,
+ .width = 160,
+ .height = 120,
+ .dvp_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 4,
+ .width = 2624,
+ .height = 1944,
+ },
+ .crop = {
+ .left = 16,
+ .top = 6,
+ .width = 160,
+ .height = 120,
+ },
+ .htot = 1896,
+ .vblank_def = 864,
+ },
+ .csi2_timings = {
+ /* Feed the full valid pixel array to the ISP. */
+ .analog_crop = {
+ .left = OV5640_PIXEL_ARRAY_LEFT,
+ .top = OV5640_PIXEL_ARRAY_TOP,
+ .width = OV5640_PIXEL_ARRAY_WIDTH,
+ .height = OV5640_PIXEL_ARRAY_HEIGHT,
+ },
+ /* Maintain a minimum processing margin. */
+ .crop = {
+ .left = 2,
+ .top = 4,
+ .width = 160,
+ .height = 120,
+ },
+ .htot = 1600,
+ .vblank_def = 878,
+ },
+ .reg_data = ov5640_setting_low_res,
+ .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
+ .max_fps = OV5640_30_FPS,
+ .def_fps = OV5640_30_FPS
+ }, {
+ /* 176x144 */
+ .id = OV5640_MODE_QCIF_176_144,
+ .dn_mode = SUBSAMPLING,
+ .pixel_rate = OV5640_PIXEL_RATE_48M,
+ .width = 176,
+ .height = 144,
+ .dvp_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 4,
+ .width = 2624,
+ .height = 1944,
+ },
+ .crop = {
+ .left = 16,
+ .top = 6,
+ .width = 176,
+ .height = 144,
+ },
+ .htot = 1896,
+ .vblank_def = 840,
+ },
+ .csi2_timings = {
+ /* Feed the full valid pixel array to the ISP. */
+ .analog_crop = {
+ .left = OV5640_PIXEL_ARRAY_LEFT,
+ .top = OV5640_PIXEL_ARRAY_TOP,
+ .width = OV5640_PIXEL_ARRAY_WIDTH,
+ .height = OV5640_PIXEL_ARRAY_HEIGHT,
+ },
+ /* Maintain a minimum processing margin. */
+ .crop = {
+ .left = 2,
+ .top = 4,
+ .width = 176,
+ .height = 144,
+ },
+ .htot = 1600,
+ .vblank_def = 854,
+ },
+ .reg_data = ov5640_setting_low_res,
+ .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
+ .max_fps = OV5640_30_FPS,
+ .def_fps = OV5640_30_FPS
+ }, {
+ /* 320x240 */
+ .id = OV5640_MODE_QVGA_320_240,
+ .dn_mode = SUBSAMPLING,
+ .width = 320,
+ .height = 240,
+ .pixel_rate = OV5640_PIXEL_RATE_48M,
+ .dvp_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 4,
+ .width = 2624,
+ .height = 1944,
+ },
+ .crop = {
+ .left = 16,
+ .top = 6,
+ .width = 320,
+ .height = 240,
+ },
+ .htot = 1896,
+ .vblank_def = 744,
+ },
+ .csi2_timings = {
+ /* Feed the full valid pixel array to the ISP. */
+ .analog_crop = {
+ .left = OV5640_PIXEL_ARRAY_LEFT,
+ .top = OV5640_PIXEL_ARRAY_TOP,
+ .width = OV5640_PIXEL_ARRAY_WIDTH,
+ .height = OV5640_PIXEL_ARRAY_HEIGHT,
+ },
+ /* Maintain a minimum processing margin. */
+ .crop = {
+ .left = 2,
+ .top = 4,
+ .width = 320,
+ .height = 240,
+ },
+ .htot = 1600,
+ .vblank_def = 760,
+ },
+ .reg_data = ov5640_setting_low_res,
+ .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
+ .max_fps = OV5640_30_FPS,
+ .def_fps = OV5640_30_FPS
+ }, {
+ /* 640x480 */
+ .id = OV5640_MODE_VGA_640_480,
+ .dn_mode = SUBSAMPLING,
+ .pixel_rate = OV5640_PIXEL_RATE_48M,
+ .width = 640,
+ .height = 480,
+ .dvp_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 4,
+ .width = 2624,
+ .height = 1944,
+ },
+ .crop = {
+ .left = 16,
+ .top = 6,
+ .width = 640,
+ .height = 480,
+ },
+ .htot = 1896,
+ .vblank_def = 600,
+ },
+ .csi2_timings = {
+ /* Feed the full valid pixel array to the ISP. */
+ .analog_crop = {
+ .left = OV5640_PIXEL_ARRAY_LEFT,
+ .top = OV5640_PIXEL_ARRAY_TOP,
+ .width = OV5640_PIXEL_ARRAY_WIDTH,
+ .height = OV5640_PIXEL_ARRAY_HEIGHT,
+ },
+ /* Maintain a minimum processing margin. */
+ .crop = {
+ .left = 2,
+ .top = 4,
+ .width = 640,
+ .height = 480,
+ },
+ .htot = 1600,
+ .vblank_def = 520,
+ },
+ .reg_data = ov5640_setting_low_res,
+ .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
+ .max_fps = OV5640_60_FPS,
+ .def_fps = OV5640_30_FPS
+ }, {
+ /* 720x480 */
+ .id = OV5640_MODE_NTSC_720_480,
+ .dn_mode = SUBSAMPLING,
+ .width = 720,
+ .height = 480,
+ .pixel_rate = OV5640_PIXEL_RATE_96M,
+ .dvp_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 4,
+ .width = 2624,
+ .height = 1944,
+ },
+ .crop = {
+ .left = 56,
+ .top = 60,
+ .width = 720,
+ .height = 480,
+ },
+ .htot = 1896,
+ .vblank_def = 504,
+ },
+ .csi2_timings = {
+ /* Feed the full valid pixel array to the ISP. */
+ .analog_crop = {
+ .left = OV5640_PIXEL_ARRAY_LEFT,
+ .top = OV5640_PIXEL_ARRAY_TOP,
+ .width = OV5640_PIXEL_ARRAY_WIDTH,
+ .height = OV5640_PIXEL_ARRAY_HEIGHT,
+ },
+ .crop = {
+ .left = 56,
+ .top = 60,
+ .width = 720,
+ .height = 480,
+ },
+ .htot = 1896,
+ .vblank_def = 1206,
+ },
+ .reg_data = ov5640_setting_low_res,
+ .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
+ .max_fps = OV5640_30_FPS,
+ .def_fps = OV5640_30_FPS
+ }, {
+ /* 720x576 */
+ .id = OV5640_MODE_PAL_720_576,
+ .dn_mode = SUBSAMPLING,
+ .width = 720,
+ .height = 576,
+ .pixel_rate = OV5640_PIXEL_RATE_96M,
+ .dvp_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 4,
+ .width = 2624,
+ .height = 1944,
+ },
+ .crop = {
+ .left = 56,
+ .top = 6,
+ .width = 720,
+ .height = 576,
+ },
+ .htot = 1896,
+ .vblank_def = 408,
+ },
+ .csi2_timings = {
+ /* Feed the full valid pixel array to the ISP. */
+ .analog_crop = {
+ .left = OV5640_PIXEL_ARRAY_LEFT,
+ .top = OV5640_PIXEL_ARRAY_TOP,
+ .width = OV5640_PIXEL_ARRAY_WIDTH,
+ .height = OV5640_PIXEL_ARRAY_HEIGHT,
+ },
+ .crop = {
+ .left = 56,
+ .top = 6,
+ .width = 720,
+ .height = 576,
+ },
+ .htot = 1896,
+ .vblank_def = 1110,
+ },
+ .reg_data = ov5640_setting_low_res,
+ .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
+ .max_fps = OV5640_30_FPS,
+ .def_fps = OV5640_30_FPS
+ }, {
+ /* 1024x768 */
+ .id = OV5640_MODE_XGA_1024_768,
+ .dn_mode = SUBSAMPLING,
+ .pixel_rate = OV5640_PIXEL_RATE_96M,
+ .width = 1024,
+ .height = 768,
+ .dvp_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 4,
+ .width = 2624,
+ .height = 1944,
+ },
+ .crop = {
+ .left = 16,
+ .top = 6,
+ .width = 1024,
+ .height = 768,
+ },
+ .htot = 1896,
+ .vblank_def = 312,
+ },
+ .csi2_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 4,
+ .width = OV5640_NATIVE_WIDTH,
+ .height = OV5640_PIXEL_ARRAY_HEIGHT,
+ },
+ .crop = {
+ .left = 16,
+ .top = 6,
+ .width = 1024,
+ .height = 768,
+ },
+ .htot = 1896,
+ .vblank_def = 918,
+ },
+ .reg_data = ov5640_setting_low_res,
+ .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res),
+ .max_fps = OV5640_30_FPS,
+ .def_fps = OV5640_30_FPS
+ }, {
+ /* 1280x720 */
+ .id = OV5640_MODE_720P_1280_720,
+ .dn_mode = SUBSAMPLING,
+ .pixel_rate = OV5640_PIXEL_RATE_124M,
+ .width = 1280,
+ .height = 720,
+ .dvp_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 250,
+ .width = 2624,
+ .height = 1456,
+ },
+ .crop = {
+ .left = 16,
+ .top = 4,
+ .width = 1280,
+ .height = 720,
+ },
+ .htot = 1892,
+ .vblank_def = 20,
+ },
+ .csi2_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 250,
+ .width = 2624,
+ .height = 1456,
+ },
+ .crop = {
+ .left = 16,
+ .top = 4,
+ .width = 1280,
+ .height = 720,
+ },
+ .htot = 1600,
+ .vblank_def = 560,
+ },
+ .reg_data = ov5640_setting_720P_1280_720,
+ .reg_data_size = ARRAY_SIZE(ov5640_setting_720P_1280_720),
+ .max_fps = OV5640_30_FPS,
+ .def_fps = OV5640_30_FPS
+ }, {
+ /* 1920x1080 */
+ .id = OV5640_MODE_1080P_1920_1080,
+ .dn_mode = SCALING,
+ .pixel_rate = OV5640_PIXEL_RATE_148M,
+ .width = 1920,
+ .height = 1080,
+ .dvp_timings = {
+ .analog_crop = {
+ .left = 336,
+ .top = 434,
+ .width = 1952,
+ .height = 1088,
+ },
+ .crop = {
+ .left = 16,
+ .top = 4,
+ .width = 1920,
+ .height = 1080,
+ },
+ .htot = 2500,
+ .vblank_def = 40,
+ },
+ .csi2_timings = {
+ /* Crop the full valid pixel array in the center. */
+ .analog_crop = {
+ .left = 336,
+ .top = 434,
+ .width = 1952,
+ .height = 1088,
+ },
+ /* Maintain a larger processing margins. */
+ .crop = {
+ .left = 16,
+ .top = 4,
+ .width = 1920,
+ .height = 1080,
+ },
+ .htot = 2234,
+ .vblank_def = 24,
+ },
+ .reg_data = ov5640_setting_1080P_1920_1080,
+ .reg_data_size = ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
+ .max_fps = OV5640_30_FPS,
+ .def_fps = OV5640_30_FPS
+ }, {
+ /* 2592x1944 */
+ .id = OV5640_MODE_QSXGA_2592_1944,
+ .dn_mode = SCALING,
+ .pixel_rate = OV5640_PIXEL_RATE_168M,
+ .width = OV5640_PIXEL_ARRAY_WIDTH,
+ .height = OV5640_PIXEL_ARRAY_HEIGHT,
+ .dvp_timings = {
+ .analog_crop = {
+ .left = 0,
+ .top = 0,
+ .width = 2624,
+ .height = 1952,
+ },
+ .crop = {
+ .left = 16,
+ .top = 4,
+ .width = 2592,
+ .height = 1944,
+ },
+ .htot = 2844,
+ .vblank_def = 24,
+ },
+ .csi2_timings = {
+ /* Give more processing margin to full resolution. */
+ .analog_crop = {
+ .left = 0,
+ .top = 0,
+ .width = OV5640_NATIVE_WIDTH,
+ .height = 1952,
+ },
+ .crop = {
+ .left = 16,
+ .top = 4,
+ .width = 2592,
+ .height = 1944,
+ },
+ .htot = 2844,
+ .vblank_def = 24,
+ },
+ .reg_data = ov5640_setting_QSXGA_2592_1944,
+ .reg_data_size = ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
+ .max_fps = OV5640_15_FPS,
+ .def_fps = OV5640_15_FPS
+ },
};
-static const struct ov5640_mode_info
-ov5640_mode_data[OV5640_NUM_MODES] = {
- {OV5640_MODE_QQVGA_160_120, SUBSAMPLING,
- 160, 1896, 120, 984,
- ov5640_setting_QQVGA_160_120,
- ARRAY_SIZE(ov5640_setting_QQVGA_160_120),
- OV5640_30_FPS},
- {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
- 176, 1896, 144, 984,
- ov5640_setting_QCIF_176_144,
- ARRAY_SIZE(ov5640_setting_QCIF_176_144),
- OV5640_30_FPS},
- {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
- 320, 1896, 240, 984,
- ov5640_setting_QVGA_320_240,
- ARRAY_SIZE(ov5640_setting_QVGA_320_240),
- OV5640_30_FPS},
- {OV5640_MODE_VGA_640_480, SUBSAMPLING,
- 640, 1896, 480, 1080,
- ov5640_setting_VGA_640_480,
- ARRAY_SIZE(ov5640_setting_VGA_640_480),
- OV5640_60_FPS},
- {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
- 720, 1896, 480, 984,
- ov5640_setting_NTSC_720_480,
- ARRAY_SIZE(ov5640_setting_NTSC_720_480),
- OV5640_30_FPS},
- {OV5640_MODE_PAL_720_576, SUBSAMPLING,
- 720, 1896, 576, 984,
- ov5640_setting_PAL_720_576,
- ARRAY_SIZE(ov5640_setting_PAL_720_576),
- OV5640_30_FPS},
- {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
- 1024, 1896, 768, 1080,
- ov5640_setting_XGA_1024_768,
- ARRAY_SIZE(ov5640_setting_XGA_1024_768),
- OV5640_30_FPS},
- {OV5640_MODE_720P_1280_720, SUBSAMPLING,
- 1280, 1892, 720, 740,
- ov5640_setting_720P_1280_720,
- ARRAY_SIZE(ov5640_setting_720P_1280_720),
- OV5640_30_FPS},
- {OV5640_MODE_1080P_1920_1080, SCALING,
- 1920, 2500, 1080, 1120,
- ov5640_setting_1080P_1920_1080,
- ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
- OV5640_30_FPS},
- {OV5640_MODE_QSXGA_2592_1944, SCALING,
- 2592, 2844, 1944, 1968,
- ov5640_setting_QSXGA_2592_1944,
- ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
- OV5640_15_FPS},
-};
+static const struct ov5640_timings *
+ov5640_timings(const struct ov5640_dev *sensor,
+ const struct ov5640_mode_info *mode)
+{
+ if (ov5640_is_csi2(sensor))
+ return &mode->csi2_timings;
+
+ return &mode->dvp_timings;
+}
static int ov5640_init_slave_id(struct ov5640_dev *sensor)
{
@@ -797,20 +1309,10 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
* +-----+-----+
* +------------> PCLK
*
- * This is deviating from the datasheet at least for the register
- * 0x3108, since it's said here that the PCLK would be clocked from
- * the PLL.
- *
- * There seems to be also (unverified) constraints:
+ * There seems to be also constraints:
* - the PLL pre-divider output rate should be in the 4-27MHz range
* - the PLL multiplier output rate should be in the 500-1000MHz range
* - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
- *
- * In the two latter cases, these constraints are met since our
- * factors are hardcoded. If we were to change that, we would need to
- * take this into account. The only varying parts are the PLL
- * multiplier and the system clock divider, which are shared between
- * all these clocks so won't cause any issue.
*/
/*
@@ -830,13 +1332,6 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
#define OV5640_SYSDIV_MAX 16
/*
- * Hardcode these values for scaler and non-scaler modes.
- * FIXME: to be re-calcualted for 1 data lanes setups
- */
-#define OV5640_MIPI_DIV_PCLK 2
-#define OV5640_MIPI_DIV_SCLK 1
-
-/*
* This is supposed to be ranging from 1 to 2, but the value is always
* set to 2 in the vendor kernels.
*/
@@ -945,70 +1440,83 @@ out:
/*
* ov5640_set_mipi_pclk() - Calculate the clock tree configuration values
* for the MIPI CSI-2 output.
- *
- * @rate: The requested bandwidth per lane in bytes per second.
- * 'Bandwidth Per Lane' is calculated as:
- * bpl = HTOT * VTOT * FPS * bpp / num_lanes;
- *
- * This function use the requested bandwidth to calculate:
- * - sample_rate = bpl / (bpp / num_lanes);
- * = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes);
- *
- * - mipi_sclk = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR)
- *
- * with these fixed parameters:
- * PLL_RDIV = 2;
- * BIT_DIVIDER = 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
- * PCLK_DIV = 1;
- *
- * The MIPI clock generation differs for modes that use the scaler and modes
- * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI
- * BIT CLk, and thus:
- *
- * - mipi_sclk = bpl / MIPI_DIV / 2;
- * MIPI_DIV = 1;
- *
- * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated
- * from the pixel clock, and thus:
- *
- * - sample_rate = bpl / (bpp / num_lanes);
- * = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes);
- * = bpl / (4 * MIPI_DIV / num_lanes);
- * - MIPI_DIV = bpp / (4 * num_lanes);
- *
- * FIXME: this have been tested with 16bpp and 2 lanes setup only.
- * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
- * above formula for setups with 1 lane or image formats with different bpp.
- *
- * FIXME: this deviates from the sensor manual documentation which is quite
- * thin on the MIPI clock tree generation part.
*/
-static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor,
- unsigned long rate)
+static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor)
{
- const struct ov5640_mode_info *mode = sensor->current_mode;
+ u8 bit_div, mipi_div, pclk_div, sclk_div, sclk2x_div, root_div;
u8 prediv, mult, sysdiv;
- u8 mipi_div;
+ unsigned long link_freq;
+ unsigned long sysclk;
+ u8 pclk_period;
+ u32 sample_rate;
+ u32 num_lanes;
int ret;
+ /* Use the link freq computed at ov5640_update_pixel_rate() time. */
+ link_freq = sensor->current_link_freq;
+
/*
- * 1280x720 is reported to use 'SUBSAMPLING' only,
- * but according to the sensor manual it goes through the
- * scaler before subsampling.
+ * - mipi_div - Additional divider for the MIPI lane clock.
+ *
+ * Higher link frequencies would make sysclk > 1GHz.
+ * Keep the sysclk low and do not divide in the MIPI domain.
*/
- if (mode->dn_mode == SCALING ||
- (mode->id == OV5640_MODE_720P_1280_720))
- mipi_div = OV5640_MIPI_DIV_SCLK;
+ if (link_freq > OV5640_LINK_RATE_MAX)
+ mipi_div = 1;
else
- mipi_div = OV5640_MIPI_DIV_PCLK;
+ mipi_div = 2;
- ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv);
+ sysclk = link_freq * mipi_div;
+ ov5640_calc_sys_clk(sensor, sysclk, &prediv, &mult, &sysdiv);
- ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
- 0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT);
+ /*
+ * Adjust PLL parameters to maintain the MIPI_SCLK-to-PCLK ratio.
+ *
+ * - root_div = 2 (fixed)
+ * - bit_div : MIPI 8-bit = 2; MIPI 10-bit = 2.5
+ * - pclk_div = 1 (fixed)
+ * - p_div = (2 lanes ? mipi_div : 2 * mipi_div)
+ *
+ * This results in the following MIPI_SCLK depending on the number
+ * of lanes:
+ *
+ * - 2 lanes: MIPI_SCLK = (4 or 5) * PCLK
+ * - 1 lanes: MIPI_SCLK = (8 or 10) * PCLK
+ */
+ root_div = OV5640_PLL_CTRL3_PLL_ROOT_DIV_2;
+ bit_div = OV5640_PLL_CTRL0_MIPI_MODE_8BIT;
+ pclk_div = ilog2(OV5640_PCLK_ROOT_DIV);
- ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
- 0xff, sysdiv << 4 | mipi_div);
+ /*
+ * Scaler clock:
+ * - YUV: PCLK >= 2 * SCLK
+ * - RAW or JPEG: PCLK >= SCLK
+ * - sclk2x_div = sclk_div / 2
+ */
+ sclk_div = ilog2(OV5640_SCLK_ROOT_DIV);
+ sclk2x_div = ilog2(OV5640_SCLK2X_ROOT_DIV);
+
+ /*
+ * Set the pixel clock period expressed in ns with 1-bit decimal
+ * (0x01=0.5ns).
+ *
+ * The register is very briefly documented. In the OV5645 datasheet it
+ * is described as (2 * pclk period), and from testing it seems the
+ * actual definition is 2 * 8-bit sample period.
+ *
+ * 2 * sample_period = (mipi_clk * 2 * num_lanes / bpp) * (bpp / 8) / 2
+ */
+ num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes;
+ sample_rate = (link_freq * mipi_div * num_lanes * 2) / 16;
+ pclk_period = 2000000000UL / sample_rate;
+
+ /* Program the clock tree registers. */
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, 0x0f, bit_div);
+ if (ret)
+ return ret;
+
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, 0xff,
+ (sysdiv << 4) | mipi_div);
if (ret)
return ret;
@@ -1016,13 +1524,29 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor,
if (ret)
return ret;
- ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
- 0x1f, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv);
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0x1f,
+ root_div | prediv);
if (ret)
return ret;
- return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER,
- 0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS);
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
+ (pclk_div << 4) | (sclk2x_div << 2) | sclk_div);
+ if (ret)
+ return ret;
+
+ return ov5640_write_reg(sensor, OV5640_REG_PCLK_PERIOD, pclk_period);
+}
+
+static u32 ov5640_calc_pixel_rate(struct ov5640_dev *sensor)
+{
+ const struct ov5640_mode_info *mode = sensor->current_mode;
+ const struct ov5640_timings *timings = &mode->dvp_timings;
+ u32 rate;
+
+ rate = timings->htot * (timings->crop.height + timings->vblank_def);
+ rate *= ov5640_framerates[sensor->current_fr];
+
+ return rate;
}
static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
@@ -1042,11 +1566,16 @@ static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
return _rate / *pll_rdiv / *bit_div / *pclk_div;
}
-static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
+static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor)
{
u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
+ u32 rate;
int ret;
+ rate = ov5640_calc_pixel_rate(sensor);
+ rate *= ov5640_code_to_bpp(sensor, sensor->fmt.code);
+ rate /= sensor->ep.bus.parallel.bus_width;
+
ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
&bit_div, &pclk_div);
@@ -1098,17 +1627,20 @@ static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
if (ret < 0)
return ret;
- ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact);
+ ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->width);
if (ret < 0)
return ret;
- return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact);
+ return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->height);
}
/* download ov5640 settings to sensor through i2c */
static int ov5640_set_timings(struct ov5640_dev *sensor,
const struct ov5640_mode_info *mode)
{
+ const struct ov5640_timings *timings;
+ const struct v4l2_rect *analog_crop;
+ const struct v4l2_rect *crop;
int ret;
if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) {
@@ -1117,32 +1649,68 @@ static int ov5640_set_timings(struct ov5640_dev *sensor,
return ret;
}
- ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
+ timings = ov5640_timings(sensor, mode);
+ analog_crop = &timings->analog_crop;
+ crop = &timings->crop;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HS,
+ analog_crop->left);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VS,
+ analog_crop->top);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HW,
+ analog_crop->left + analog_crop->width - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VH,
+ analog_crop->top + analog_crop->height - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HOFFS, crop->left);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VOFFS, crop->top);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->width);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->height);
if (ret < 0)
return ret;
- ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact);
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, timings->htot);
if (ret < 0)
return ret;
- ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot);
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
+ mode->height + timings->vblank_def);
if (ret < 0)
return ret;
- return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot);
+ return 0;
}
-static int ov5640_load_regs(struct ov5640_dev *sensor,
- const struct ov5640_mode_info *mode)
+static void ov5640_load_regs(struct ov5640_dev *sensor,
+ const struct reg_value *regs, unsigned int regnum)
{
- const struct reg_value *regs = mode->reg_data;
unsigned int i;
u32 delay_ms;
u16 reg_addr;
u8 mask, val;
int ret = 0;
- for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
+ for (i = 0; i < regnum; ++i, ++regs) {
delay_ms = regs->delay_ms;
reg_addr = regs->reg_addr;
val = regs->val;
@@ -1151,7 +1719,7 @@ static int ov5640_load_regs(struct ov5640_dev *sensor,
/* remain in power down mode for DVP */
if (regs->reg_addr == OV5640_REG_SYS_CTRL0 &&
val == OV5640_REG_SYS_CTRL0_SW_PWUP &&
- sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY)
+ !ov5640_is_csi2(sensor))
continue;
if (mask)
@@ -1164,8 +1732,6 @@ static int ov5640_load_regs(struct ov5640_dev *sensor,
if (delay_ms)
usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
}
-
- return ov5640_set_timings(sensor, mode);
}
static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on)
@@ -1550,37 +2116,22 @@ static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
}
static const struct ov5640_mode_info *
-ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
- int width, int height, bool nearest)
+ov5640_find_mode(struct ov5640_dev *sensor, int width, int height, bool nearest)
{
const struct ov5640_mode_info *mode;
mode = v4l2_find_nearest_size(ov5640_mode_data,
ARRAY_SIZE(ov5640_mode_data),
- hact, vact,
- width, height);
+ width, height, width, height);
if (!mode ||
- (!nearest && (mode->hact != width || mode->vact != height)))
- return NULL;
-
- /* Check to see if the current mode exceeds the max frame rate */
- if (ov5640_framerates[fr] > ov5640_framerates[mode->max_fps])
+ (!nearest &&
+ (mode->width != width || mode->height != height)))
return NULL;
return mode;
}
-static u64 ov5640_calc_pixel_rate(struct ov5640_dev *sensor)
-{
- u64 rate;
-
- rate = sensor->current_mode->vtot * sensor->current_mode->htot;
- rate *= ov5640_framerates[sensor->current_fr];
-
- return rate;
-}
-
/*
* sensor changes between scaling and subsampling, go through
* exposure calculation
@@ -1628,7 +2179,8 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor,
return ret;
/* Write capture setting */
- ret = ov5640_load_regs(sensor, mode);
+ ov5640_load_regs(sensor, mode->reg_data, mode->reg_data_size);
+ ret = ov5640_set_timings(sensor, mode);
if (ret < 0)
return ret;
@@ -1752,7 +2304,8 @@ static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
return -EINVAL;
/* Write capture setting */
- return ov5640_load_regs(sensor, mode);
+ ov5640_load_regs(sensor, mode->reg_data, mode->reg_data_size);
+ return ov5640_set_timings(sensor, mode);
}
static int ov5640_set_mode(struct ov5640_dev *sensor)
@@ -1762,7 +2315,6 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
enum ov5640_downsize_mode dn_mode, orig_dn_mode;
bool auto_gain = sensor->ctrls.auto_gain->val == 1;
bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
- unsigned long rate;
int ret;
dn_mode = mode->dn_mode;
@@ -1781,19 +2333,10 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
goto restore_auto_gain;
}
- /*
- * All the formats we support have 16 bits per pixel, seems to require
- * the same rate than YUV, so we can just use 16 bpp all the time.
- */
- rate = ov5640_calc_pixel_rate(sensor) * 16;
- if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
- rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes;
- ret = ov5640_set_mipi_pclk(sensor, rate);
- } else {
- rate = rate / sensor->ep.bus.parallel.bus_width;
- ret = ov5640_set_dvp_pclk(sensor, rate);
- }
-
+ if (ov5640_is_csi2(sensor))
+ ret = ov5640_set_mipi_pclk(sensor);
+ else
+ ret = ov5640_set_dvp_pclk(sensor);
if (ret < 0)
return 0;
@@ -1860,10 +2403,8 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor)
int ret;
/* first load the initial register values */
- ret = ov5640_load_regs(sensor, &ov5640_mode_init_data);
- if (ret < 0)
- return ret;
- sensor->last_mode = &ov5640_mode_init_data;
+ ov5640_load_regs(sensor, ov5640_init_setting,
+ ARRAY_SIZE(ov5640_init_setting));
ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
(ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) |
@@ -2224,7 +2765,7 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
fi->denominator = best_fps;
find_mode:
- mode = ov5640_find_mode(sensor, rate, width, height, false);
+ mode = ov5640_find_mode(sensor, width, height, false);
return mode ? rate : -EINVAL;
}
@@ -2260,25 +2801,34 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
{
struct ov5640_dev *sensor = to_ov5640_dev(sd);
const struct ov5640_mode_info *mode;
- int i;
+ const struct ov5640_pixfmt *pixfmt;
+ unsigned int bpp;
- mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
+ mode = ov5640_find_mode(sensor, fmt->width, fmt->height, true);
if (!mode)
return -EINVAL;
- fmt->width = mode->hact;
- fmt->height = mode->vact;
+
+ pixfmt = ov5640_code_to_pixfmt(sensor, fmt->code);
+ bpp = pixfmt->bpp;
+
+ /*
+ * Adjust mode according to bpp:
+ * - 8bpp modes work for resolution >= 1280x720
+ * - 24bpp modes work resolution < 1280x720
+ */
+ if (bpp == 8 && mode->width < 1280)
+ mode = &ov5640_mode_data[OV5640_MODE_720P_1280_720];
+ else if (bpp == 24 && mode->width > 1024)
+ mode = &ov5640_mode_data[OV5640_MODE_XGA_1024_768];
+
+ fmt->width = mode->width;
+ fmt->height = mode->height;
if (new_mode)
*new_mode = mode;
- for (i = 0; i < ARRAY_SIZE(ov5640_formats); i++)
- if (ov5640_formats[i].code == fmt->code)
- break;
- if (i >= ARRAY_SIZE(ov5640_formats))
- i = 0;
-
- fmt->code = ov5640_formats[i].code;
- fmt->colorspace = ov5640_formats[i].colorspace;
+ fmt->code = pixfmt->code;
+ fmt->colorspace = pixfmt->colorspace;
fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
@@ -2286,6 +2836,107 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
return 0;
}
+static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
+{
+ const struct ov5640_mode_info *mode = sensor->current_mode;
+ enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
+ struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
+ const struct ov5640_timings *timings;
+ s32 exposure_val, exposure_max;
+ unsigned int hblank;
+ unsigned int i = 0;
+ u32 pixel_rate;
+ s64 link_freq;
+ u32 num_lanes;
+ u32 vblank;
+ u32 bpp;
+
+ /*
+ * Update the pixel rate control value.
+ *
+ * For DVP mode, maintain the pixel rate calculation using fixed FPS.
+ */
+ if (!ov5640_is_csi2(sensor)) {
+ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
+ ov5640_calc_pixel_rate(sensor));
+
+ return 0;
+ }
+
+ /*
+ * The MIPI CSI-2 link frequency should comply with the CSI-2
+ * specification and be lower than 1GHz.
+ *
+ * Start from the suggested pixel_rate for the current mode and
+ * progressively slow it down if it exceeds 1GHz.
+ */
+ num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes;
+ bpp = ov5640_code_to_bpp(sensor, fmt->code);
+ do {
+ pixel_rate = ov5640_pixel_rates[pixel_rate_id];
+ link_freq = pixel_rate * bpp / (2 * num_lanes);
+ } while (link_freq >= 1000000000U &&
+ ++pixel_rate_id < OV5640_NUM_PIXEL_RATES);
+
+ sensor->current_link_freq = link_freq;
+
+ /*
+ * Higher link rates require the clock tree to be programmed with
+ * 'mipi_div' = 1; this has the effect of halving the actual output
+ * pixel rate in the MIPI domain.
+ *
+ * Adjust the pixel rate and link frequency control value to report it
+ * correctly to userspace.
+ */
+ if (link_freq > OV5640_LINK_RATE_MAX) {
+ pixel_rate /= 2;
+ link_freq /= 2;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ov5640_csi2_link_freqs); ++i) {
+ if (ov5640_csi2_link_freqs[i] == link_freq)
+ break;
+ }
+ WARN_ON(i == ARRAY_SIZE(ov5640_csi2_link_freqs));
+
+ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, pixel_rate);
+ __v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, i);
+
+ timings = ov5640_timings(sensor, mode);
+ hblank = timings->htot - mode->width;
+ __v4l2_ctrl_modify_range(sensor->ctrls.hblank,
+ hblank, hblank, 1, hblank);
+
+ vblank = timings->vblank_def;
+
+ if (sensor->current_fr != mode->def_fps) {
+ /*
+ * Compute the vertical blanking according to the framerate
+ * configured with s_frame_interval.
+ */
+ int fie_num = sensor->frame_interval.numerator;
+ int fie_denom = sensor->frame_interval.denominator;
+
+ vblank = ((fie_num * pixel_rate / fie_denom) / timings->htot) -
+ mode->height;
+ }
+
+ __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
+ OV5640_MAX_VTS - mode->height, 1, vblank);
+ __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, vblank);
+
+ exposure_max = timings->crop.height + vblank - 4;
+ exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
+ sensor->ctrls.exposure->minimum,
+ exposure_max);
+
+ __v4l2_ctrl_modify_range(sensor->ctrls.exposure,
+ sensor->ctrls.exposure->minimum,
+ exposure_max, 1, exposure_val);
+
+ return 0;
+}
+
static int ov5640_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
@@ -2316,6 +2967,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
}
if (new_mode != sensor->current_mode) {
+ sensor->current_fr = new_mode->def_fps;
sensor->current_mode = new_mode;
sensor->pending_mode_change = true;
}
@@ -2325,80 +2977,70 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
/* update format even if code is unchanged, resolution might change */
sensor->fmt = *mbus_fmt;
- __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
- ov5640_calc_pixel_rate(sensor));
+ ov5640_update_pixel_rate(sensor);
+
out:
mutex_unlock(&sensor->lock);
return ret;
}
+static int ov5640_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ const struct ov5640_mode_info *mode = sensor->current_mode;
+ const struct ov5640_timings *timings;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP: {
+ mutex_lock(&sensor->lock);
+ timings = ov5640_timings(sensor, mode);
+ sel->r = timings->analog_crop;
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+ }
+
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = OV5640_NATIVE_WIDTH;
+ sel->r.height = OV5640_NATIVE_HEIGHT;
+
+ return 0;
+
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r.top = OV5640_PIXEL_ARRAY_TOP;
+ sel->r.left = OV5640_PIXEL_ARRAY_LEFT;
+ sel->r.width = OV5640_PIXEL_ARRAY_WIDTH;
+ sel->r.height = OV5640_PIXEL_ARRAY_HEIGHT;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
static int ov5640_set_framefmt(struct ov5640_dev *sensor,
struct v4l2_mbus_framefmt *format)
{
+ bool is_jpeg = format->code == MEDIA_BUS_FMT_JPEG_1X8;
+ const struct ov5640_pixfmt *pixfmt;
int ret = 0;
- bool is_jpeg = false;
- u8 fmt, mux;
- switch (format->code) {
- case MEDIA_BUS_FMT_UYVY8_1X16:
- case MEDIA_BUS_FMT_UYVY8_2X8:
- /* YUV422, UYVY */
- fmt = 0x3f;
- mux = OV5640_FMT_MUX_YUV422;
- break;
- case MEDIA_BUS_FMT_YUYV8_1X16:
- case MEDIA_BUS_FMT_YUYV8_2X8:
- /* YUV422, YUYV */
- fmt = 0x30;
- mux = OV5640_FMT_MUX_YUV422;
- break;
- case MEDIA_BUS_FMT_RGB565_2X8_LE:
- /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
- fmt = 0x6F;
- mux = OV5640_FMT_MUX_RGB;
- break;
- case MEDIA_BUS_FMT_RGB565_2X8_BE:
- /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
- fmt = 0x61;
- mux = OV5640_FMT_MUX_RGB;
- break;
- case MEDIA_BUS_FMT_JPEG_1X8:
- /* YUV422, YUYV */
- fmt = 0x30;
- mux = OV5640_FMT_MUX_YUV422;
- is_jpeg = true;
- break;
- case MEDIA_BUS_FMT_SBGGR8_1X8:
- /* Raw, BGBG... / GRGR... */
- fmt = 0x00;
- mux = OV5640_FMT_MUX_RAW_DPC;
- break;
- case MEDIA_BUS_FMT_SGBRG8_1X8:
- /* Raw bayer, GBGB... / RGRG... */
- fmt = 0x01;
- mux = OV5640_FMT_MUX_RAW_DPC;
- break;
- case MEDIA_BUS_FMT_SGRBG8_1X8:
- /* Raw bayer, GRGR... / BGBG... */
- fmt = 0x02;
- mux = OV5640_FMT_MUX_RAW_DPC;
- break;
- case MEDIA_BUS_FMT_SRGGB8_1X8:
- /* Raw bayer, RGRG... / GBGB... */
- fmt = 0x03;
- mux = OV5640_FMT_MUX_RAW_DPC;
- break;
- default:
- return -EINVAL;
- }
+ pixfmt = ov5640_code_to_pixfmt(sensor, format->code);
/* FORMAT CONTROL00: YUV and RGB formatting */
- ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, fmt);
+ ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00,
+ pixfmt->ctrl00);
if (ret)
return ret;
/* FORMAT MUX CONTROL: ISP YUV or RGB */
- ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, mux);
+ ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL,
+ pixfmt->mux);
if (ret)
return ret;
@@ -2655,6 +3297,15 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
(BIT(2) | BIT(1)) : 0);
}
+static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value)
+{
+ const struct ov5640_mode_info *mode = sensor->current_mode;
+
+ /* Update the VTOT timing register value. */
+ return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
+ mode->height + value);
+}
+
static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
@@ -2685,10 +3336,25 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ const struct ov5640_mode_info *mode = sensor->current_mode;
+ const struct ov5640_timings *timings;
+ unsigned int exp_max;
int ret;
/* v4l2_ctrl_lock() locks our own mutex */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update the exposure range to the newly programmed vblank. */
+ timings = ov5640_timings(sensor, mode);
+ exp_max = mode->height + ctrl->val - 4;
+ __v4l2_ctrl_modify_range(sensor->ctrls.exposure,
+ sensor->ctrls.exposure->minimum,
+ exp_max, sensor->ctrls.exposure->step,
+ timings->vblank_def);
+ break;
+ }
+
/*
* If the device is not powered up by the host driver do
* not apply any controls to H/W at this time. Instead
@@ -2728,6 +3394,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_VFLIP:
ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
break;
+ case V4L2_CID_VBLANK:
+ ret = ov5640_set_ctrl_vblank(sensor, ctrl->val);
+ break;
default:
ret = -EINVAL;
break;
@@ -2743,9 +3412,14 @@ static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
static int ov5640_init_controls(struct ov5640_dev *sensor)
{
+ const struct ov5640_mode_info *mode = sensor->current_mode;
const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
struct ov5640_ctrls *ctrls = &sensor->ctrls;
struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+ struct v4l2_fwnode_device_properties props;
+ const struct ov5640_timings *timings;
+ unsigned int max_vblank;
+ unsigned int hblank;
int ret;
v4l2_ctrl_handler_init(hdl, 32);
@@ -2755,8 +3429,25 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
/* Clock related controls */
ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
- 0, INT_MAX, 1,
- ov5640_calc_pixel_rate(sensor));
+ ov5640_pixel_rates[OV5640_NUM_PIXEL_RATES - 1],
+ ov5640_pixel_rates[0], 1,
+ ov5640_pixel_rates[mode->pixel_rate]);
+
+ ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(ov5640_csi2_link_freqs) - 1,
+ OV5640_DEFAULT_LINK_FREQ,
+ ov5640_csi2_link_freqs);
+
+ timings = ov5640_timings(sensor, mode);
+ hblank = timings->htot - mode->width;
+ ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank,
+ hblank, 1, hblank);
+
+ max_vblank = OV5640_MAX_VTS - mode->height;
+ ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+ OV5640_MIN_VBLANK, max_vblank,
+ 1, timings->vblank_def);
/* Auto/manual white balance */
ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
@@ -2805,7 +3496,20 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
goto free_ctrls;
}
+ ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &props);
+ if (ret)
+ goto free_ctrls;
+
+ if (props.rotation == 180)
+ sensor->upside_down = true;
+
+ ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
+ if (ret)
+ goto free_ctrls;
+
ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
@@ -2825,16 +3529,29 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ u32 bpp = ov5640_code_to_bpp(sensor, fse->code);
+ unsigned int index = fse->index;
+
if (fse->pad != 0)
return -EINVAL;
- if (fse->index >= OV5640_NUM_MODES)
+ if (!bpp)
+ return -EINVAL;
+
+ /* Only low-resolution modes are supported for 24bpp formats. */
+ if (bpp == 24 && index >= OV5640_MODE_720P_1280_720)
+ return -EINVAL;
+
+ /* FIXME: Low resolution modes don't work in 8bpp formats. */
+ if (bpp == 8)
+ index += OV5640_MODE_720P_1280_720;
+
+ if (index >= OV5640_NUM_MODES)
return -EINVAL;
- fse->min_width =
- ov5640_mode_data[fse->index].hact;
+ fse->min_width = ov5640_mode_data[index].width;
fse->max_width = fse->min_width;
- fse->min_height =
- ov5640_mode_data[fse->index].vact;
+ fse->min_height = ov5640_mode_data[index].height;
fse->max_height = fse->min_height;
return 0;
@@ -2898,20 +3615,25 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
mode = sensor->current_mode;
frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
- mode->hact, mode->vact);
+ mode->width,
+ mode->height);
if (frame_rate < 0) {
/* Always return a valid frame interval value */
fi->interval = sensor->frame_interval;
goto out;
}
- mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
- mode->vact, true);
+ mode = ov5640_find_mode(sensor, mode->width, mode->height, true);
if (!mode) {
ret = -EINVAL;
goto out;
}
+ if (ov5640_framerates[frame_rate] > ov5640_framerates[mode->max_fps]) {
+ ret = -EINVAL;
+ goto out;
+ }
+
if (mode != sensor->current_mode ||
frame_rate != sensor->current_fr) {
sensor->current_fr = frame_rate;
@@ -2919,8 +3641,7 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
sensor->current_mode = mode;
sensor->pending_mode_change = true;
- __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
- ov5640_calc_pixel_rate(sensor));
+ ov5640_update_pixel_rate(sensor);
}
out:
mutex_unlock(&sensor->lock);
@@ -2931,12 +3652,23 @@ static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
- if (code->pad != 0)
- return -EINVAL;
- if (code->index >= ARRAY_SIZE(ov5640_formats))
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ const struct ov5640_pixfmt *formats;
+ unsigned int num_formats;
+
+ if (ov5640_is_csi2(sensor)) {
+ formats = ov5640_csi2_formats;
+ num_formats = ARRAY_SIZE(ov5640_csi2_formats) - 1;
+ } else {
+ formats = ov5640_dvp_formats;
+ num_formats = ARRAY_SIZE(ov5640_dvp_formats) - 1;
+ }
+
+ if (code->index >= num_formats)
return -EINVAL;
- code->code = ov5640_formats[code->index].code;
+ code->code = formats[code->index].code;
+
return 0;
}
@@ -2961,7 +3693,7 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
sensor->pending_fmt_change = false;
}
- if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
+ if (ov5640_is_csi2(sensor))
ret = ov5640_set_stream_mipi(sensor, enable);
else
ret = ov5640_set_stream_dvp(sensor, enable);
@@ -2974,6 +3706,23 @@ out:
return ret;
}
+static int ov5640_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_get_try_format(sd, state, 0);
+ struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0);
+
+ *fmt = ov5640_default_fmt;
+
+ crop->left = OV5640_PIXEL_ARRAY_LEFT;
+ crop->top = OV5640_PIXEL_ARRAY_TOP;
+ crop->width = OV5640_PIXEL_ARRAY_WIDTH;
+ crop->height = OV5640_PIXEL_ARRAY_HEIGHT;
+
+ return 0;
+}
+
static const struct v4l2_subdev_core_ops ov5640_core_ops = {
.s_power = ov5640_s_power,
.log_status = v4l2_ctrl_subdev_log_status,
@@ -2988,9 +3737,11 @@ static const struct v4l2_subdev_video_ops ov5640_video_ops = {
};
static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
+ .init_cfg = ov5640_init_cfg,
.enum_mbus_code = ov5640_enum_mbus_code,
.get_fmt = ov5640_get_fmt,
.set_fmt = ov5640_set_fmt,
+ .get_selection = ov5640_get_selection,
.enum_frame_size = ov5640_enum_frame_size,
.enum_frame_interval = ov5640_enum_frame_interval,
};
@@ -3046,8 +3797,6 @@ static int ov5640_probe(struct i2c_client *client)
struct device *dev = &client->dev;
struct fwnode_handle *endpoint;
struct ov5640_dev *sensor;
- struct v4l2_mbus_framefmt *fmt;
- u32 rotation;
int ret;
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
@@ -3060,40 +3809,17 @@ static int ov5640_probe(struct i2c_client *client)
* default init sequence initialize sensor to
* YUV422 UYVY VGA@30fps
*/
- fmt = &sensor->fmt;
- fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
- fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
- fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
- fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
- fmt->width = 640;
- fmt->height = 480;
- fmt->field = V4L2_FIELD_NONE;
+ sensor->fmt = ov5640_default_fmt;
sensor->frame_interval.numerator = 1;
sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
sensor->current_fr = OV5640_30_FPS;
sensor->current_mode =
&ov5640_mode_data[OV5640_MODE_VGA_640_480];
sensor->last_mode = sensor->current_mode;
+ sensor->current_link_freq = OV5640_DEFAULT_LINK_FREQ;
sensor->ae_target = 52;
- /* optional indication of physical rotation of sensor */
- ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
- &rotation);
- if (!ret) {
- switch (rotation) {
- case 180:
- sensor->upside_down = true;
- fallthrough;
- case 0:
- break;
- default:
- dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
- rotation);
- }
- }
-
endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
NULL);
if (!endpoint) {
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 117ff5403312..82a9b2de7735 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -127,11 +127,16 @@
#define OV5693_LINK_FREQ_419_2MHZ 419200000
#define OV5693_PIXEL_RATE 167680000
-/* Miscellaneous */
-#define OV5693_NUM_SUPPLIES 2
-
#define to_ov5693_sensor(x) container_of(x, struct ov5693_device, sd)
+static const char * const ov5693_supply_names[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital circuit power */
+};
+
+#define OV5693_NUM_SUPPLIES ARRAY_SIZE(ov5693_supply_names)
+
struct ov5693_reg {
u32 reg;
u8 val;
@@ -152,7 +157,7 @@ struct ov5693_device {
struct gpio_desc *reset;
struct gpio_desc *powerdown;
struct regulator_bulk_data supplies[OV5693_NUM_SUPPLIES];
- struct clk *clk;
+ struct clk *xvclk;
struct ov5693_mode {
struct v4l2_rect crop;
@@ -352,11 +357,6 @@ static const s64 link_freq_menu_items[] = {
OV5693_LINK_FREQ_419_2MHZ
};
-static const char * const ov5693_supply_names[] = {
- "avdd",
- "dovdd",
-};
-
static const char * const ov5693_test_pattern_menu[] = {
"Disabled",
"Random Data",
@@ -794,7 +794,7 @@ static void ov5693_sensor_powerdown(struct ov5693_device *ov5693)
regulator_bulk_disable(OV5693_NUM_SUPPLIES, ov5693->supplies);
- clk_disable_unprepare(ov5693->clk);
+ clk_disable_unprepare(ov5693->xvclk);
}
static int ov5693_sensor_powerup(struct ov5693_device *ov5693)
@@ -804,7 +804,7 @@ static int ov5693_sensor_powerup(struct ov5693_device *ov5693)
gpiod_set_value_cansleep(ov5693->reset, 1);
gpiod_set_value_cansleep(ov5693->powerdown, 1);
- ret = clk_prepare_enable(ov5693->clk);
+ ret = clk_prepare_enable(ov5693->xvclk);
if (ret) {
dev_err(ov5693->dev, "Failed to enable clk\n");
goto fail_power;
@@ -1390,7 +1390,7 @@ out_free_bus_cfg:
static int ov5693_probe(struct i2c_client *client)
{
struct ov5693_device *ov5693;
- u32 clk_rate;
+ u32 xvclk_rate;
int ret = 0;
ov5693 = devm_kzalloc(&client->dev, sizeof(*ov5693), GFP_KERNEL);
@@ -1408,16 +1408,28 @@ static int ov5693_probe(struct i2c_client *client)
v4l2_i2c_subdev_init(&ov5693->sd, client, &ov5693_ops);
- ov5693->clk = devm_clk_get(&client->dev, "xvclk");
- if (IS_ERR(ov5693->clk)) {
- dev_err(&client->dev, "Error getting clock\n");
- return PTR_ERR(ov5693->clk);
+ ov5693->xvclk = devm_clk_get_optional(&client->dev, "xvclk");
+ if (IS_ERR(ov5693->xvclk))
+ return dev_err_probe(&client->dev, PTR_ERR(ov5693->xvclk),
+ "failed to get xvclk: %ld\n",
+ PTR_ERR(ov5693->xvclk));
+
+ if (ov5693->xvclk) {
+ xvclk_rate = clk_get_rate(ov5693->xvclk);
+ } else {
+ ret = fwnode_property_read_u32(dev_fwnode(&client->dev),
+ "clock-frequency",
+ &xvclk_rate);
+
+ if (ret) {
+ dev_err(&client->dev, "can't get clock frequency");
+ return ret;
+ }
}
- clk_rate = clk_get_rate(ov5693->clk);
- if (clk_rate != OV5693_XVCLK_FREQ)
+ if (xvclk_rate != OV5693_XVCLK_FREQ)
dev_warn(&client->dev, "Found clk freq %u, expected %u\n",
- clk_rate, OV5693_XVCLK_FREQ);
+ xvclk_rate, OV5693_XVCLK_FREQ);
ret = ov5693_configure_gpios(ov5693);
if (ret)
@@ -1521,10 +1533,17 @@ static const struct acpi_device_id ov5693_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, ov5693_acpi_match);
+static const struct of_device_id ov5693_of_match[] = {
+ { .compatible = "ovti,ov5693", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ov5693_of_match);
+
static struct i2c_driver ov5693_driver = {
.driver = {
.name = "ov5693",
.acpi_match_table = ov5693_acpi_match,
+ .of_match_table = ov5693_of_match,
.pm = &ov5693_pm_ops,
},
.probe_new = ov5693_probe,
diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c
index 0e7be15bc20a..1bd797c7926b 100644
--- a/drivers/media/i2c/ov7251.c
+++ b/drivers/media/i2c/ov7251.c
@@ -934,6 +934,8 @@ static int ov7251_set_power_on(struct device *dev)
ARRAY_SIZE(ov7251_global_init_setting));
if (ret < 0) {
dev_err(ov7251->dev, "error during global init\n");
+ gpiod_set_value_cansleep(ov7251->enable_gpio, 0);
+ clk_disable_unprepare(ov7251->xclk);
ov7251_regulators_disable(ov7251);
return ret;
}
@@ -1340,7 +1342,7 @@ static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable)
if (enable) {
ret = pm_runtime_get_sync(ov7251->dev);
if (ret < 0)
- goto unlock_out;
+ goto err_power_down;
ret = ov7251_pll_configure(ov7251);
if (ret) {
@@ -1372,12 +1374,11 @@ static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable)
pm_runtime_put(ov7251->dev);
}
-unlock_out:
mutex_unlock(&ov7251->lock);
return ret;
err_power_down:
- pm_runtime_put_noidle(ov7251->dev);
+ pm_runtime_put(ov7251->dev);
mutex_unlock(&ov7251->lock);
return ret;
}
diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c
index ef976d085d72..16cc547976dd 100644
--- a/drivers/media/i2c/st-mipid02.c
+++ b/drivers/media/i2c/st-mipid02.c
@@ -50,6 +50,7 @@
/* Bits definition for MIPID02_MODE_REG2 */
#define MODE_HSYNC_ACTIVE_HIGH BIT(1)
#define MODE_VSYNC_ACTIVE_HIGH BIT(2)
+#define MODE_PCLK_SAMPLE_RISING BIT(3)
/* Bits definition for MIPID02_DATA_SELECTION_CTRL */
#define SELECTION_MANUAL_DATA BIT(2)
#define SELECTION_MANUAL_WIDTH BIT(3)
@@ -61,9 +62,12 @@ static const u32 mipid02_supported_fmt_codes[] = {
MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10,
MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SGBRG12_1X12,
MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SRGGB12_1X12,
- MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_BGR888_1X24,
+ MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YVYU8_1X16,
+ MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_VYUY8_1X16,
+ MEDIA_BUS_FMT_RGB565_1X16, MEDIA_BUS_FMT_BGR888_1X24,
MEDIA_BUS_FMT_RGB565_2X8_LE, MEDIA_BUS_FMT_RGB565_2X8_BE,
- MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YVYU8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_VYUY8_2X8,
MEDIA_BUS_FMT_JPEG_1X8
};
@@ -130,9 +134,15 @@ static int bpp_from_code(__u32 code)
case MEDIA_BUS_FMT_SGRBG12_1X12:
case MEDIA_BUS_FMT_SRGGB12_1X12:
return 12;
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YVYU8_1X16:
case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_VYUY8_1X16:
+ case MEDIA_BUS_FMT_RGB565_1X16:
case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
case MEDIA_BUS_FMT_RGB565_2X8_LE:
case MEDIA_BUS_FMT_RGB565_2X8_BE:
return 16;
@@ -161,12 +171,18 @@ static u8 data_type_from_code(__u32 code)
case MEDIA_BUS_FMT_SGRBG12_1X12:
case MEDIA_BUS_FMT_SRGGB12_1X12:
return 0x2c;
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YVYU8_1X16:
case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_VYUY8_1X16:
case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
return 0x1e;
case MEDIA_BUS_FMT_BGR888_1X24:
return 0x24;
+ case MEDIA_BUS_FMT_RGB565_1X16:
case MEDIA_BUS_FMT_RGB565_2X8_LE:
case MEDIA_BUS_FMT_RGB565_2X8_BE:
return 0x22;
@@ -201,8 +217,16 @@ static __u32 get_fmt_code(__u32 code)
static __u32 serial_to_parallel_code(__u32 serial)
{
+ if (serial == MEDIA_BUS_FMT_RGB565_1X16)
+ return MEDIA_BUS_FMT_RGB565_2X8_LE;
+ if (serial == MEDIA_BUS_FMT_YUYV8_1X16)
+ return MEDIA_BUS_FMT_YUYV8_2X8;
+ if (serial == MEDIA_BUS_FMT_YVYU8_1X16)
+ return MEDIA_BUS_FMT_YVYU8_2X8;
if (serial == MEDIA_BUS_FMT_UYVY8_1X16)
return MEDIA_BUS_FMT_UYVY8_2X8;
+ if (serial == MEDIA_BUS_FMT_VYUY8_1X16)
+ return MEDIA_BUS_FMT_VYUY8_2X8;
if (serial == MEDIA_BUS_FMT_BGR888_1X24)
return MEDIA_BUS_FMT_BGR888_3X8;
@@ -494,6 +518,8 @@ static int mipid02_configure_from_tx(struct mipid02_dev *bridge)
bridge->r.mode_reg2 |= MODE_HSYNC_ACTIVE_HIGH;
if (ep->bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
bridge->r.mode_reg2 |= MODE_VSYNC_ACTIVE_HIGH;
+ if (ep->bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ bridge->r.mode_reg2 |= MODE_PCLK_SAMPLE_RISING;
return 0;
}
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index 8fafce26d62f..0de7acdf58a7 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -2798,6 +2798,7 @@ err_free_mutex:
cancel_delayed_work(&state->delayed_work_enable_hpd);
mutex_destroy(&state->page_lock);
mutex_destroy(&state->lock);
+ tda1997x_set_power(state, 0);
err_free_state:
kfree(state);
dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 65472438444b..93a980c4e899 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -1285,7 +1285,7 @@ static int tvp5150_disable_all_input_links(struct tvp5150 *decoder)
int err;
for (i = 0; i < TVP5150_NUM_PADS - 1; i++) {
- connector_pad = media_entity_remote_pad(&decoder->pads[i]);
+ connector_pad = media_pad_remote_pad_first(&decoder->pads[i]);
if (!connector_pad)
continue;