// SPDX-License-Identifier: GPL-2.0 /* * V4L2 Support for the IMX283 * * Diagonal 15.86 mm (Type 1) CMOS Image Sensor with Square Pixel for Color * Cameras. * * Copyright (C) 2024 Ideas on Board Oy. * * Based on Sony IMX283 driver prepared by Will Whang * * Based on Sony imx477 camera driver * Copyright (C) 2019-2020 Raspberry Pi (Trading) Ltd
*/
/* Mode : resolution and related config values */ struct imx283_mode { unsignedint mode;
/* Bits per pixel */ unsignedint bpp;
/* Frame width */ unsignedint width;
/* Frame height */ unsignedint height;
/* * Minimum horizontal timing in pixel-units * * Note that HMAX is written in 72MHz units, and the datasheet assumes a * 720MHz link frequency. Convert datasheet values with the following: * * For 12 bpp modes (480Mbps) convert with: * hmax = [hmax in 72MHz units] * 480 / 72 * * For 10 bpp modes (576Mbps) convert with: * hmax = [hmax in 72MHz units] * 576 / 72
*/
u32 min_hmax;
/* minimum V-timing in lines */
u32 min_vmax;
/* default H-timing */
u32 default_hmax;
/* default V-timing */
u32 default_vmax;
/* minimum SHR */
u32 min_shr;
/* * Per-mode vertical crop constants used to calculate values * of IMX283REG_WIDCUT and IMX283_REG_VWINPOS.
*/
u32 veff;
u32 vst;
u32 vct;
/* Horizontal and vertical binning ratio */
u8 hbin_ratio;
u8 vbin_ratio;
staticinlinevoid get_mode_table(unsignedint code, conststruct imx283_mode **mode_list, unsignedint *num_modes)
{ switch (code) { case MEDIA_BUS_FMT_SRGGB12_1X12: case MEDIA_BUS_FMT_SGRBG12_1X12: case MEDIA_BUS_FMT_SGBRG12_1X12: case MEDIA_BUS_FMT_SBGGR12_1X12:
*mode_list = supported_modes_12bit;
*num_modes = ARRAY_SIZE(supported_modes_12bit); break;
case MEDIA_BUS_FMT_SRGGB10_1X10: case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: case MEDIA_BUS_FMT_SBGGR10_1X10:
*mode_list = supported_modes_10bit;
*num_modes = ARRAY_SIZE(supported_modes_10bit); break; default:
*mode_list = NULL;
*num_modes = 0; break;
}
}
/* Calculate the Pixel Rate based on the current mode */ static u64 imx283_pixel_rate(struct imx283 *imx283, conststruct imx283_mode *mode)
{
u64 link_frequency = link_frequencies[__ffs(imx283->link_freq_bitmap)]; unsignedint bpp = mode->bpp; constunsignedint ddr = 2; /* Double Data Rate */ constunsignedint lanes = 4; /* Only 4 lane support */
u64 numerator = link_frequency * ddr * lanes;
do_div(numerator, bpp);
return numerator;
}
/* Convert from a variable pixel_rate to 72 MHz clock cycles */ static u64 imx283_internal_clock(unsignedint pixel_rate, unsignedint pixels)
{ /* * Determine the following operation without overflow: * pixels = 72 Mhz / pixel_rate * * The internal clock at 72MHz and Pixel Rate (between 240 and 576MHz) * can easily overflow this calculation, so pre-divide to simplify.
*/ const u32 iclk_pre = 72; const u32 pclk_pre = pixel_rate / HZ_PER_MHZ;
u64 numerator = pixels * iclk_pre;
do_div(numerator, pclk_pre);
return numerator;
}
/* Internal clock (72MHz) to Pixel Rate clock (Variable) */ static u64 imx283_iclk_to_pix(unsignedint pixel_rate, unsignedint cycles)
{ /* * Determine the following operation without overflow: * cycles * pixel_rate / 72 MHz * * The internal clock at 72MHz and Pixel Rate (between 240 and 576MHz) * can easily overflow this calculation, so pre-divide to simplify.
*/ const u32 iclk_pre = 72; const u32 pclk_pre = pixel_rate / HZ_PER_MHZ;
u64 numerator = cycles * pclk_pre;
do_div(numerator, iclk_pre);
return numerator;
}
/* Determine the exposure based on current hmax, vmax and a given SHR */ static u32 imx283_exposure(struct imx283 *imx283, conststruct imx283_mode *mode, u64 shr)
{
u32 svr = 0; /* SVR feature is not currently supported */
u32 offset;
u64 numerator;
/* Number of clocks per internal offset period */
offset = mode->mode == IMX283_MODE_0 ? 209 : 157;
numerator = (imx283->vmax * (svr + 1) - shr) * imx283->hmax + offset;
do_div(numerator, imx283->hmax);
return clamp(numerator, 0, U32_MAX);
}
staticvoid imx283_exposure_limits(struct imx283 *imx283, conststruct imx283_mode *mode,
s64 *min_exposure, s64 *max_exposure)
{
u32 svr = 0; /* SVR feature is not currently supported */
u64 min_shr = mode->min_shr; /* Global Shutter is not supported */
u64 max_shr = (svr + 1) * imx283->vmax - 4;
/* * Integration Time [s] = [ {VMAX x (SVR + 1) – (SHR)} x HMAX + offset ] * / [ 72 x 10^6 ]
*/ static u32 imx283_shr(struct imx283 *imx283, conststruct imx283_mode *mode,
u32 exposure)
{
u32 svr = 0; /* SVR feature is not currently supported */
u32 offset;
u64 temp;
/* Number of clocks per internal offset period */
offset = mode->mode == IMX283_MODE_0 ? 209 : 157;
temp = ((u64)exposure * imx283->hmax - offset);
do_div(temp, imx283->hmax);
return (imx283->vmax * (svr + 1) - temp);
}
staticconstchar * const imx283_tpg_menu[] = { "Disabled", "All 000h", "All FFFh", "All 555h", "All AAAh", "Horizontal color bars", "Vertical color bars",
};
/* * The VBLANK control may change the limits of usable exposure, so check * and adjust if necessary.
*/ if (ctrl->id == V4L2_CID_VBLANK) { /* Honour the VBLANK limits when setting exposure. */
s64 current_exposure, max_exposure, min_exposure;
case V4L2_CID_ANALOGUE_GAIN:
ret = cci_write(imx283->cci, IMX283_REG_ANALOG_GAIN, ctrl->val, NULL); break;
case V4L2_CID_DIGITAL_GAIN:
ret = cci_write(imx283->cci, IMX283_REG_DIGITAL_GAIN, ctrl->val, NULL); break;
case V4L2_CID_VFLIP: /* * VFLIP is managed by BIT(0) of IMX283_REG_HTRIMMING address, hence * both need to be set simultaneously.
*/ if (ctrl->val) {
cci_write(imx283->cci, IMX283_REG_HTRIMMING,
IMX283_HTRIMMING_EN | IMX283_MDVREV, &ret);
} else {
cci_write(imx283->cci, IMX283_REG_HTRIMMING,
IMX283_HTRIMMING_EN, &ret);
} break;
case V4L2_CID_TEST_PATTERN:
ret = imx283_update_test_pattern(imx283, ctrl->val); break;
default:
dev_err(imx283->dev, "ctrl(id:0x%x, val:0x%x) is not handled\n",
ctrl->id, ctrl->val); break;
}
ret = regulator_bulk_enable(ARRAY_SIZE(imx283_supply_name),
imx283->supplies); if (ret) {
dev_err(imx283->dev, "failed to enable regulators\n"); return ret;
}
ret = clk_prepare_enable(imx283->xclk); if (ret) {
dev_err(imx283->dev, "failed to enable clock\n"); goto reg_off;
}
/* Verify chip ID */ staticint imx283_identify_module(struct imx283 *imx283)
{ int ret;
u64 val;
ret = cci_read(imx283->cci, IMX283_REG_CHIP_ID, &val, NULL); if (ret) {
dev_err(imx283->dev, "failed to read chip id %x, with error %d\n",
IMX283_CHIP_ID, ret); return ret;
}
if (val != IMX283_CHIP_ID) {
dev_err(imx283->dev, "chip id mismatch: %x!=%llx\n",
IMX283_CHIP_ID, val); return -EIO;
}
fwnode = dev_fwnode(imx283->dev);
ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) {
dev_err(imx283->dev, "Failed to get next endpoint\n"); return -ENXIO;
}
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
fwnode_handle_put(ep); if (ret) return ret;
if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
dev_err(imx283->dev, "number of CSI2 data lanes %d is not supported\n",
bus_cfg.bus.mipi_csi2.num_data_lanes);
ret = -EINVAL; goto done_endpoint_free;
}
ret = v4l2_link_freq_to_bitmap(imx283->dev, bus_cfg.link_frequencies,
bus_cfg.nr_of_link_frequencies,
link_frequencies, ARRAY_SIZE(link_frequencies),
&imx283->link_freq_bitmap);
imx283->cci = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(imx283->cci)) {
ret = PTR_ERR(imx283->cci);
dev_err(imx283->dev, "failed to initialize CCI: %d\n", ret); return ret;
}
/* Get system clock (xclk) */
imx283->xclk = devm_clk_get(imx283->dev, NULL); if (IS_ERR(imx283->xclk)) { return dev_err_probe(imx283->dev, PTR_ERR(imx283->xclk), "failed to get xclk\n");
}
xclk_freq = clk_get_rate(imx283->xclk); for (i = 0; i < ARRAY_SIZE(imx283_frequencies); i++) { if (xclk_freq == imx283_frequencies[i].mhz) {
imx283->freq = &imx283_frequencies[i]; break;
}
} if (!imx283->freq) {
dev_err(imx283->dev, "xclk frequency unsupported: %d Hz\n", xclk_freq); return -EINVAL;
}
ret = imx283_get_regulators(imx283); if (ret) { return dev_err_probe(imx283->dev, ret, "failed to get regulators\n");
}
ret = imx283_parse_endpoint(imx283); if (ret) {
dev_err(imx283->dev, "failed to parse endpoint configuration\n"); return ret;
}
/* Request optional enable pin */
imx283->reset_gpio = devm_gpiod_get_optional(imx283->dev, "reset",
GPIOD_OUT_LOW); if (IS_ERR(imx283->reset_gpio)) return dev_err_probe(imx283->dev, PTR_ERR(imx283->reset_gpio), "failed to get reset GPIO\n");
/* * The sensor must be powered for imx283_identify_module() * to be able to read the CHIP_ID register
*/
ret = imx283_power_on(imx283->dev); if (ret) return ret;
ret = imx283_identify_module(imx283); if (ret) goto error_power_off;
/* * 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(imx283->dev);
pm_runtime_get_noresume(imx283->dev);
pm_runtime_enable(imx283->dev);
pm_runtime_set_autosuspend_delay(imx283->dev, 1000);
pm_runtime_use_autosuspend(imx283->dev);
/* This needs the pm runtime to be registered. */
ret = imx283_init_controls(imx283); if (ret) goto error_pm;
ret = media_entity_pads_init(&imx283->sd.entity, 1, &imx283->pad); if (ret) {
dev_err(imx283->dev, "failed to init entity pads: %d\n", ret); goto error_handler_free;
}
imx283->sd.state_lock = imx283->ctrl_handler.lock;
ret = v4l2_subdev_init_finalize(&imx283->sd); if (ret < 0) {
dev_err(imx283->dev, "subdev init error: %d\n", ret); goto error_media_entity;
}
ret = v4l2_async_register_subdev_sensor(&imx283->sd); if (ret < 0) {
dev_err(imx283->dev, "failed to register sensor sub-device: %d\n", ret); goto error_subdev_cleanup;
}
/* * Decrease the PM usage count. The device will get suspended after the * autosuspend delay, turning the power off.
*/
pm_runtime_put_autosuspend(imx283->dev);
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.8Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-05)
¤
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.