diff options
Diffstat (limited to 'drivers/media/i2c/ov7251.c')
-rw-r--r-- | drivers/media/i2c/ov7251.c | 750 |
1 files changed, 531 insertions, 219 deletions
diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index ebb299f207e5..0e7be15bc20a 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; @@ -755,51 +929,29 @@ 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"); + 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 +1010,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 +1061,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; } @@ -1034,6 +1215,7 @@ static int ov7251_set_format(struct v4l2_subdev *sd, { 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; @@ -1052,16 +1234,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); @@ -1077,6 +1249,14 @@ 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; } @@ -1123,13 +1303,29 @@ static int ov7251_get_selection(struct v4l2_subdev *sd, { 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, sd_state, 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; } @@ -1142,6 +1338,16 @@ static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) mutex_lock(&ov7251->lock); if (enable) { + ret = pm_runtime_get_sync(ov7251->dev); + if (ret < 0) + goto unlock_out; + + 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); @@ -1149,23 +1355,30 @@ 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: +unlock_out: mutex_unlock(&ov7251->lock); + return ret; +err_power_down: + pm_runtime_put_noidle(ov7251->dev); + mutex_unlock(&ov7251->lock); return ret; } @@ -1192,16 +1405,6 @@ static int ov7251_set_frame_interval(struct v4l2_subdev *subdev, 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); @@ -1228,10 +1431,6 @@ 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, @@ -1249,97 +1448,97 @@ static const struct v4l2_subdev_pad_ops ov7251_subdev_pad_ops = { }; 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 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; - } + endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!endpoint) + return -EPROBE_DEFER; /* could be provided by cio2-bridge */ - 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; @@ -1356,25 +1555,138 @@ 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; + unsigned int rate = 0, clk_rate = 0; + 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_clk_get_optional(dev, NULL); + if (IS_ERR(ov7251->xclk)) + return dev_err_probe(dev, PTR_ERR(ov7251->xclk), + "could not get xclk"); + + /* + * We could have either a 24MHz or 19.2MHz clock rate from either DT or + * ACPI. We also need to support the IPU3 case which will have both an + * external clock AND a clock-frequency property. + */ + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &rate); + if (ret && !ov7251->xclk) + return dev_err_probe(dev, ret, "invalid clock config\n"); + + clk_rate = clk_get_rate(ov7251->xclk); + ov7251->xclk_freq = clk_rate ? clk_rate : rate; + + if (ov7251->xclk_freq == 0) + return dev_err_probe(dev, -EINVAL, "invalid clock frequency\n"); + + if (!ret && ov7251->xclk) { + ret = clk_set_rate(ov7251->xclk, rate); + if (ret) + return dev_err_probe(dev, ret, + "failed to set clock rate\n"); + } + + 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_HIGH); + 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); @@ -1389,47 +1701,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, @@ -1437,7 +1726,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, @@ -1445,10 +1734,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) { @@ -1460,12 +1751,16 @@ static int ov7251_probe(struct i2c_client *client) 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; @@ -1481,19 +1776,36 @@ static int ov7251_remove(struct i2c_client *client) v4l2_ctrl_handler_free(&ov7251->ctrls); mutex_destroy(&ov7251->lock); + pm_runtime_disable(ov7251->dev); + if (!pm_runtime_status_suspended(ov7251->dev)) + ov7251_set_power_off(ov7251->dev); + pm_runtime_set_suspended(ov7251->dev); + return 0; } +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, .remove = ov7251_remove, |