/* DSPAUTO (DSP Auto Function ON/OFF Control) */ #define AWB_ACTRL 0x80 /* AWB auto threshold control */ #define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */ #define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */ #define UV_ACTRL 0x10 /* UV adjust auto slope control */ #define SCAL0_ACTRL 0x08 /* Auto scaling factor control */ #define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */
/* Use image size (with blankings) to calculate desired pixel clock. */ switch (cfmt->com7 & OFMT_MASK) { case OFMT_BRAW:
fsize = win->sizeimage; break; case OFMT_RGB: case OFMT_YUV: default:
fsize = win->sizeimage * 2; break;
}
pclk = fps * fsize;
/* * Pixel clock generation circuit is pretty simple: * * Fin -> [ / CLKRC_div] -> [ * PLL_mult] -> pclk * * Try to approximate the desired pixel clock testing all available * PLL multipliers (1x, 4x, 6x, 8x) and calculate corresponding * divisor with: * * div = PLL_mult * Fin / pclk * * and re-calculate the pixel clock using it: * * pclk = Fin * PLL_mult / CLKRC_div * * Choose the PLL_mult and CLKRC_div pair that gives a pixel clock * closer to the desired one. * * The desired pixel clock is calculated using a known frame size * (blanking included) and FPS.
*/
best_diff = ~0L; for (i = 0; i < ARRAY_SIZE(ov772x_pll); i++) { unsignedint pll_mult = ov772x_pll[i].mult; unsignedint pll_out = pll_mult * fin; unsignedint t_pclk; unsignedint div;
/* * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 * subdev active state API.
*/ if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL;
/* * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 * subdev active state API.
*/ if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL;
mutex_lock(&priv->lock);
if (priv->streaming) {
ret = -EBUSY; goto error;
}
fps = ov772x_select_fps(priv, tpf);
/* * If the device is not powered up by the host driver do * not apply any changes to H/W at this time. Instead * the frame rate will be restored right after power-up.
*/ if (priv->power_count > 0) {
ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win); if (ret) goto error;
}
/* * If the device is not powered up by the host driver do * not apply any controls to H/W at this time. Instead * the controls will be restored right after power-up.
*/ if (priv->power_count == 0) return 0;
switch (ctrl->id) { case V4L2_CID_VFLIP:
val = ctrl->val ? VFLIP_IMG : 0x00; if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP))
val ^= VFLIP_IMG; return regmap_update_bits(regmap, COM3, VFLIP_IMG, val); case V4L2_CID_HFLIP:
val = ctrl->val ? HFLIP_IMG : 0x00; if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP))
val ^= HFLIP_IMG; return regmap_update_bits(regmap, COM3, HFLIP_IMG, val); case V4L2_CID_BAND_STOP_FILTER: if (!ctrl->val) { /* Switch the filter off, it is on now */
ret = regmap_update_bits(regmap, BDBASE, 0xff, 0xff); if (!ret)
ret = regmap_update_bits(regmap, COM8,
BNDF_ON_OFF, 0);
} else { /* Switch the filter on, set AEC low limit */
val = 256 - ctrl->val;
ret = regmap_update_bits(regmap, COM8,
BNDF_ON_OFF, BNDF_ON_OFF); if (!ret)
ret = regmap_update_bits(regmap, BDBASE,
0xff, val);
}
return ret; case V4L2_CID_TEST_PATTERN:
priv->test_pattern = ctrl->val; return 0;
}
if (priv->clk) {
ret = clk_prepare_enable(priv->clk); if (ret) return ret;
}
if (priv->pwdn_gpio) {
gpiod_set_value(priv->pwdn_gpio, 1);
usleep_range(500, 1000);
}
/* * FIXME: The reset signal is connected to a shared GPIO on some * platforms (namely the SuperH Migo-R). Until a framework becomes * available to handle this cleanly, request the GPIO temporarily * to avoid conflicts.
*/
priv->rstb_gpio = gpiod_get_optional(&client->dev, "reset",
GPIOD_OUT_LOW); if (IS_ERR(priv->rstb_gpio)) {
dev_info(&client->dev, "Unable to get GPIO \"reset\"");
clk_disable_unprepare(priv->clk); return PTR_ERR(priv->rstb_gpio);
}
staticint ov772x_s_power(struct v4l2_subdev *sd, int on)
{ struct ov772x_priv *priv = to_ov772x(sd); int ret = 0;
mutex_lock(&priv->lock);
/* If the power count is modified from 0 to != 0 or from != 0 to 0, * update the power state.
*/ if (priv->power_count == !on) { if (on) {
ret = ov772x_power_on(priv); /* * Restore the format, the frame rate, and * the controls
*/ if (!ret)
ret = ov772x_set_params(priv, priv->cfmt,
priv->win);
} else {
ret = ov772x_power_off(priv);
}
}
if (!ret) { /* Update the power count. */
priv->power_count += on ? 1 : -1;
WARN(priv->power_count < 0, "Unbalanced power count\n");
WARN(priv->power_count > 1, "Duplicated s_power call\n");
}
if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { /* * Manual Edge Control Mode. * * Edge auto strength bit is set by default. * Remove it when manual mode.
*/
ret = regmap_update_bits(regmap, DSPAUTO, EDGE_ACTRL, 0x00); if (ret < 0) return ret;
ret = regmap_update_bits(regmap, EDGE_TRSHLD,
OV772X_EDGE_THRESHOLD_MASK,
priv->info->edgectrl.threshold); if (ret < 0) return ret;
ret = regmap_update_bits(regmap, EDGE_STRNGT,
OV772X_EDGE_STRENGTH_MASK,
priv->info->edgectrl.strength); if (ret < 0) return ret;
} elseif (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { /* * Auto Edge Control Mode. * * Set upper and lower limit.
*/
ret = regmap_update_bits(regmap, EDGE_UPPER,
OV772X_EDGE_UPPER_MASK,
priv->info->edgectrl.upper); if (ret < 0) return ret;
ret = regmap_update_bits(regmap, EDGE_LOWER,
OV772X_EDGE_LOWER_MASK,
priv->info->edgectrl.lower); if (ret < 0) return ret;
}
/* Edge Ctrl. */
ret = ov772x_edgectrl(priv); if (ret < 0) return ret;
/* Format and window size. */
ret = regmap_write(priv->regmap, HSTART, win->rect.left >> 2); if (ret < 0) goto ov772x_set_fmt_error;
ret = regmap_write(priv->regmap, HSIZE, win->rect.width >> 2); if (ret < 0) goto ov772x_set_fmt_error;
ret = regmap_write(priv->regmap, VSTART, win->rect.top >> 1); if (ret < 0) goto ov772x_set_fmt_error;
ret = regmap_write(priv->regmap, VSIZE, win->rect.height >> 1); if (ret < 0) goto ov772x_set_fmt_error;
ret = regmap_write(priv->regmap, HOUTSIZE, win->rect.width >> 2); if (ret < 0) goto ov772x_set_fmt_error;
ret = regmap_write(priv->regmap, VOUTSIZE, win->rect.height >> 1); if (ret < 0) goto ov772x_set_fmt_error;
ret = regmap_write(priv->regmap, HREF,
((win->rect.top & 1) << HREF_VSTART_SHIFT) |
((win->rect.left & 3) << HREF_HSTART_SHIFT) |
((win->rect.height & 1) << HREF_VSIZE_SHIFT) |
((win->rect.width & 3) << HREF_HSIZE_SHIFT)); if (ret < 0) goto ov772x_set_fmt_error;
ret = regmap_write(priv->regmap, EXHCH,
((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) |
((win->rect.width & 3) << EXHCH_HSIZE_SHIFT)); if (ret < 0) goto ov772x_set_fmt_error;
/* Set DSP_CTRL3. */
val = cfmt->dsp3; if (val) {
ret = regmap_update_bits(priv->regmap, DSP_CTRL3, UV_MASK, val); if (ret < 0) goto ov772x_set_fmt_error;
}
/* DSP_CTRL4: AEC reference point and DSP output format. */ if (cfmt->dsp4) {
ret = regmap_write(priv->regmap, DSP_CTRL4, cfmt->dsp4); if (ret < 0) goto ov772x_set_fmt_error;
}
/* Set COM3. */
val = cfmt->com3; if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP))
val |= VFLIP_IMG; if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP))
val |= HFLIP_IMG; if (priv->vflip_ctrl->val)
val ^= VFLIP_IMG; if (priv->hflip_ctrl->val)
val ^= HFLIP_IMG; if (priv->test_pattern)
val |= SCOLOR_TEST;
ret = regmap_update_bits(priv->regmap, COM3, SWAP_MASK | IMG_MASK, val); if (ret < 0) goto ov772x_set_fmt_error;
/* COM7: Sensor resolution and output format control. */
ret = regmap_write(priv->regmap, COM7, win->com7_bit | cfmt->com7); if (ret < 0) goto ov772x_set_fmt_error;
/* COM4, CLKRC: Set pixel clock and framerate. */
ret = ov772x_set_frame_rate(priv, priv->fps, cfmt, win); if (ret < 0) goto ov772x_set_fmt_error;
/* Set COM8. */ if (priv->band_filter_ctrl->val) { unsignedshort band_filter = priv->band_filter_ctrl->val;
ret = regmap_update_bits(priv->regmap, COM8,
BNDF_ON_OFF, BNDF_ON_OFF); if (!ret)
ret = regmap_update_bits(priv->regmap, BDBASE,
0xff, 256 - band_filter); if (ret < 0) goto ov772x_set_fmt_error;
}
if (priv->streaming) {
ret = -EBUSY; goto error;
}
/* * If the device is not powered up by the host driver do * not apply any changes to H/W at this time. Instead * the format will be restored right after power-up.
*/ if (priv->power_count > 0) {
ret = ov772x_set_params(priv, cfmt, win); if (ret < 0) goto error;
}
priv->win = win;
priv->cfmt = cfmt;
error:
mutex_unlock(&priv->lock);
return ret;
}
staticint ov772x_video_probe(struct ov772x_priv *priv)
{ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); int pid, ver, midh, midl; constchar *devname; int ret;
ret = ov772x_power_on(priv); if (ret < 0) return ret;
/* Check and show product ID and manufacturer ID. */
ret = regmap_read(priv->regmap, PID, &pid); if (ret < 0) return ret;
ret = regmap_read(priv->regmap, VER, &ver); if (ret < 0) return ret;
switch (VERSION(pid, ver)) { case OV7720:
devname = "ov7720"; break; case OV7725:
devname = "ov7725"; break; default:
dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
ret = -ENODEV; goto done;
}
ret = regmap_read(priv->regmap, MIDH, &midh); if (ret < 0) return ret;
ret = regmap_read(priv->regmap, MIDL, &midl); if (ret < 0) return ret;
dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
devname, pid, ver, midh, midl);
ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); if (!ep) {
dev_err(&client->dev, "Endpoint node not found\n"); return -EINVAL;
}
/* * For backward compatibility with older DTS where the * bus-type property was not mandatory, assume * V4L2_MBUS_PARALLEL as it was the only supported bus at the * time. v4l2_fwnode_endpoint_alloc_parse() will not fail if * 'bus-type' is not specified.
*/
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); if (ret) {
bus_cfg = (struct v4l2_fwnode_endpoint)
{ .bus_type = V4L2_MBUS_BT656 };
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); if (ret) goto error_fwnode_put;
}
priv->clk = clk_get(&client->dev, NULL); if (IS_ERR(priv->clk)) {
dev_err(&client->dev, "Unable to get xclk clock\n");
ret = PTR_ERR(priv->clk); goto error_ctrl_free;
}
priv->pwdn_gpio = gpiod_get_optional(&client->dev, "powerdown",
GPIOD_OUT_LOW); if (IS_ERR(priv->pwdn_gpio)) {
dev_info(&client->dev, "Unable to get GPIO \"powerdown\"");
ret = PTR_ERR(priv->pwdn_gpio); goto error_clk_put;
}
ret = ov772x_parse_dt(client, priv); if (ret) goto error_clk_put;
ret = ov772x_video_probe(priv); if (ret < 0) goto error_gpio_put;
priv->pad.flags = MEDIA_PAD_FL_SOURCE;
priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); if (ret < 0) goto error_gpio_put;
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.