/* * MT9V111 is a 1/4-Inch CMOS digital image sensor with an integrated * Image Flow Processing (IFP) engine and a sensor core loosely based on * MT9V011. * * The IFP can produce several output image formats from the sensor core * output. This driver currently supports only YUYV format permutations. * * The driver allows manual frame rate control through set_frame_interval subdev * operation or V4L2_CID_V/HBLANK controls, but it is known that the * auto-exposure algorithm might modify the programmed frame rate. While the * driver initially programs the sensor with auto-exposure and * auto-white-balancing enabled, it is possible to disable them and more * precisely control the frame rate. * * While it seems possible to instruct the auto-exposure control algorithm to * respect a programmed frame rate when adjusting the pixel integration time, * registers controlling this feature are not documented in the public * available sensor manual used to develop this driver (09005aef80e90084, * MT9V111_1.fm - Rev. G 1/05 EN).
*/
/* * mt9v111_mbus_fmt - List all media bus formats supported by the driver. * * Only list the media bus code here. The image sizes are freely configurable * in the pixel array sizes range. * * The desired frame interval, in the supported frame interval range, is * obtained by configuring blanking as the sensor does not have a PLL but * only a fixed clock divider that generates the output pixel clock.
*/ staticstruct mt9v111_mbus_fmt {
u32 code;
} mt9v111_formats[] = {
{
.code = MEDIA_BUS_FMT_UYVY8_2X8,
},
{
.code = MEDIA_BUS_FMT_YUYV8_2X8,
},
{
.code = MEDIA_BUS_FMT_VYUY8_2X8,
},
{
.code = MEDIA_BUS_FMT_YVYU8_2X8,
},
};
/* Approximate to the closest supported frame interval. */
best_diff = ~0L; for (i = 0, idx = 0; i < ARRAY_SIZE(mt9v111_frame_intervals); i++) {
diff = abs(fps - mt9v111_frame_intervals[i]); if (diff < best_diff) {
idx = i;
best_diff = diff;
}
}
fps = mt9v111_frame_intervals[idx];
/* * The sensor does not provide a PLL circuitry and pixel clock is * generated dividing the master clock source by two. * * Trow = (W + Hblank + 114) * 2 * (1 / SYSCLK) * TFrame = Trow * (H + Vblank + 2) * * FPS = (SYSCLK / 2) / (Trow * (H + Vblank + 2)) * * This boils down to tune H and V blanks to best approximate the * above equation. * * Test all available H/V blank values, until we reach the * desired frame rate.
*/
best_fps = vb = hb = 0;
pclk = DIV_ROUND_CLOSEST(mt9v111->sysclk, 2);
row_pclk = MT9V111_PIXEL_ARRAY_WIDTH + 7 + MT9V111_CORE_R04_WIN_H_OFFS;
frm_cols = MT9V111_PIXEL_ARRAY_HEIGHT + 7 + MT9V111_CORE_R03_WIN_V_OFFS;
/* * Configure output image format components ordering. * * TODO: IFP block can also output several RGB permutations, we only * support YUYV permutations at the moment.
*/ switch (mt9v111->fmt.code) { case MEDIA_BUS_FMT_YUYV8_2X8:
outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC; break; case MEDIA_BUS_FMT_VYUY8_2X8:
outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR; break; case MEDIA_BUS_FMT_YVYU8_2X8:
outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC |
MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR; break; case MEDIA_BUS_FMT_UYVY8_2X8: default:
outfmtctrl2 = 0; break;
}
ret = mt9v111_update(c, MT9V111_R01_IFP, MT9V111_IFP_R3A_OUTFMT_CTRL2,
MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_MASK,
outfmtctrl2); if (ret) return ret;
/* * Do not change default sensor's core configuration: * output the whole 640x480 pixel array, skip 18 columns and 6 rows. * * Instead, control the output image size through IFP block. * * TODO: No zoom&pan support. Currently we control the output image * size only through decimation, with no zoom support.
*/
ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA5_HPAN,
MT9V111_IFP_DECIMATION_FREEZE); if (ret) return ret;
ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA8_VPAN,
MT9V111_IFP_DECIMATION_FREEZE); if (ret) return ret;
ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA6_HZOOM,
MT9V111_IFP_DECIMATION_FREEZE |
MT9V111_PIXEL_ARRAY_WIDTH); if (ret) return ret;
ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA9_VZOOM,
MT9V111_IFP_DECIMATION_FREEZE |
MT9V111_PIXEL_ARRAY_HEIGHT); if (ret) return ret;
ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA7_HOUT,
MT9V111_IFP_DECIMATION_FREEZE |
mt9v111->fmt.width); if (ret) return ret;
ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RAA_VOUT,
mt9v111->fmt.height); if (ret) return ret;
/* Apply controls to set auto exp, auto awb and timings */
ret = v4l2_ctrl_handler_setup(&mt9v111->ctrls); if (ret) return ret;
/* * Set pixel integration time to the whole frame time. * This value controls the shutter delay when running with AE * disabled. If longer than frame time, it affects the output * frame rate.
*/ return mt9v111_write(c, MT9V111_R01_CORE, MT9V111_CORE_R09_PIXEL_INT,
MT9V111_PIXEL_ARRAY_HEIGHT);
}
/* --- V4L2 subdev operations --- */
staticint mt9v111_s_power(struct v4l2_subdev *sd, int on)
{ struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); int pwr_count; int ret = 0;
mutex_lock(&mt9v111->pwr_mutex);
/* * Make sure we're transitioning from 0 to 1, or viceversa, * before actually changing the power state.
*/
pwr_count = mt9v111->pwr_count;
pwr_count += on ? 1 : -1; if (pwr_count == !!on) {
ret = on ? __mt9v111_power_on(sd) :
__mt9v111_power_off(sd); if (!ret) /* All went well, updated power counter. */
mt9v111->pwr_count = pwr_count;
mutex_unlock(&mt9v111->pwr_mutex);
return ret;
}
/* * Update power counter to keep track of how many nested calls we * received.
*/
WARN_ON(pwr_count < 0 || pwr_count > 1);
mt9v111->pwr_count = pwr_count;
mutex_unlock(&mt9v111->pwr_mutex);
return ret;
}
staticint mt9v111_s_stream(struct v4l2_subdev *subdev, int enable)
{ struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); int ret;
mutex_lock(&mt9v111->stream_mutex);
if (mt9v111->streaming == enable) {
mutex_unlock(&mt9v111->stream_mutex); return 0;
}
ret = mt9v111_s_power(subdev, enable); if (ret) goto error_unlock;
if (enable && mt9v111->pending) {
ret = mt9v111_hw_config(mt9v111); if (ret) goto error_unlock;
/* * No need to update control here as far as only H/VBLANK are * supported and immediately programmed to registers in .s_ctrl
*/
/* * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 * subdev active state API.
*/ if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL;
if (!tpf->numerator)
tpf->numerator = 1;
mutex_lock(&mt9v111->stream_mutex);
if (mt9v111->streaming) {
mutex_unlock(&mt9v111->stream_mutex); return -EBUSY;
}
if (mt9v111->fps == fps) {
mutex_unlock(&mt9v111->stream_mutex); return 0;
}
/* * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 * subdev active state API.
*/ if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL;
if (fie->pad || fie->index >= ARRAY_SIZE(mt9v111_frame_intervals)) return -EINVAL;
for (i = 0; i < ARRAY_SIZE(mt9v111_frame_sizes); i++) if (fie->width == mt9v111_frame_sizes[i].width &&
fie->height == mt9v111_frame_sizes[i].height) break;
if (i == ARRAY_SIZE(mt9v111_frame_sizes)) return -EINVAL;
mutex_lock(&mt9v111->stream_mutex); if (mt9v111->streaming) {
mutex_unlock(&mt9v111->stream_mutex); return -EBUSY;
}
if (format->pad) {
mutex_unlock(&mt9v111->stream_mutex); return -EINVAL;
}
/* Update mbus format code and sizes. */ for (i = 0; i < ARRAY_SIZE(mt9v111_formats); i++) { if (format->format.code == mt9v111_formats[i].code) {
new_fmt.code = mt9v111_formats[i].code; break;
}
} if (i == ARRAY_SIZE(mt9v111_formats))
new_fmt.code = mt9v111_formats[0].code;
for (i = 0; i < ARRAY_SIZE(mt9v111_frame_sizes); i++) { unsignedint fit = abs(mt9v111_frame_sizes[i].width -
format->format.width) +
abs(mt9v111_frame_sizes[i].height -
format->format.height); if (fit < best_fit) {
best_fit = fit;
idx = i;
/* Update the format and sizes, then mark changes as pending. */
__fmt->code = new_fmt.code;
__fmt->width = new_fmt.width;
__fmt->height = new_fmt.height;
if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
mt9v111->pending = true;
mutex_lock(&mt9v111->pwr_mutex); /* * If sensor is powered down, just cache new control values, * no actual register access.
*/ if (!mt9v111->pwr_count) {
mt9v111->pending = true;
mutex_unlock(&mt9v111->pwr_mutex); return 0;
}
mutex_unlock(&mt9v111->pwr_mutex);
/* * Flickering control gets disabled if both auto exp and auto awb * are disabled too. If any of the two is enabled, enable it. * * Disabling flickering when ae and awb are off allows a more precise * control of the programmed frame rate.
*/ if (mt9v111->auto_exp->is_new || mt9v111->auto_awb->is_new) { if (mt9v111->auto_exp->val == V4L2_EXPOSURE_MANUAL &&
mt9v111->auto_awb->val == V4L2_WHITE_BALANCE_MANUAL)
ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP,
MT9V111_IFP_R08_OUTFMT_CTRL,
MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER,
0); else
ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP,
MT9V111_IFP_R08_OUTFMT_CTRL,
MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER,
1); if (ret) return ret;
}
ret = -EINVAL; switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE:
ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP,
MT9V111_IFP_R06_OPMODE_CTRL,
MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN,
ctrl->val == V4L2_WHITE_BALANCE_AUTO ?
MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN : 0); break; case V4L2_CID_EXPOSURE_AUTO:
ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP,
MT9V111_IFP_R06_OPMODE_CTRL,
MT9V111_IFP_R06_OPMODE_CTRL_AE_EN,
ctrl->val == V4L2_EXPOSURE_AUTO ?
MT9V111_IFP_R06_OPMODE_CTRL_AE_EN : 0); break; case V4L2_CID_HBLANK:
ret = mt9v111_update(mt9v111->client, MT9V111_R01_CORE,
MT9V111_CORE_R05_HBLANK,
MT9V111_CORE_R05_MAX_HBLANK,
mt9v111->hblank->val); break; case V4L2_CID_VBLANK:
ret = mt9v111_update(mt9v111->client, MT9V111_R01_CORE,
MT9V111_CORE_R06_VBLANK,
MT9V111_CORE_R06_MAX_VBLANK,
mt9v111->vblank->val); break;
}
/* PIXEL_RATE is fixed: just expose it to user space. */
v4l2_ctrl_new_std(&mt9v111->ctrls, &mt9v111_ctrl_ops,
V4L2_CID_PIXEL_RATE, 0,
DIV_ROUND_CLOSEST(mt9v111->sysclk, 2), 1,
DIV_ROUND_CLOSEST(mt9v111->sysclk, 2));
if (mt9v111->ctrls.error) {
ret = mt9v111->ctrls.error; goto error_free_ctrls;
}
mt9v111->sd.ctrl_handler = &mt9v111->ctrls;
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.