/* * See "SHR, SVR Setting" in datasheet
*/ #define IMX274_DEFAULT_FRAME_LENGTH (4550) #define IMX274_MAX_FRAME_LENGTH (0x000fffff)
/* * See "Frame Rate Adjustment" in datasheet
*/ #define IMX274_PIXCLK_CONST1 (72000000) #define IMX274_PIXCLK_CONST2 (1000000)
/* * The input gain is shifted by IMX274_GAIN_SHIFT to get * decimal number. The real gain is * (float)input_gain_value / (1 << IMX274_GAIN_SHIFT)
*/ #define IMX274_GAIN_SHIFT (8) #define IMX274_GAIN_SHIFT_MASK ((1 << IMX274_GAIN_SHIFT) - 1)
/* * See "Analog Gain" and "Digital Gain" in datasheet * min gain is 1X * max gain is calculated based on IMX274_GAIN_REG_MAX
*/ #define IMX274_GAIN_REG_MAX (1957) #define IMX274_MIN_GAIN (0x01 << IMX274_GAIN_SHIFT) #define IMX274_MAX_ANALOG_GAIN ((2048 << IMX274_GAIN_SHIFT)\
/ (2048 - IMX274_GAIN_REG_MAX)) #define IMX274_MAX_DIGITAL_GAIN (8) #define IMX274_DEF_GAIN (20 << IMX274_GAIN_SHIFT) #define IMX274_GAIN_CONST (2048) /* for gain formula */
/* * 1 line time in us = (HMAX / 72), minimal is 4 lines
*/ #define IMX274_MIN_EXPOSURE_TIME (4 * 260 / 72)
/* * Parameters for each imx274 readout mode. * * These are the values to configure the sensor in one of the * implemented modes. * * @init_regs: registers to initialize the mode * @wbin_ratio: width downscale factor (e.g. 3 for 1280; 3 = 3840/1280) * @hbin_ratio: height downscale factor (e.g. 3 for 720; 3 = 2160/720) * @min_frame_len: Minimum frame length for each mode (see "Frame Rate * Adjustment (CSI-2)" in the datasheet) * @min_SHR: Minimum SHR register value (see "Shutter Setting (CSI-2)" in the * datasheet) * @max_fps: Maximum frames per second * @nocpiop: Number of clocks per internal offset period (see "Integration Time * in Each Readout Drive Mode (CSI-2)" in the datasheet)
*/ struct imx274_mode { conststruct reg_8 *init_regs;
u8 wbin_ratio;
u8 hbin_ratio; int min_frame_len; int min_SHR; int max_fps; int nocpiop;
};
/** * imx274_read_mbreg - Read a multibyte register. * * Uses a bulk read where possible. * * @priv: Pointer to device structure * @addr: Address of the LSB register. Other registers must be * consecutive, least-to-most significant. * @val: Pointer to store the register value (cpu endianness) * @nbytes: Number of bytes to read (range: [1..3]). * Other bytes are zet to 0. * * Return: 0 on success, errors otherwise
*/ staticint imx274_read_mbreg(struct stimx274 *priv, u16 addr, u32 *val,
size_t nbytes)
{
__le32 val_le = 0; int err;
/** * imx274_write_mbreg - Write a multibyte register. * * Uses a bulk write where possible. * * @priv: Pointer to device structure * @addr: Address of the LSB register. Other registers must be * consecutive, least-to-most significant. * @val: Value to be written to the register (cpu endianness) * @nbytes: Number of bytes to write (range: [1..3])
*/ staticint imx274_write_mbreg(struct stimx274 *priv, u16 addr, u32 val,
size_t nbytes)
{
__le32 val_le = cpu_to_le32(val); int err;
/* * imx274_start_stream - Function for starting stream per mode index * @priv: Pointer to device structure * * Return: 0 on success, errors otherwise
*/ staticint imx274_start_stream(struct stimx274 *priv)
{ int err = 0;
/* * Refer to "Standby Cancel Sequence when using CSI-2" in * imx274 datasheet, it should wait 10ms or more here. * give it 1 extra ms for margin
*/
msleep_range(11);
err = imx274_write_table(priv, imx274_start_2); if (err) return err;
/* * Refer to "Standby Cancel Sequence when using CSI-2" in * imx274 datasheet, it should wait 7ms or more here. * give it 1 extra ms for margin
*/
msleep_range(8);
err = imx274_write_table(priv, imx274_start_3); if (err) return err;
return 0;
}
/* * imx274_reset - Function called to reset the sensor * @priv: Pointer to device structure * @rst: Input value for determining the sensor's end state after reset * * Set the senor in reset and then * if rst = 0, keep it in reset; * if rst = 1, bring it out of reset. *
*/ staticvoid imx274_reset(struct stimx274 *priv, int rst)
{
gpiod_set_value_cansleep(priv->reset_gpio, 0);
usleep_range(IMX274_RESET_DELAY1, IMX274_RESET_DELAY2);
gpiod_set_value_cansleep(priv->reset_gpio, !!rst);
usleep_range(IMX274_RESET_DELAY1, IMX274_RESET_DELAY2);
}
/** * imx274_s_ctrl - This is used to set the imx274 V4L2 controls * @ctrl: V4L2 control to be set * * This function is used to set the V4L2 controls for the imx274 sensor. * * Return: 0 on success, errors otherwise
*/ staticint imx274_s_ctrl(struct v4l2_ctrl *ctrl)
{ struct v4l2_subdev *sd = ctrl_to_sd(ctrl); struct stimx274 *imx274 = to_imx274(sd); int ret = -EINVAL;
if (!pm_runtime_get_if_in_use(&imx274->client->dev)) return 0;
switch (ctrl->id) { case V4L2_CID_EXPOSURE:
dev_dbg(&imx274->client->dev, "%s : set V4L2_CID_EXPOSURE\n", __func__);
ret = imx274_set_exposure(imx274, ctrl->val); break;
case V4L2_CID_GAIN:
dev_dbg(&imx274->client->dev, "%s : set V4L2_CID_GAIN\n", __func__);
ret = imx274_set_gain(imx274, ctrl); break;
case V4L2_CID_VFLIP:
dev_dbg(&imx274->client->dev, "%s : set V4L2_CID_VFLIP\n", __func__);
ret = imx274_set_vflip(imx274, ctrl->val); break;
case V4L2_CID_TEST_PATTERN:
dev_dbg(&imx274->client->dev, "%s : set V4L2_CID_TEST_PATTERN\n", __func__);
ret = imx274_set_test_pattern(imx274, ctrl->val); break;
}
pm_runtime_put(&imx274->client->dev);
return ret;
}
staticint imx274_binning_goodness(struct stimx274 *imx274, int w, int ask_w, int h, int ask_h, u32 flags)
{ struct device *dev = &imx274->client->dev; constint goodness = 100000; int val = 0;
if (flags & V4L2_SEL_FLAG_GE) { if (w < ask_w)
val -= goodness; if (h < ask_h)
val -= goodness;
}
if (flags & V4L2_SEL_FLAG_LE) { if (w > ask_w)
val -= goodness; if (h > ask_h)
val -= goodness;
}
/** * __imx274_change_compose - Helper function to change binning and set both * compose and format. * * We have two entry points to change binning: set_fmt and * set_selection(COMPOSE). Both have to compute the new output size * and set it in both the compose rect and the frame format size. We * also need to do the same things after setting cropping to restore * 1:1 binning. * * This function contains the common code for these three cases, it * has many arguments in order to accommodate the needs of all of * them. * * Must be called with imx274->lock locked. * * @imx274: The device object * @sd_state: The subdev state we are editing for TRY requests * @which: V4L2_SUBDEV_FORMAT_ACTIVE or V4L2_SUBDEV_FORMAT_TRY from the caller * @width: Input-output parameter: set to the desired width before * the call, contains the chosen value after returning successfully * @height: Input-output parameter for height (see @width) * @flags: Selection flags from struct v4l2_subdev_selection, or 0 if not * available (when called from set_fmt)
*/ staticint __imx274_change_compose(struct stimx274 *imx274, struct v4l2_subdev_state *sd_state,
u32 which,
u32 *width,
u32 *height,
u32 flags)
{ struct device *dev = &imx274->client->dev; conststruct v4l2_rect *cur_crop; struct v4l2_mbus_framefmt *tgt_fmt; unsignedint i; conststruct imx274_mode *best_mode = &imx274_modes[0]; int best_goodness = INT_MIN;
/** * imx274_get_fmt - Get the pad format * @sd: Pointer to V4L2 Sub device structure * @sd_state: Pointer to sub device state structure * @fmt: Pointer to pad level media bus format * * This function is used to get the pad format information. * * Return: 0 on success
*/ staticint imx274_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt)
{ struct stimx274 *imx274 = to_imx274(sd);
/** * imx274_set_fmt - This is used to set the pad format * @sd: Pointer to V4L2 Sub device structure * @sd_state: Pointer to sub device state information structure * @format: Pointer to pad level media bus format * * This function is used to set the pad format. * * Return: 0 on success
*/ staticint imx274_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format)
{ struct v4l2_mbus_framefmt *fmt = &format->format; struct stimx274 *imx274 = to_imx274(sd); int err = 0;
/* * __imx274_change_compose already set width and height in the * applicable format, but we need to keep all other format * values, so do a full copy here
*/
fmt->field = V4L2_FIELD_NONE; if (format->which == V4L2_SUBDEV_FORMAT_TRY)
*v4l2_subdev_state_get_format(sd_state, 0) = *fmt; else
imx274->format = *fmt;
/* * h_step could be 12 or 24 depending on the binning. But we * won't know the binning until we choose the mode later in * __imx274_change_compose(). Thus let's be safe and use the * most conservative value in all cases.
*/ const u32 h_step = 24;
/* __imx274_change_compose needs the new size in *tgt_crop */
*tgt_crop = new_crop;
/* if crop size changed then reset the output image size */ if (size_changed)
__imx274_change_compose(imx274, sd_state, sel->which,
&new_crop.width, &new_crop.height,
sel->flags);
/* Use the minimum allowed value of HMAX */ /* Note: except in mode 1, (width / 16 + 23) is always < hmax_min */ /* Note: 260 is the minimum HMAX in all implemented modes */
hmax = max_t(u32, 260, (imx274->crop.width) / 16 + 23);
/* * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 * subdev active state API.
*/ if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL;
/* * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 * subdev active state API.
*/ if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL;
ret = pm_runtime_resume_and_get(&imx274->client->dev); if (ret < 0) return ret;
mutex_lock(&imx274->lock);
ret = __imx274_set_frame_interval(imx274, fi->interval);
if (!ret) {
fi->interval = imx274->frame_interval;
/* * exposure time range is decided by frame interval * need to update it after frame interval changes
*/
min = IMX274_MIN_EXPOSURE_TIME;
max = fi->interval.numerator * 1000000
/ fi->interval.denominator;
def = max;
ret = __v4l2_ctrl_modify_range(ctrl, min, max, 1, def); if (ret) {
dev_err(&imx274->client->dev, "Exposure ctrl range update failed\n"); goto unlock;
}
/* update exposure time accordingly */
imx274_set_exposure(imx274, ctrl->val);
/** * imx274_s_stream - It is used to start/stop the streaming. * @sd: V4L2 Sub device * @on: Flag (True / False) * * This function controls the start or stop of streaming for the * imx274 sensor. * * Return: 0 on success, errors otherwise
*/ staticint imx274_s_stream(struct v4l2_subdev *sd, int on)
{ struct stimx274 *imx274 = to_imx274(sd); int ret = 0;
dev_dbg(&imx274->client->dev, "%s : %s, mode index = %td\n", __func__,
on ? "Stream Start" : "Stream Stop",
imx274->mode - &imx274_modes[0]);
mutex_lock(&imx274->lock);
if (on) {
ret = pm_runtime_resume_and_get(&imx274->client->dev); if (ret < 0) {
mutex_unlock(&imx274->lock); return ret;
}
/* load mode registers */
ret = imx274_mode_regs(imx274); if (ret) goto fail;
ret = imx274_apply_trimming(imx274); if (ret) goto fail;
/* * update frame rate & exposure. if the last mode is different, * HMAX could be changed. As the result, frame rate & exposure * are changed. * gain is not affected.
*/
ret = __imx274_set_frame_interval(imx274,
imx274->frame_interval); if (ret) goto fail;
/* start stream */
ret = imx274_start_stream(imx274); if (ret) goto fail;
} else { /* stop stream */
ret = imx274_write_table(imx274, imx274_stop); if (ret) goto fail;
/* * imx274_get_frame_length - Function for obtaining current frame length * @priv: Pointer to device structure * @val: Pointer to obtained value * * frame_length = vmax x (svr + 1), in unit of hmax. * * Return: 0 on success
*/ staticint imx274_get_frame_length(struct stimx274 *priv, u32 *val)
{ int err;
u32 svr;
u32 vmax;
err = imx274_read_mbreg(priv, IMX274_SVR_REG_LSB, &svr, 2); if (err) goto fail;
err = imx274_read_mbreg(priv, IMX274_VMAX_REG_3, &vmax, 3); if (err) goto fail;
err = imx274_get_frame_length(priv, frame_length); if (err) return err;
if (*frame_length < priv->mode->min_frame_len)
*frame_length = priv->mode->min_frame_len;
*val = *frame_length - *val; /* convert to raw shr */ if (*val > *frame_length - IMX274_SHR_LIMIT_CONST)
*val = *frame_length - IMX274_SHR_LIMIT_CONST; elseif (*val < priv->mode->min_SHR)
*val = priv->mode->min_SHR;
return 0;
}
/* * imx274_set_digital gain - Function called when setting digital gain * @priv: Pointer to device structure * @dgain: Value of digital gain. * * Digital gain has only 4 steps: 1x, 2x, 4x, and 8x * * Return: 0 on success
*/ staticint imx274_set_digital_gain(struct stimx274 *priv, u32 dgain)
{
u8 reg_val;
/* * imx274_set_gain - Function called when setting gain * @priv: Pointer to device structure * @val: Value of gain. the real value = val << IMX274_GAIN_SHIFT; * @ctrl: v4l2 control pointer * * Set the gain based on input value. * The caller should hold the mutex lock imx274->lock if necessary * * Return: 0 on success
*/ staticint imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl)
{ int err;
u32 gain, analog_gain, digital_gain, gain_reg;
gain = (u32)(ctrl->val);
dev_dbg(&priv->client->dev, "%s : input gain = %d.%d\n", __func__,
gain >> IMX274_GAIN_SHIFT,
((gain & IMX274_GAIN_SHIFT_MASK) * 100) >> IMX274_GAIN_SHIFT);
if (gain > IMX274_MAX_DIGITAL_GAIN * IMX274_MAX_ANALOG_GAIN)
gain = IMX274_MAX_DIGITAL_GAIN * IMX274_MAX_ANALOG_GAIN; elseif (gain < IMX274_MIN_GAIN)
gain = IMX274_MIN_GAIN;
/* * imx274_set_coarse_time - Function called when setting SHR value * @priv: Pointer to device structure * @val: Value for exposure time in number of line_length, or [HMAX] * * Set SHR value based on input value. * * Return: 0 on success
*/ staticint imx274_set_coarse_time(struct stimx274 *priv, u32 *val)
{ int err;
u32 coarse_time, frame_length;
coarse_time = *val;
/* convert exposure_time to appropriate SHR value */
err = imx274_clamp_coarse_time(priv, &coarse_time, &frame_length); if (err) goto fail;
err = imx274_write_mbreg(priv, IMX274_SHR_REG_LSB, coarse_time, 2); if (err) goto fail;
/* * imx274_set_exposure - Function called when setting exposure time * @priv: Pointer to device structure * @val: Variable for exposure time, in the unit of micro-second * * Set exposure time based on input value. * The caller should hold the mutex lock imx274->lock if necessary * * Return: 0 on success
*/ staticint imx274_set_exposure(struct stimx274 *priv, int val)
{ int err;
u32 hmax;
u32 coarse_time; /* exposure time in unit of line (HMAX)*/
dev_dbg(&priv->client->dev, "%s : EXPOSURE control input = %d\n", __func__, val);
/* step 1: convert input exposure_time (val) into number of 1[HMAX] */
err = imx274_read_mbreg(priv, IMX274_HMAX_REG_LSB, &hmax, 2); if (err) goto fail;
/* * imx274_set_vflip - Function called when setting vertical flip * @priv: Pointer to device structure * @val: Value for vflip setting * * Set vertical flip based on input value. * val = 0: normal, no vertical flip * val = 1: vertical flip enabled * The caller should hold the mutex lock imx274->lock if necessary * * Return: 0 on success
*/ staticint imx274_set_vflip(struct stimx274 *priv, int val)
{ int err;
err = imx274_write_reg(priv, IMX274_VFLIP_REG, val); if (err) {
dev_err(&priv->client->dev, "VFLIP control error\n"); return err;
}
dev_dbg(&priv->client->dev, "%s : VFLIP control success\n", __func__);
return 0;
}
/* * imx274_set_test_pattern - Function called when setting test pattern * @priv: Pointer to device structure * @val: Variable for test pattern * * Set to different test patterns based on input value. * * Return: 0 on success
*/ staticint imx274_set_test_pattern(struct stimx274 *priv, int val)
{ int err = 0;
if (!err)
dev_dbg(&priv->client->dev, "%s : TEST PATTERN control success\n", __func__); else
dev_err(&priv->client->dev, "%s error = %d\n", __func__, err);
return err;
}
/* * imx274_set_frame_length - Function called when setting frame length * @priv: Pointer to device structure * @val: Variable for frame length (= VMAX, i.e. vertical drive period length) * * Set frame length based on input value. * * Return: 0 on success
*/ staticint imx274_set_frame_length(struct stimx274 *priv, u32 val)
{ int err;
u32 frame_length;
/* * __imx274_set_frame_interval - Function called when setting frame interval * @priv: Pointer to device structure * @frame_interval: Variable for frame interval * * Change frame interval by updating VMAX value * The caller should hold the mutex lock imx274->lock if necessary * * Return: 0 on success
*/ staticint __imx274_set_frame_interval(struct stimx274 *priv, struct v4l2_fract frame_interval)
{ int err;
u32 frame_length, req_frame_rate;
u32 svr;
u32 hmax;
staticint imx274_fwnode_parse(struct device *dev)
{ struct fwnode_handle *endpoint; /* Only CSI2 is supported */ struct v4l2_fwnode_endpoint ep = {
.bus_type = V4L2_MBUS_CSI2_DPHY
}; int ret;
endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); if (!endpoint) {
dev_err(dev, "Endpoint node not found\n"); return -EINVAL;
}
ret = v4l2_fwnode_endpoint_parse(endpoint, &ep);
fwnode_handle_put(endpoint); if (ret == -ENXIO) {
dev_err(dev, "Unsupported bus type, should be CSI2\n"); return ret;
} elseif (ret) {
dev_err(dev, "Parsing endpoint node failed %d\n", ret); return ret;
}
/* Check number of data lanes, only 4 lanes supported */ if (ep.bus.mipi_csi2.num_data_lanes != 4) {
dev_err(dev, "Invalid data lanes: %d\n",
ep.bus.mipi_csi2.num_data_lanes); return -EINVAL;
}
pm_runtime_disable(&client->dev); if (!pm_runtime_status_suspended(&client->dev))
imx274_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.