From patchwork Mon Jan 16 14:44:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 643326 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id ECAA5C54EBE for ; Mon, 16 Jan 2023 14:56:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232739AbjAPO4c (ORCPT ); Mon, 16 Jan 2023 09:56:32 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51842 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232471AbjAPOz5 (ORCPT ); Mon, 16 Jan 2023 09:55:57 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B1D0522A0A for ; Mon, 16 Jan 2023 06:44:56 -0800 (PST) Received: from pendragon.ideasonboard.com (213-243-189-158.bb.dnainternet.fi [213.243.189.158]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0C661D82; Mon, 16 Jan 2023 15:44:54 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1673880295; bh=jwgiiDsxdjj5KyqfgNdGkH9gDakEfr7pfNUZ8CZSjfs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=A+NrwBlc+gZswPXr91AYz7QJcUbRDWq/2WKOQNGSX9aSWFMAZ7o3SxyCKSovGQXbQ smga1msGay+l4onfHqqxIWt3vicH5sVMUwAdd40OlFo0tzf45f6oB1Um/GwmPc3nj1 jO60S0uqPlNbOJHV6g9kz4zCDTrY2ak/V3NJPiYI= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Sakari Ailus , Manivannan Sadhasivam , Alexander Stein , Dave Stevenson Subject: [PATCH v3 01/17] media: i2c: imx290: Group functions in sections Date: Mon, 16 Jan 2023 16:44:38 +0200 Message-Id: <20230116144454.1012-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.38.2 In-Reply-To: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> References: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Move functions around to group them in logical sections, in order to improve readability. As a result, the IMX290_NUM_SUPPLIES macro has to be changed. No other code change is included, only moves. Signed-off-by: Laurent Pinchart Reviewed-by: Alexander Stein --- drivers/media/i2c/imx290.c | 626 +++++++++++++++++++------------------ 1 file changed, 325 insertions(+), 301 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 218ded13fd80..ca2fa57c28fe 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -152,13 +152,7 @@ #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH 1920 #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT 1080 -static const char * const imx290_supply_name[] = { - "vdda", - "vddd", - "vdddo", -}; - -#define IMX290_NUM_SUPPLIES ARRAY_SIZE(imx290_supply_name) +#define IMX290_NUM_SUPPLIES 3 struct imx290_regval { u32 reg; @@ -199,31 +193,14 @@ struct imx290 { struct mutex lock; }; -struct imx290_pixfmt { - u32 code; - u8 bpp; -}; +static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx290, sd); +} -static const struct imx290_pixfmt imx290_formats[] = { - { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, -}; - -static const struct regmap_config imx290_regmap_config = { - .reg_bits = 16, - .val_bits = 8, -}; - -static const char * const imx290_test_pattern_menu[] = { - "Disabled", - "Sequence Pattern 1", - "Horizontal Color-bar Chart", - "Vertical Color-bar Chart", - "Sequence Pattern 2", - "Gradation Pattern 1", - "Gradation Pattern 2", - "000/555h Toggle Pattern", -}; +/* ----------------------------------------------------------------------------- + * Modes and formats + */ static const struct imx290_regval imx290_global_init_settings[] = { { IMX290_CTRL_07, IMX290_WINMODE_1080P }, @@ -438,10 +415,19 @@ static inline int imx290_modes_num(const struct imx290 *imx290) return ARRAY_SIZE(imx290_modes_4lanes); } -static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) -{ - return container_of(_sd, struct imx290, sd); -} +struct imx290_pixfmt { + u32 code; + u8 bpp; +}; + +static const struct imx290_pixfmt imx290_formats[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, +}; + +/* ----------------------------------------------------------------------------- + * Register access + */ static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *value) { @@ -501,18 +487,94 @@ static int imx290_set_register_array(struct imx290 *imx290, return 0; } -/* Stop streaming */ -static int imx290_stop_streaming(struct imx290 *imx290) +static int imx290_set_data_lanes(struct imx290 *imx290) { - int ret = 0; + int ret = 0, laneval, frsel; - imx290_write(imx290, IMX290_STANDBY, 0x01, &ret); + switch (imx290->nlanes) { + case 2: + laneval = 0x01; + frsel = 0x02; + break; + case 4: + laneval = 0x03; + frsel = 0x01; + break; + default: + /* + * We should never hit this since the data lane count is + * validated in probe itself + */ + dev_err(imx290->dev, "Lane configuration not supported\n"); + return -EINVAL; + } - msleep(30); + imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret); + imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret); + imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret); - return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret); + return ret; } +static int imx290_write_current_format(struct imx290 *imx290) +{ + int ret; + + switch (imx290->current_format.code) { + case MEDIA_BUS_FMT_SRGGB10_1X10: + ret = imx290_set_register_array(imx290, imx290_10bit_settings, + ARRAY_SIZE( + imx290_10bit_settings)); + if (ret < 0) { + dev_err(imx290->dev, "Could not set format registers\n"); + return ret; + } + break; + case MEDIA_BUS_FMT_SRGGB12_1X12: + ret = imx290_set_register_array(imx290, imx290_12bit_settings, + ARRAY_SIZE( + imx290_12bit_settings)); + if (ret < 0) { + dev_err(imx290->dev, "Could not set format registers\n"); + return ret; + } + break; + default: + dev_err(imx290->dev, "Unknown pixel format\n"); + return -EINVAL; + } + + return 0; +} + +static inline u8 imx290_get_link_freq_index(struct imx290 *imx290) +{ + return imx290->current_mode->link_freq_index; +} + +static s64 imx290_get_link_freq(struct imx290 *imx290) +{ + u8 index = imx290_get_link_freq_index(imx290); + + return *(imx290_link_freqs_ptr(imx290) + index); +} + +static u64 imx290_calc_pixel_rate(struct imx290 *imx290) +{ + s64 link_freq = imx290_get_link_freq(imx290); + u8 nlanes = imx290->nlanes; + u64 pixel_rate; + + /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + pixel_rate = link_freq * 2 * nlanes; + do_div(pixel_rate, imx290->bpp); + return pixel_rate; +} + +/* ---------------------------------------------------------------------------- + * Controls + */ + static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx290 *imx290 = container_of(ctrl->handler, @@ -566,6 +628,187 @@ static const struct v4l2_ctrl_ops imx290_ctrl_ops = { .s_ctrl = imx290_set_ctrl, }; +static const char * const imx290_test_pattern_menu[] = { + "Disabled", + "Sequence Pattern 1", + "Horizontal Color-bar Chart", + "Vertical Color-bar Chart", + "Sequence Pattern 2", + "Gradation Pattern 1", + "Gradation Pattern 2", + "000/555h Toggle Pattern", +}; + +static int imx290_ctrl_init(struct imx290 *imx290) +{ + struct v4l2_fwnode_device_properties props; + unsigned int blank; + int ret; + + ret = v4l2_fwnode_device_parse(imx290->dev, &props); + if (ret < 0) + return ret; + + v4l2_ctrl_handler_init(&imx290->ctrls, 9); + imx290->ctrls.lock = &imx290->lock; + + /* + * The sensor has an analog gain and a digital gain, both controlled + * through a single gain value, expressed in 0.3dB increments. Values + * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values + * up to 72.0dB (240) add further digital gain. Limit the range to + * analog gain only, support for digital gain can be added separately + * if needed. + * + * The IMX327 and IMX462 are largely compatible with the IMX290, but + * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital + * gain. When support for those sensors gets added to the driver, the + * gain control should be adjusted accordingly. + */ + v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); + + v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1, + IMX290_VMAX_DEFAULT - 2); + + imx290->link_freq = + v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_LINK_FREQ, + imx290_link_freqs_num(imx290) - 1, 0, + imx290_link_freqs_ptr(imx290)); + if (imx290->link_freq) + imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_PIXEL_RATE, + 1, INT_MAX, 1, + imx290_calc_pixel_rate(imx290)); + + v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx290_test_pattern_menu) - 1, + 0, 0, imx290_test_pattern_menu); + + blank = imx290->current_mode->hmax - imx290->current_mode->width; + imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_HBLANK, blank, blank, 1, + blank); + if (imx290->hblank) + imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height; + imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_VBLANK, blank, blank, 1, + blank); + if (imx290->vblank) + imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops, + &props); + + imx290->sd.ctrl_handler = &imx290->ctrls; + + if (imx290->ctrls.error) { + ret = imx290->ctrls.error; + v4l2_ctrl_handler_free(&imx290->ctrls); + return ret; + } + + return 0; +} + +/* ---------------------------------------------------------------------------- + * Subdev operations + */ + +/* Start streaming */ +static int imx290_start_streaming(struct imx290 *imx290) +{ + int ret; + + /* Set init register settings */ + ret = imx290_set_register_array(imx290, imx290_global_init_settings, + ARRAY_SIZE( + imx290_global_init_settings)); + if (ret < 0) { + dev_err(imx290->dev, "Could not set init registers\n"); + return ret; + } + + /* Apply the register values related to current frame format */ + ret = imx290_write_current_format(imx290); + if (ret < 0) { + dev_err(imx290->dev, "Could not set frame format\n"); + return ret; + } + + /* Apply default values of current mode */ + ret = imx290_set_register_array(imx290, imx290->current_mode->data, + imx290->current_mode->data_size); + if (ret < 0) { + dev_err(imx290->dev, "Could not set current mode\n"); + return ret; + } + + ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax, + NULL); + if (ret) + return ret; + + /* Apply customized values from user */ + ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); + if (ret) { + dev_err(imx290->dev, "Could not sync v4l2 controls\n"); + return ret; + } + + imx290_write(imx290, IMX290_STANDBY, 0x00, &ret); + + msleep(30); + + /* Start streaming */ + return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret); +} + +/* Stop streaming */ +static int imx290_stop_streaming(struct imx290 *imx290) +{ + int ret = 0; + + imx290_write(imx290, IMX290_STANDBY, 0x01, &ret); + + msleep(30); + + return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret); +} + +static int imx290_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx290 *imx290 = to_imx290(sd); + int ret = 0; + + if (enable) { + ret = pm_runtime_resume_and_get(imx290->dev); + if (ret < 0) + goto unlock_and_return; + + ret = imx290_start_streaming(imx290); + if (ret) { + dev_err(imx290->dev, "Start stream failed\n"); + pm_runtime_put(imx290->dev); + goto unlock_and_return; + } + } else { + imx290_stop_streaming(imx290); + pm_runtime_put(imx290->dev); + } + +unlock_and_return: + + return ret; +} + static struct v4l2_mbus_framefmt * imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state *state, u32 which) @@ -627,30 +870,6 @@ static int imx290_get_fmt(struct v4l2_subdev *sd, return 0; } -static inline u8 imx290_get_link_freq_index(struct imx290 *imx290) -{ - return imx290->current_mode->link_freq_index; -} - -static s64 imx290_get_link_freq(struct imx290 *imx290) -{ - u8 index = imx290_get_link_freq_index(imx290); - - return *(imx290_link_freqs_ptr(imx290) + index); -} - -static u64 imx290_calc_pixel_rate(struct imx290 *imx290) -{ - s64 link_freq = imx290_get_link_freq(imx290); - u8 nlanes = imx290->nlanes; - u64 pixel_rate; - - /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - pixel_rate = link_freq * 2 * nlanes; - do_div(pixel_rate, imx290->bpp); - return pixel_rate; -} - static int imx290_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -774,151 +993,31 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, return 0; } -static int imx290_write_current_format(struct imx290 *imx290) -{ - int ret; - - switch (imx290->current_format.code) { - case MEDIA_BUS_FMT_SRGGB10_1X10: - ret = imx290_set_register_array(imx290, imx290_10bit_settings, - ARRAY_SIZE( - imx290_10bit_settings)); - if (ret < 0) { - dev_err(imx290->dev, "Could not set format registers\n"); - return ret; - } - break; - case MEDIA_BUS_FMT_SRGGB12_1X12: - ret = imx290_set_register_array(imx290, imx290_12bit_settings, - ARRAY_SIZE( - imx290_12bit_settings)); - if (ret < 0) { - dev_err(imx290->dev, "Could not set format registers\n"); - return ret; - } - break; - default: - dev_err(imx290->dev, "Unknown pixel format\n"); - return -EINVAL; - } - - return 0; -} - -/* Start streaming */ -static int imx290_start_streaming(struct imx290 *imx290) -{ - int ret; - - /* Set init register settings */ - ret = imx290_set_register_array(imx290, imx290_global_init_settings, - ARRAY_SIZE( - imx290_global_init_settings)); - if (ret < 0) { - dev_err(imx290->dev, "Could not set init registers\n"); - return ret; - } - - /* Apply the register values related to current frame format */ - ret = imx290_write_current_format(imx290); - if (ret < 0) { - dev_err(imx290->dev, "Could not set frame format\n"); - return ret; - } - - /* Apply default values of current mode */ - ret = imx290_set_register_array(imx290, imx290->current_mode->data, - imx290->current_mode->data_size); - if (ret < 0) { - dev_err(imx290->dev, "Could not set current mode\n"); - return ret; - } - - ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax, - NULL); - if (ret) - return ret; - - /* Apply customized values from user */ - ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); - if (ret) { - dev_err(imx290->dev, "Could not sync v4l2 controls\n"); - return ret; - } - - imx290_write(imx290, IMX290_STANDBY, 0x00, &ret); - - msleep(30); - - /* Start streaming */ - return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret); -} - -static int imx290_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct imx290 *imx290 = to_imx290(sd); - int ret = 0; - - if (enable) { - ret = pm_runtime_resume_and_get(imx290->dev); - if (ret < 0) - goto unlock_and_return; - - ret = imx290_start_streaming(imx290); - if (ret) { - dev_err(imx290->dev, "Start stream failed\n"); - pm_runtime_put(imx290->dev); - goto unlock_and_return; - } - } else { - imx290_stop_streaming(imx290); - pm_runtime_put(imx290->dev); - } - -unlock_and_return: - - return ret; -} - -static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++) - imx290->supplies[i].supply = imx290_supply_name[i]; - - return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies), - imx290->supplies); -} - -static int imx290_set_data_lanes(struct imx290 *imx290) -{ - int ret = 0, laneval, frsel; - - switch (imx290->nlanes) { - case 2: - laneval = 0x01; - frsel = 0x02; - break; - case 4: - laneval = 0x03; - frsel = 0x01; - break; - default: - /* - * We should never hit this since the data lane count is - * validated in probe itself - */ - dev_err(imx290->dev, "Lane configuration not supported\n"); - return -EINVAL; - } - - imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret); - imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret); - imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret); - - return ret; -} +static const struct v4l2_subdev_video_ops imx290_video_ops = { + .s_stream = imx290_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx290_pad_ops = { + .init_cfg = imx290_entity_init_cfg, + .enum_mbus_code = imx290_enum_mbus_code, + .enum_frame_size = imx290_enum_frame_size, + .get_fmt = imx290_get_fmt, + .set_fmt = imx290_set_fmt, + .get_selection = imx290_get_selection, +}; + +static const struct v4l2_subdev_ops imx290_subdev_ops = { + .video = &imx290_video_ops, + .pad = &imx290_pad_ops, +}; + +static const struct media_entity_operations imx290_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* ---------------------------------------------------------------------------- + * Power management + */ static int imx290_power_on(struct device *dev) { @@ -966,105 +1065,30 @@ static const struct dev_pm_ops imx290_pm_ops = { SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL) }; -static const struct v4l2_subdev_video_ops imx290_video_ops = { - .s_stream = imx290_set_stream, -}; - -static const struct v4l2_subdev_pad_ops imx290_pad_ops = { - .init_cfg = imx290_entity_init_cfg, - .enum_mbus_code = imx290_enum_mbus_code, - .enum_frame_size = imx290_enum_frame_size, - .get_fmt = imx290_get_fmt, - .set_fmt = imx290_set_fmt, - .get_selection = imx290_get_selection, -}; +/* ---------------------------------------------------------------------------- + * Probe & remove + */ -static const struct v4l2_subdev_ops imx290_subdev_ops = { - .video = &imx290_video_ops, - .pad = &imx290_pad_ops, +static const struct regmap_config imx290_regmap_config = { + .reg_bits = 16, + .val_bits = 8, }; -static const struct media_entity_operations imx290_subdev_entity_ops = { - .link_validate = v4l2_subdev_link_validate, +static const char * const imx290_supply_name[IMX290_NUM_SUPPLIES] = { + "vdda", + "vddd", + "vdddo", }; -static int imx290_ctrl_init(struct imx290 *imx290) +static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) { - struct v4l2_fwnode_device_properties props; - unsigned int blank; - int ret; + unsigned int i; - ret = v4l2_fwnode_device_parse(imx290->dev, &props); - if (ret < 0) - return ret; + for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++) + imx290->supplies[i].supply = imx290_supply_name[i]; - v4l2_ctrl_handler_init(&imx290->ctrls, 9); - imx290->ctrls.lock = &imx290->lock; - - /* - * The sensor has an analog gain and a digital gain, both controlled - * through a single gain value, expressed in 0.3dB increments. Values - * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values - * up to 72.0dB (240) add further digital gain. Limit the range to - * analog gain only, support for digital gain can be added separately - * if needed. - * - * The IMX327 and IMX462 are largely compatible with the IMX290, but - * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital - * gain. When support for those sensors gets added to the driver, the - * gain control should be adjusted accordingly. - */ - v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); - - v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1, - IMX290_VMAX_DEFAULT - 2); - - imx290->link_freq = - v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_LINK_FREQ, - imx290_link_freqs_num(imx290) - 1, 0, - imx290_link_freqs_ptr(imx290)); - if (imx290->link_freq) - imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_PIXEL_RATE, - 1, INT_MAX, 1, - imx290_calc_pixel_rate(imx290)); - - v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(imx290_test_pattern_menu) - 1, - 0, 0, imx290_test_pattern_menu); - - blank = imx290->current_mode->hmax - imx290->current_mode->width; - imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_HBLANK, blank, blank, 1, - blank); - if (imx290->hblank) - imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height; - imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_VBLANK, blank, blank, 1, - blank); - if (imx290->vblank) - imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops, - &props); - - imx290->sd.ctrl_handler = &imx290->ctrls; - - if (imx290->ctrls.error) { - ret = imx290->ctrls.error; - v4l2_ctrl_handler_free(&imx290->ctrls); - return ret; - } - - return 0; + return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies), + imx290->supplies); } /* From patchwork Mon Jan 16 14:44:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 643329 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2804FC677F1 for ; Mon, 16 Jan 2023 14:56:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232351AbjAPO4X (ORCPT ); Mon, 16 Jan 2023 09:56:23 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51094 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232533AbjAPOz6 (ORCPT ); Mon, 16 Jan 2023 09:55:58 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CCA312202C for ; Mon, 16 Jan 2023 06:44:59 -0800 (PST) Received: from pendragon.ideasonboard.com (213-243-189-158.bb.dnainternet.fi [213.243.189.158]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id F164610B1; Mon, 16 Jan 2023 15:44:57 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1673880298; bh=ihX+WGIzDOdgiiJerX7c0/pF2m/OEbk9OpPRnb0+esg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PUqTElRlRBih7Hh3PSZ/7w6dMXRnU68a+z8uRvQy46/caSfW+GuJuaRPMIjQAqY23 0P2dlEpIoMK3ityvOPfY1K9QUjnvUt35UmofEb4D6ZYjA2HOvmU1jk7lLWv3CPjiGa 6mLvbMBGiMjSeQRFYamPfOBno0P9ZzyDPw3eAqY8= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Sakari Ailus , Manivannan Sadhasivam , Alexander Stein , Dave Stevenson Subject: [PATCH v3 03/17] media: i2c: imx290: Factor out control update code to a function Date: Mon, 16 Jan 2023 16:44:40 +0200 Message-Id: <20230116144454.1012-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.38.2 In-Reply-To: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> References: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Move the control update code to a separate function to group it with all the control-related code and make imx290_set_fmt() more readable. Signed-off-by: Laurent Pinchart Reviewed-by: Alexander Stein --- Changes since v1: - Correctly handle the case where imx290_ctrl_update() gets called before controls are initialized --- drivers/media/i2c/imx290.c | 43 ++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 5529bd39238f..991e7285c40c 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -639,6 +639,28 @@ static const char * const imx290_test_pattern_menu[] = { "000/555h Toggle Pattern", }; +static void imx290_ctrl_update(struct imx290 *imx290, + const struct imx290_mode *mode) +{ + unsigned int hblank = mode->hmax - mode->width; + unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height; + + /* + * This function may be called from imx290_set_fmt() before controls + * get created by imx290_ctrl_init(). Return immediately in that case. + */ + if (!imx290->ctrls.lock) + return; + + __v4l2_ctrl_s_ctrl(imx290->link_freq, + imx290_get_link_freq_index(imx290)); + __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, + imx290_calc_pixel_rate(imx290)); + + __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank); + __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank); +} + static int imx290_ctrl_init(struct imx290 *imx290) { struct v4l2_fwnode_device_properties props; @@ -904,26 +926,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, imx290->current_mode = mode; imx290->bpp = imx290_formats[i].bpp; - if (imx290->link_freq) - __v4l2_ctrl_s_ctrl(imx290->link_freq, - imx290_get_link_freq_index(imx290)); - if (imx290->pixel_rate) - __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, - imx290_calc_pixel_rate(imx290)); - - if (imx290->hblank) { - unsigned int hblank = mode->hmax - mode->width; - - __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, - 1, hblank); - } - - if (imx290->vblank) { - unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height; - - __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, - 1, vblank); - } + imx290_ctrl_update(imx290, mode); } *format = fmt->format; From patchwork Mon Jan 16 14:44:42 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 643328 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BD488C54EBE for ; Mon, 16 Jan 2023 14:56:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232523AbjAPO4Z (ORCPT ); Mon, 16 Jan 2023 09:56:25 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51926 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232643AbjAPO4B (ORCPT ); Mon, 16 Jan 2023 09:56:01 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AE6ED26873 for ; Mon, 16 Jan 2023 06:45:02 -0800 (PST) Received: from pendragon.ideasonboard.com (213-243-189-158.bb.dnainternet.fi [213.243.189.158]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0DA331211; Mon, 16 Jan 2023 15:45:00 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1673880301; bh=irV9W4lIfBwcElNntp/8epdHcNjLNu+RmFt7zHbhXho=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ocTK9TPRaqXAiF+MELmdcMfjGt0UiRpsumiMXeVUZF+QqPZskKemlk1Q+DqFp0ih2 /du8SFbryaazGL39J5BbfuS5tY3MYBLLFLtoMDbgA+Il3kwMKUSkZPsCiMMgm9Tm8F 4MlGGoqlsjQ6E/IvGEZ0IzjYXYV8ngYeM56OeK+o= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Sakari Ailus , Manivannan Sadhasivam , Alexander Stein , Dave Stevenson Subject: [PATCH v3 05/17] media: i2c: imx290: Pass format and mode to imx290_calc_pixel_rate() Date: Mon, 16 Jan 2023 16:44:42 +0200 Message-Id: <20230116144454.1012-6-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.38.2 In-Reply-To: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> References: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Avoid accessing the imx290 current_format and current_mode fields in imx290_calc_pixel_rate() to prepare for the removal of those fields. Among the two callers of the function, imx290_ctrl_update() has an explicit mode pointer already, and we can also give it a format pointer. Use those explicitly. While at it, inline the imx290_get_link_freq() function in imx290_calc_pixel_rate() as it is only called there. Signed-off-by: Laurent Pinchart --- Changes since v2: - Really drop format argument from imx290_calc_pixel_rate() Changes since v1: - Drop format argument from imx290_calc_pixel_rate() --- drivers/media/i2c/imx290.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 4ad6eab4f2e2..25671ded7c2a 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -547,21 +547,14 @@ static int imx290_write_current_format(struct imx290 *imx290) return 0; } -static s64 imx290_get_link_freq(struct imx290 *imx290) +static u64 imx290_calc_pixel_rate(struct imx290 *imx290, + const struct imx290_mode *mode) { - u8 index = imx290->current_mode->link_freq_index; - - return *(imx290_link_freqs_ptr(imx290) + index); -} - -static u64 imx290_calc_pixel_rate(struct imx290 *imx290) -{ - s64 link_freq = imx290_get_link_freq(imx290); - u8 nlanes = imx290->nlanes; + s64 link_freq = imx290_link_freqs_ptr(imx290)[mode->link_freq_index]; u64 pixel_rate; /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - pixel_rate = link_freq * 2 * nlanes; + pixel_rate = link_freq * 2 * imx290->nlanes; do_div(pixel_rate, imx290->bpp); return pixel_rate; } @@ -649,7 +642,7 @@ static void imx290_ctrl_update(struct imx290 *imx290, __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, - imx290_calc_pixel_rate(imx290)); + imx290_calc_pixel_rate(imx290, mode)); __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank); __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank); @@ -659,6 +652,7 @@ static int imx290_ctrl_init(struct imx290 *imx290) { struct v4l2_fwnode_device_properties props; unsigned int blank; + u64 pixel_rate; int ret; ret = v4l2_fwnode_device_parse(imx290->dev, &props); @@ -696,10 +690,10 @@ static int imx290_ctrl_init(struct imx290 *imx290) if (imx290->link_freq) imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + pixel_rate = imx290_calc_pixel_rate(imx290, imx290->current_mode); imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_PIXEL_RATE, - 1, INT_MAX, 1, - imx290_calc_pixel_rate(imx290)); + 1, INT_MAX, 1, pixel_rate); v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_TEST_PATTERN, From patchwork Mon Jan 16 14:44:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 643327 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D3FC5C46467 for ; Mon, 16 Jan 2023 14:56:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232796AbjAPO42 (ORCPT ); Mon, 16 Jan 2023 09:56:28 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45676 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232694AbjAPO4B (ORCPT ); Mon, 16 Jan 2023 09:56:01 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DDB3626854 for ; Mon, 16 Jan 2023 06:45:03 -0800 (PST) Received: from pendragon.ideasonboard.com (213-243-189-158.bb.dnainternet.fi [213.243.189.158]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6DFC4AD5; Mon, 16 Jan 2023 15:45:02 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1673880302; bh=zcxytw9kWLXrBKNNkblyQCOjNiw79dcDrL6kDctq+BA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VyFzhDt1QCX1Oi2IzkIrCuTRapfnNC/ZLUmz+SAtM1SSMwRQcYmfBclqlwPPJSI9r Xl/qL+OdvXUUTNyntpwltDzMkVGAVxhOS8jsBPgb5KPK7Ppd2DlTVmA39dX27E8BxH lXfFJ+SPImuK2AytmqqcuufhsQuYXJK2QwxyAuFQ= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Sakari Ailus , Manivannan Sadhasivam , Alexander Stein , Dave Stevenson Subject: [PATCH v3 06/17] media: i2c: imx290: Compute pixel rate and blanking in one place Date: Mon, 16 Jan 2023 16:44:43 +0200 Message-Id: <20230116144454.1012-7-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.38.2 In-Reply-To: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> References: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org The hblank, vblank, pixel rate and link frequency values and limits are currently computed when creating controls, in imx290_ctrl_init(), and updated in imx290_ctrl_update(). This duplicates the logic in different places. Simplify the code by setting the control values and limits to hardcoded values when creating the controls, and call imx290_ctrl_update() to then update them. Signed-off-by: Laurent Pinchart Acked-by: Alexander Stein --- Changes since v1: - Fix typo in comment --- drivers/media/i2c/imx290.c | 43 +++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 25671ded7c2a..d3279d88f253 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -547,18 +547,6 @@ static int imx290_write_current_format(struct imx290 *imx290) return 0; } -static u64 imx290_calc_pixel_rate(struct imx290 *imx290, - const struct imx290_mode *mode) -{ - s64 link_freq = imx290_link_freqs_ptr(imx290)[mode->link_freq_index]; - u64 pixel_rate; - - /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - pixel_rate = link_freq * 2 * imx290->nlanes; - do_div(pixel_rate, imx290->bpp); - return pixel_rate; -} - /* ---------------------------------------------------------------------------- * Controls */ @@ -632,6 +620,8 @@ static void imx290_ctrl_update(struct imx290 *imx290, { unsigned int hblank = mode->hmax - mode->width; unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height; + s64 link_freq = imx290_link_freqs_ptr(imx290)[mode->link_freq_index]; + u64 pixel_rate; /* * This function may be called from imx290_set_fmt() before controls @@ -640,9 +630,12 @@ static void imx290_ctrl_update(struct imx290 *imx290, if (!imx290->ctrls.lock) return; + /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + pixel_rate = link_freq * 2 * imx290->nlanes; + do_div(pixel_rate, imx290->bpp); + __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, - imx290_calc_pixel_rate(imx290, mode)); + __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, pixel_rate); __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank); __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank); @@ -651,8 +644,6 @@ static void imx290_ctrl_update(struct imx290 *imx290, static int imx290_ctrl_init(struct imx290 *imx290) { struct v4l2_fwnode_device_properties props; - unsigned int blank; - u64 pixel_rate; int ret; ret = v4l2_fwnode_device_parse(imx290->dev, &props); @@ -682,6 +673,11 @@ static int imx290_ctrl_init(struct imx290 *imx290) V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1, IMX290_VMAX_DEFAULT - 2); + /* + * Set the link frequency, pixel rate, horizontal blanking and vertical + * blanking to hardcoded values, they will be updated by + * imx290_ctrl_update(). + */ imx290->link_freq = v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_LINK_FREQ, @@ -690,27 +686,22 @@ static int imx290_ctrl_init(struct imx290 *imx290) if (imx290->link_freq) imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - pixel_rate = imx290_calc_pixel_rate(imx290, imx290->current_mode); imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_PIXEL_RATE, - 1, INT_MAX, 1, pixel_rate); + 1, INT_MAX, 1, 1); v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(imx290_test_pattern_menu) - 1, 0, 0, imx290_test_pattern_menu); - blank = imx290->current_mode->hmax - imx290->current_mode->width; imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_HBLANK, blank, blank, 1, - blank); + V4L2_CID_HBLANK, 1, 1, 1, 1); if (imx290->hblank) imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height; imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_VBLANK, blank, blank, 1, - blank); + V4L2_CID_VBLANK, 1, 1, 1, 1); if (imx290->vblank) imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; @@ -725,6 +716,10 @@ static int imx290_ctrl_init(struct imx290 *imx290) return ret; } + mutex_lock(imx290->ctrls.lock); + imx290_ctrl_update(imx290, imx290->current_mode); + mutex_unlock(imx290->ctrls.lock); + return 0; } From patchwork Mon Jan 16 14:44:47 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 643325 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A51B7C54EBE for ; Mon, 16 Jan 2023 14:56:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230497AbjAPO4h (ORCPT ); Mon, 16 Jan 2023 09:56:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52090 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232738AbjAPO4K (ORCPT ); Mon, 16 Jan 2023 09:56:10 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D453122A1C for ; Mon, 16 Jan 2023 06:45:09 -0800 (PST) Received: from pendragon.ideasonboard.com (213-243-189-158.bb.dnainternet.fi [213.243.189.158]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 26CAD1802; Mon, 16 Jan 2023 15:45:08 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1673880308; bh=RBGJ8KrH4cLXCrEs1R5tdbqqmAjnrcfGq1QbCZdTM5Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GhxeO8Y7NjxuRUnPsMZHMRxQV0kPeQPwcfKMExs185Fbw6v/ly24S9bxGqC6VQU9t QNc7EF1UWjQEroxl2m2RmdHP66fuReV9GNSREqFajGt9LZ5t0Oo1MceWqr/oUJCR5H tH2d15ar8gKhXlgVNDYIfhVXKn6gLbN9NNJFCTRU= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Sakari Ailus , Manivannan Sadhasivam , Alexander Stein , Dave Stevenson Subject: [PATCH v3 10/17] media: i2c: imx290: Factor out clock initialization to separate function Date: Mon, 16 Jan 2023 16:44:47 +0200 Message-Id: <20230116144454.1012-11-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.38.2 In-Reply-To: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> References: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Move the external clock initialization code from probe() to a separate function to improve readability. No functional change intended. Signed-off-by: Laurent Pinchart Reviewed-by: Alexander Stein --- drivers/media/i2c/imx290.c | 57 +++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 51f430ca3652..c1d18ec51e41 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -1120,6 +1120,34 @@ static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) imx290->supplies); } +static int imx290_init_clk(struct imx290 *imx290) +{ + u32 xclk_freq; + int ret; + + ret = fwnode_property_read_u32(dev_fwnode(imx290->dev), + "clock-frequency", &xclk_freq); + if (ret) { + dev_err(imx290->dev, "Could not get xclk frequency\n"); + return ret; + } + + /* external clock must be 37.125 MHz */ + if (xclk_freq != 37125000) { + dev_err(imx290->dev, "External clock frequency %u is not supported\n", + xclk_freq); + return -EINVAL; + } + + ret = clk_set_rate(imx290->xclk, xclk_freq); + if (ret) { + dev_err(imx290->dev, "Could not set xclk frequency\n"); + return ret; + } + + return 0; +} + /* * Returns 0 if all link frequencies used by the driver for the given number * of MIPI data lanes are mentioned in the device tree, or the value of the @@ -1204,7 +1232,6 @@ static int imx290_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct imx290 *imx290; - u32 xclk_freq; int ret; imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); @@ -1222,32 +1249,12 @@ static int imx290_probe(struct i2c_client *client) if (ret) return ret; - /* get system clock (xclk) */ + /* Acquire resources. */ imx290->xclk = devm_clk_get(dev, "xclk"); if (IS_ERR(imx290->xclk)) return dev_err_probe(dev, PTR_ERR(imx290->xclk), "Could not get xclk"); - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &xclk_freq); - if (ret) { - dev_err(dev, "Could not get xclk frequency\n"); - return ret; - } - - /* external clock must be 37.125 MHz */ - if (xclk_freq != 37125000) { - dev_err(dev, "External clock frequency %u is not supported\n", - xclk_freq); - return -EINVAL; - } - - ret = clk_set_rate(imx290->xclk, xclk_freq); - if (ret) { - dev_err(dev, "Could not set xclk frequency\n"); - return ret; - } - ret = imx290_get_regulators(dev, imx290); if (ret < 0) return dev_err_probe(dev, ret, "Cannot get regulators\n"); @@ -1258,8 +1265,14 @@ static int imx290_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(imx290->rst_gpio), "Cannot get reset gpio\n"); + /* Initialize external clock frequency. */ + ret = imx290_init_clk(imx290); + if (ret) + return ret; + mutex_init(&imx290->lock); + /* Initialize and register subdev. */ ret = imx290_subdev_init(imx290); if (ret) goto err_mutex; From patchwork Mon Jan 16 14:44:50 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 643324 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E247DC63797 for ; Mon, 16 Jan 2023 14:56:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230063AbjAPO4k (ORCPT ); Mon, 16 Jan 2023 09:56:40 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52100 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232799AbjAPO4K (ORCPT ); Mon, 16 Jan 2023 09:56:10 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CF2E922031 for ; Mon, 16 Jan 2023 06:45:13 -0800 (PST) Received: from pendragon.ideasonboard.com (213-243-189-158.bb.dnainternet.fi [213.243.189.158]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4AC9D1211; Mon, 16 Jan 2023 15:45:12 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1673880312; bh=chk4lwu76avjoora6WI50YHlzUsWp+N10fd4v4VY9AU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sJIYqG5KJsW2jhLHyxSLahjIcyhBMo7L3oAcr/9Y0jpMGAIHo8T60qbLZRrL43Jxb Kxm4QC5buUXRFXioPFCqm+Jw6OsI0Om4ZR96j/jtyO8EdAG28tsM2BHH6+uczYdP8g WhjOhJibp+t5zFnsFGGgds4IiUD8bZDpfU5Z9iFI= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Sakari Ailus , Manivannan Sadhasivam , Alexander Stein , Dave Stevenson Subject: [PATCH v3 13/17] media: i2c: imx290: Use runtime PM autosuspend Date: Mon, 16 Jan 2023 16:44:50 +0200 Message-Id: <20230116144454.1012-14-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.38.2 In-Reply-To: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> References: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Use runtime PM autosuspend to avoid powering off the sensor during fast stop-reconfigure-restart cycles. This also fixes runtime PM handling in the probe function that didn't suspend the device, effectively leaving it resumed forever. While at it, improve documentation of power management in probe() and remove(). Signed-off-by: Laurent Pinchart Acked-by: Alexander Stein --- Changes since v2: - Use the correct error label when subdev registration fails Changes since v1: - Enable autosuspend before registering the subdev - Decrease the PM usage count after registering the subdev - Use pm_runtime_put_autosuspend() in imx290_set_ctrl() --- drivers/media/i2c/imx290.c | 58 +++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 7356279822e8..324d30ed5617 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -626,7 +626,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(imx290->dev); + pm_runtime_mark_last_busy(imx290->dev); + pm_runtime_put_autosuspend(imx290->dev); return ret; } @@ -827,12 +828,13 @@ static int imx290_set_stream(struct v4l2_subdev *sd, int enable) ret = imx290_start_streaming(imx290, state); if (ret) { dev_err(imx290->dev, "Start stream failed\n"); - pm_runtime_put(imx290->dev); + pm_runtime_put_sync(imx290->dev); goto unlock; } } else { imx290_stop_streaming(imx290); - pm_runtime_put(imx290->dev); + pm_runtime_mark_last_busy(imx290->dev); + pm_runtime_put_autosuspend(imx290->dev); } unlock: @@ -1269,33 +1271,59 @@ static int imx290_probe(struct i2c_client *client) if (ret) return ret; - /* Initialize and register subdev. */ + /* Initialize the V4L2 subdev. */ ret = imx290_subdev_init(imx290); if (ret) return ret; - ret = v4l2_async_register_subdev(&imx290->sd); - if (ret < 0) { - dev_err(dev, "Could not register v4l2 device\n"); - goto err_subdev; - } - - /* Power on the device to match runtime PM state below */ + /* + * Enable power management. The driver supports runtime PM, but needs to + * work when runtime PM is disabled in the kernel. To that end, power + * the sensor on manually here. + */ ret = imx290_power_on(dev); if (ret < 0) { dev_err(dev, "Could not power on the device\n"); goto err_subdev; } + /* + * Enable runtime PM with autosuspend. As the device has been powered + * manually, mark it as active, and increase the usage count without + * resuming the device. + */ pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); pm_runtime_enable(dev); - pm_runtime_idle(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + /* + * Finally, register the V4L2 subdev. This must be done after + * initializing everything as the subdev can be used immediately after + * being registered. + */ + ret = v4l2_async_register_subdev(&imx290->sd); + if (ret < 0) { + dev_err(dev, "Could not register v4l2 device\n"); + goto err_pm; + } + + /* + * Decrease the PM usage count. The device will get suspended after the + * autosuspend delay, turning the power off. + */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return 0; +err_pm: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + imx290_power_off(dev); err_subdev: imx290_subdev_cleanup(imx290); - return ret; } @@ -1307,6 +1335,10 @@ static void imx290_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); imx290_subdev_cleanup(imx290); + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ pm_runtime_disable(imx290->dev); if (!pm_runtime_status_suspended(imx290->dev)) imx290_power_off(imx290->dev); From patchwork Mon Jan 16 14:44:51 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 643323 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 09C52C54EBE for ; Mon, 16 Jan 2023 14:56:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229810AbjAPO4n (ORCPT ); Mon, 16 Jan 2023 09:56:43 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52110 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232800AbjAPO4K (ORCPT ); Mon, 16 Jan 2023 09:56:10 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3321822A0F for ; Mon, 16 Jan 2023 06:45:15 -0800 (PST) Received: from pendragon.ideasonboard.com (213-243-189-158.bb.dnainternet.fi [213.243.189.158]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A7D8AE7B; Mon, 16 Jan 2023 15:45:13 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1673880313; bh=c0ZZ8wsBeQ6eBljoEGCtuwjhuG80H1IwisAC/6peUDM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lsyw7ZOJoB8FoLDxHh+XeMscKI7IgdF8ETrhhd/W4jnrC4y5ZmDKGzJ9l2TzIZcPh yyMEJdq4DNYNSTAkYnb9Mr9opAOd11rnOQ8UyWRp+oesB9coYkWdxeinBbpcCPsuTN vQYnicsLFZRFmQXFgCb7Yl7z3417HkZf1apuuAac= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Sakari Ailus , Manivannan Sadhasivam , Alexander Stein , Dave Stevenson Subject: [PATCH v3 14/17] media: i2c: imx290: Initialize runtime PM before subdev Date: Mon, 16 Jan 2023 16:44:51 +0200 Message-Id: <20230116144454.1012-15-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.38.2 In-Reply-To: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> References: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Initializing the subdev before runtime PM means that no subdev initialization can interact with the runtime PM framework. This can be problematic when modifying controls, as the .s_ctrl() handler commonly calls pm_runtime_get_if_in_use(). These code paths are not trivial, making the driver fragile and possibly causing subtle bugs. To make the subdev initialization more robust, initialize runtime PM first. Signed-off-by: Laurent Pinchart Acked-by: Alexander Stein --- Changes since v1: - Use the correct error label when subdev registration fails --- drivers/media/i2c/imx290.c | 59 ++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 324d30ed5617..4185835f065d 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -581,9 +581,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) /* * Return immediately for controls that don't need to be applied to the - * device. Those controls are modified in imx290_ctrl_update(), which - * is called at probe time before runtime PM is initialized, so we - * can't proceed to the pm_runtime_get_if_in_use() call below. + * device. */ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) return 0; @@ -1049,22 +1047,20 @@ static void imx290_subdev_cleanup(struct imx290 *imx290) * Power management */ -static int imx290_power_on(struct device *dev) +static int imx290_power_on(struct imx290 *imx290) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct imx290 *imx290 = to_imx290(sd); int ret; ret = clk_prepare_enable(imx290->xclk); if (ret) { - dev_err(dev, "Failed to enable clock\n"); + dev_err(imx290->dev, "Failed to enable clock\n"); return ret; } ret = regulator_bulk_enable(ARRAY_SIZE(imx290->supplies), imx290->supplies); if (ret) { - dev_err(dev, "Failed to enable regulators\n"); + dev_err(imx290->dev, "Failed to enable regulators\n"); clk_disable_unprepare(imx290->xclk); return ret; } @@ -1079,20 +1075,33 @@ static int imx290_power_on(struct device *dev) return 0; } -static int imx290_power_off(struct device *dev) +static void imx290_power_off(struct imx290 *imx290) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct imx290 *imx290 = to_imx290(sd); - clk_disable_unprepare(imx290->xclk); gpiod_set_value_cansleep(imx290->rst_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(imx290->supplies), imx290->supplies); +} + +static int imx290_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx290 *imx290 = to_imx290(sd); + + return imx290_power_on(imx290); +} + +static int imx290_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx290 *imx290 = to_imx290(sd); + + imx290_power_off(imx290); return 0; } static const struct dev_pm_ops imx290_pm_ops = { - SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL) + SET_RUNTIME_PM_OPS(imx290_runtime_suspend, imx290_runtime_resume, NULL) }; /* ---------------------------------------------------------------------------- @@ -1271,20 +1280,15 @@ static int imx290_probe(struct i2c_client *client) if (ret) return ret; - /* Initialize the V4L2 subdev. */ - ret = imx290_subdev_init(imx290); - if (ret) - return ret; - /* * Enable power management. The driver supports runtime PM, but needs to * work when runtime PM is disabled in the kernel. To that end, power * the sensor on manually here. */ - ret = imx290_power_on(dev); + ret = imx290_power_on(imx290); if (ret < 0) { dev_err(dev, "Could not power on the device\n"); - goto err_subdev; + return ret; } /* @@ -1298,6 +1302,11 @@ static int imx290_probe(struct i2c_client *client) pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); + /* Initialize the V4L2 subdev. */ + ret = imx290_subdev_init(imx290); + if (ret) + goto err_pm; + /* * Finally, register the V4L2 subdev. This must be done after * initializing everything as the subdev can be used immediately after @@ -1306,7 +1315,7 @@ static int imx290_probe(struct i2c_client *client) ret = v4l2_async_register_subdev(&imx290->sd); if (ret < 0) { dev_err(dev, "Could not register v4l2 device\n"); - goto err_pm; + goto err_subdev; } /* @@ -1318,12 +1327,12 @@ static int imx290_probe(struct i2c_client *client) return 0; +err_subdev: + imx290_subdev_cleanup(imx290); err_pm: pm_runtime_disable(dev); pm_runtime_put_noidle(dev); - imx290_power_off(dev); -err_subdev: - imx290_subdev_cleanup(imx290); + imx290_power_off(imx290); return ret; } @@ -1341,7 +1350,7 @@ static void imx290_remove(struct i2c_client *client) */ pm_runtime_disable(imx290->dev); if (!pm_runtime_status_suspended(imx290->dev)) - imx290_power_off(imx290->dev); + imx290_power_off(imx290); pm_runtime_set_suspended(imx290->dev); } From patchwork Mon Jan 16 14:44:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 643322 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 73D43C46467 for ; Mon, 16 Jan 2023 14:56:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232817AbjAPO4p (ORCPT ); Mon, 16 Jan 2023 09:56:45 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52148 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231562AbjAPO4M (ORCPT ); Mon, 16 Jan 2023 09:56:12 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9555222DCD for ; Mon, 16 Jan 2023 06:45:18 -0800 (PST) Received: from pendragon.ideasonboard.com (213-243-189-158.bb.dnainternet.fi [213.243.189.158]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6EF1F18D7; Mon, 16 Jan 2023 15:45:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1673880316; bh=m2ZN94ifHW8JD6k3IDffnI8FozK8zvvFG4WWASpDBPQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=u6ot23OPNyb4iSEHgQv2Ie7CrJe/pg2m/I6w+9oQjhsnbLCBcpGEVBDzVt5Az55f4 wKZITL0QIEXNTnkbFV4sTY+nAh/5pge2pi0hTeBm0YifmHYvWkk5cx9CjwJ6ubbtjH 1gpnt66i71gPdLighVEUziy+JPp5BQ3uYdCFb61E= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Sakari Ailus , Manivannan Sadhasivam , Alexander Stein , Dave Stevenson Subject: [PATCH v3 16/17] media: i2c: imx290: Simplify imx290_set_data_lanes() Date: Mon, 16 Jan 2023 16:44:53 +0200 Message-Id: <20230116144454.1012-17-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.38.2 In-Reply-To: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> References: <20230116144454.1012-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org There's no need to check for an incorrect number of data lanes in imx290_set_data_lanes() as the value is validated at probe() time. Drop the check. The PHY_LANE_NUM and CSI_LANE_MODE registers are programmed with a value equal to the number of lanes minus one. Compute it instead of handling it in the switch/case. Signed-off-by: Laurent Pinchart Reviewed-by: Alexander Stein --- drivers/media/i2c/imx290.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 34278d098218..bb8713813e29 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -512,28 +512,21 @@ static int imx290_set_register_array(struct imx290 *imx290, static int imx290_set_data_lanes(struct imx290 *imx290) { - int ret = 0, laneval, frsel; + int ret = 0; + u32 frsel; switch (imx290->nlanes) { case 2: - laneval = 0x01; + default: frsel = 0x02; break; case 4: - laneval = 0x03; frsel = 0x01; break; - default: - /* - * We should never hit this since the data lane count is - * validated in probe itself - */ - dev_err(imx290->dev, "Lane configuration not supported\n"); - return -EINVAL; } - imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret); - imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret); + imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret); + imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret); imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret); return ret;