/* In here we set the v4l2 controls w.r.t. our pipeline config */
v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]);
v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]);
v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]);
v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]);
for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { /* gains have a fixed point at 9 decimals */
isc->ctrls.gain[c] = 1 << 9; /* offsets are in 2's complements */
isc->ctrls.offset[c] = 0;
}
}
h = isc->fmt.fmt.pix.height;
w = isc->fmt.fmt.pix.width;
/* * In case the sensor is not RAW, it will output a pixel (12-16 bits) * with two samples on the ISC Data bus (which is 8-12) * ISC will count each sample, so, we need to multiply these values * by two, to get the real number of samples for the required pixels.
*/ if (!ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) {
h <<= 1;
w <<= 1;
}
/* * We limit the column/row count that the ISC will output according * to the configured resolution that we want. * This will avoid the situation where the sensor is misconfigured, * sending more data, and the ISC will just take it and DMA to memory, * causing corruption.
*/
regmap_write(regmap, ISC_PFE_CFG1,
(ISC_PFE_CFG1_COLMIN(0) & ISC_PFE_CFG1_COLMIN_MASK) |
(ISC_PFE_CFG1_COLMAX(w - 1) & ISC_PFE_CFG1_COLMAX_MASK));
/* Set the pipeline */
isc_set_pipeline(isc, pipeline);
/* * The current implemented histogram is available for RAW R, B, GB, GR * channels. We need to check if sensor is outputting RAW BAYER
*/ if (isc->ctrls.awb &&
ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
isc_set_histogram(isc, true); else
isc_set_histogram(isc, false);
/* Enable stream on the sub device */
ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1); if (ret && ret != -ENOIOCTLCMD) {
dev_err(isc->dev, "stream on failed in subdev %d\n", ret); goto err_start_stream;
}
ret = pm_runtime_resume_and_get(isc->dev); if (ret < 0) {
dev_err(isc->dev, "RPM resume failed in subdev %d\n",
ret); goto err_pm_get;
}
ret = isc_configure(isc); if (unlikely(ret)) goto err_configure;
/* if we streaming from RAW, we can do one-shot white balance adj */ if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
v4l2_ctrl_activate(isc->do_wb_ctrl, true);
/* Wait until the end of the current frame */ if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ))
dev_err(isc->dev, "Timeout waiting for end of the capture\n");
/* Disable stream on the sub device */
ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); if (ret && ret != -ENOIOCTLCMD)
dev_err(isc->dev, "stream off failed in subdev\n");
/* Release all active buffers */
spin_lock_irqsave(&isc->dma_queue_lock, flags); if (unlikely(isc->cur_frm)) {
vb2_buffer_done(&isc->cur_frm->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
isc->cur_frm = NULL;
}
list_for_each_entry(buf, &isc->dma_queue, list)
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
INIT_LIST_HEAD(&isc->dma_queue);
spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
}
/* * If we are not asked a specific mbus_code, we have to report all * the formats that we can output.
*/ if (!f->mbus_code) { if (index >= isc->controller_formats_size) return -EINVAL;
/* * If a specific mbus_code is requested, check if we support * this mbus_code as input for the ISC. * If it's supported, then we report the corresponding pixelformat * as first possible option for the ISC. * E.g. mbus MEDIA_BUS_FMT_YUYV8_2X8 and report * 'YUYV' (YUYV 4:2:2)
*/
fmt = isc_find_format_by_code(isc, f->mbus_code, &i); if (!fmt) return -EINVAL;
if (!index) {
f->pixelformat = fmt->fourcc;
return 0;
}
supported_index++;
/* If the index is not raw, we don't have anymore formats to report */ if (!ISC_IS_FORMAT_RAW(f->mbus_code)) return -EINVAL;
/* * We are asked for a specific mbus code, which is raw. * We have to search through the formats we can convert to. * We have to skip the raw formats, we cannot convert to raw. * E.g. 'AR12' (16-bit ARGB 4-4-4-4), 'AR15' (16-bit ARGB 1-5-5-5), etc.
*/ for (i = 0; i < isc->controller_formats_size; i++) { if (isc->controller_formats[i].raw) continue; if (index == supported_index) {
f->pixelformat = isc->controller_formats[i].fourcc; return 0;
}
supported_index++;
}
/* * Checks the current configured format, if ISC can output it, * considering which type of format the ISC receives from the sensor
*/ staticint isc_try_validate_formats(struct isc_device *isc)
{ int ret; bool bayer = false, yuv = false, rgb = false, grey = false;
/* all formats supported by the RLP module are OK */ switch (isc->try_config.fourcc) { case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: case V4L2_PIX_FMT_SBGGR10: case V4L2_PIX_FMT_SGBRG10: case V4L2_PIX_FMT_SGRBG10: case V4L2_PIX_FMT_SRGGB10: case V4L2_PIX_FMT_SBGGR12: case V4L2_PIX_FMT_SGBRG12: case V4L2_PIX_FMT_SGRBG12: case V4L2_PIX_FMT_SRGGB12:
ret = 0;
bayer = true; break;
case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUV422P: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_VYUY:
ret = 0;
yuv = true; break;
case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_ABGR32: case V4L2_PIX_FMT_XBGR32: case V4L2_PIX_FMT_ARGB444: case V4L2_PIX_FMT_ARGB555:
ret = 0;
rgb = true; break; case V4L2_PIX_FMT_GREY: case V4L2_PIX_FMT_Y10: case V4L2_PIX_FMT_Y16:
ret = 0;
grey = true; break; default: /* any other different formats are not supported */
dev_err(isc->dev, "Requested unsupported format.\n");
ret = -EINVAL;
}
dev_dbg(isc->dev, "Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n",
rgb, yuv, grey, bayer);
if (bayer &&
!ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
dev_err(isc->dev, "Cannot output RAW if we do not receive RAW.\n"); return -EINVAL;
}
if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) &&
!ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) {
dev_err(isc->dev, "Cannot output GREY if we do not receive RAW/GREY.\n"); return -EINVAL;
}
if ((rgb || bayer || yuv) &&
ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) {
dev_err(isc->dev, "Cannot convert GREY to another format.\n"); return -EINVAL;
}
return ret;
}
/* * Configures the RLP and DMA modules, depending on the output format * configured for the ISC. * If direct_dump == true, just dump raw data 8/16 bits depending on format.
*/ staticint isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
{
isc->try_config.rlp_cfg_mode = 0;
/* find if the format requested is supported */ for (i = 0; i < isc->controller_formats_size; i++) if (isc->controller_formats[i].fourcc == pixfmt->pixelformat) {
isc->try_config.fourcc = pixfmt->pixelformat; break;
}
isc_try_configure_rlp_dma(isc, false);
/* Limit to Microchip ISC hardware capabilities */
v4l_bound_align_image(&pixfmt->width, 16, isc->max_width, 0,
&pixfmt->height, 16, isc->max_height, 0, 0); /* If we did not find the requested format, we will fallback here */
pixfmt->pixelformat = isc->try_config.fourcc;
pixfmt->colorspace = V4L2_COLORSPACE_SRGB;
pixfmt->field = V4L2_FIELD_NONE;
/* Get current format from subdev */
ret = v4l2_subdev_call(isc->current_subdev->sd, pad, get_fmt, NULL,
&format); if (ret) return ret;
/* Identify the subdev's format configuration */ for (i = 0; i < isc->formats_list_size; i++) if (isc->formats_list[i].mbus_code == format.format.code) {
sd_fmt = &isc->formats_list[i]; break;
}
/* Check if the format is not supported */ if (!sd_fmt) {
dev_err(isc->dev, "Current subdevice is streaming a media bus code that is not supported 0x%x\n",
format.format.code); return -EPIPE;
}
/* At this moment we know which format the subdev will use */
isc->try_config.sd_format = sd_fmt;
/* If the sensor is not RAW, we can only do a direct dump */ if (!ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
isc_try_configure_rlp_dma(isc, true);
/* Check if the frame size is the same. Otherwise we may overflow */ if (pixfmt->height != format.format.height ||
pixfmt->width != format.format.width) {
dev_err(isc->dev, "ISC not configured with the proper frame size: %dx%d\n",
format.format.width, format.format.height); return -EPIPE;
}
dev_dbg(isc->dev, "Identified subdev using format %.4s with %dx%d %d bpp\n",
(char *)&sd_fmt->fourcc, pixfmt->width, pixfmt->height,
isc->try_config.bpp);
/* Reset and restart AWB if the subdevice changed the format */ if (isc->try_config.sd_format && isc->config.sd_format &&
isc->try_config.sd_format != isc->config.sd_format) {
isc->ctrls.hist_stat = HIST_INIT;
isc_reset_awb_ctrls(isc);
isc_update_v4l2_ctrls(isc);
}
/* Validate formats */
ret = isc_try_validate_formats(isc); if (ret) return ret;
/* Configure ISC pipeline for the config */
ret = isc_try_configure_pipeline(isc); if (ret) return ret;
isc->config = isc->try_config;
dev_dbg(isc->dev, "New ISC configuration in place\n");
*hist_count = 0; /* * we deliberately ignore the end of the histogram, * the most white pixels
*/ for (i = 1; i < HIST_ENTRIES; i++) { if (*hist_entry && !*min)
*min = i; if (*hist_entry)
*max = i;
*hist_count += i * (*hist_entry++);
}
staticvoid isc_wb_update(struct isc_ctrls *ctrls)
{ struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls);
u32 *hist_count = &ctrls->hist_count[0];
u32 c, offset[4];
u64 avg = 0; /* We compute two gains, stretch gain and grey world gain */
u32 s_gain[4], gw_gain[4];
/* * According to Grey World, we need to set gains for R/B to normalize * them towards the green channel. * Thus we want to keep Green as fixed and adjust only Red/Blue * Compute the average of the both green channels first
*/
avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] +
(u64)hist_count[ISC_HIS_CFG_MODE_GB];
avg >>= 1;
dev_dbg(isc->dev, "isc wb: green components average %llu\n", avg);
/* Green histogram is null, nothing to do */ if (!avg) return;
for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { /* * the color offset is the minimum value of the histogram. * we stretch this color to the full range by substracting * this value from the color component.
*/
offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX]; /* * The offset is always at least 1. If the offset is 1, we do * not need to adjust it, so our result must be zero. * the offset is computed in a histogram on 9 bits (0..512) * but the offset in register is based on * 12 bits pipeline (0..4096). * we need to shift with the 3 bits that the histogram is * ignoring
*/
ctrls->offset[c] = (offset[c] - 1) << 3;
/* * the offset is then taken and converted to 2's complements, * and must be negative, as we subtract this value from the * color components
*/
ctrls->offset[c] = -ctrls->offset[c];
/* * the stretch gain is the total number of histogram bins * divided by the actual range of color component (Max - Min) * If we compute gain like this, the actual color component * will be stretched to the full histogram. * We need to shift 9 bits for precision, we have 9 bits for * decimals
*/
s_gain[c] = (HIST_ENTRIES << 9) /
(ctrls->hist_minmax[c][HIST_MAX_INDEX] -
ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1);
/* * Now we have to compute the gain w.r.t. the average. * Add/lose gain to the component towards the average. * If it happens that the component is zero, use the * fixed point value : 1.0 gain.
*/ if (hist_count[c])
gw_gain[c] = div_u64(avg << 9, hist_count[c]); else
gw_gain[c] = 1 << 9;
dev_dbg(isc->dev, "isc wb: component %d, s_gain %u, gw_gain %u\n",
c, s_gain[c], gw_gain[c]); /* multiply both gains and adjust for decimals */
ctrls->gain[c] = s_gain[c] * gw_gain[c];
ctrls->gain[c] >>= 9;
/* make sure we are not out of range */
ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0));
dev_dbg(isc->dev, "isc wb: component %d, final gain %u\n",
c, ctrls->gain[c]);
}
}
ret = pm_runtime_resume_and_get(isc->dev); if (ret < 0) return;
/* * only update if we have all the required histograms and controls * if awb has been disabled, we need to reset registers as well.
*/ if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) { /* * It may happen that DMA Done IRQ will trigger while we are * updating white balance registers here. * In that case, only parts of the controls have been updated. * We can avoid that by locking the section.
*/
spin_lock_irqsave(&isc->awb_lock, flags);
isc_update_awb_ctrls(isc);
spin_unlock_irqrestore(&isc->awb_lock, flags);
/* * if we are doing just the one time white balance adjustment, * we are basically done.
*/ if (ctrls->awb == ISC_WB_ONETIME) {
dev_info(isc->dev, "Completed one time white-balance adjustment.\n"); /* update the v4l2 controls values */
isc_update_v4l2_ctrls(isc);
ctrls->awb = ISC_WB_NONE;
}
}
regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his,
hist_id | baysel | ISC_HIS_CFG_RAR);
/* * We have to make sure the streaming has not stopped meanwhile. * ISC requires a frame to clock the internal profile update. * To avoid issues, lock the sequence with a mutex
*/
mutex_lock(&isc->awb_mutex);
/* streaming is not active anymore */ if (isc->stop) {
mutex_unlock(&isc->awb_mutex); return;
}
isc_update_profile(isc);
mutex_unlock(&isc->awb_mutex);
/* if awb has been disabled, we don't need to start another histogram */ if (ctrls->awb)
regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) return 0;
switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: if (ctrl->val == 1)
ctrls->awb = ISC_WB_AUTO; else
ctrls->awb = ISC_WB_NONE;
/* configure the controls with new values from v4l2 */ if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new)
ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val; if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new)
ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val; if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new)
ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val; if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new)
ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val;
if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new)
ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val; if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new)
ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val; if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new)
ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val; if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new)
ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val;
isc_update_awb_ctrls(isc);
mutex_lock(&isc->awb_mutex); if (vb2_is_streaming(&isc->vb2_vidq)) { /* * If we are streaming, we can update profile to * have the new settings in place.
*/
isc_update_profile(isc);
} else { /* * The auto cluster will activate automatically this * control. This has to be deactivated when not * streaming.
*/
v4l2_ctrl_activate(isc->do_wb_ctrl, false);
}
mutex_unlock(&isc->awb_mutex);
/* if we have autowhitebalance on, start histogram procedure */ if (ctrls->awb == ISC_WB_AUTO &&
vb2_is_streaming(&isc->vb2_vidq) &&
ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
isc_set_histogram(isc, true);
/* * for one time whitebalance adjustment, check the button, * if it's pressed, perform the one time operation.
*/ if (ctrls->awb == ISC_WB_NONE &&
ctrl->cluster[ISC_CTRL_DO_WB]->is_new &&
!(ctrl->cluster[ISC_CTRL_DO_WB]->flags &
V4L2_CTRL_FLAG_INACTIVE)) {
ctrls->awb = ISC_WB_ONETIME;
isc_set_histogram(isc, true);
dev_dbg(isc->dev, "One time white-balance started.\n");
} return 0;
} return 0;
}
switch (ctrl->id) { /* being a cluster, this id will be called for every control */ case V4L2_CID_AUTO_WHITE_BALANCE:
ctrl->cluster[ISC_CTRL_R_GAIN]->val =
ctrls->gain[ISC_HIS_CFG_MODE_R];
ctrl->cluster[ISC_CTRL_B_GAIN]->val =
ctrls->gain[ISC_HIS_CFG_MODE_B];
ctrl->cluster[ISC_CTRL_GR_GAIN]->val =
ctrls->gain[ISC_HIS_CFG_MODE_GR];
ctrl->cluster[ISC_CTRL_GB_GAIN]->val =
ctrls->gain[ISC_HIS_CFG_MODE_GB];
/* do_white_balance is a button, so min,max,step,default are ignored */
isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
V4L2_CID_DO_WHITE_BALANCE,
0, 0, 0, 0);
if (!isc->do_wb_ctrl) {
ret = hdl->error;
v4l2_ctrl_handler_free(hdl); return ret;
}
if (video_is_registered(&isc->video_dev)) {
dev_err(isc->dev, "only supports one sub-device.\n"); return -EBUSY;
}
subdev_entity->sd = subdev;
pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
MEDIA_PAD_FL_SOURCE); if (pad < 0) {
dev_err(isc->dev, "failed to find pad for %s\n", subdev->name); return pad;
}
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.