diff options
Diffstat (limited to 'drivers/media/i2c/ov8865.c')
-rw-r--r-- | drivers/media/i2c/ov8865.c | 478 |
1 files changed, 339 insertions, 139 deletions
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index ce50f3ea87b8..d9d016cfa9ac 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -9,6 +9,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/i2c.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of_graph.h> #include <linux/pm_runtime.h> @@ -20,10 +21,6 @@ #include <media/v4l2-image-sizes.h> #include <media/v4l2-mediabus.h> -/* Clock rate */ - -#define OV8865_EXTCLK_RATE 24000000 - /* Register definitions */ /* System */ @@ -146,6 +143,7 @@ #define OV8865_EXPOSURE_CTRL_L_REG 0x3502 #define OV8865_EXPOSURE_CTRL_L(v) ((v) & GENMASK(7, 0)) #define OV8865_EXPOSURE_GAIN_MANUAL_REG 0x3503 +#define OV8865_INTEGRATION_TIME_MARGIN 8 #define OV8865_GAIN_CTRL_H_REG 0x3508 #define OV8865_GAIN_CTRL_H(v) (((v) & GENMASK(12, 8)) >> 8) @@ -186,6 +184,8 @@ #define OV8865_VTS_H(v) (((v) & GENMASK(11, 8)) >> 8) #define OV8865_VTS_L_REG 0x380f #define OV8865_VTS_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_TIMING_MAX_VTS 0xffff +#define OV8865_TIMING_MIN_VTS 0x04 #define OV8865_OFFSET_X_H_REG 0x3810 #define OV8865_OFFSET_X_H(v) (((v) & GENMASK(15, 8)) >> 8) #define OV8865_OFFSET_X_L_REG 0x3811 @@ -453,6 +453,15 @@ #define OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES 2 #define OV8865_PRE_CTRL0_PATTERN_BLACK 3 +/* Pixel Array */ + +#define OV8865_NATIVE_WIDTH 3296 +#define OV8865_NATIVE_HEIGHT 2528 +#define OV8865_ACTIVE_START_TOP 32 +#define OV8865_ACTIVE_START_LEFT 80 +#define OV8865_ACTIVE_WIDTH 3264 +#define OV8865_ACTIVE_HEIGHT 2448 + /* Macros */ #define ov8865_subdev_sensor(s) \ @@ -566,6 +575,25 @@ struct ov8865_sclk_config { unsigned int sclk_div; }; +struct ov8865_pll_configs { + const struct ov8865_pll1_config *pll1_config; + const struct ov8865_pll2_config *pll2_config_native; + const struct ov8865_pll2_config *pll2_config_binning; +}; + +/* Clock rate */ + +enum extclk_rate { + OV8865_19_2_MHZ, + OV8865_24_MHZ, + OV8865_NUM_SUPPORTED_RATES +}; + +static const unsigned long supported_extclk_rates[] = { + [OV8865_19_2_MHZ] = 19200000, + [OV8865_24_MHZ] = 24000000, +}; + /* * General formulas for (array-centered) mode calculation: * - photo_array_width = 3296 @@ -632,11 +660,7 @@ struct ov8865_mode { unsigned int blc_anchor_right_start; unsigned int blc_anchor_right_end; - struct v4l2_fract frame_interval; - - const struct ov8865_pll1_config *pll1_config; - const struct ov8865_pll2_config *pll2_config; - const struct ov8865_sclk_config *sclk_config; + bool pll2_binning; const struct ov8865_register_value *register_values; unsigned int register_values_count; @@ -652,6 +676,9 @@ struct ov8865_state { struct ov8865_ctrls { struct v4l2_ctrl *link_freq; struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; struct v4l2_ctrl_handler handler; }; @@ -664,6 +691,9 @@ struct ov8865_sensor { struct regulator *avdd; struct regulator *dvdd; struct regulator *dovdd; + + unsigned long extclk_rate; + const struct ov8865_pll_configs *pll_configs; struct clk *extclk; struct v4l2_fwnode_endpoint endpoint; @@ -679,43 +709,70 @@ struct ov8865_sensor { /* Static definitions */ /* - * EXTCLK = 24 MHz * PHY_SCLK = 720 MHz * MIPI_PCLK = 90 MHz */ -static const struct ov8865_pll1_config ov8865_pll1_config_native = { - .pll_pre_div_half = 1, - .pll_pre_div = 0, - .pll_mul = 30, - .m_div = 1, - .mipi_div = 3, - .pclk_div = 1, - .sys_pre_div = 1, - .sys_div = 2, + +static const struct ov8865_pll1_config ov8865_pll1_config_native_19_2mhz = { + .pll_pre_div_half = 1, + .pll_pre_div = 2, + .pll_mul = 75, + .m_div = 1, + .mipi_div = 3, + .pclk_div = 1, + .sys_pre_div = 1, + .sys_div = 2, +}; + +static const struct ov8865_pll1_config ov8865_pll1_config_native_24mhz = { + .pll_pre_div_half = 1, + .pll_pre_div = 0, + .pll_mul = 30, + .m_div = 1, + .mipi_div = 3, + .pclk_div = 1, + .sys_pre_div = 1, + .sys_div = 2, }; /* - * EXTCLK = 24 MHz * DAC_CLK = 360 MHz * SCLK = 144 MHz */ -static const struct ov8865_pll2_config ov8865_pll2_config_native = { - .pll_pre_div_half = 1, - .pll_pre_div = 0, - .pll_mul = 30, - .dac_div = 2, - .sys_pre_div = 5, - .sys_div = 0, +static const struct ov8865_pll2_config ov8865_pll2_config_native_19_2mhz = { + .pll_pre_div_half = 1, + .pll_pre_div = 5, + .pll_mul = 75, + .dac_div = 1, + .sys_pre_div = 1, + .sys_div = 3, +}; + +static const struct ov8865_pll2_config ov8865_pll2_config_native_24mhz = { + .pll_pre_div_half = 1, + .pll_pre_div = 0, + .pll_mul = 30, + .dac_div = 2, + .sys_pre_div = 5, + .sys_div = 0, }; /* - * EXTCLK = 24 MHz * DAC_CLK = 360 MHz - * SCLK = 80 MHz + * SCLK = 72 MHz */ -static const struct ov8865_pll2_config ov8865_pll2_config_binning = { +static const struct ov8865_pll2_config ov8865_pll2_config_binning_19_2mhz = { + .pll_pre_div_half = 1, + .pll_pre_div = 2, + .pll_mul = 75, + .dac_div = 2, + .sys_pre_div = 10, + .sys_div = 0, +}; + +static const struct ov8865_pll2_config ov8865_pll2_config_binning_24mhz = { .pll_pre_div_half = 1, .pll_pre_div = 0, .pll_mul = 30, @@ -724,6 +781,23 @@ static const struct ov8865_pll2_config ov8865_pll2_config_binning = { .sys_div = 0, }; +static const struct ov8865_pll_configs ov8865_pll_configs_19_2mhz = { + .pll1_config = &ov8865_pll1_config_native_19_2mhz, + .pll2_config_native = &ov8865_pll2_config_native_19_2mhz, + .pll2_config_binning = &ov8865_pll2_config_binning_19_2mhz, +}; + +static const struct ov8865_pll_configs ov8865_pll_configs_24mhz = { + .pll1_config = &ov8865_pll1_config_native_24mhz, + .pll2_config_native = &ov8865_pll2_config_native_24mhz, + .pll2_config_binning = &ov8865_pll2_config_binning_24mhz, +}; + +static const struct ov8865_pll_configs *ov8865_pll_configs[] = { + &ov8865_pll_configs_19_2mhz, + &ov8865_pll_configs_24mhz, +}; + static const struct ov8865_sclk_config ov8865_sclk_config_native = { .sys_sel = 1, .sclk_sel = 0, @@ -890,7 +964,7 @@ static const struct ov8865_mode ov8865_modes[] = { { /* Horizontal */ .output_size_x = 3264, - .hts = 1944, + .hts = 3888, /* Vertical */ .output_size_y = 2448, @@ -929,13 +1003,8 @@ static const struct ov8865_mode ov8865_modes[] = { .blc_anchor_right_start = 1984, .blc_anchor_right_end = 2239, - /* Frame Interval */ - .frame_interval = { 1, 30 }, - /* PLL */ - .pll1_config = &ov8865_pll1_config_native, - .pll2_config = &ov8865_pll2_config_native, - .sclk_config = &ov8865_sclk_config_native, + .pll2_binning = false, /* Registers */ .register_values = ov8865_register_values_native, @@ -946,11 +1015,11 @@ static const struct ov8865_mode ov8865_modes[] = { { /* Horizontal */ .output_size_x = 3264, - .hts = 2582, + .hts = 3888, /* Vertical */ .output_size_y = 1836, - .vts = 2002, + .vts = 2470, .size_auto = true, .size_auto_boundary_x = 8, @@ -985,13 +1054,8 @@ static const struct ov8865_mode ov8865_modes[] = { .blc_anchor_right_start = 1984, .blc_anchor_right_end = 2239, - /* Frame Interval */ - .frame_interval = { 1, 30 }, - /* PLL */ - .pll1_config = &ov8865_pll1_config_native, - .pll2_config = &ov8865_pll2_config_native, - .sclk_config = &ov8865_sclk_config_native, + .pll2_binning = false, /* Registers */ .register_values = ov8865_register_values_native, @@ -1045,13 +1109,8 @@ static const struct ov8865_mode ov8865_modes[] = { .blc_anchor_right_start = 992, .blc_anchor_right_end = 1119, - /* Frame Interval */ - .frame_interval = { 1, 30 }, - /* PLL */ - .pll1_config = &ov8865_pll1_config_native, - .pll2_config = &ov8865_pll2_config_binning, - .sclk_config = &ov8865_sclk_config_native, + .pll2_binning = true, /* Registers */ .register_values = ov8865_register_values_binning, @@ -1111,13 +1170,8 @@ static const struct ov8865_mode ov8865_modes[] = { .blc_anchor_right_start = 992, .blc_anchor_right_end = 1119, - /* Frame Interval */ - .frame_interval = { 1, 90 }, - /* PLL */ - .pll1_config = &ov8865_pll1_config_native, - .pll2_config = &ov8865_pll2_config_binning, - .sclk_config = &ov8865_sclk_config_native, + .pll2_binning = true, /* Registers */ .register_values = ov8865_register_values_binning, @@ -1512,12 +1566,11 @@ static int ov8865_isp_configure(struct ov8865_sensor *sensor) static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor, const struct ov8865_mode *mode) { - const struct ov8865_pll1_config *config = mode->pll1_config; - unsigned long extclk_rate; + const struct ov8865_pll1_config *config; unsigned long pll1_rate; - extclk_rate = clk_get_rate(sensor->extclk); - pll1_rate = extclk_rate * config->pll_mul / config->pll_pre_div_half; + config = sensor->pll_configs->pll1_config; + pll1_rate = sensor->extclk_rate * config->pll_mul / config->pll_pre_div_half; switch (config->pll_pre_div) { case 0: @@ -1551,10 +1604,12 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor, const struct ov8865_mode *mode, u32 mbus_code) { - const struct ov8865_pll1_config *config = mode->pll1_config; + const struct ov8865_pll1_config *config; u8 value; int ret; + config = sensor->pll_configs->pll1_config; + switch (mbus_code) { case MEDIA_BUS_FMT_SBGGR10_1X10: value = OV8865_MIPI_BIT_SEL(10); @@ -1621,9 +1676,12 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor, static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor, const struct ov8865_mode *mode) { - const struct ov8865_pll2_config *config = mode->pll2_config; + const struct ov8865_pll2_config *config; int ret; + config = mode->pll2_binning ? sensor->pll_configs->pll2_config_binning : + sensor->pll_configs->pll2_config_native; + ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG, OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) | OV8865_PLL_CTRL12_DAC_DIV(config->dac_div)); @@ -1657,7 +1715,7 @@ static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor, static int ov8865_mode_sclk_configure(struct ov8865_sensor *sensor, const struct ov8865_mode *mode) { - const struct ov8865_sclk_config *config = mode->sclk_config; + const struct ov8865_sclk_config *config = &ov8865_sclk_config_native; int ret; ret = ov8865_write(sensor, OV8865_CLK_SEL0_REG, @@ -2052,9 +2110,11 @@ static int ov8865_mode_configure(struct ov8865_sensor *sensor, static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor, const struct ov8865_mode *mode) { - const struct ov8865_pll1_config *config = mode->pll1_config; + const struct ov8865_pll1_config *config; unsigned long pll1_rate; + config = sensor->pll_configs->pll1_config; + pll1_rate = ov8865_mode_pll1_rate(sensor, mode); return pll1_rate / config->m_div / 2; @@ -2066,6 +2126,9 @@ static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure) { int ret; + /* The sensor stores exposure in units of 1/16th of a line */ + exposure *= 16; + ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_HH_REG, OV8865_EXPOSURE_CTRL_HH(exposure)); if (ret) @@ -2082,7 +2145,7 @@ static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure) /* Gain */ -static int ov8865_gain_configure(struct ov8865_sensor *sensor, u32 gain) +static int ov8865_analog_gain_configure(struct ov8865_sensor *sensor, u32 gain) { int ret; @@ -2157,6 +2220,20 @@ static int ov8865_test_pattern_configure(struct ov8865_sensor *sensor, ov8865_test_pattern_bits[index]); } +/* Blanking */ + +static int ov8865_vts_configure(struct ov8865_sensor *sensor, u32 vblank) +{ + u16 vts = sensor->state.mode->output_size_y + vblank; + int ret; + + ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(vts)); + if (ret) + return ret; + + return ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(vts)); +} + /* State */ static int ov8865_state_mipi_configure(struct ov8865_sensor *sensor, @@ -2330,27 +2407,27 @@ static int ov8865_sensor_power(struct ov8865_sensor *sensor, bool on) if (ret) { dev_err(sensor->dev, "failed to enable DOVDD regulator\n"); - goto disable; + return ret; } ret = regulator_enable(sensor->avdd); if (ret) { dev_err(sensor->dev, "failed to enable AVDD regulator\n"); - goto disable; + goto disable_dovdd; } ret = regulator_enable(sensor->dvdd); if (ret) { dev_err(sensor->dev, "failed to enable DVDD regulator\n"); - goto disable; + goto disable_avdd; } ret = clk_prepare_enable(sensor->extclk); if (ret) { dev_err(sensor->dev, "failed to enable EXTCLK clock\n"); - goto disable; + goto disable_dvdd; } gpiod_set_value_cansleep(sensor->reset, 0); @@ -2359,14 +2436,16 @@ static int ov8865_sensor_power(struct ov8865_sensor *sensor, bool on) /* Time to enter streaming mode according to power timings. */ usleep_range(10000, 12000); } else { -disable: gpiod_set_value_cansleep(sensor->powerdown, 1); gpiod_set_value_cansleep(sensor->reset, 1); clk_disable_unprepare(sensor->extclk); +disable_dvdd: regulator_disable(sensor->dvdd); +disable_avdd: regulator_disable(sensor->avdd); +disable_dovdd: regulator_disable(sensor->dovdd); } @@ -2382,6 +2461,20 @@ static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl) unsigned int index; int ret; + /* If VBLANK is altered we need to update exposure to compensate */ + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max; + + exposure_max = sensor->state.mode->output_size_y + ctrl->val - + OV8865_INTEGRATION_TIME_MARGIN; + __v4l2_ctrl_modify_range(sensor->ctrls.exposure, + sensor->ctrls.exposure->minimum, + exposure_max, + sensor->ctrls.exposure->step, + min(sensor->ctrls.exposure->val, + exposure_max)); + } + /* Wait for the sensor to be on before setting controls. */ if (pm_runtime_suspended(sensor->dev)) return 0; @@ -2392,8 +2485,8 @@ static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl) if (ret) return ret; break; - case V4L2_CID_GAIN: - ret = ov8865_gain_configure(sensor, ctrl->val); + case V4L2_CID_ANALOGUE_GAIN: + ret = ov8865_analog_gain_configure(sensor, ctrl->val); if (ret) return ret; break; @@ -2408,6 +2501,8 @@ static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TEST_PATTERN: index = (unsigned int)ctrl->val; return ov8865_test_pattern_configure(sensor, index); + case V4L2_CID_VBLANK: + return ov8865_vts_configure(sensor, ctrl->val); default: return -EINVAL; } @@ -2424,6 +2519,10 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) struct ov8865_ctrls *ctrls = &sensor->ctrls; struct v4l2_ctrl_handler *handler = &ctrls->handler; const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops; + const struct ov8865_mode *mode = &ov8865_modes[0]; + struct v4l2_fwnode_device_properties props; + unsigned int vblank_max, vblank_def; + unsigned int hblank; int ret; v4l2_ctrl_handler_init(handler, 32); @@ -2433,12 +2532,13 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) /* Exposure */ - v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 16, 1048575, 16, - 512); + ctrls->exposure = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 2, + 65535, 1, 32); /* Gain */ - v4l2_ctrl_new_std(handler, ops, V4L2_CID_GAIN, 128, 8191, 128, 128); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_ANALOGUE_GAIN, 128, 2048, 128, + 128); /* White Balance */ @@ -2459,6 +2559,20 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) ARRAY_SIZE(ov8865_test_pattern_menu) - 1, 0, 0, ov8865_test_pattern_menu); + /* Blanking */ + hblank = mode->hts - mode->output_size_x; + ctrls->hblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HBLANK, hblank, + hblank, 1, hblank); + + if (ctrls->hblank) + ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_max = OV8865_TIMING_MAX_VTS - mode->output_size_y; + vblank_def = mode->vts - mode->output_size_y; + ctrls->vblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_VBLANK, + OV8865_TIMING_MIN_VTS, vblank_max, 1, + vblank_def); + /* MIPI CSI-2 */ ctrls->link_freq = @@ -2470,6 +2584,15 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); + /* set properties from fwnode (e.g. rotation, orientation) */ + ret = v4l2_fwnode_device_parse(sensor->dev, &props); + if (ret) + goto error_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(handler, ops, &props); + if (ret) + goto error_ctrls; + if (handler->error) { ret = handler->error; goto error_ctrls; @@ -2522,11 +2645,18 @@ static int ov8865_g_frame_interval(struct v4l2_subdev *subdev, { struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); const struct ov8865_mode *mode; + unsigned int framesize; + unsigned int fps; mutex_lock(&sensor->mutex); mode = sensor->state.mode; - interval->interval = mode->frame_interval; + framesize = mode->hts * (mode->output_size_y + + sensor->ctrls.vblank->val); + fps = DIV_ROUND_CLOSEST(sensor->ctrls.pixel_rate->val, framesize); + + interval->interval.numerator = 1; + interval->interval.denominator = fps; mutex_unlock(&sensor->mutex); @@ -2599,7 +2729,9 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *mbus_format = &format->format; const struct ov8865_mode *mode; u32 mbus_code = 0; + unsigned int hblank; unsigned int index; + int exposure_max; int ret = 0; mutex_lock(&sensor->mutex); @@ -2639,6 +2771,21 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev, sensor->state.mbus_code != mbus_code) ret = ov8865_state_configure(sensor, mode, mbus_code); + __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV8865_TIMING_MIN_VTS, + OV8865_TIMING_MAX_VTS - mode->output_size_y, + 1, mode->vts - mode->output_size_y); + + hblank = mode->hts - mode->output_size_x; + __v4l2_ctrl_modify_range(sensor->ctrls.hblank, hblank, hblank, 1, + hblank); + + exposure_max = mode->vts - OV8865_INTEGRATION_TIME_MARGIN; + __v4l2_ctrl_modify_range(sensor->ctrls.exposure, + sensor->ctrls.exposure->minimum, exposure_max, + sensor->ctrls.exposure->step, + min(sensor->ctrls.exposure->val, + exposure_max)); + complete: mutex_unlock(&sensor->mutex); @@ -2662,37 +2809,55 @@ static int ov8865_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int ov8865_enum_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_interval_enum *interval_enum) +static void +__ov8865_get_pad_crop(struct ov8865_sensor *sensor, + struct v4l2_subdev_state *state, unsigned int pad, + enum v4l2_subdev_format_whence which, struct v4l2_rect *r) { - const struct ov8865_mode *mode = NULL; - unsigned int mode_index; - unsigned int interval_index; - - if (interval_enum->index > 0) - return -EINVAL; - /* - * Multiple modes with the same dimensions may have different frame - * intervals, so look up each relevant mode. - */ - for (mode_index = 0, interval_index = 0; - mode_index < ARRAY_SIZE(ov8865_modes); mode_index++) { - mode = &ov8865_modes[mode_index]; - - if (mode->output_size_x == interval_enum->width && - mode->output_size_y == interval_enum->height) { - if (interval_index == interval_enum->index) - break; + const struct ov8865_mode *mode = sensor->state.mode; - interval_index++; - } + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + *r = *v4l2_subdev_get_try_crop(&sensor->subdev, state, pad); + break; + case V4L2_SUBDEV_FORMAT_ACTIVE: + r->height = mode->output_size_y; + r->width = mode->output_size_x; + r->top = (OV8865_NATIVE_HEIGHT - mode->output_size_y) / 2; + r->left = (OV8865_NATIVE_WIDTH - mode->output_size_x) / 2; + break; } +} - if (mode_index == ARRAY_SIZE(ov8865_modes)) - return -EINVAL; +static int ov8865_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - interval_enum->interval = mode->frame_interval; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + mutex_lock(&sensor->mutex); + __ov8865_get_pad_crop(sensor, state, sel->pad, + sel->which, &sel->r); + mutex_unlock(&sensor->mutex); + break; + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV8865_NATIVE_WIDTH; + sel->r.height = OV8865_NATIVE_HEIGHT; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.top = OV8865_ACTIVE_START_TOP; + sel->r.left = OV8865_ACTIVE_START_LEFT; + sel->r.width = OV8865_ACTIVE_WIDTH; + sel->r.height = OV8865_ACTIVE_HEIGHT; + break; + default: + return -EINVAL; + } return 0; } @@ -2702,7 +2867,8 @@ static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = { .get_fmt = ov8865_get_fmt, .set_fmt = ov8865_set_fmt, .enum_frame_size = ov8865_enum_frame_size, - .enum_frame_interval = ov8865_enum_frame_interval, + .get_selection = ov8865_get_selection, + .set_selection = ov8865_get_selection, }; static const struct v4l2_subdev_ops ov8865_subdev_ops = { @@ -2782,7 +2948,8 @@ static int ov8865_probe(struct i2c_client *client) struct ov8865_sensor *sensor; struct v4l2_subdev *subdev; struct media_pad *pad; - unsigned long rate; + unsigned int rate = 0; + unsigned int i; int ret; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); @@ -2792,13 +2959,31 @@ static int ov8865_probe(struct i2c_client *client) sensor->dev = dev; sensor->i2c_client = client; + /* Regulators */ + + /* DVDD: digital core */ + sensor->dvdd = devm_regulator_get(dev, "dvdd"); + if (IS_ERR(sensor->dvdd)) + return dev_err_probe(dev, PTR_ERR(sensor->dvdd), + "cannot get DVDD regulator\n"); + + /* DOVDD: digital I/O */ + sensor->dovdd = devm_regulator_get(dev, "dovdd"); + if (IS_ERR(sensor->dovdd)) + return dev_err_probe(dev, PTR_ERR(sensor->dovdd), + "cannot get DOVDD regulator\n"); + + /* AVDD: analog */ + sensor->avdd = devm_regulator_get(dev, "avdd"); + if (IS_ERR(sensor->avdd)) + return dev_err_probe(dev, PTR_ERR(sensor->avdd), + "cannot get AVDD (analog) regulator\n"); + /* Graph Endpoint */ handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); - if (!handle) { - dev_err(dev, "unable to find endpoint node\n"); - return -EINVAL; - } + if (!handle) + return -EPROBE_DEFER; sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY; @@ -2824,48 +3009,54 @@ static int ov8865_probe(struct i2c_client *client) goto error_endpoint; } - /* Regulators */ - - /* DVDD: digital core */ - sensor->dvdd = devm_regulator_get(dev, "dvdd"); - if (IS_ERR(sensor->dvdd)) { - dev_err(dev, "cannot get DVDD (digital core) regulator\n"); - ret = PTR_ERR(sensor->dvdd); - goto error_endpoint; - } + /* External Clock */ - /* DOVDD: digital I/O */ - sensor->dovdd = devm_regulator_get(dev, "dovdd"); - if (IS_ERR(sensor->dovdd)) { - dev_err(dev, "cannot get DOVDD (digital I/O) regulator\n"); - ret = PTR_ERR(sensor->dovdd); + sensor->extclk = devm_clk_get(dev, NULL); + if (PTR_ERR(sensor->extclk) == -ENOENT) { + dev_info(dev, "no external clock found, continuing...\n"); + sensor->extclk = NULL; + } else if (IS_ERR(sensor->extclk)) { + dev_err(dev, "failed to get external clock\n"); + ret = PTR_ERR(sensor->extclk); goto error_endpoint; } - /* AVDD: analog */ - sensor->avdd = devm_regulator_get(dev, "avdd"); - if (IS_ERR(sensor->avdd)) { - dev_err(dev, "cannot get AVDD (analog) regulator\n"); - ret = PTR_ERR(sensor->avdd); - goto error_endpoint; + /* + * We could have either a 24MHz or 19.2MHz clock rate from either dt or + * ACPI...but we also need to support the weird IPU3 case which will + * have an external clock AND a clock-frequency property. Check for the + * clock-frequency property and if found, set that rate if we managed + * to acquire a clock. This should cover the ACPI case. If the system + * uses devicetree then the configured rate should already be set, so + * we can just read it. + */ + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &rate); + if (!ret && sensor->extclk) { + ret = clk_set_rate(sensor->extclk, rate); + if (ret) + return dev_err_probe(dev, ret, + "failed to set clock rate\n"); + } else if (ret && !sensor->extclk) { + return dev_err_probe(dev, ret, "invalid clock config\n"); } - /* External Clock */ + sensor->extclk_rate = rate ? rate : clk_get_rate(sensor->extclk); - sensor->extclk = devm_clk_get(dev, NULL); - if (IS_ERR(sensor->extclk)) { - dev_err(dev, "failed to get external clock\n"); - ret = PTR_ERR(sensor->extclk); - goto error_endpoint; + for (i = 0; i < ARRAY_SIZE(supported_extclk_rates); i++) { + if (sensor->extclk_rate == supported_extclk_rates[i]) + break; } - rate = clk_get_rate(sensor->extclk); - if (rate != OV8865_EXTCLK_RATE) { - dev_err(dev, "clock rate %lu Hz is unsupported\n", rate); + if (i == ARRAY_SIZE(supported_extclk_rates)) { + dev_err(dev, "clock rate %lu Hz is unsupported\n", + sensor->extclk_rate); ret = -EINVAL; goto error_endpoint; } + sensor->pll_configs = ov8865_pll_configs[i]; + /* Subdev, entity and pad */ subdev = &sensor->subdev; @@ -2891,14 +3082,16 @@ static int ov8865_probe(struct i2c_client *client) if (ret) goto error_mutex; + mutex_lock(&sensor->mutex); ret = ov8865_state_init(sensor); + mutex_unlock(&sensor->mutex); if (ret) goto error_ctrls; /* Runtime PM */ - pm_runtime_enable(sensor->dev); pm_runtime_set_suspended(sensor->dev); + pm_runtime_enable(sensor->dev); /* V4L2 subdev register */ @@ -2946,6 +3139,12 @@ static const struct dev_pm_ops ov8865_pm_ops = { SET_RUNTIME_PM_OPS(ov8865_suspend, ov8865_resume, NULL) }; +static const struct acpi_device_id ov8865_acpi_match[] = { + {"INT347A"}, + { } +}; +MODULE_DEVICE_TABLE(acpi, ov8865_acpi_match); + static const struct of_device_id ov8865_of_match[] = { { .compatible = "ovti,ov8865" }, { } @@ -2956,6 +3155,7 @@ static struct i2c_driver ov8865_driver = { .driver = { .name = "ov8865", .of_match_table = ov8865_of_match, + .acpi_match_table = ov8865_acpi_match, .pm = &ov8865_pm_ops, }, .probe_new = ov8865_probe, |