diff options
Diffstat (limited to 'drivers/media/i2c')
-rw-r--r-- | drivers/media/i2c/Kconfig | 18 | ||||
-rw-r--r-- | drivers/media/i2c/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/i2c/adv7180.c | 5 | ||||
-rw-r--r-- | drivers/media/i2c/adv748x/adv748x.h | 2 | ||||
-rw-r--r-- | drivers/media/i2c/adv7604.c | 5 | ||||
-rw-r--r-- | drivers/media/i2c/ar0521.c | 1061 | ||||
-rw-r--r-- | drivers/media/i2c/mt9p031.c | 93 | ||||
-rw-r--r-- | drivers/media/i2c/ov5640.c | 1650 | ||||
-rw-r--r-- | drivers/media/i2c/ov5693.c | 57 | ||||
-rw-r--r-- | drivers/media/i2c/ov7251.c | 7 | ||||
-rw-r--r-- | drivers/media/i2c/st-mipid02.c | 30 | ||||
-rw-r--r-- | drivers/media/i2c/tda1997x.c | 1 | ||||
-rw-r--r-- | drivers/media/i2c/tvp5150.c | 2 |
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; |