📄 ov5640.c
字号:
static int reset_ov5640(struct i2c_client *client){ int ret = i2cc_set_reg(client, 0x3008, 0x82); mdelay(5); return (ret);}static int ov5640_get_params(struct i2c_client *client, unsigned int *width, unsigned int *height, enum v4l2_mbus_pixelcode code){ struct ov5640_priv *priv = to_ov5640(client); int ret = -EINVAL; int i; /* select format */ priv->cfmt = NULL; for (i = 0; i < ARRAY_SIZE(ov5640_cfmts); i++) { if (code == ov5640_cfmts[i].code) { priv->cfmt = ov5640_cfmts + i; break; } } if (NULL == priv->cfmt) { return (ret); } priv->win = select_win(*width, *height); *width = priv->win->width; *height = priv->win->height; INFO_PURLPLE("current params: %s %dX%d\n", priv->win->name, *width, *height); return (0);}static inline void init_setting_values(struct ov5640_priv *priv){ priv->flip_flag = OV5640_HFLIP; }static void get_preview_params(struct i2c_client *client, unsigned int *exposure, unsigned short *maxlines, unsigned short *gain){ unsigned char ret_h = 0; unsigned char ret_m = 0; unsigned char ret_l = 0; i2cc_get_reg(client, 0x3500, &ret_h); i2cc_get_reg(client, 0x3501, &ret_m); i2cc_get_reg(client, 0x3502, &ret_l); *exposure = ((ret_h << 16) + (ret_m << 8) + ret_l) >> 4; INFO_GREEN("expl:0x%x, expm:0x%x, exph:0x%x\n", ret_l, ret_m, ret_h); i2cc_get_reg(client, 0x350c, &ret_h); i2cc_get_reg(client, 0x350d, &ret_l); *maxlines = (ret_h << 8) + ret_l; //i2cc_get_reg(client, 0x350a, &ret_h); i2cc_get_reg(client, 0x350b, &ret_l); *gain = /*((ret_h & 0x1) << 8) + */ret_l; INFO_GREEN("exposure:0x%x, maxlines:0x%x, gain:0x%x\n", *exposure, *maxlines, *gain);}static void manual_set_exposure_and_gain(struct i2c_client *client, unsigned int p_exposure, unsigned short p_maxlines, unsigned short p_gain){ unsigned char ret_h = 0; unsigned char ret_l = 0; unsigned char exp_h; unsigned char exp_m; unsigned char exp_l; unsigned char lines_10ms; unsigned short cap_maxlines; unsigned short cap_exposure; unsigned short cap_gain; unsigned short gain; unsigned int cap_exp_gain; i2cc_get_reg(client, 0x350c, &ret_h); i2cc_get_reg(client, 0x350d, &ret_l); cap_maxlines = (ret_h << 8) + ret_l; //p_maxlines = 980; //cap_maxlines = 1964; INFO_GREEN("cap_maxlines: 0x%x\n", cap_maxlines); // for 50HZ, if 60HZ, devided by 12000 lines_10ms = CAPTURE_FRAME_RATE * cap_maxlines / 10000 * 13 / 12; if (0 == p_maxlines) { p_maxlines = 1; } cap_exposure = ((p_exposure * CAPTURE_FRAME_RATE * cap_maxlines) / (p_maxlines * PREVIEW_FRAME_RATE)) * 6 / 5; //cap_exp_gain = 1126 * cap_exposure * cap_gain; cap_exp_gain = cap_exposure * p_gain; // in night mode, need multiply 2 again. //cap_exp_gain >>= 9; if (cap_exp_gain < (long)cap_maxlines * 16) { cap_exposure = cap_exp_gain / 16; if (cap_exposure > lines_10ms) { cap_exposure /= lines_10ms; cap_exposure *= lines_10ms; } } else { cap_exposure = cap_maxlines; } if (0 == cap_exposure) { cap_exposure = 1; } cap_gain = ((cap_exp_gain << 1) / cap_exposure + 1) >> 1; exp_l = (cap_exposure << 4) & 0xFF; exp_m = (cap_exposure >> 4) & 0xFF; exp_h = (cap_exposure >> 12) & 0xFF; INFO_GREEN("gain:0x%x, expl:0x%x, expm:0x%x, exph:0x%x, cap_maxlines:0x%x\n", cap_gain, exp_l, exp_m, exp_h, cap_maxlines); i2cc_set_reg(client, 0x350b, cap_gain); i2cc_set_reg(client, 0x3502, exp_l); i2cc_set_reg(client, 0x3501, exp_m); i2cc_set_reg(client, 0x3500, exp_h); /* * add delay-time, to avoid boundary problem between dark and bright */ mdelay(100);}static int ov5640_set_params(struct i2c_client *client, unsigned int *width, unsigned int *height, enum v4l2_mbus_pixelcode code){ struct ov5640_priv *priv = to_ov5640(client); int ret = -EINVAL; int i; const struct regval *reg_list = NULL; int list_len = 0; /* select format */ priv->cfmt = NULL; for (i = 0; i < ARRAY_SIZE(ov5640_cfmts); i++) { if (code == ov5640_cfmts[i].code) { priv->cfmt = ov5640_cfmts + i; break; } } if (NULL == priv->cfmt) { return (ret); } /* select win size, now only one, so select directly */ priv->win = select_win(*width, *height); /* set hardware regs needed */ if (RESV_VGA == priv->win->resv) { reset_ov5640(client); /* set default regs */ write_regs(client, ov5640_init_regs, ARRAY_SIZE(ov5640_init_regs)); init_setting_values(priv); } switch (priv->win->resv) { case RESV_XGA: { reg_list = ov5640_qsxga_to_xga_regs; list_len = ARRAY_SIZE(ov5640_qsxga_to_xga_regs); break; } case RESV_SXGA: { reg_list = ov5640_qsxga_to_sxga_regs; list_len = ARRAY_SIZE(ov5640_qsxga_to_sxga_regs); break; } case RESV_UXGA: { reg_list = ov5640_qsxga_to_uxga_regs; list_len = ARRAY_SIZE(ov5640_qsxga_to_uxga_regs); break; } case RESV_QXGA: { reg_list = ov5640_qsxga_to_qxga_regs; list_len = ARRAY_SIZE(ov5640_qsxga_to_qxga_regs); break; } case RESV_QSXGA: default: break; } if (RESV_VGA != priv->win->resv) { unsigned int preview_exp; unsigned short preview_maxl; unsigned short preview_gain; /* manually set exposure and gain */ i2cc_set_reg(client, 0x3503, 0x07); get_preview_params(client, &preview_exp, &preview_maxl, &preview_gain); write_regs(client, ov5640_qsxga_regs, ARRAY_SIZE(ov5640_qsxga_regs)); if (NULL != reg_list) { write_regs(client, reg_list, list_len); } manual_set_exposure_and_gain(client, preview_exp, preview_maxl, preview_gain); } *width = priv->win->width; *height = priv->win->height; INFO_PURLPLE("ok, params are width:%d-height:%d\n", *width, *height); return (0);}static int ov5640_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *vmf){ struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5640_priv *priv = to_ov5640(client); if (NULL == priv->win || NULL == priv->cfmt) { unsigned int width = VGA_WIDTH; unsigned int height = VGA_HEIGHT; int ret = 0; ret = ov5640_get_params(client, &width, &height, V4L2_MBUS_FMT_YUYV8_2X8_BE); if (ret < 0) { return (ret); } } vmf->width = priv->win->width; vmf->height = priv->win->height; vmf->code = priv->cfmt->code; vmf->colorspace = priv->cfmt->colorspace; vmf->field = V4L2_FIELD_NONE; INFO_GREEN("ok, get fmt w:%dXh:%d-code:%d-csp:%d\n", vmf->width, vmf->height, vmf->code, vmf->colorspace); return (0);}static int ov5640_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *vmf){ struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5640_priv *priv = to_ov5640(client); int ret = ov5640_set_params(client, &vmf->width, &vmf->height, vmf->code); if (!ret) vmf->colorspace = priv->cfmt->colorspace; INFO_PURLPLE("\n"); return (ret);}// TODO..... modifystatic int ov5640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *vcc){ vcc->bounds.left = OV5640_COLUMN_SKIP; vcc->bounds.top = OV5640_ROW_SKIP; vcc->bounds.width = OV5640_MAX_WIDTH; vcc->bounds.height = OV5640_MAX_HEIGHT; vcc->defrect = vcc->bounds; /* set default rect. */ vcc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vcc->pixelaspect.numerator = 1; vcc->pixelaspect.denominator = 1; INFO_PURLPLE("\n"); return (0);}// TODO..... modifystatic int ov5640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *vc){ vc->c.left = 0; vc->c.top = 0; vc->c.width = QSXGA_WIDTH; vc->c.height = QSXGA_HEIGHT; vc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; INFO_BLUE("\n"); return (0);}static int ov5640_enum_fmt(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code){ if (index >= ARRAY_SIZE(ov5640_cfmts)) return -EINVAL; *code = ov5640_cfmts[index].code; OV_INFO("fmt index:%d\n", index); return 0;}static int ov5640_set_brightness(struct i2c_client *client, int bright){ struct ov5640_priv *priv = to_ov5640(client); unsigned char reg5587, reg5588; if (bright < -4 || 4 < bright) { OV_ERR("brightness - %d is out of range[-4, 4]\n", bright); return (-ERANGE); } if (bright < 0) { reg5587 = 0x10 * (-bright); reg5588 = 0x09; /* bit[3] is Y bright sign */ } else { reg5587 = 0x10 * bright; reg5588 = 0x01; } i2cc_set_reg(client, 0x5001, 0xff); i2cc_set_reg(client, 0x5587, reg5587); i2cc_set_reg(client, 0x5580, 0x04); i2cc_set_reg(client, 0x5588, reg5588); priv->brightness = bright; OV_INFO("brightness:%d, reg5587:0x%x, reg5588:0x%x\n", bright, reg5587, reg5588); return (0);}static int ov5640_set_contrast(struct i2c_client *client, int contrast){ struct ov5640_priv *priv = to_ov5640(client); unsigned char y_gain, reg5588; if (contrast < -4 || 4 < contrast) { OV_ERR("contrast - %d is out of range[-4, 4]\n", contrast); return (-ERANGE); } if (0 == contrast) { reg5588 = 0x1; } else { reg5588 = 0x41; } y_gain = 0x20 + 0x4 * contrast; i2cc_set_reg(client, 0x5001, 0xff); i2cc_set_reg(client, 0x5580, 0x04); i2cc_set_reg(client, 0x5586, y_gain); i2cc_set_reg(client, 0x5585, y_gain); i2cc_set_reg(client, 0x5588, reg5588); priv->contrast = contrast; OV_INFO("contrast:%d, y_gain:0x%x, reg5588:0x%x\n", contrast, y_gain, reg5588); return (0);}/* auto, manual seperated is ok?? */static int ov5640_set_saturation(struct i2c_client *client, int saturation){ struct ov5640_priv *priv = to_ov5640(client); unsigned char uv_max, uv_min, reg5588; if (saturation < -4 || 4 < saturation) { OV_ERR("saturation - %d is out of range[-4, 4]\n", saturation); return (-ERANGE); } if (0 == saturation) { /* different from application notes */ uv_max = 0x40; /* max value for UV adjust */ uv_min = 0x10; /* min value for UV adjust */ reg5588 = 0x01; /* bit[6]==0, auto saturation */ } else { uv_max = 0x40 + 0x10 * saturation; /* U sat. */ uv_min = uv_max; /* v sat */ reg5588 = 0x41; /* bit[6]==1, manual saturation */ } i2cc_set_reg(client, 0x5001, 0xff); /* init is 0xa3 */ i2cc_set_reg(client, 0x5583, uv_max); i2cc_set_reg(client, 0x5584, uv_min); i2cc_set_reg(client, 0x5580, 0x02); /* bit[1], enable(1)/disabe(0) saturation */ i2cc_set_reg(client, 0x5588, reg5588); priv->saturation = saturation; OV_INFO("saturation:%d\n", saturation); return (0);}/* XXX:effect is reversed to note's picture exept -180. check it */static int ov5640_set_hue(struct i2c_client *client, int hue){ struct ov5640_priv *priv = to_ov5640(client); unsigned char reg5581, reg5582, reg5588; switch (hue) { case -180: reg5581 = 0x80; reg5582 = 0x00; reg5588 = 0x32; break; case -150: reg5581 = 0x6f; reg5582 = 0x40; reg5588 = 0x32; break; case -120: reg5581 = 0x40; reg5582 = 0x6f; reg5588 = 0x32; break; case -90: reg5581 = 0x00; reg5582 = 0x80; reg5588 = 0x02; break; case -60: reg5581 = 0x40; reg5582 = 0x6f; reg5588 = 0x02; break; case -30: reg5581 = 0x6f; reg5582 = 0x40; reg5588 = 0x02; break; case 0: reg5581 = 0x80; reg5582 = 0x00; reg5588 = 0x01; break; case 30: reg5581 = 0x6f; reg5582 = 0x40; reg5588 = 0x01; break; case 60: reg5581 = 0x40; reg5582 = 0x6f; reg5588 = 0x01; break; case 90: reg5581 = 0x00; reg5582 = 0x80; reg5588 = 0x31; break; case 120: reg5581 = 0x40; reg5582 = 0x6f; reg5588 = 0x31; break; case 150: reg5581 = 0x6f; reg5582 = 0x40; reg5588 = 0x31; break; default: OV_ERR("hue - %d is out of range[-180, 150]/step-30\n", hue); return (-ERANGE); } i2cc_set_reg(client, 0x5001, 0xff); i2cc_set_reg(client, 0x5580, 0x01); /* XXXX:diff. from defualt value */ i2cc_set_reg(client, 0x5581, reg5581); /* hue cos coefficient */ i2cc_set_reg(client, 0x5582, reg5582); /* hue sin coefficient */ i2cc_set_reg(client, 0x5588, reg5588); priv->hue = hue; OV_INFO("hue: %d, 5581:0x%x, 5582:0x%x, 5588:0x%x\n", hue, reg5581, reg5582, reg5588); return (0);}/* default value here is different from init one. */static int ov5640_set_exposure_level(struct i2c_client *client, int level){ struct ov5640_priv *priv = to_ov5640(client); unsigned char reg3a0f, reg3a10; unsigned char reg3a1b, reg3a1e; unsigned char reg3a11, reg3a1f; reg3a0f = 0x38 + 0x8 * level; reg3a10 = 0x30 + 0x8 * level; reg3a1b = reg3a0f; reg3a1e = reg3a10; reg3a1f = 0x10; switch (level) { case -5: /* -1.7EV */ reg3a11 = 0x20; break; case -4: /* -1.3EV */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -