diff options
Diffstat (limited to 'drivers/media/i2c/imx214.c')
-rw-r--r-- | drivers/media/i2c/imx214.c | 247 |
1 files changed, 122 insertions, 125 deletions
diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index a0cef9e61b41..94ebe625c9e6 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -881,6 +881,109 @@ static const struct v4l2_ctrl_ops imx214_ctrl_ops = { .s_ctrl = imx214_set_ctrl, }; +static int imx214_pll_calculate(struct imx214 *imx214, struct ccs_pll *pll, + unsigned int link_freq) +{ + struct ccs_pll_limits limits = { + .min_ext_clk_freq_hz = 6000000, + .max_ext_clk_freq_hz = 27000000, + + .vt_fr = { + .min_pre_pll_clk_div = 1, + .max_pre_pll_clk_div = 15, + /* Value is educated guess as we don't have a spec */ + .min_pll_ip_clk_freq_hz = 6000000, + /* Value is educated guess as we don't have a spec */ + .max_pll_ip_clk_freq_hz = 12000000, + .min_pll_multiplier = 12, + .max_pll_multiplier = 1200, + .min_pll_op_clk_freq_hz = 338000000, + .max_pll_op_clk_freq_hz = 1200000000, + }, + .vt_bk = { + .min_sys_clk_div = 2, + .max_sys_clk_div = 4, + .min_pix_clk_div = 5, + .max_pix_clk_div = 10, + .min_pix_clk_freq_hz = 30000000, + .max_pix_clk_freq_hz = 120000000, + }, + .op_bk = { + .min_sys_clk_div = 1, + .max_sys_clk_div = 2, + .min_pix_clk_div = 6, + .max_pix_clk_div = 10, + .min_pix_clk_freq_hz = 30000000, + .max_pix_clk_freq_hz = 120000000, + }, + + .min_line_length_pck_bin = IMX214_PPL_DEFAULT, + .min_line_length_pck = IMX214_PPL_DEFAULT, + }; + unsigned int num_lanes = imx214->bus_cfg.bus.mipi_csi2.num_data_lanes; + + /* + * There are no documented constraints on the sys clock frequency, for + * either branch. Recover them based on the PLL output clock frequency + * and sys_clk_div limits on one hand, and the pix clock frequency and + * the pix_clk_div limits on the other hand. + */ + limits.vt_bk.min_sys_clk_freq_hz = + max(limits.vt_fr.min_pll_op_clk_freq_hz / limits.vt_bk.max_sys_clk_div, + limits.vt_bk.min_pix_clk_freq_hz * limits.vt_bk.min_pix_clk_div); + limits.vt_bk.max_sys_clk_freq_hz = + min(limits.vt_fr.max_pll_op_clk_freq_hz / limits.vt_bk.min_sys_clk_div, + limits.vt_bk.max_pix_clk_freq_hz * limits.vt_bk.max_pix_clk_div); + + limits.op_bk.min_sys_clk_freq_hz = + max(limits.vt_fr.min_pll_op_clk_freq_hz / limits.op_bk.max_sys_clk_div, + limits.op_bk.min_pix_clk_freq_hz * limits.op_bk.min_pix_clk_div); + limits.op_bk.max_sys_clk_freq_hz = + min(limits.vt_fr.max_pll_op_clk_freq_hz / limits.op_bk.min_sys_clk_div, + limits.op_bk.max_pix_clk_freq_hz * limits.op_bk.max_pix_clk_div); + + memset(pll, 0, sizeof(*pll)); + + pll->bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY; + pll->op_lanes = num_lanes; + pll->vt_lanes = num_lanes; + pll->csi2.lanes = num_lanes; + + pll->binning_horizontal = 1; + pll->binning_vertical = 1; + pll->scale_m = 1; + pll->scale_n = 1; + pll->bits_per_pixel = + IMX214_CSI_DATA_FORMAT_RAW10 & IMX214_BITS_PER_PIXEL_MASK; + pll->flags = CCS_PLL_FLAG_LANE_SPEED_MODEL; + pll->link_freq = link_freq; + pll->ext_clk_freq_hz = clk_get_rate(imx214->xclk); + + return ccs_pll_calculate(imx214->dev, &limits, pll); +} + +static int imx214_pll_update(struct imx214 *imx214) +{ + u64 link_freq; + int ret; + + link_freq = imx214->bus_cfg.link_frequencies[imx214->link_freq->val]; + ret = imx214_pll_calculate(imx214, &imx214->pll, link_freq); + if (ret) { + dev_err(imx214->dev, "PLL calculations failed: %d\n", ret); + return ret; + } + + ret = v4l2_ctrl_s_ctrl_int64(imx214->pixel_rate, + imx214->pll.pixel_rate_pixel_array); + if (ret) { + dev_err(imx214->dev, "failed to set pixel rate\n"); + return ret; + } + + return 0; +} + static int imx214_ctrls_init(struct imx214 *imx214) { static const struct v4l2_area unit_size = { @@ -1003,6 +1106,13 @@ static int imx214_ctrls_init(struct imx214 *imx214) return ret; } + ret = imx214_pll_update(imx214); + if (ret < 0) { + v4l2_ctrl_handler_free(ctrl_hdlr); + dev_err(imx214->dev, "failed to update PLL\n"); + return ret; + } + imx214->sd.ctrl_handler = ctrl_hdlr; return 0; @@ -1029,8 +1139,8 @@ static int imx214_start_streaming(struct imx214 *imx214) return ret; } - bit_rate_mbps = (imx214->pll.pixel_rate_csi / 1000000) - * imx214->pll.bits_per_pixel; + bit_rate_mbps = imx214->pll.pixel_rate_csi / 1000000 + * imx214->pll.bits_per_pixel; ret = cci_write(imx214->regmap, IMX214_REG_REQ_LINK_BIT_RATE, IMX214_LINK_BIT_RATE_MBPS(bit_rate_mbps), NULL); if (ret) { @@ -1115,109 +1225,6 @@ err_rpm_put: return ret; } -static int imx214_pll_calculate(struct imx214 *imx214, struct ccs_pll *pll, - unsigned int link_freq) -{ - struct ccs_pll_limits limits = { - .min_ext_clk_freq_hz = 6000000, - .max_ext_clk_freq_hz = 27000000, - - .vt_fr = { - .min_pre_pll_clk_div = 1, - .max_pre_pll_clk_div = 15, - /* Value is educated guess as we don't have a spec */ - .min_pll_ip_clk_freq_hz = 6000000, - /* Value is educated guess as we don't have a spec */ - .max_pll_ip_clk_freq_hz = 12000000, - .min_pll_multiplier = 12, - .max_pll_multiplier = 1200, - .min_pll_op_clk_freq_hz = 338000000, - .max_pll_op_clk_freq_hz = 1200000000, - }, - .vt_bk = { - .min_sys_clk_div = 2, - .max_sys_clk_div = 4, - .min_pix_clk_div = 5, - .max_pix_clk_div = 10, - .min_pix_clk_freq_hz = 30000000, - .max_pix_clk_freq_hz = 120000000, - }, - .op_bk = { - .min_sys_clk_div = 1, - .max_sys_clk_div = 2, - .min_pix_clk_div = 6, - .max_pix_clk_div = 10, - .min_pix_clk_freq_hz = 30000000, - .max_pix_clk_freq_hz = 120000000, - }, - - .min_line_length_pck_bin = IMX214_PPL_DEFAULT, - .min_line_length_pck = IMX214_PPL_DEFAULT, - }; - unsigned int num_lanes = imx214->bus_cfg.bus.mipi_csi2.num_data_lanes; - - /* - * There are no documented constraints on the sys clock frequency, for - * either branch. Recover them based on the PLL output clock frequency - * and sys_clk_div limits on one hand, and the pix clock frequency and - * the pix_clk_div limits on the other hand. - */ - limits.vt_bk.min_sys_clk_freq_hz = - max(limits.vt_fr.min_pll_op_clk_freq_hz / limits.vt_bk.max_sys_clk_div, - limits.vt_bk.min_pix_clk_freq_hz * limits.vt_bk.min_pix_clk_div); - limits.vt_bk.max_sys_clk_freq_hz = - min(limits.vt_fr.max_pll_op_clk_freq_hz / limits.vt_bk.min_sys_clk_div, - limits.vt_bk.max_pix_clk_freq_hz * limits.vt_bk.max_pix_clk_div); - - limits.op_bk.min_sys_clk_freq_hz = - max(limits.vt_fr.min_pll_op_clk_freq_hz / limits.op_bk.max_sys_clk_div, - limits.op_bk.min_pix_clk_freq_hz * limits.op_bk.min_pix_clk_div); - limits.op_bk.max_sys_clk_freq_hz = - min(limits.vt_fr.max_pll_op_clk_freq_hz / limits.op_bk.min_sys_clk_div, - limits.op_bk.max_pix_clk_freq_hz * limits.op_bk.max_pix_clk_div); - - memset(pll, 0, sizeof(*pll)); - - pll->bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY; - pll->op_lanes = num_lanes; - pll->vt_lanes = num_lanes; - pll->csi2.lanes = num_lanes; - - pll->binning_horizontal = 1; - pll->binning_vertical = 1; - pll->scale_m = 1; - pll->scale_n = 1; - pll->bits_per_pixel = - IMX214_CSI_DATA_FORMAT_RAW10 & IMX214_BITS_PER_PIXEL_MASK; - pll->flags = CCS_PLL_FLAG_LANE_SPEED_MODEL; - pll->link_freq = link_freq; - pll->ext_clk_freq_hz = clk_get_rate(imx214->xclk); - - return ccs_pll_calculate(imx214->dev, &limits, pll); -} - -static int imx214_pll_update(struct imx214 *imx214) -{ - u64 link_freq; - int ret; - - link_freq = imx214->bus_cfg.link_frequencies[imx214->link_freq->val]; - ret = imx214_pll_calculate(imx214, &imx214->pll, link_freq); - if (ret) { - dev_err(imx214->dev, "PLL calculations failed: %d\n", ret); - return ret; - } - - ret = v4l2_ctrl_s_ctrl_int64(imx214->pixel_rate, - imx214->pll.pixel_rate_pixel_array); - if (ret) { - dev_err(imx214->dev, "failed to set pixel rate\n"); - return ret; - } - - return 0; -} - static int imx214_get_frame_interval(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fival) @@ -1324,10 +1331,11 @@ static int imx214_identify_module(struct imx214 *imx214) return 0; } -static int imx214_parse_fwnode(struct device *dev, struct imx214 *imx214) +static int imx214_parse_fwnode(struct imx214 *imx214) { + struct fwnode_handle *endpoint __free(fwnode_handle) = NULL; struct v4l2_fwnode_endpoint *bus_cfg = &imx214->bus_cfg; - struct fwnode_handle *endpoint; + struct device *dev = imx214->dev; unsigned int i; int ret; @@ -1337,11 +1345,8 @@ static int imx214_parse_fwnode(struct device *dev, struct imx214 *imx214) bus_cfg->bus_type = V4L2_MBUS_CSI2_DPHY; ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, bus_cfg); - fwnode_handle_put(endpoint); - if (ret) { - dev_err_probe(dev, ret, "parsing endpoint node failed\n"); - goto error; - } + if (ret) + return dev_err_probe(dev, ret, "parsing endpoint node failed\n"); /* Check the number of MIPI CSI2 data lanes */ if (bus_cfg->bus.mipi_csi2.num_data_lanes != 4) { @@ -1357,18 +1362,16 @@ static int imx214_parse_fwnode(struct device *dev, struct imx214 *imx214) u64 freq = bus_cfg->link_frequencies[i]; struct ccs_pll pll; - if (!imx214_pll_calculate(imx214, &pll, freq)) - break; if (freq == IMX214_DEFAULT_LINK_FREQ_LEGACY) { dev_warn(dev, "link-frequencies %d not supported, please review your DT. Continuing anyway\n", IMX214_DEFAULT_LINK_FREQ); freq = IMX214_DEFAULT_LINK_FREQ; - if (imx214_pll_calculate(imx214, &pll, freq)) - continue; bus_cfg->link_frequencies[i] = freq; - break; } + + if (!imx214_pll_calculate(imx214, &pll, freq)) + break; } if (i == bus_cfg->nr_of_link_frequencies) @@ -1396,7 +1399,7 @@ static int imx214_probe(struct i2c_client *client) imx214->dev = dev; - imx214->xclk = devm_clk_get(dev, NULL); + imx214->xclk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(imx214->xclk)) return dev_err_probe(dev, PTR_ERR(imx214->xclk), "failed to get xclk\n"); @@ -1415,7 +1418,7 @@ static int imx214_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(imx214->regmap), "failed to initialize CCI\n"); - ret = imx214_parse_fwnode(dev, imx214); + ret = imx214_parse_fwnode(imx214); if (ret) return ret; @@ -1459,12 +1462,6 @@ static int imx214_probe(struct i2c_client *client) pm_runtime_set_active(imx214->dev); pm_runtime_enable(imx214->dev); - ret = imx214_pll_update(imx214); - if (ret < 0) { - dev_err_probe(dev, ret, "failed to update PLL\n"); - goto error_subdev_cleanup; - } - ret = v4l2_async_register_subdev_sensor(&imx214->sd); if (ret < 0) { dev_err_probe(dev, ret, |