diff options
Diffstat (limited to 'drivers/media/i2c/ov7251.c')
| -rw-r--r-- | drivers/media/i2c/ov7251.c | 809 |
1 files changed, 565 insertions, 244 deletions
diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index 0c10203f822b..27afc3fc0175 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -14,6 +14,8 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/types.h> @@ -41,6 +43,29 @@ #define OV7251_TIMING_FORMAT2_MIRROR BIT(2) #define OV7251_PRE_ISP_00 0x5e00 #define OV7251_PRE_ISP_00_TEST_PATTERN BIT(7) +#define OV7251_PLL1_PRE_DIV_REG 0x30b4 +#define OV7251_PLL1_MULT_REG 0x30b3 +#define OV7251_PLL1_DIVIDER_REG 0x30b1 +#define OV7251_PLL1_PIX_DIV_REG 0x30b0 +#define OV7251_PLL1_MIPI_DIV_REG 0x30b5 +#define OV7251_PLL2_PRE_DIV_REG 0x3098 +#define OV7251_PLL2_MULT_REG 0x3099 +#define OV7251_PLL2_DIVIDER_REG 0x309d +#define OV7251_PLL2_SYS_DIV_REG 0x309a +#define OV7251_PLL2_ADC_DIV_REG 0x309b + +#define OV7251_NATIVE_WIDTH 656 +#define OV7251_NATIVE_HEIGHT 496 +#define OV7251_ACTIVE_START_LEFT 4 +#define OV7251_ACTIVE_START_TOP 4 +#define OV7251_ACTIVE_WIDTH 648 +#define OV7251_ACTIVE_HEIGHT 488 + +#define OV7251_FIXED_PPL 928 +#define OV7251_TIMING_VTS_REG 0x380e +#define OV7251_TIMING_MIN_VTS 1 +#define OV7251_TIMING_MAX_VTS 0xffff +#define OV7251_INTEGRATION_MARGIN 20 struct reg_value { u16 reg; @@ -50,6 +75,7 @@ struct reg_value { struct ov7251_mode_info { u32 width; u32 height; + u32 vts; const struct reg_value *data; u32 data_size; u32 pixel_clock; @@ -59,6 +85,43 @@ struct ov7251_mode_info { struct v4l2_fract timeperframe; }; +struct ov7251_pll1_cfg { + unsigned int pre_div; + unsigned int mult; + unsigned int div; + unsigned int pix_div; + unsigned int mipi_div; +}; + +struct ov7251_pll2_cfg { + unsigned int pre_div; + unsigned int mult; + unsigned int div; + unsigned int sys_div; + unsigned int adc_div; +}; + +/* + * Rubbish ordering, but only PLL1 needs to have a separate configuration per + * link frequency and the array member needs to be last. + */ +struct ov7251_pll_cfgs { + const struct ov7251_pll2_cfg *pll2; + const struct ov7251_pll1_cfg *pll1[]; +}; + +enum xclk_rate { + OV7251_19_2_MHZ, + OV7251_24_MHZ, + OV7251_NUM_SUPPORTED_RATES +}; + +enum supported_link_freqs { + OV7251_LINK_FREQ_240_MHZ, + OV7251_LINK_FREQ_319_2_MHZ, + OV7251_NUM_SUPPORTED_LINK_FREQS +}; + struct ov7251 { struct i2c_client *i2c_client; struct device *dev; @@ -74,6 +137,8 @@ struct ov7251 { struct regulator *core_regulator; struct regulator *analog_regulator; + const struct ov7251_pll_cfgs *pll_cfgs; + enum supported_link_freqs link_freq_idx; const struct ov7251_mode_info *current_mode; struct v4l2_ctrl_handler ctrls; @@ -81,6 +146,8 @@ struct ov7251 { struct v4l2_ctrl *link_freq; struct v4l2_ctrl *exposure; struct v4l2_ctrl *gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; /* Cached register values */ u8 aec_pk_manual; @@ -99,6 +166,75 @@ static inline struct ov7251 *to_ov7251(struct v4l2_subdev *sd) return container_of(sd, struct ov7251, sd); } +static const struct ov7251_pll1_cfg ov7251_pll1_cfg_19_2_mhz_240_mhz = { + .pre_div = 0x03, + .mult = 0x4b, + .div = 0x01, + .pix_div = 0x0a, + .mipi_div = 0x05, +}; + +static const struct ov7251_pll1_cfg ov7251_pll1_cfg_19_2_mhz_319_2_mhz = { + .pre_div = 0x01, + .mult = 0x85, + .div = 0x04, + .pix_div = 0x0a, + .mipi_div = 0x05, +}; + +static const struct ov7251_pll1_cfg ov7251_pll1_cfg_24_mhz_240_mhz = { + .pre_div = 0x03, + .mult = 0x64, + .div = 0x01, + .pix_div = 0x0a, + .mipi_div = 0x05, +}; + +static const struct ov7251_pll1_cfg ov7251_pll1_cfg_24_mhz_319_2_mhz = { + .pre_div = 0x05, + .mult = 0x85, + .div = 0x02, + .pix_div = 0x0a, + .mipi_div = 0x05, +}; + +static const struct ov7251_pll2_cfg ov7251_pll2_cfg_19_2_mhz = { + .pre_div = 0x04, + .mult = 0x32, + .div = 0x00, + .sys_div = 0x05, + .adc_div = 0x04, +}; + +static const struct ov7251_pll2_cfg ov7251_pll2_cfg_24_mhz = { + .pre_div = 0x04, + .mult = 0x28, + .div = 0x00, + .sys_div = 0x05, + .adc_div = 0x04, +}; + +static const struct ov7251_pll_cfgs ov7251_pll_cfgs_19_2_mhz = { + .pll2 = &ov7251_pll2_cfg_19_2_mhz, + .pll1 = { + [OV7251_LINK_FREQ_240_MHZ] = &ov7251_pll1_cfg_19_2_mhz_240_mhz, + [OV7251_LINK_FREQ_319_2_MHZ] = &ov7251_pll1_cfg_19_2_mhz_319_2_mhz, + }, +}; + +static const struct ov7251_pll_cfgs ov7251_pll_cfgs_24_mhz = { + .pll2 = &ov7251_pll2_cfg_24_mhz, + .pll1 = { + [OV7251_LINK_FREQ_240_MHZ] = &ov7251_pll1_cfg_24_mhz_240_mhz, + [OV7251_LINK_FREQ_319_2_MHZ] = &ov7251_pll1_cfg_24_mhz_319_2_mhz, + }, +}; + +static const struct ov7251_pll_cfgs *ov7251_pll_cfgs[] = { + [OV7251_19_2_MHZ] = &ov7251_pll_cfgs_19_2_mhz, + [OV7251_24_MHZ] = &ov7251_pll_cfgs_24_mhz, +}; + static const struct reg_value ov7251_global_init_setting[] = { { 0x0103, 0x01 }, { 0x303b, 0x02 }, @@ -117,16 +253,6 @@ static const struct reg_value ov7251_setting_vga_30fps[] = { { 0x301c, 0xf0 }, { 0x3023, 0x05 }, { 0x3037, 0xf0 }, - { 0x3098, 0x04 }, /* pll2 pre divider */ - { 0x3099, 0x28 }, /* pll2 multiplier */ - { 0x309a, 0x05 }, /* pll2 sys divider */ - { 0x309b, 0x04 }, /* pll2 adc divider */ - { 0x309d, 0x00 }, /* pll2 divider */ - { 0x30b0, 0x0a }, /* pll1 pix divider */ - { 0x30b1, 0x01 }, /* pll1 divider */ - { 0x30b3, 0x64 }, /* pll1 multiplier */ - { 0x30b4, 0x03 }, /* pll1 pre divider */ - { 0x30b5, 0x05 }, /* pll1 mipi divider */ { 0x3106, 0xda }, { 0x3503, 0x07 }, { 0x3509, 0x10 }, @@ -255,16 +381,6 @@ static const struct reg_value ov7251_setting_vga_60fps[] = { { 0x301c, 0x00 }, { 0x3023, 0x05 }, { 0x3037, 0xf0 }, - { 0x3098, 0x04 }, /* pll2 pre divider */ - { 0x3099, 0x28 }, /* pll2 multiplier */ - { 0x309a, 0x05 }, /* pll2 sys divider */ - { 0x309b, 0x04 }, /* pll2 adc divider */ - { 0x309d, 0x00 }, /* pll2 divider */ - { 0x30b0, 0x0a }, /* pll1 pix divider */ - { 0x30b1, 0x01 }, /* pll1 divider */ - { 0x30b3, 0x64 }, /* pll1 multiplier */ - { 0x30b4, 0x03 }, /* pll1 pre divider */ - { 0x30b5, 0x05 }, /* pll1 mipi divider */ { 0x3106, 0xda }, { 0x3503, 0x07 }, { 0x3509, 0x10 }, @@ -393,16 +509,6 @@ static const struct reg_value ov7251_setting_vga_90fps[] = { { 0x301c, 0x00 }, { 0x3023, 0x05 }, { 0x3037, 0xf0 }, - { 0x3098, 0x04 }, /* pll2 pre divider */ - { 0x3099, 0x28 }, /* pll2 multiplier */ - { 0x309a, 0x05 }, /* pll2 sys divider */ - { 0x309b, 0x04 }, /* pll2 adc divider */ - { 0x309d, 0x00 }, /* pll2 divider */ - { 0x30b0, 0x0a }, /* pll1 pix divider */ - { 0x30b1, 0x01 }, /* pll1 divider */ - { 0x30b3, 0x64 }, /* pll1 multiplier */ - { 0x30b4, 0x03 }, /* pll1 pre divider */ - { 0x30b5, 0x05 }, /* pll1 mipi divider */ { 0x3106, 0xda }, { 0x3503, 0x07 }, { 0x3509, 0x10 }, @@ -518,18 +624,28 @@ static const struct reg_value ov7251_setting_vga_90fps[] = { { 0x5001, 0x80 }, }; +static const unsigned long supported_xclk_rates[] = { + [OV7251_19_2_MHZ] = 19200000, + [OV7251_24_MHZ] = 24000000, +}; + static const s64 link_freq[] = { - 240000000, + [OV7251_LINK_FREQ_240_MHZ] = 240000000, + [OV7251_LINK_FREQ_319_2_MHZ] = 319200000, +}; + +static const s64 pixel_rates[] = { + [OV7251_LINK_FREQ_240_MHZ] = 48000000, + [OV7251_LINK_FREQ_319_2_MHZ] = 63840000, }; static const struct ov7251_mode_info ov7251_mode_info_data[] = { { .width = 640, .height = 480, + .vts = 1724, .data = ov7251_setting_vga_30fps, .data_size = ARRAY_SIZE(ov7251_setting_vga_30fps), - .pixel_clock = 48000000, - .link_freq = 0, /* an index in link_freq[] */ .exposure_max = 1704, .exposure_def = 504, .timeperframe = { @@ -540,10 +656,9 @@ static const struct ov7251_mode_info ov7251_mode_info_data[] = { { .width = 640, .height = 480, + .vts = 860, .data = ov7251_setting_vga_60fps, .data_size = ARRAY_SIZE(ov7251_setting_vga_60fps), - .pixel_clock = 48000000, - .link_freq = 0, /* an index in link_freq[] */ .exposure_max = 840, .exposure_def = 504, .timeperframe = { @@ -554,10 +669,9 @@ static const struct ov7251_mode_info ov7251_mode_info_data[] = { { .width = 640, .height = 480, + .vts = 572, .data = ov7251_setting_vga_90fps, .data_size = ARRAY_SIZE(ov7251_setting_vga_90fps), - .pixel_clock = 48000000, - .link_freq = 0, /* an index in link_freq[] */ .exposure_max = 552, .exposure_def = 504, .timeperframe = { @@ -691,6 +805,63 @@ static int ov7251_read_reg(struct ov7251 *ov7251, u16 reg, u8 *val) return 0; } +static int ov7251_pll_configure(struct ov7251 *ov7251) +{ + const struct ov7251_pll_cfgs *configs; + int ret; + + configs = ov7251->pll_cfgs; + + ret = ov7251_write_reg(ov7251, OV7251_PLL1_PRE_DIV_REG, + configs->pll1[ov7251->link_freq_idx]->pre_div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL1_MULT_REG, + configs->pll1[ov7251->link_freq_idx]->mult); + if (ret < 0) + return ret; + ret = ov7251_write_reg(ov7251, OV7251_PLL1_DIVIDER_REG, + configs->pll1[ov7251->link_freq_idx]->div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL1_PIX_DIV_REG, + configs->pll1[ov7251->link_freq_idx]->pix_div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL1_MIPI_DIV_REG, + configs->pll1[ov7251->link_freq_idx]->mipi_div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL2_PRE_DIV_REG, + configs->pll2->pre_div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL2_MULT_REG, + configs->pll2->mult); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL2_DIVIDER_REG, + configs->pll2->div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL2_SYS_DIV_REG, + configs->pll2->sys_div); + if (ret < 0) + return ret; + + ret = ov7251_write_reg(ov7251, OV7251_PLL2_ADC_DIV_REG, + configs->pll2->adc_div); + + return ret; +} + static int ov7251_set_exposure(struct ov7251 *ov7251, s32 exposure) { u16 reg; @@ -732,8 +903,11 @@ static int ov7251_set_register_array(struct ov7251 *ov7251, return 0; } -static int ov7251_set_power_on(struct ov7251 *ov7251) +static int ov7251_set_power_on(struct device *dev) { + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov7251 *ov7251 = to_ov7251(sd); int ret; u32 wait_us; @@ -748,6 +922,8 @@ static int ov7251_set_power_on(struct ov7251 *ov7251) return ret; } + usleep_range(1000, 1100); + gpiod_set_value_cansleep(ov7251->enable_gpio, 1); /* wait at least 65536 external clock cycles */ @@ -755,51 +931,31 @@ static int ov7251_set_power_on(struct ov7251 *ov7251) DIV_ROUND_UP(ov7251->xclk_freq, 1000)); usleep_range(wait_us, wait_us + 1000); - return 0; -} + ret = ov7251_set_register_array(ov7251, + ov7251_global_init_setting, + 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; + } -static void ov7251_set_power_off(struct ov7251 *ov7251) -{ - clk_disable_unprepare(ov7251->xclk); - gpiod_set_value_cansleep(ov7251->enable_gpio, 0); - ov7251_regulators_disable(ov7251); + return ret; } -static int ov7251_s_power(struct v4l2_subdev *sd, int on) +static int ov7251_set_power_off(struct device *dev) { + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov7251 *ov7251 = to_ov7251(sd); - int ret = 0; - - mutex_lock(&ov7251->lock); - - /* If the power state is not modified - no work to do. */ - if (ov7251->power_on == !!on) - goto exit; - - if (on) { - ret = ov7251_set_power_on(ov7251); - if (ret < 0) - goto exit; - - ret = ov7251_set_register_array(ov7251, - ov7251_global_init_setting, - ARRAY_SIZE(ov7251_global_init_setting)); - if (ret < 0) { - dev_err(ov7251->dev, "could not set init registers\n"); - ov7251_set_power_off(ov7251); - goto exit; - } - - ov7251->power_on = true; - } else { - ov7251_set_power_off(ov7251); - ov7251->power_on = false; - } -exit: - mutex_unlock(&ov7251->lock); + clk_disable_unprepare(ov7251->xclk); + gpiod_set_value_cansleep(ov7251->enable_gpio, 0); + ov7251_regulators_disable(ov7251); - return ret; + return 0; } static int ov7251_set_hflip(struct ov7251 *ov7251, s32 value) @@ -858,15 +1014,39 @@ static const char * const ov7251_test_pattern_menu[] = { "Vertical Pattern Bars", }; +static int ov7251_vts_configure(struct ov7251 *ov7251, s32 vblank) +{ + u8 vts[2]; + + vts[0] = ((ov7251->current_mode->height + vblank) & 0xff00) >> 8; + vts[1] = ((ov7251->current_mode->height + vblank) & 0x00ff); + + return ov7251_write_seq_regs(ov7251, OV7251_TIMING_VTS_REG, vts, 2); +} + static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl) { struct ov7251 *ov7251 = container_of(ctrl->handler, struct ov7251, ctrls); int ret; + /* If VBLANK is altered we need to update exposure to compensate */ + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max; + + exposure_max = ov7251->current_mode->height + ctrl->val - + OV7251_INTEGRATION_MARGIN; + __v4l2_ctrl_modify_range(ov7251->exposure, + ov7251->exposure->minimum, + exposure_max, + ov7251->exposure->step, + min(ov7251->exposure->val, + exposure_max)); + } + /* v4l2_ctrl_lock() locks our mutex */ - if (!ov7251->power_on) + if (!pm_runtime_get_if_in_use(ov7251->dev)) return 0; switch (ctrl->id) { @@ -885,11 +1065,16 @@ static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VFLIP: ret = ov7251_set_vflip(ov7251, ctrl->val); break; + case V4L2_CID_VBLANK: + ret = ov7251_vts_configure(ov7251, ctrl->val); + break; default: ret = -EINVAL; break; } + pm_runtime_put(ov7251->dev); + return ret; } @@ -898,7 +1083,7 @@ static const struct v4l2_ctrl_ops ov7251_ctrl_ops = { }; static int ov7251_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -910,7 +1095,7 @@ static int ov7251_enum_mbus_code(struct v4l2_subdev *sd, } static int ov7251_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->code != MEDIA_BUS_FMT_Y10_1X10) @@ -928,7 +1113,7 @@ static int ov7251_enum_frame_size(struct v4l2_subdev *subdev, } static int ov7251_enum_frame_ival(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { unsigned int index = fie->index; @@ -950,13 +1135,13 @@ static int ov7251_enum_frame_ival(struct v4l2_subdev *subdev, static struct v4l2_mbus_framefmt * __ov7251_get_pad_format(struct ov7251 *ov7251, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov7251->sd, cfg, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov7251->fmt; default: @@ -965,13 +1150,14 @@ __ov7251_get_pad_format(struct ov7251 *ov7251, } static int ov7251_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov7251 *ov7251 = to_ov7251(sd); mutex_lock(&ov7251->lock); - format->format = *__ov7251_get_pad_format(ov7251, cfg, format->pad, + format->format = *__ov7251_get_pad_format(ov7251, sd_state, + format->pad, format->which); mutex_unlock(&ov7251->lock); @@ -979,12 +1165,13 @@ static int ov7251_get_format(struct v4l2_subdev *sd, } static struct v4l2_rect * -__ov7251_get_pad_crop(struct ov7251 *ov7251, struct v4l2_subdev_pad_config *cfg, +__ov7251_get_pad_crop(struct ov7251 *ov7251, + struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov7251->sd, cfg, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov7251->crop; default: @@ -1027,18 +1214,20 @@ ov7251_find_mode_by_ival(struct ov7251 *ov7251, struct v4l2_fract *timeperframe) } static int ov7251_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct ov7251 *ov7251 = to_ov7251(sd); struct v4l2_mbus_framefmt *__format; + int vblank_max, vblank_def; struct v4l2_rect *__crop; const struct ov7251_mode_info *new_mode; int ret = 0; mutex_lock(&ov7251->lock); - __crop = __ov7251_get_pad_crop(ov7251, cfg, format->pad, format->which); + __crop = __ov7251_get_pad_crop(ov7251, sd_state, format->pad, + format->which); new_mode = v4l2_find_nearest_size(ov7251_mode_info_data, ARRAY_SIZE(ov7251_mode_info_data), @@ -1049,16 +1238,6 @@ static int ov7251_set_format(struct v4l2_subdev *sd, __crop->height = new_mode->height; if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock, - new_mode->pixel_clock); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq, - new_mode->link_freq); - if (ret < 0) - goto exit; - ret = __v4l2_ctrl_modify_range(ov7251->exposure, 1, new_mode->exposure_max, 1, new_mode->exposure_def); @@ -1074,10 +1253,18 @@ static int ov7251_set_format(struct v4l2_subdev *sd, if (ret < 0) goto exit; + vblank_max = OV7251_TIMING_MAX_VTS - new_mode->height; + vblank_def = new_mode->vts - new_mode->height; + ret = __v4l2_ctrl_modify_range(ov7251->vblank, + OV7251_TIMING_MIN_VTS, + vblank_max, 1, vblank_def); + if (ret < 0) + goto exit; + ov7251->current_mode = new_mode; } - __format = __ov7251_get_pad_format(ov7251, cfg, format->pad, + __format = __ov7251_get_pad_format(ov7251, sd_state, format->pad, format->which); __format->width = __crop->width; __format->height = __crop->height; @@ -1097,36 +1284,52 @@ exit: return ret; } -static int ov7251_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) +static int ov7251_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { - .which = cfg ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, .format = { .width = 640, .height = 480 } }; - ov7251_set_format(subdev, cfg, &fmt); + ov7251_set_format(subdev, sd_state, &fmt); return 0; } static int ov7251_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { struct ov7251 *ov7251 = to_ov7251(sd); - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - + switch (sel->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: mutex_lock(&ov7251->lock); - sel->r = *__ov7251_get_pad_crop(ov7251, cfg, sel->pad, - sel->which); - mutex_unlock(&ov7251->lock); + sel->r = *__ov7251_get_pad_crop(ov7251, sd_state, sel->pad, + sel->which); + mutex_unlock(&ov7251->lock); + break; + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV7251_NATIVE_WIDTH; + sel->r.height = OV7251_NATIVE_HEIGHT; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = OV7251_ACTIVE_START_TOP; + sel->r.left = OV7251_ACTIVE_START_LEFT; + sel->r.width = OV7251_ACTIVE_WIDTH; + sel->r.height = OV7251_ACTIVE_HEIGHT; + break; + default: + return -EINVAL; + } return 0; } @@ -1139,6 +1342,18 @@ static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) mutex_lock(&ov7251->lock); if (enable) { + ret = pm_runtime_resume_and_get(ov7251->dev); + if (ret) { + mutex_unlock(&ov7251->lock); + return ret; + } + + ret = ov7251_pll_configure(ov7251); + if (ret) { + dev_err(ov7251->dev, "error configuring PLLs\n"); + goto err_power_down; + } + ret = ov7251_set_register_array(ov7251, ov7251->current_mode->data, ov7251->current_mode->data_size); @@ -1146,31 +1361,45 @@ static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) dev_err(ov7251->dev, "could not set mode %dx%d\n", ov7251->current_mode->width, ov7251->current_mode->height); - goto exit; + goto err_power_down; } ret = __v4l2_ctrl_handler_setup(&ov7251->ctrls); if (ret < 0) { dev_err(ov7251->dev, "could not sync v4l2 controls\n"); - goto exit; + goto err_power_down; } ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT, OV7251_SC_MODE_SELECT_STREAMING); + if (ret) + goto err_power_down; } else { ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT, OV7251_SC_MODE_SELECT_SW_STANDBY); + pm_runtime_put(ov7251->dev); } -exit: mutex_unlock(&ov7251->lock); + return ret; +err_power_down: + pm_runtime_put(ov7251->dev); + mutex_unlock(&ov7251->lock); return ret; } static int ov7251_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) { struct ov7251 *ov7251 = to_ov7251(subdev); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&ov7251->lock); fi->interval = ov7251->current_mode->timeperframe; mutex_unlock(&ov7251->lock); @@ -1179,26 +1408,24 @@ static int ov7251_get_frame_interval(struct v4l2_subdev *subdev, } static int ov7251_set_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) { struct ov7251 *ov7251 = to_ov7251(subdev); const struct ov7251_mode_info *new_mode; int ret = 0; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&ov7251->lock); new_mode = ov7251_find_mode_by_ival(ov7251, &fi->interval); if (new_mode != ov7251->current_mode) { - ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock, - new_mode->pixel_clock); - if (ret < 0) - goto exit; - - ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq, - new_mode->link_freq); - if (ret < 0) - goto exit; - ret = __v4l2_ctrl_modify_range(ov7251->exposure, 1, new_mode->exposure_max, 1, new_mode->exposure_def); @@ -1225,118 +1452,122 @@ exit: return ret; } -static const struct v4l2_subdev_core_ops ov7251_core_ops = { - .s_power = ov7251_s_power, -}; - static const struct v4l2_subdev_video_ops ov7251_video_ops = { .s_stream = ov7251_s_stream, - .g_frame_interval = ov7251_get_frame_interval, - .s_frame_interval = ov7251_set_frame_interval, }; static const struct v4l2_subdev_pad_ops ov7251_subdev_pad_ops = { - .init_cfg = ov7251_entity_init_cfg, .enum_mbus_code = ov7251_enum_mbus_code, .enum_frame_size = ov7251_enum_frame_size, .enum_frame_interval = ov7251_enum_frame_ival, .get_fmt = ov7251_get_format, .set_fmt = ov7251_set_format, .get_selection = ov7251_get_selection, + .get_frame_interval = ov7251_get_frame_interval, + .set_frame_interval = ov7251_set_frame_interval, }; static const struct v4l2_subdev_ops ov7251_subdev_ops = { - .core = &ov7251_core_ops, .video = &ov7251_video_ops, .pad = &ov7251_subdev_pad_ops, }; -static int ov7251_probe(struct i2c_client *client) +static const struct v4l2_subdev_internal_ops ov7251_internal_ops = { + .init_state = ov7251_init_state, +}; + +static int ov7251_check_hwcfg(struct ov7251 *ov7251) { - struct device *dev = &client->dev; + struct fwnode_handle *fwnode = dev_fwnode(ov7251->dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; struct fwnode_handle *endpoint; - struct ov7251 *ov7251; - u8 chip_id_high, chip_id_low, chip_rev; + unsigned int i, j; int ret; - ov7251 = devm_kzalloc(dev, sizeof(struct ov7251), GFP_KERNEL); - if (!ov7251) - return -ENOMEM; - - ov7251->i2c_client = client; - ov7251->dev = dev; - - endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); - if (!endpoint) { - dev_err(dev, "endpoint node not found\n"); - return -EINVAL; - } + /* + * Sometimes the fwnode graph is initialized by the bridge driver + * Bridge drivers doing this may also add GPIO mappings, wait for this. + */ + endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!endpoint) + return dev_err_probe(ov7251->dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); - ret = v4l2_fwnode_endpoint_parse(endpoint, &ov7251->ep); + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); fwnode_handle_put(endpoint); - if (ret < 0) { - dev_err(dev, "parsing endpoint node failed\n"); - return ret; + if (ret) + return dev_err_probe(ov7251->dev, ret, + "parsing endpoint node failed\n"); + + if (!bus_cfg.nr_of_link_frequencies) { + ret = dev_err_probe(ov7251->dev, -EINVAL, + "no link frequencies defined\n"); + goto out_free_bus_cfg; } - if (ov7251->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { - dev_err(dev, "invalid bus type (%u), must be CSI2 (%u)\n", - ov7251->ep.bus_type, V4L2_MBUS_CSI2_DPHY); - return -EINVAL; - } + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { + for (j = 0; j < ARRAY_SIZE(link_freq); j++) + if (bus_cfg.link_frequencies[i] == link_freq[j]) + break; - /* get system clock (xclk) */ - ov7251->xclk = devm_clk_get(dev, "xclk"); - if (IS_ERR(ov7251->xclk)) { - dev_err(dev, "could not get xclk"); - return PTR_ERR(ov7251->xclk); + if (j < ARRAY_SIZE(link_freq)) + break; } - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &ov7251->xclk_freq); - if (ret) { - dev_err(dev, "could not get xclk frequency\n"); - return ret; + if (i == bus_cfg.nr_of_link_frequencies) { + ret = dev_err_probe(ov7251->dev, -EINVAL, + "no supported link freq found\n"); + goto out_free_bus_cfg; } - /* external clock must be 24MHz, allow 1% tolerance */ - if (ov7251->xclk_freq < 23760000 || ov7251->xclk_freq > 24240000) { - dev_err(dev, "external clock frequency %u is not supported\n", - ov7251->xclk_freq); - return -EINVAL; - } + ov7251->link_freq_idx = i; - ret = clk_set_rate(ov7251->xclk, ov7251->xclk_freq); - if (ret) { - dev_err(dev, "could not set xclk frequency\n"); - return ret; - } +out_free_bus_cfg: + v4l2_fwnode_endpoint_free(&bus_cfg); - ov7251->io_regulator = devm_regulator_get(dev, "vdddo"); - if (IS_ERR(ov7251->io_regulator)) { - dev_err(dev, "cannot get io regulator\n"); - return PTR_ERR(ov7251->io_regulator); - } + return ret; +} - ov7251->core_regulator = devm_regulator_get(dev, "vddd"); - if (IS_ERR(ov7251->core_regulator)) { - dev_err(dev, "cannot get core regulator\n"); - return PTR_ERR(ov7251->core_regulator); - } +static int ov7251_detect_chip(struct ov7251 *ov7251) +{ + u8 chip_id_high, chip_id_low, chip_rev; + int ret; - ov7251->analog_regulator = devm_regulator_get(dev, "vdda"); - if (IS_ERR(ov7251->analog_regulator)) { - dev_err(dev, "cannot get analog regulator\n"); - return PTR_ERR(ov7251->analog_regulator); - } + ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_HIGH, &chip_id_high); + if (ret < 0 || chip_id_high != OV7251_CHIP_ID_HIGH_BYTE) + return dev_err_probe(ov7251->dev, -ENODEV, + "could not read ID high\n"); - ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); - if (IS_ERR(ov7251->enable_gpio)) { - dev_err(dev, "cannot get enable gpio\n"); - return PTR_ERR(ov7251->enable_gpio); - } + ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_LOW, &chip_id_low); + if (ret < 0 || chip_id_low != OV7251_CHIP_ID_LOW_BYTE) + return dev_err_probe(ov7251->dev, -ENODEV, + "could not read ID low\n"); - mutex_init(&ov7251->lock); + ret = ov7251_read_reg(ov7251, OV7251_SC_GP_IO_IN1, &chip_rev); + if (ret < 0) + return dev_err_probe(ov7251->dev, -ENODEV, + "could not read revision\n"); + chip_rev >>= 4; + + dev_info(ov7251->dev, + "OV7251 revision %x (%s) detected at address 0x%02x\n", + chip_rev, + chip_rev == 0x4 ? "1A / 1B" : + chip_rev == 0x5 ? "1C / 1D" : + chip_rev == 0x6 ? "1E" : + chip_rev == 0x7 ? "1F" : "unknown", + ov7251->i2c_client->addr); + + return 0; +} + +static int ov7251_init_ctrls(struct ov7251 *ov7251) +{ + int vblank_max, vblank_def; + s64 pixel_rate; + int hblank; v4l2_ctrl_handler_init(&ov7251->ctrls, 7); ov7251->ctrls.lock = &ov7251->lock; @@ -1353,28 +1584,120 @@ static int ov7251_probe(struct i2c_client *client) V4L2_CID_TEST_PATTERN, ARRAY_SIZE(ov7251_test_pattern_menu) - 1, 0, 0, ov7251_test_pattern_menu); + + pixel_rate = pixel_rates[ov7251->link_freq_idx]; ov7251->pixel_clock = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, V4L2_CID_PIXEL_RATE, - 1, INT_MAX, 1, 1); + pixel_rate, INT_MAX, + pixel_rate, pixel_rate); ov7251->link_freq = v4l2_ctrl_new_int_menu(&ov7251->ctrls, &ov7251_ctrl_ops, V4L2_CID_LINK_FREQ, ARRAY_SIZE(link_freq) - 1, - 0, link_freq); + ov7251->link_freq_idx, + link_freq); if (ov7251->link_freq) ov7251->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (ov7251->pixel_clock) + ov7251->pixel_clock->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + hblank = OV7251_FIXED_PPL - ov7251->current_mode->width; + ov7251->hblank = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, 1, + hblank); + if (ov7251->hblank) + ov7251->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_max = OV7251_TIMING_MAX_VTS - ov7251->current_mode->height; + vblank_def = ov7251->current_mode->vts - ov7251->current_mode->height; + ov7251->vblank = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_VBLANK, + OV7251_TIMING_MIN_VTS, vblank_max, 1, + vblank_def); ov7251->sd.ctrl_handler = &ov7251->ctrls; if (ov7251->ctrls.error) { - dev_err(dev, "%s: control initialization error %d\n", - __func__, ov7251->ctrls.error); - ret = ov7251->ctrls.error; - goto free_ctrl; + v4l2_ctrl_handler_free(&ov7251->ctrls); + return ov7251->ctrls.error; + } + + return 0; +} + +static int ov7251_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ov7251 *ov7251; + int ret; + int i; + + ov7251 = devm_kzalloc(dev, sizeof(struct ov7251), GFP_KERNEL); + if (!ov7251) + return -ENOMEM; + + ov7251->i2c_client = client; + ov7251->dev = dev; + + ret = ov7251_check_hwcfg(ov7251); + if (ret) + return ret; + + /* get system clock (xclk) */ + ov7251->xclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(ov7251->xclk)) + return dev_err_probe(dev, PTR_ERR(ov7251->xclk), + "could not get xclk"); + + ov7251->xclk_freq = clk_get_rate(ov7251->xclk); + + for (i = 0; i < ARRAY_SIZE(supported_xclk_rates); i++) + if (ov7251->xclk_freq == supported_xclk_rates[i]) + break; + + if (i == ARRAY_SIZE(supported_xclk_rates)) + return dev_err_probe(dev, -EINVAL, + "clock rate %u Hz is unsupported\n", + ov7251->xclk_freq); + + ov7251->pll_cfgs = ov7251_pll_cfgs[i]; + + ov7251->io_regulator = devm_regulator_get(dev, "vdddo"); + if (IS_ERR(ov7251->io_regulator)) { + dev_err(dev, "cannot get io regulator\n"); + return PTR_ERR(ov7251->io_regulator); + } + + ov7251->core_regulator = devm_regulator_get(dev, "vddd"); + if (IS_ERR(ov7251->core_regulator)) { + dev_err(dev, "cannot get core regulator\n"); + return PTR_ERR(ov7251->core_regulator); + } + + ov7251->analog_regulator = devm_regulator_get(dev, "vdda"); + if (IS_ERR(ov7251->analog_regulator)) { + dev_err(dev, "cannot get analog regulator\n"); + return PTR_ERR(ov7251->analog_regulator); + } + + ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(ov7251->enable_gpio)) { + dev_err(dev, "cannot get enable gpio\n"); + return PTR_ERR(ov7251->enable_gpio); + } + + mutex_init(&ov7251->lock); + + ov7251->current_mode = &ov7251_mode_info_data[0]; + ret = ov7251_init_ctrls(ov7251); + if (ret) { + dev_err_probe(dev, ret, "error during v4l2 ctrl init\n"); + goto destroy_mutex; } v4l2_i2c_subdev_init(&ov7251->sd, client, &ov7251_subdev_ops); + ov7251->sd.internal_ops = &ov7251_internal_ops; ov7251->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov7251->pad.flags = MEDIA_PAD_FL_SOURCE; ov7251->sd.dev = &client->dev; @@ -1386,47 +1709,24 @@ static int ov7251_probe(struct i2c_client *client) goto free_ctrl; } - ret = ov7251_s_power(&ov7251->sd, true); - if (ret < 0) { - dev_err(dev, "could not power up OV7251\n"); + ret = ov7251_set_power_on(ov7251->dev); + if (ret) goto free_entity; - } - ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_HIGH, &chip_id_high); - if (ret < 0 || chip_id_high != OV7251_CHIP_ID_HIGH_BYTE) { - dev_err(dev, "could not read ID high\n"); - ret = -ENODEV; - goto power_down; - } - ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_LOW, &chip_id_low); - if (ret < 0 || chip_id_low != OV7251_CHIP_ID_LOW_BYTE) { - dev_err(dev, "could not read ID low\n"); - ret = -ENODEV; + ret = ov7251_detect_chip(ov7251); + if (ret) goto power_down; - } - ret = ov7251_read_reg(ov7251, OV7251_SC_GP_IO_IN1, &chip_rev); - if (ret < 0) { - dev_err(dev, "could not read revision\n"); - ret = -ENODEV; - goto power_down; - } - chip_rev >>= 4; - - dev_info(dev, "OV7251 revision %x (%s) detected at address 0x%02x\n", - chip_rev, - chip_rev == 0x4 ? "1A / 1B" : - chip_rev == 0x5 ? "1C / 1D" : - chip_rev == 0x6 ? "1E" : - chip_rev == 0x7 ? "1F" : "unknown", - client->addr); + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(&client->dev); ret = ov7251_read_reg(ov7251, OV7251_PRE_ISP_00, &ov7251->pre_isp_00); if (ret < 0) { dev_err(dev, "could not read test pattern value\n"); ret = -ENODEV; - goto power_down; + goto err_pm_runtime; } ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT1, @@ -1434,7 +1734,7 @@ static int ov7251_probe(struct i2c_client *client) if (ret < 0) { dev_err(dev, "could not read vflip value\n"); ret = -ENODEV; - goto power_down; + goto err_pm_runtime; } ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT2, @@ -1442,10 +1742,12 @@ static int ov7251_probe(struct i2c_client *client) if (ret < 0) { dev_err(dev, "could not read hflip value\n"); ret = -ENODEV; - goto power_down; + goto err_pm_runtime; } - ov7251_s_power(&ov7251->sd, false); + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); ret = v4l2_async_register_subdev(&ov7251->sd); if (ret < 0) { @@ -1453,22 +1755,26 @@ static int ov7251_probe(struct i2c_client *client) goto free_entity; } - ov7251_entity_init_cfg(&ov7251->sd, NULL); + ov7251_init_state(&ov7251->sd, NULL); return 0; +err_pm_runtime: + pm_runtime_disable(ov7251->dev); + pm_runtime_put_noidle(ov7251->dev); power_down: - ov7251_s_power(&ov7251->sd, false); + ov7251_set_power_off(ov7251->dev); free_entity: media_entity_cleanup(&ov7251->sd.entity); free_ctrl: v4l2_ctrl_handler_free(&ov7251->ctrls); +destroy_mutex: mutex_destroy(&ov7251->lock); return ret; } -static int ov7251_remove(struct i2c_client *client) +static void ov7251_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov7251 *ov7251 = to_ov7251(sd); @@ -1478,21 +1784,36 @@ static int ov7251_remove(struct i2c_client *client) v4l2_ctrl_handler_free(&ov7251->ctrls); mutex_destroy(&ov7251->lock); - return 0; + pm_runtime_disable(ov7251->dev); + if (!pm_runtime_status_suspended(ov7251->dev)) + ov7251_set_power_off(ov7251->dev); + pm_runtime_set_suspended(ov7251->dev); } +static const struct dev_pm_ops ov7251_pm_ops = { + SET_RUNTIME_PM_OPS(ov7251_set_power_off, ov7251_set_power_on, NULL) +}; + static const struct of_device_id ov7251_of_match[] = { { .compatible = "ovti,ov7251" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ov7251_of_match); +static const struct acpi_device_id ov7251_acpi_match[] = { + { "INT347E" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ov7251_acpi_match); + static struct i2c_driver ov7251_i2c_driver = { .driver = { .of_match_table = ov7251_of_match, + .acpi_match_table = ov7251_acpi_match, .name = "ov7251", + .pm = &ov7251_pm_ops, }, - .probe_new = ov7251_probe, + .probe = ov7251_probe, .remove = ov7251_remove, }; |
