// SPDX-License-Identifier: GPL-2.0 /* * A V4L2 driver for ST VD56G3 (Mono) and VD66GY (RGB) global shutter cameras. * Copyright (C) 2024, STMicroelectronics SA
*/
/* The VD56G3 is a portrait image sensor with native resolution of 1124x1364. */ #define VD56G3_NATIVE_WIDTH 1124 #define VD56G3_NATIVE_HEIGHT 1364 #define VD56G3_DEFAULT_MODE 0
staticint vd56g3_poll_reg(struct vd56g3 *sensor, u32 reg, u8 poll_val, int *err)
{ unsignedint val = 0; int ret;
if (err && *err) return *err;
/* * Timeout must be higher than longuest frame duration. With current * blanking constraints, frame duration can take up to 504ms.
*/
ret = regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val,
(val == poll_val), 2000,
600 * USEC_PER_MSEC);
if (ret && err)
*err = ret;
return ret;
}
staticint vd56g3_wait_state(struct vd56g3 *sensor, int state, int *err)
{ return vd56g3_poll_reg(sensor, VD56G3_REG_SYSTEM_FSM, state, err);
}
/* ----------------------------------------------------------------------------- * Controls: definitions, helpers and handlers
*/
static u8 vd56g3_get_bpp(__u32 code)
{ switch (code) { case MEDIA_BUS_FMT_Y8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SBGGR8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: default: return 8; case MEDIA_BUS_FMT_Y10_1X10: case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SRGGB10_1X10: case MEDIA_BUS_FMT_SBGGR10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: return 10;
}
}
static u8 vd56g3_get_datatype(__u32 code)
{ switch (code) { case MEDIA_BUS_FMT_Y8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SBGGR8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: default: return MIPI_CSI2_DT_RAW8; case MEDIA_BUS_FMT_Y10_1X10: case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SRGGB10_1X10: case MEDIA_BUS_FMT_SBGGR10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: return MIPI_CSI2_DT_RAW10;
}
}
staticint vd56g3_read_expo_cluster(struct vd56g3 *sensor, bool force_cur_val)
{
u64 exposure;
u64 again;
u64 dgain; int ret = 0;
/* * When 'force_cur_val' is enabled, save the ctrl value in 'cur.val' * instead of the normal 'val', this is used during poweroff to cache * volatile ctrls and enable coldstart.
*/
cci_read(sensor->regmap, VD56G3_REG_APPLIED_COARSE_EXPOSURE, &exposure,
&ret);
cci_read(sensor->regmap, VD56G3_REG_APPLIED_ANALOG_GAIN, &again, &ret);
cci_read(sensor->regmap, VD56G3_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret); if (ret) return ret;
staticint vd56g3_update_expo_cluster(struct vd56g3 *sensor, bool is_auto)
{
u8 expo_state = is_auto ? VD56G3_EXP_MODE_AUTO : VD56G3_EXP_MODE_MANUAL; int ret = 0;
if (sensor->ae_ctrl->is_new)
cci_write(sensor->regmap, VD56G3_REG_EXP_MODE, expo_state,
&ret);
/* In Auto expo, set coldstart parameters */ if (is_auto && sensor->ae_ctrl->is_new) {
cci_write(sensor->regmap,
VD56G3_REG_AE_COLDSTART_COARSE_EXPOSURE,
sensor->expo_ctrl->val, &ret);
cci_write(sensor->regmap, VD56G3_REG_AE_COLDSTART_ANALOG_GAIN,
sensor->again_ctrl->val, &ret);
cci_write(sensor->regmap, VD56G3_REG_AE_COLDSTART_DIGITAL_GAIN,
sensor->dgain_ctrl->val, &ret);
}
/* In Manual expo, set exposure, analog and digital gains */ if (!is_auto && sensor->expo_ctrl->is_new)
cci_write(sensor->regmap, VD56G3_REG_MANUAL_COARSE_EXPOSURE,
sensor->expo_ctrl->val, &ret);
if (!is_auto && sensor->again_ctrl->is_new)
cci_write(sensor->regmap, VD56G3_REG_MANUAL_ANALOG_GAIN,
sensor->again_ctrl->val, &ret);
state = v4l2_subdev_get_locked_active_state(&sensor->sd);
crop = v4l2_subdev_state_get_crop(state, 0);
if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) return 0;
/* Update controls state, range, etc. whatever the state of the HW */ switch (ctrl->id) { case V4L2_CID_VBLANK:
frame_length = crop->height + ctrl->val;
expo_max = frame_length - VD56G3_EXPOSURE_MARGIN;
ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl,
VD56G3_EXPOSURE_MIN, expo_max, 1,
min(VD56G3_EXPOSURE_DEFAULT,
expo_max)); break; case V4L2_CID_EXPOSURE_AUTO:
is_auto = (ctrl->val == V4L2_EXPOSURE_AUTO);
__v4l2_ctrl_grab(sensor->ae_lock_ctrl, !is_auto);
__v4l2_ctrl_grab(sensor->ae_bias_ctrl, !is_auto); break; default: break;
}
if (ret) return ret;
/* Interact with HW only when it is powered ON */ if (!pm_runtime_get_if_in_use(sensor->dev)) return 0;
switch (ctrl->id) { case V4L2_CID_HFLIP:
ret = cci_write(sensor->regmap, VD56G3_REG_ORIENTATION,
sensor->hflip_ctrl->val |
(sensor->vflip_ctrl->val << 1),
NULL); break; case V4L2_CID_TEST_PATTERN:
ret = vd56g3_update_patgen(sensor, ctrl->val); break; case V4L2_CID_EXPOSURE_AUTO:
ret = vd56g3_update_expo_cluster(sensor, is_auto); break; case V4L2_CID_3A_LOCK:
ret = vd56g3_lock_exposure(sensor, ctrl->val); break; case V4L2_CID_AUTO_EXPOSURE_BIAS:
ae_compensation =
DIV_ROUND_CLOSEST((int)vd56g3_ev_bias_qmenu[ctrl->val] *
256, 1000);
ret = cci_write(sensor->regmap, VD56G3_REG_AE_COMPENSATION,
ae_compensation, NULL); break; case V4L2_CID_VBLANK:
ret = cci_write(sensor->regmap, VD56G3_REG_FRAME_LENGTH,
frame_length, NULL); break; case V4L2_CID_FLASH_LED_MODE:
ret = vd56g3_write_gpiox(sensor, sensor->ext_leds_mask); break; default:
ret = -EINVAL; break;
}
/* * Analog gain [1, 8] is computed with the following logic : * 32/(32 - again_reg), with again_reg in the range [0:28] * Digital gain [1.00, 8.00] is coded as a Fixed Point 5.8
*/
sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
0, 28, 1, 0);
sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
0x100, 0x800, 1, 0x100);
/* * Set the exposure, horizontal and vertical blanking ctrls * to hardcoded values, they will be updated in vd56g3_update_controls. * Exposure being in an auto-cluster, set a significant value here.
*/
sensor->expo_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
VD56G3_EXPOSURE_DEFAULT,
VD56G3_EXPOSURE_DEFAULT, 1,
VD56G3_EXPOSURE_DEFAULT);
sensor->hblank_ctrl =
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, 1, 1, 1, 1); if (sensor->hblank_ctrl)
sensor->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
sensor->vblank_ctrl =
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, 1, 1, 1, 1);
/* Additional control based on device tree properties */ if (sensor->ext_leds_mask)
sensor->led_ctrl =
v4l2_ctrl_new_std_menu(hdl, ops,
V4L2_CID_FLASH_LED_MODE,
V4L2_FLASH_LED_MODE_FLASH, 0,
V4L2_FLASH_LED_MODE_NONE);
if (hdl->error) {
ret = hdl->error; goto free_ctrls;
}
/* Optional controls coming from fwnode (e.g. rotation, orientation). */
ret = v4l2_fwnode_device_parse(sensor->dev, &fwnode_props); if (ret) goto free_ctrls;
ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props); if (ret) goto free_ctrls;
sensor->sd.ctrl_handler = hdl;
return 0;
free_ctrls:
v4l2_ctrl_handler_free(hdl);
return ret;
}
/* ----------------------------------------------------------------------------- * Pad ops
*/
/* Media bus code is dependent of : * - 8bits or 10bits output * - variant : Mono or RGB * - H/V flips parameters in case of RGB
*/ static u32 vd56g3_get_mbus_code(struct vd56g3 *sensor, u32 code)
{ unsignedint i_bpp; unsignedint j;
for (i_bpp = 0; i_bpp < ARRAY_SIZE(vd56g3_mbus_codes); i_bpp++) { for (j = 0; j < ARRAY_SIZE(vd56g3_mbus_codes[i_bpp]); j++) { if (vd56g3_mbus_codes[i_bpp][j] == code) goto endloops;
}
}
endloops: if (i_bpp >= ARRAY_SIZE(vd56g3_mbus_codes))
i_bpp = 0;
/* Setup default GPIO values; could be overridden by V4L2 ctrl setup */
ret = vd56g3_write_gpiox(sensor, GENMASK(VD56G3_NB_GPIOS - 1, 0)); if (ret) goto rpm_put;
/* Apply settings from V4L2 ctrls */
ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler); if (ret) goto rpm_put;
/* some controls are locked during streaming */
__v4l2_ctrl_grab(sensor->hflip_ctrl, true);
__v4l2_ctrl_grab(sensor->vflip_ctrl, true);
__v4l2_ctrl_grab(sensor->patgen_ctrl, true);
return ret;
rpm_put:
dev_err(sensor->dev, "Failed to start streaming\n");
pm_runtime_put_sync(sensor->dev);
/* power on */
ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies),
sensor->supplies); if (ret) {
dev_err(dev, "Failed to enable regulators: %d\n", ret); return ret;
}
ret = clk_prepare_enable(sensor->xclk); if (ret) {
dev_err(dev, "Failed to enable clock: %d\n", ret); goto disable_reg;
}
/* ----------------------------------------------------------------------------- * Probe and initialization
*/
staticint vd56g3_check_csi_conf(struct vd56g3 *sensor, struct fwnode_handle *endpoint)
{ struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
u32 phy_data_lanes[VD56G3_MAX_CSI_DATA_LANES] = { ~0, ~0 };
u8 n_lanes;
u64 frequency; int p, l; int ret = 0;
ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); if (ret) return -EINVAL;
/* Check lanes number */
n_lanes = ep.bus.mipi_csi2.num_data_lanes; if (n_lanes != 1 && n_lanes != 2) {
dev_err(sensor->dev, "Invalid data lane number: %d\n", n_lanes);
ret = -EINVAL; goto done;
}
sensor->nb_of_lane = n_lanes;
/* Clock lane must be first */ if (ep.bus.mipi_csi2.clock_lane != 0) {
dev_err(sensor->dev, "Clock lane must be mapped to lane 0\n");
ret = -EINVAL; goto done;
}
/* * Prepare Output Interface conf based on lane settings * logical to physical lane conversion (+ pad remaining slots)
*/ for (l = 0; l < n_lanes; l++)
phy_data_lanes[ep.bus.mipi_csi2.data_lanes[l] - 1] = l; for (p = 0; p < VD56G3_MAX_CSI_DATA_LANES; p++) { if (phy_data_lanes[p] != ~0) continue;
phy_data_lanes[p] = l;
l++;
}
sensor->oif_ctrl = n_lanes |
(ep.bus.mipi_csi2.lane_polarities[0] << 3) |
((phy_data_lanes[0]) << 4) |
(ep.bus.mipi_csi2.lane_polarities[1] << 6) |
((phy_data_lanes[1]) << 7) |
(ep.bus.mipi_csi2.lane_polarities[2] << 9);
/* Check link frequency */ if (!ep.nr_of_link_frequencies) {
dev_err(sensor->dev, "link-frequency not found in DT\n");
ret = -EINVAL; goto done;
}
frequency = (n_lanes == 2) ? VD56G3_LINK_FREQ_DEF_2LANES :
VD56G3_LINK_FREQ_DEF_1LANE; if (ep.nr_of_link_frequencies != 1 ||
ep.link_frequencies[0] != frequency) {
dev_err(sensor->dev, "Link frequency not supported: %lld\n",
ep.link_frequencies[0]);
ret = -EINVAL; goto done;
}
/* Initialize GPIOs to default */ for (i = 0; i < VD56G3_NB_GPIOS; i++)
sensor->gpios[i] = VD56G3_GPIOX_GPIO_IN;
sensor->ext_leds_mask = 0;
/* Take into account optional 'st,leds' output for GPIOs */
ret = vd56g3_parse_dt_gpios_array(sensor, "st,leds", led_gpios,
&nb_gpios_leds); if (ret) return ret; for (i = 0; i < nb_gpios_leds; i++) {
sensor->gpios[led_gpios[i]] = VD56G3_GPIOX_STROBE_MODE;
set_bit(led_gpios[i], &sensor->ext_leds_mask);
}
return 0;
}
staticint vd56g3_parse_dt(struct vd56g3 *sensor)
{ struct fwnode_handle *endpoint; int ret;
endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(sensor->dev), 0,
0, 0); if (!endpoint) {
dev_err(sensor->dev, "Endpoint node not found\n"); return -EINVAL;
}
ret = vd56g3_check_csi_conf(sensor, endpoint);
fwnode_handle_put(endpoint); if (ret) return ret;
/* External clock must be in [6Mhz-27Mhz] */ if (sensor->xclk_freq < VD56G3_XCLK_FREQ_MIN ||
sensor->xclk_freq > VD56G3_XCLK_FREQ_MAX) {
dev_err(sensor->dev, "Only 6Mhz-27Mhz clock range supported. Provided %lu MHz\n",
sensor->xclk_freq / HZ_PER_MHZ); return -EINVAL;
}
/* PLL input should be in [6Mhz-12Mhz[ */ for (i = 0; i < ARRAY_SIZE(predivs); i++) {
sensor->pll_prediv = predivs[i]; if (sensor->xclk_freq / sensor->pll_prediv < 12 * HZ_PER_MHZ) break;
}
/* PLL output clock must be as close as possible to 804Mhz */
sensor->pll_mult = (VD56G3_TARGET_PLL * sensor->pll_prediv +
sensor->xclk_freq / 2) /
sensor->xclk_freq;
pll_out = sensor->xclk_freq * sensor->pll_mult / sensor->pll_prediv;
/* Target Pixel Clock for standard 10bit ADC mode : 160.8Mhz */
sensor->pixel_clock = pll_out / VD56G3_VT_CLOCK_DIV;
/* Update controls according to the resolution set */
state = v4l2_subdev_lock_and_get_active_state(&sensor->sd);
ret = vd56g3_update_controls(sensor);
v4l2_subdev_unlock_state(state); if (ret) {
dev_err(sensor->dev, "Controls update failed: %d\n", ret); goto err_ctrls;
}
ret = vd56g3_parse_dt(sensor); if (ret) return dev_err_probe(dev, ret, "Failed to parse Device Tree\n");
/* Get (and check) resources : power regs, ext clock, reset gpio */
ret = vd56g3_get_regulators(sensor); if (ret) return dev_err_probe(dev, ret, "Failed to get regulators\n");
sensor->xclk = devm_clk_get(dev, NULL); if (IS_ERR(sensor->xclk)) return dev_err_probe(dev, PTR_ERR(sensor->xclk), "Failed to get xclk\n");
sensor->xclk_freq = clk_get_rate(sensor->xclk);
ret = vd56g3_prepare_clock_tree(sensor); if (ret) return ret;
sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH); if (IS_ERR(sensor->reset_gpio)) return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio), "Failed to get reset gpio\n");
sensor->regmap = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(sensor->regmap)) return dev_err_probe(dev, PTR_ERR(sensor->regmap), "Failed to init regmap\n");
/* Power ON */
ret = vd56g3_power_on(dev); if (ret) return dev_err_probe(dev, ret, "Sensor power on failed\n");
/* Enable PM runtime with autosuspend (sensor being ON, set active) */
pm_runtime_set_active(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
/* Check HW model/version */
ret = vd56g3_detect(sensor); if (ret) {
dev_err(dev, "Sensor detect failed: %d\n", ret); goto err_power_off;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.