/** * struct imx412_reg_list - imx412 sensor register list * @num_of_regs: Number of registers in the list * @regs: Pointer to register list
*/ struct imx412_reg_list {
u32 num_of_regs; conststruct imx412_reg *regs;
};
/** * struct imx412_mode - imx412 sensor mode structure * @width: Frame width * @height: Frame height * @code: Format code * @hblank: Horizontal blanking in lines * @vblank: Vertical blanking in lines * @vblank_min: Minimum vertical blanking in lines * @vblank_max: Maximum vertical blanking in lines * @pclk: Sensor pixel clock * @link_freq_idx: Link frequency index * @reg_list: Register list for sensor mode
*/ struct imx412_mode {
u32 width;
u32 height;
u32 code;
u32 hblank;
u32 vblank;
u32 vblank_min;
u32 vblank_max;
u64 pclk;
u32 link_freq_idx; struct imx412_reg_list reg_list;
};
staticconstchar * const imx412_supply_names[] = { "dovdd", /* Digital I/O power */ "avdd", /* Analog power */ "dvdd", /* Digital core power */
};
/** * struct imx412 - imx412 sensor device structure * @dev: Pointer to generic device * @client: Pointer to i2c client * @sd: V4L2 sub-device * @pad: Media pad. Only one pad supported * @reset_gpio: Sensor reset gpio * @inclk: Sensor input clock * @supplies: Regulator supplies * @ctrl_handler: V4L2 control handler * @link_freq_ctrl: Pointer to link frequency control * @pclk_ctrl: Pointer to pixel clock control * @hblank_ctrl: Pointer to horizontal blanking control * @vblank_ctrl: Pointer to vertical blanking control * @exp_ctrl: Pointer to exposure control * @again_ctrl: Pointer to analog gain control * @vblank: Vertical blanking in lines * @cur_mode: Pointer to current selected sensor mode * @mutex: Mutex for serializing sensor controls
*/ struct imx412 { struct device *dev; struct i2c_client *client; struct v4l2_subdev sd; struct media_pad pad; struct gpio_desc *reset_gpio; struct clk *inclk; struct regulator_bulk_data supplies[ARRAY_SIZE(imx412_supply_names)]; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *link_freq_ctrl; struct v4l2_ctrl *pclk_ctrl; struct v4l2_ctrl *hblank_ctrl; struct v4l2_ctrl *vblank_ctrl; struct { struct v4l2_ctrl *exp_ctrl; struct v4l2_ctrl *again_ctrl;
};
u32 vblank; conststruct imx412_mode *cur_mode; struct mutex mutex;
};
/* Read data from register */
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = len;
msgs[1].buf = &data_buf[4 - len];
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret != ARRAY_SIZE(msgs)) return -EIO;
*val = get_unaligned_be32(data_buf);
return 0;
}
/** * imx412_write_reg() - Write register * @imx412: pointer to imx412 device * @reg: register address * @len: length of bytes. Max supported bytes is 4 * @val: register value * * Return: 0 if successful, error code otherwise.
*/ staticint imx412_write_reg(struct imx412 *imx412, u16 reg, u32 len, u32 val)
{ struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd);
u8 buf[6] = {0};
if (WARN_ON(len > 4)) return -EINVAL;
put_unaligned_be16(reg, buf);
put_unaligned_be32(val << (8 * (4 - len)), buf + 2); if (i2c_master_send(client, buf, len + 2) != len + 2) return -EIO;
return 0;
}
/** * imx412_write_regs() - Write a list of registers * @imx412: pointer to imx412 device * @regs: list of registers to be written * @len: length of registers array * * Return: 0 if successful, error code otherwise.
*/ staticint imx412_write_regs(struct imx412 *imx412, conststruct imx412_reg *regs, u32 len)
{ unsignedint i; int ret;
for (i = 0; i < len; i++) {
ret = imx412_write_reg(imx412, regs[i].address, 1, regs[i].val); if (ret) return ret;
}
return 0;
}
/** * imx412_update_controls() - Update control ranges based on streaming mode * @imx412: pointer to imx412 device * @mode: pointer to imx412_mode sensor mode * * Return: 0 if successful, error code otherwise.
*/ staticint imx412_update_controls(struct imx412 *imx412, conststruct imx412_mode *mode)
{ int ret;
ret = __v4l2_ctrl_s_ctrl(imx412->link_freq_ctrl, mode->link_freq_idx); if (ret) return ret;
ret = __v4l2_ctrl_s_ctrl(imx412->hblank_ctrl, mode->hblank); if (ret) return ret;
/** * imx412_update_exp_gain() - Set updated exposure and gain * @imx412: pointer to imx412 device * @exposure: updated exposure value * @gain: updated analog gain value * * Return: 0 if successful, error code otherwise.
*/ staticint imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain)
{
u32 lpfr; int ret;
lpfr = imx412->vblank + imx412->cur_mode->height;
dev_dbg(imx412->dev, "Set exp %u, analog gain %u, lpfr %u\n",
exposure, gain, lpfr);
ret = imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 1); if (ret) return ret;
ret = imx412_write_reg(imx412, IMX412_REG_LPFR, 2, lpfr); if (ret) goto error_release_group_hold;
ret = imx412_write_reg(imx412, IMX412_REG_EXPOSURE_CIT, 2, exposure); if (ret) goto error_release_group_hold;
ret = imx412_write_reg(imx412, IMX412_REG_AGAIN, 2, gain);
ret = __v4l2_ctrl_modify_range(imx412->exp_ctrl,
IMX412_EXPOSURE_MIN,
imx412->vblank +
imx412->cur_mode->height -
IMX412_EXPOSURE_OFFSET,
1, IMX412_EXPOSURE_DEFAULT); break; case V4L2_CID_EXPOSURE: /* Set controls only if sensor is in power on state */ if (!pm_runtime_get_if_in_use(imx412->dev)) return 0;
/* Write sensor mode registers */
reg_list = &imx412->cur_mode->reg_list;
ret = imx412_write_regs(imx412, reg_list->regs,
reg_list->num_of_regs); if (ret) {
dev_err(imx412->dev, "fail to write initial registers\n"); return ret;
}
/* Setup handler will write actual exposure and gain */
ret = __v4l2_ctrl_handler_setup(imx412->sd.ctrl_handler); if (ret) {
dev_err(imx412->dev, "fail to setup handler\n"); return ret;
}
/* Delay is required before streaming*/
usleep_range(7400, 8000);
/* Start streaming */
ret = imx412_write_reg(imx412, IMX412_REG_MODE_SELECT,
1, IMX412_MODE_STREAMING); if (ret) {
dev_err(imx412->dev, "fail to start streaming\n"); return ret;
}
/** * imx412_detect() - Detect imx412 sensor * @imx412: pointer to imx412 device * * Return: 0 if successful, -EIO if sensor id does not match
*/ staticint imx412_detect(struct imx412 *imx412)
{ int ret;
u32 val;
ret = imx412_read_reg(imx412, IMX412_REG_ID, 2, &val); if (ret) return ret;
if (val != IMX412_ID) {
dev_err(imx412->dev, "chip id mismatch: %x!=%x\n",
IMX412_ID, val); return -ENXIO;
}
/* Request optional reset pin */
imx412->reset_gpio = devm_gpiod_get_optional(imx412->dev, "reset",
GPIOD_OUT_LOW); if (IS_ERR(imx412->reset_gpio)) {
dev_err(imx412->dev, "failed to get reset gpio %ld\n",
PTR_ERR(imx412->reset_gpio)); return PTR_ERR(imx412->reset_gpio);
}
/* Get sensor input clock */
imx412->inclk = devm_clk_get(imx412->dev, NULL); if (IS_ERR(imx412->inclk)) {
dev_err(imx412->dev, "could not get inclk\n"); return PTR_ERR(imx412->inclk);
}
rate = clk_get_rate(imx412->inclk); if (rate != IMX412_INCLK_RATE) {
dev_err(imx412->dev, "inclk frequency mismatch\n"); return -EINVAL;
}
/* Get optional DT defined regulators */ for (i = 0; i < ARRAY_SIZE(imx412_supply_names); i++)
imx412->supplies[i].supply = imx412_supply_names[i];
ret = devm_regulator_bulk_get(imx412->dev,
ARRAY_SIZE(imx412_supply_names),
imx412->supplies); if (ret) return ret;
ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) 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 != IMX412_NUM_DATA_LANES) {
dev_err(imx412->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;
}
if (!bus_cfg.nr_of_link_frequencies) {
dev_err(imx412->dev, "no link frequencies defined\n");
ret = -EINVAL; goto done_endpoint_free;
}
for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) if (bus_cfg.link_frequencies[i] == IMX412_LINK_FREQ) goto done_endpoint_free;
pm_runtime_disable(&client->dev); if (!pm_runtime_status_suspended(&client->dev))
imx412_power_off(&client->dev);
pm_runtime_set_suspended(&client->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.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.