/* * 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;
return 0;
}
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) return 0;
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
¤ 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.27Bemerkung:
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
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.