/* * The IMX415 data sheet uses lane rates but v4l2 uses link frequency to * describe MIPI CSI-2 speed. This driver uses lane rates wherever possible * and converts them to link frequencies by a factor of two when needed.
*/ staticconst s64 link_freq_menu_items[] = { 594000000 / 2, 720000000 / 2, 891000000 / 2, 1440000000 / 2, 1485000000 / 2,
};
/* * This table includes fixed register settings and a bunch of undocumented * registers that have to be set to another value than default.
*/ staticconststruct cci_reg_sequence imx415_init_table[] = { /* use all-pixel readout mode, no flip */
{ IMX415_WINMODE, 0x00 },
{ IMX415_ADDMODE, 0x00 },
{ IMX415_REVERSE, 0x00 }, /* use RAW 10-bit mode */
{ IMX415_ADBIT, 0x00 },
{ IMX415_MDBIT, 0x00 }, /* output VSYNC on XVS and low on XHS */
{ IMX415_OUTSEL, 0x22 },
{ IMX415_DRV, 0x00 },
if (!pm_runtime_get_if_in_use(sensor->dev)) return0;
switch (ctrl->id) { case V4L2_CID_VBLANK:
ret = cci_write(sensor->regmap, IMX415_VMAX,
format->height + ctrl->val, NULL); if (ret) return ret; /* * Exposure is set based on VMAX which has just changed, so * program exposure register as well
*/
ctrl = sensor->exposure;
fallthrough; case V4L2_CID_EXPOSURE: /* clamp the exposure value to VMAX. */
vmax = format->height + sensor->vblank->cur.val;
ctrl->val = min_t(int, ctrl->val, vmax);
ret = cci_write(sensor->regmap, IMX415_SHR0,
vmax - ctrl->val, NULL); break;
case V4L2_CID_ANALOGUE_GAIN: /* analogue gain in 0.3 dB step size */
ret = cci_write(sensor->regmap, IMX415_GAIN_PCG_0,
ctrl->val, NULL); break;
case V4L2_CID_HFLIP: case V4L2_CID_VFLIP:
flip = (sensor->hflip->val << IMX415_HREVERSE_SHIFT) |
(sensor->vflip->val << IMX415_VREVERSE_SHIFT);
ret = cci_write(sensor->regmap, IMX415_REVERSE, flip, NULL); break;
case V4L2_CID_TEST_PATTERN:
ret = imx415_set_testpattern(sensor, ctrl->val); break;
case V4L2_CID_HBLANK:
ret = cci_write(sensor->regmap, IMX415_HMAX,
(format->width + ctrl->val) /
IMX415_HMAX_MULTIPLIER,
NULL); break;
ret = v4l2_fwnode_device_parse(sensor->dev, &props); if (ret < 0) return ret;
v4l2_ctrl_handler_init(&sensor->ctrls, 10);
for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); ++i) { if (lane_rate == link_freq_menu_items[i] * 2) break;
} if (i == ARRAY_SIZE(link_freq_menu_items)) { return dev_err_probe(sensor->dev, -EINVAL, "lane rate %llu not supported\n",
lane_rate);
}
ctrl = v4l2_ctrl_new_int_menu(&sensor->ctrls, &imx415_ctrl_ops,
V4L2_CID_LINK_FREQ,
ARRAY_SIZE(link_freq_menu_items) - 1, i,
link_freq_menu_items);
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
staticint imx415_wakeup(struct imx415 *sensor)
{ int ret;
ret = cci_write(sensor->regmap, IMX415_MODE,
IMX415_MODE_OPERATING, NULL); if (ret) return ret;
/* * According to the datasheet we have to wait at least 63 us after * leaving standby mode. But this doesn't work even after 30 ms. * So probably this should be 63 ms and therefore we wait for 80 ms.
*/
msleep(80);
return0;
}
staticint imx415_stream_on(struct imx415 *sensor)
{ int ret;
ret = imx415_wakeup(sensor); return cci_write(sensor->regmap, IMX415_XMSTA,
IMX415_XMSTA_START, &ret);
}
staticint imx415_stream_off(struct imx415 *sensor)
{ int ret;
staticint imx415_s_stream(struct v4l2_subdev *sd, int enable)
{ struct imx415 *sensor = to_imx415(sd); struct v4l2_subdev_state *state; int ret;
state = v4l2_subdev_lock_and_get_active_state(sd);
if (!enable) {
ret = imx415_stream_off(sensor);
pm_runtime_put_autosuspend(sensor->dev);
goto unlock;
}
ret = pm_runtime_resume_and_get(sensor->dev); if (ret < 0) goto unlock;
ret = imx415_setup(sensor, state); if (ret) goto err_pm;
ret = __v4l2_ctrl_handler_setup(&sensor->ctrls); if (ret < 0) goto err_pm;
ret = imx415_stream_on(sensor); if (ret) goto err_pm;
ret = 0;
unlock:
v4l2_subdev_unlock_state(state);
return ret;
err_pm: /* * In case of error, turn the power off synchronously as the device * likely has no other chance to recover.
*/
pm_runtime_put_sync(sensor->dev);
staticint imx415_power_on(struct imx415 *sensor)
{ int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies),
sensor->supplies); if (ret < 0) return ret;
gpiod_set_value_cansleep(sensor->reset, 0);
udelay(1);
ret = clk_prepare_enable(sensor->clk); if (ret < 0) goto err_reset;
/* * Data sheet states that 20 us are required before communication start, * but this doesn't work in all cases. Use 100 us to be on the safe * side.
*/
usleep_range(100, 200);
staticint imx415_identify_model(struct imx415 *sensor)
{ int model, ret;
u64 chip_id;
/* * While most registers can be read when the sensor is in standby, this * is not the case of the sensor info register :-(
*/
ret = imx415_wakeup(sensor); if (ret) return dev_err_probe(sensor->dev, ret, "failed to get sensor out of standby\n");
ret = cci_read(sensor->regmap, IMX415_SENSOR_INFO, &chip_id, NULL); if (ret < 0) {
dev_err_probe(sensor->dev, ret, "failed to read sensor information\n"); goto done;
}
model = chip_id & IMX415_SENSOR_INFO_MASK;
switch (model) { case IMX415_CHIP_ID:
dev_info(sensor->dev, "Detected IMX415 image sensor\n"); break; default:
ret = dev_err_probe(sensor->dev, -ENODEV, "invalid device model 0x%04x\n", model); goto done;
}
for (i = 0; i < ARRAY_SIZE(sensor->supplies); ++i)
sensor->supplies[i].supply = imx415_supply_names[i];
ret = devm_regulator_bulk_get(sensor->dev, ARRAY_SIZE(sensor->supplies),
sensor->supplies); if (ret) return dev_err_probe(sensor->dev, ret, "failed to get supplies\n");
sensor->reset = devm_gpiod_get_optional(sensor->dev, "reset",
GPIOD_OUT_HIGH); if (IS_ERR(sensor->reset)) return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset), "failed to get reset GPIO\n");
sensor->clk = devm_clk_get(sensor->dev, NULL); if (IS_ERR(sensor->clk)) return dev_err_probe(sensor->dev, PTR_ERR(sensor->clk), "failed to get clock\n");
ep = fwnode_graph_get_next_endpoint(dev_fwnode(sensor->dev), NULL); if (!ep) return -ENXIO;
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
fwnode_handle_put(ep); if (ret) return ret;
switch (bus_cfg.bus.mipi_csi2.num_data_lanes) { case2: case4:
sensor->num_data_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; break; default:
ret = dev_err_probe(sensor->dev, -EINVAL, "invalid number of CSI2 data lanes %d\n",
bus_cfg.bus.mipi_csi2.num_data_lanes); goto done_endpoint_free;
}
if (!bus_cfg.nr_of_link_frequencies) {
ret = dev_err_probe(sensor->dev, -EINVAL, "no link frequencies defined"); goto done_endpoint_free;
}
/* * Check if there exists a sensor mode defined for current INCK, * number of lanes and given lane rates.
*/
inck = clk_get_rate(sensor->clk); for (i = 0; i < bus_cfg.nr_of_link_frequencies; ++i) { if (imx415_check_inck(inck, bus_cfg.link_frequencies[i])) {
dev_dbg(sensor->dev, "INCK %lu Hz not supported for this link freq",
inck); continue;
}
for (j = 0; j < ARRAY_SIZE(supported_modes); ++j) { if (bus_cfg.link_frequencies[i] * 2 !=
supported_modes[j].lane_rate) continue;
sensor->cur_mode = j; break;
} if (j < ARRAY_SIZE(supported_modes)) break;
} if (i == bus_cfg.nr_of_link_frequencies) {
ret = dev_err_probe(sensor->dev, -EINVAL, "no valid sensor mode defined\n"); goto done_endpoint_free;
} switch (inck) { case27000000: case37125000: case74250000:
sensor->pixel_rate = IMX415_PIXEL_RATE_74_25MHZ; break; case24000000: case72000000:
sensor->pixel_rate = IMX415_PIXEL_RATE_72MHZ; break;
}
lane_rate = supported_modes[sensor->cur_mode].lane_rate; for (i = 0; i < ARRAY_SIZE(imx415_clk_params); ++i) { if (lane_rate == imx415_clk_params[i].lane_rate &&
inck == imx415_clk_params[i].inck) {
sensor->clk_params = &imx415_clk_params[i]; break;
}
} if (i == ARRAY_SIZE(imx415_clk_params)) {
ret = dev_err_probe(sensor->dev, -EINVAL, "Mode %d not supported\n",
sensor->cur_mode); goto done_endpoint_free;
}
staticint imx415_probe(struct i2c_client *client)
{ struct imx415 *sensor; int ret;
sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) return -ENOMEM;
sensor->dev = &client->dev;
ret = imx415_parse_hw_config(sensor); if (ret) return ret;
sensor->regmap = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(sensor->regmap)) return PTR_ERR(sensor->regmap);
/* * 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, identify it, and fully initialize it.
*/
ret = imx415_power_on(sensor); if (ret) return ret;
ret = imx415_identify_model(sensor); if (ret) goto err_power;
ret = imx415_subdev_init(sensor); if (ret) goto err_power;
/* * Enable runtime PM. As the device has been powered manually, mark it * as active, and increase the usage count without resuming the device.
*/
pm_runtime_set_active(sensor->dev);
pm_runtime_get_noresume(sensor->dev);
pm_runtime_enable(sensor->dev);
ret = v4l2_async_register_subdev_sensor(&sensor->subdev); if (ret < 0) goto err_pm;
/* * Finally, enable autosuspend and decrease the usage count. The device * will get suspended after the autosuspend delay, turning the power * off.
*/
pm_runtime_set_autosuspend_delay(sensor->dev, 1000);
pm_runtime_use_autosuspend(sensor->dev);
pm_runtime_put_autosuspend(sensor->dev);
/* * Disable runtime PM. In case runtime PM is disabled in the kernel, * make sure to turn power off manually.
*/
pm_runtime_disable(sensor->dev); if (!pm_runtime_status_suspended(sensor->dev))
imx415_power_off(sensor);
pm_runtime_set_suspended(sensor->dev);
}
¤ 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.0.26Bemerkung:
(vorverarbeitet am 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.