/* * The value that we write to the register is the index in the * data-lanes array, so we need to do a conversion. Do this in the same * pass as validating data-lanes.
*/ for (i = 0; i < num_lanes; i++) { if (lanes[i] < 1 || lanes[i] > 4) return -EINVAL;
if (used_lanes & (BIT(lanes[i]))) return -EINVAL;
used_lanes |= BIT(lanes[i]);
/* * data-lanes is 1-indexed while the field position in the * register is 0-indexed.
*/
val |= i << ((lanes[i] - 1) * 2);
}
*lane_remap = val;
return0;
}
staticint thp7312_set_mipi_lanes(struct thp7312_device *thp7312)
{ struct device *dev = thp7312->dev; int ret = 0;
u64 val;
ret = thp7312_read_poll_timeout(thp7312, TH7312_REG_CUSTOM_MIPI_STATUS,
val, val == 0x00, 100000, 2000000); if (ret) {
dev_err(dev, "Failed to poll MIPI lane status: %d\n", ret); return ret;
}
/* * TODO: The mode and rate should be cached in the subdev state, once * support for extending states will be available.
*/
fmt = v4l2_subdev_state_get_format(sd_state, 0);
interval = v4l2_subdev_state_get_interval(sd_state, 0);
/* * The minimum reset duration is 8 clock cycles, make it 10 to provide * a safety margin.
*/
rate = clk_get_rate(thp7312->iclk);
fsleep(DIV_ROUND_UP(10 * USEC_PER_SEC, rate));
gpiod_set_value_cansleep(thp7312->reset_gpio, 0);
/* * TODO: The documentation states that the device needs 2ms to * initialize after reset is deasserted. It then proceeds to load the * firmware from the flash memory, which takes an unspecified amount of * time. Check if this delay could be reduced.
*/
fsleep(300000);
}
/* ----------------------------------------------------------------------------- * Power Management
*/
/* Avoid divisions by 0, pick the highest frame if the interval is 0. */
fps = fi->interval.numerator
? DIV_ROUND_CLOSEST(fi->interval.denominator, fi->interval.numerator)
: UINT_MAX;
staticint thp7312_set_focus(struct thp7312_device *thp7312)
{ enum thp7312_focus_state new_state = thp7312->focus_state; bool continuous;
u8 af_control;
u8 af_setting; int ret = 0;
/* Start by programming the manual focus position if it has changed. */ if (thp7312->focus_absolute->is_new) { unsignedint value;
value = thp7312_focus_values[thp7312->focus_absolute->val];
ret = cci_write(thp7312->regmap,
THP7312_REG_MANUAL_FOCUS_POSITION, value, NULL); if (ret) return ret;
}
/* Calculate the new focus state. */ switch (thp7312->focus_state) { case THP7312_FOCUS_STATE_MANUAL: default: if (thp7312->focus_auto->val)
new_state = THP7312_FOCUS_STATE_AUTO; elseif (thp7312->focus_start->is_new)
new_state = THP7312_FOCUS_STATE_ONESHOT; break;
case THP7312_FOCUS_STATE_AUTO: if (!thp7312->focus_auto->val)
new_state = THP7312_FOCUS_STATE_LOCKED; break;
case THP7312_FOCUS_STATE_LOCKED: if (thp7312->focus_auto->val)
new_state = THP7312_FOCUS_STATE_AUTO; elseif (thp7312->focus_start->is_new)
new_state = THP7312_FOCUS_STATE_ONESHOT; elseif (thp7312->focus_absolute->is_new)
new_state = THP7312_FOCUS_STATE_MANUAL; break;
case THP7312_FOCUS_STATE_ONESHOT: if (thp7312->focus_auto->val)
new_state = THP7312_FOCUS_STATE_AUTO; elseif (thp7312->focus_start->is_new)
new_state = THP7312_FOCUS_STATE_ONESHOT; elseif (thp7312->focus_absolute->is_new)
new_state = THP7312_FOCUS_STATE_MANUAL; break;
}
/* * If neither the state nor the focus method has changed, and no new * one-shot focus is requested, there's nothing new to program to the * hardware.
*/ if (thp7312->focus_state == new_state &&
!thp7312->focus_method->is_new && !thp7312->focus_start->is_new) return0;
case V4L2_CID_FOCUS_AUTO: case V4L2_CID_FOCUS_ABSOLUTE: case V4L2_CID_AUTO_FOCUS_START: case V4L2_CID_THP7312_AUTO_FOCUS_METHOD:
ret = thp7312_set_focus(thp7312); break;
case V4L2_CID_HFLIP: case V4L2_CID_VFLIP:
value = (thp7312->hflip->val ? THP7312_REG_FLIP_MIRROR_MIRROR : 0)
| (thp7312->vflip->val ? THP7312_REG_FLIP_MIRROR_FLIP : 0);
case V4L2_CID_THP7312_NOISE_REDUCTION_AUTO: case V4L2_CID_THP7312_NOISE_REDUCTION_ABSOLUTE:
value = thp7312->noise_reduction_auto->val ? 0
: THP7312_REG_NOISE_REDUCTION_FIXED |
thp7312->noise_reduction_absolute->val;
case V4L2_CID_RED_BALANCE:
cci_write(thp7312->regmap, THP7312_REG_MANUAL_WB_RED_GAIN,
ctrl->val, &ret); break;
case V4L2_CID_BLUE_BALANCE:
cci_write(thp7312->regmap, THP7312_REG_MANUAL_WB_BLUE_GAIN,
ctrl->val, &ret); break;
case V4L2_CID_AUTO_EXPOSURE_BIAS:
cci_write(thp7312->regmap, THP7312_REG_AE_EXPOSURE_COMPENSATION,
ctrl->val, &ret); break;
case V4L2_CID_POWER_LINE_FREQUENCY: if (ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) {
value = THP7312_AE_FLICKER_MODE_60;
} elseif (ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) {
value = THP7312_AE_FLICKER_MODE_50;
} else { if (thp7312->fw_version == THP7312_FW_VERSION(40, 3)) { /* THP7312_AE_FLICKER_MODE_DISABLE is not supported */
value = THP7312_AE_FLICKER_MODE_50;
} else {
value = THP7312_AE_FLICKER_MODE_DISABLE;
}
}
/* * Check what auto-focus methods the connected sensor supports, if any. * Firmwares before v90.03 didn't expose the AF_SUPPORT register, * consider both CDAF and PDAF as supported in that case.
*/ if (thp7312->fw_version >= THP7312_FW_VERSION(90, 3)) {
u64 val;
ret = cci_read(thp7312->regmap, THP7312_REG_AF_SUPPORT, &val,
NULL); if (ret) return ret;
/* Set properties from fwnode (e.g. rotation, orientation). */
ret = v4l2_fwnode_device_parse(dev, &props); if (ret) {
dev_err(dev, "Failed to parse fwnode: %d\n", ret); goto error;
}
ret = v4l2_ctrl_new_fwnode_properties(hdl, &thp7312_ctrl_ops, &props); if (ret) {
dev_err(dev, "Failed to create new v4l2 ctrl for fwnode properties: %d\n", ret); goto error;
}
for (i = 0; i < ARRAY_SIZE(thp7312_v4l2_ctrls_custom); i++) { conststruct v4l2_ctrl_config *ctrl_cfg =
&thp7312_v4l2_ctrls_custom[i]; struct v4l2_ctrl *ctrl;
/* * The firmware data is made of 128kB of RAM firmware, followed by a * variable-size "header". Both are stored in flash memory.
*/ #define THP7312_FW_RAM_SIZE (128 * 1024) #define THP7312_FW_MIN_SIZE (THP7312_FW_RAM_SIZE + 4) #define THP7312_FW_MAX_SIZE (THP7312_FW_RAM_SIZE + 64 * 1024)
/* * Data is first uploaded to the THP7312 128kB SRAM, and then written to flash. * The SRAM is exposed over I2C as 32kB banks, and up to 4kB of data can be * transferred in a single I2C write.
*/ #define THP7312_RAM_BANK_SIZE (32 * 1024) #define THP7312_FW_DOWNLOAD_UNIT (4 * 1024)
ret = cci_write(thp7312->regmap, THP7312_REG_FW_MEMORY_IO_SETTING,
THP7312_FW_MEMORY_IO_GPIO0, NULL); if (ret) {
dev_err(dev, "Failed to set flash memory I/O\n"); return FW_UPLOAD_ERR_HW_ERROR;
}
/* Set max drivability. */
ret = cci_write(thp7312->regmap, THP7312_REG_FW_DRIVABILITY, 0x00777777,
NULL); if (ret) {
dev_err(dev, "Failed to set drivability: %d\n", ret); return FW_UPLOAD_ERR_HW_ERROR;
}
/* Get JEDEC ID */
ret = thp7312_flash_reg_read(thp7312, thp7312_jedec_rdid, read_buf); if (ret) {
dev_err(dev, "Failed to get JEDEC ID: %d\n", ret); return FW_UPLOAD_ERR_HW_ERROR;
}
ret = thp7312_flash_reg_write(thp7312, thp7312_jedec_wen); if (ret < 0) {
dev_err(dev, "Failed to enable flash for writing\n"); return FW_UPLOAD_ERR_RW_ERROR;
}
ret = thp7312_flash_reg_write(thp7312, jedec_se); if (ret < 0) {
dev_err(dev, "Failed to erase flash sector\n"); return FW_UPLOAD_ERR_RW_ERROR;
}
for (i = 0; i < THP7312_FLASH_MEMORY_ERASE_TIMEOUT; i++) {
usleep_range(100000, 101000);
thp7312_flash_reg_read(thp7312, thp7312_jedec_rdsr,
read_buf);
/* Check Busy bit. Busy == 0x0 means erase complete. */ if (!(read_buf[0] & SR_WIP)) break;
}
if (i == THP7312_FLASH_MEMORY_ERASE_TIMEOUT) return FW_UPLOAD_ERR_TIMEOUT;
}
/* * THP7312 Firmware download to RAM * Command ID (address to download): 0x0000 - 0x7fff * Format:: 0000 XX XX XX ........ XX
*/
ret = thp7312_write_buf(thp7312, write_buf, size + 2); if (ret < 0)
dev_err(dev, "Unit transfer ERROR %s(): ret = %d\n", __func__, ret);
return ret >= 0 ? FW_UPLOAD_ERR_NONE : FW_UPLOAD_ERR_RW_ERROR;
}
ret = thp7312_write_download_data_by_unit(thp7312, chunk_addr,
data, chunk_size); if (ret != FW_UPLOAD_ERR_NONE) {
dev_err(dev, "Unit transfer ERROR at bank transfer %s(): %d\n",
__func__, j); return ret;
}
/* * This may be called asynchronously with an on-going update. All other * functions are called sequentially in a single thread. To avoid contention on * register accesses, only update the cancel_request flag. Other functions will * check this flag and handle the cancel request synchronously.
*/ staticvoid thp7312_fw_cancel(struct fw_upload *fw_upload)
{ struct thp7312_device *thp7312 = fw_upload->dd_handle;
/* Retrieve the sensor index from the reg property. */
ret = fwnode_property_read_u32(node, "reg", ®); if (ret < 0) {
dev_err(dev, "'reg' property missing in sensor node\n"); return -EINVAL;
}
if (reg >= ARRAY_SIZE(thp7312->sensors)) {
dev_err(dev, "Out-of-bounds 'reg' value %u\n", reg); return -EINVAL;
}
sensor = &thp7312->sensors[reg]; if (sensor->info) {
dev_err(dev, "Duplicate entry for sensor %u\n", reg); return -EINVAL;
}
ret = fwnode_property_read_string(node, "thine,model", &model); if (ret < 0) {
dev_err(dev, "'thine,model' property missing in sensor node\n"); return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(thp7312_sensor_info); i++) { conststruct thp7312_sensor_info *info =
&thp7312_sensor_info[i];
if (!strcmp(info->model, model)) {
sensor->info = info; break;
}
}
if (!sensor->info) {
dev_err(dev, "Unsupported sensor model %s\n", model); return -EINVAL;
}
ret = fwnode_property_read_u32_array(node, "data-lanes", values,
ARRAY_SIZE(values)); if (ret < 0) {
dev_err(dev, "Failed to read property data-lanes: %d\n", ret); return ret;
}
for (i = 0; i < ARRAY_SIZE(data_lanes); ++i)
data_lanes[i] = values[i];
ret = thp7312_map_data_lanes(&sensor->lane_remap, data_lanes,
ARRAY_SIZE(data_lanes)); if (ret) {
dev_err(dev, "Invalid sensor@%u data-lanes value\n", reg); return ret;
}
endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); if (!endpoint) return dev_err_probe(dev, -EINVAL, "Endpoint node not found\n");
ret = v4l2_fwnode_endpoint_parse(endpoint, &ep);
fwnode_handle_put(endpoint); if (ret) return dev_err_probe(dev, ret, "Could not parse endpoint\n");
ret = thp7312_map_data_lanes(&thp7312->lane_remap,
ep.bus.mipi_csi2.data_lanes,
ep.bus.mipi_csi2.num_data_lanes); if (ret) {
dev_err(dev, "Invalid data-lanes value\n"); return ret;
}
/* * The thine,boot-mode property is optional and default to * THP7312_BOOT_MODE_SPI_MASTER (1).
*/
thp7312->boot_mode = THP7312_BOOT_MODE_SPI_MASTER;
ret = device_property_read_u32(dev, "thine,boot-mode",
&thp7312->boot_mode); if (ret && ret != -EINVAL) return dev_err_probe(dev, ret, "Property '%s' is invalid\n", "thine,boot-mode");
if (thp7312->boot_mode != THP7312_BOOT_MODE_2WIRE_SLAVE &&
thp7312->boot_mode != THP7312_BOOT_MODE_SPI_MASTER) return dev_err_probe(dev, -EINVAL, "Invalid '%s' value %u\n", "thine,boot-mode", thp7312->boot_mode);
/* Sensors */
sensors = device_get_named_child_node(dev, "sensors"); if (!sensors) {
dev_err(dev, "'sensors' child node not found\n"); return -EINVAL;
}
fwnode_for_each_available_child_node(sensors, node) { if (fwnode_name_eq(node, "sensor")) { if (!thp7312_sensor_parse_dt(thp7312, node))
num_sensors++;
}
}
fwnode_handle_put(sensors);
if (!num_sensors) {
dev_err(dev, "No sensor found\n"); return -EINVAL;
}
thp7312 = devm_kzalloc(dev, sizeof(*thp7312), GFP_KERNEL); if (!thp7312) return -ENOMEM;
thp7312->dev = dev;
thp7312->regmap = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(thp7312->regmap)) return dev_err_probe(dev, PTR_ERR(thp7312->regmap), "Unable to initialize I2C\n");
ret = thp7312_parse_dt(thp7312); if (ret < 0) return ret;
ret = thp7312_get_regulators(thp7312); if (ret) return dev_err_probe(dev, ret, "Failed to get regulators\n");
thp7312->iclk = devm_clk_get(dev, NULL); if (IS_ERR(thp7312->iclk)) return dev_err_probe(dev, PTR_ERR(thp7312->iclk), "Failed to get iclk\n");
thp7312->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(thp7312->reset_gpio)) return dev_err_probe(dev, PTR_ERR(thp7312->reset_gpio), "Failed to get reset gpio\n");
if (thp7312->boot_mode == THP7312_BOOT_MODE_2WIRE_SLAVE) return thp7312_register_flash_mode(thp7312);
ret = media_entity_pads_init(&thp7312->sd.entity, 1, &thp7312->pad); 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 device manually here.
*/
ret = thp7312_power_on(thp7312); if (ret) goto err_entity_cleanup;
ret = thp7312_read_firmware_version(thp7312); if (ret < 0) {
dev_err(dev, "Camera is not found\n"); goto err_power_off;
}
ret = thp7312_init_controls(thp7312); if (ret) {
dev_err(dev, "Failed to initialize controls\n"); goto err_power_off;
}
ret = v4l2_subdev_init_finalize(&thp7312->sd); if (ret < 0) {
dev_err(dev, "Subdev active state initialization failed\n"); goto err_free_ctrls;
}
/* * 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_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
ret = v4l2_async_register_subdev(&thp7312->sd); if (ret < 0) {
dev_err(dev, "Subdev registration failed\n"); goto err_pm;
}
/* * Decrease the PM usage count. The device will get suspended after the * autosuspend delay, turning the power off.
*/
pm_runtime_put_autosuspend(dev);
dev_info(dev, "THP7312 firmware version %02u.%02u\n",
THP7312_FW_VERSION_MAJOR(thp7312->fw_version),
THP7312_FW_VERSION_MINOR(thp7312->fw_version));
/* * Disable runtime PM. In case runtime PM is disabled in the kernel, * make sure to turn power off manually.
*/
pm_runtime_disable(thp7312->dev); if (!pm_runtime_status_suspended(thp7312->dev))
thp7312_power_off(thp7312);
pm_runtime_set_suspended(thp7312->dev);
}
MODULE_DESCRIPTION("THP7312 MIPI Camera Subdev Driver");
MODULE_LICENSE("GPL");
Messung V0.5 in Prozent
¤ 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.11Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-08)
¤
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.