📄 ov511.c
字号:
rc = i2c_r(ov, 0x10); if (rc < 0) return rc; else *val = rc; break; case SEN_KS0127: case SEN_KS0127B: case SEN_SAA7111A: val = 0; PDEBUG(3, "Unsupported with this sensor"); return -EPERM; default: err("Sensor not supported for get_exposure"); return -EINVAL; } PDEBUG(3, "%d", *val); ov->exposure = *val; return 0; }#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */ /* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */ static inline void ov51x_led_control(struct usb_ov511 *ov, int enable) { PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); if (ov->bridge == BRG_OV511PLUS) reg_w(ov, R511_SYS_LED_CTL, enable ? 1 : 0); else if (ov->bclass == BCL_OV518) reg_w_mask(ov, R518_GPIO_OUT, enable ? 0x02 : 0x00, 0x02); return; } /* Matches the sensor's internal frame rate to the lighting frequency. * Valid frequencies are: * 50 - 50Hz, for European and Asian lighting * 60 - 60Hz, for American lighting * * Tested with: OV7610, OV7620, OV76BE, OV6620 * Unsupported: KS0127, KS0127B, SAA7111A * Returns: 0 for success */ static int sensor_set_light_freq(struct usb_ov511 *ov, int freq) { int sixty; PDEBUG(4, "%d Hz", freq); if (freq == 60) sixty = 1; else if (freq == 50) sixty = 0; else { err("Invalid light freq (%d Hz)", freq); return -EINVAL; } switch (ov->sensor) { case SEN_OV7610: i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); i2c_w(ov, 0x2b, sixty?0x00:0xac); i2c_w_mask(ov, 0x13, 0x10, 0x10); i2c_w_mask(ov, 0x13, 0x00, 0x10); break; case SEN_OV7620: case SEN_OV76BE: case SEN_OV8600: i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); i2c_w(ov, 0x2b, sixty?0x00:0xac); i2c_w_mask(ov, 0x76, 0x01, 0x01); break; case SEN_OV6620: case SEN_OV6630: i2c_w(ov, 0x2b, sixty?0xa8:0x28); i2c_w(ov, 0x2a, sixty?0x84:0xa4); break; case SEN_KS0127: case SEN_KS0127B: case SEN_SAA7111A: PDEBUG(5, "Unsupported with this sensor"); return -EPERM; default: err("Sensor not supported for set_light_freq"); return -EINVAL; } ov->lightfreq = freq; return 0; } /* If enable is true, turn on the sensor's banding filter, otherwise turn it * off. This filter tries to reduce the pattern of horizontal light/dark bands * caused by some (usually fluorescent) lighting. The light frequency must be * set either before or after enabling it with ov51x_set_light_freq(). * * Tested with: OV7610, OV7620, OV76BE, OV6620. * Unsupported: KS0127, KS0127B, SAA7111A * Returns: 0 for success */ static inline int sensor_set_banding_filter(struct usb_ov511 *ov, int enable) { int rc; PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B || ov->sensor == SEN_SAA7111A) { PDEBUG(5, "Unsupported with this sensor"); return -EPERM; } rc = i2c_w_mask(ov, 0x2d, enable?0x04:0x00, 0x04); if (rc < 0) return rc; ov->bandfilt = enable; return 0; } /* If enable is true, turn on the sensor's auto brightness control, otherwise * turn it off. * * Unsupported: KS0127, KS0127B, SAA7111A * Returns: 0 for success */ static inline int sensor_set_auto_brightness(struct usb_ov511 *ov, int enable) { int rc; PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B || ov->sensor == SEN_SAA7111A) { PDEBUG(5, "Unsupported with this sensor"); return -EPERM; } rc = i2c_w_mask(ov, 0x2d, enable?0x10:0x00, 0x10); if (rc < 0) return rc; ov->auto_brt = enable; return 0; } /* If enable is true, turn on the sensor's auto exposure control, otherwise * turn it off. * * Unsupported: KS0127, KS0127B, SAA7111A * Returns: 0 for success */ static inline int sensor_set_auto_exposure(struct usb_ov511 *ov, int enable) { PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); switch (ov->sensor) { case SEN_OV7610: i2c_w_mask(ov, 0x29, enable?0x00:0x80, 0x80); break; case SEN_OV6620: case SEN_OV7620: case SEN_OV76BE: case SEN_OV8600: i2c_w_mask(ov, 0x13, enable?0x01:0x00, 0x01); break; case SEN_OV6630: i2c_w_mask(ov, 0x28, enable?0x00:0x10, 0x10); break; case SEN_KS0127: case SEN_KS0127B: case SEN_SAA7111A: PDEBUG(5, "Unsupported with this sensor"); return -EPERM; default: err("Sensor not supported for set_auto_exposure"); return -EINVAL; } ov->auto_exp = enable; return 0; } /* Modifies the sensor's exposure algorithm to allow proper exposure of objects * that are illuminated from behind. * * Tested with: OV6620, OV7620 * Unsupported: OV7610, OV76BE, KS0127, KS0127B, SAA7111A * Returns: 0 for success */ static int sensor_set_backlight(struct usb_ov511 *ov, int enable) { PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); switch (ov->sensor) { case SEN_OV7620: case SEN_OV8600: i2c_w_mask(ov, 0x68, enable?0xe0:0xc0, 0xe0); i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); break; case SEN_OV6620: i2c_w_mask(ov, 0x4e, enable?0xe0:0xc0, 0xe0); i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); i2c_w_mask(ov, 0x0e, enable?0x80:0x00, 0x80); break; case SEN_OV6630: i2c_w_mask(ov, 0x4e, enable?0x80:0x60, 0xe0); i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); break; case SEN_OV7610: case SEN_OV76BE: case SEN_KS0127: case SEN_KS0127B: case SEN_SAA7111A: PDEBUG(5, "Unsupported with this sensor"); return -EPERM; default: err("Sensor not supported for set_backlight"); return -EINVAL; } ov->backlight = enable; return 0; } static inline int sensor_set_mirror(struct usb_ov511 *ov, int enable) { PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); switch (ov->sensor) { case SEN_OV6620: case SEN_OV6630: case SEN_OV7610: case SEN_OV7620: case SEN_OV76BE: case SEN_OV8600: i2c_w_mask(ov, 0x12, enable?0x40:0x00, 0x40); break; case SEN_KS0127: case SEN_KS0127B: case SEN_SAA7111A: PDEBUG(5, "Unsupported with this sensor"); return -EPERM; default: err("Sensor not supported for set_mirror"); return -EINVAL; } ov->mirror = enable; return 0; } /* Returns number of bits per pixel (regardless of where they are located; * planar or not), or zero for unsupported format. */ static inline int get_depth(int palette) { switch (palette) { case VIDEO_PALETTE_GREY: return 8; case VIDEO_PALETTE_YUV420: return 12; case VIDEO_PALETTE_YUV420P: return 12; /* Planar */ case VIDEO_PALETTE_RGB565: return 16; case VIDEO_PALETTE_RGB24: return 24; case VIDEO_PALETTE_YUV422: return 16; case VIDEO_PALETTE_YUYV: return 16; case VIDEO_PALETTE_YUV422P: return 16; /* Planar */ default: return 0; /* Invalid format */ } } /* Bytes per frame. Used by read(). Return of 0 indicates error */ static inline long int get_frame_length(struct ov511_frame *frame) { if (!frame) return 0; else return ((frame->width * frame->height * get_depth(frame->format)) >> 3); } static int mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag, int qvga) { int clock; /******** Mode (VGA/QVGA) and sensor specific regs ********/ switch (ov->sensor) { case SEN_OV7610: i2c_w(ov, 0x14, qvga?0x24:0x04); // FIXME: Does this improve the image quality or frame rate?#if 0 i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); i2c_w(ov, 0x24, 0x10); i2c_w(ov, 0x25, qvga?0x40:0x8a); i2c_w(ov, 0x2f, qvga?0x30:0xb0); i2c_w(ov, 0x35, qvga?0x1c:0x9c);#endif break; case SEN_OV7620: // i2c_w(ov, 0x2b, 0x00); i2c_w(ov, 0x14, qvga?0xa4:0x84); i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); i2c_w(ov, 0x24, qvga?0x20:0x3a); i2c_w(ov, 0x25, qvga?0x30:0x60); i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); i2c_w_mask(ov, 0x67, qvga?0xf0:0x90, 0xf0); i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); break; case SEN_OV76BE: // i2c_w(ov, 0x2b, 0x00); i2c_w(ov, 0x14, qvga?0xa4:0x84); // FIXME: Enable this once 7620AE uses 7620 initial settings#if 0 i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); i2c_w(ov, 0x24, qvga?0x20:0x3a); i2c_w(ov, 0x25, qvga?0x30:0x60); i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); i2c_w_mask(ov, 0x67, qvga?0xb0:0x90, 0xf0); i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20);#endif break; case SEN_OV6620: case SEN_OV6630: i2c_w(ov, 0x14, qvga?0x24:0x04); /* No special settings yet */ break; default: err("Invalid sensor"); return -EINVAL; } /******** Palette-specific regs ********/ if (mode == VIDEO_PALETTE_GREY) { if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { /* these aren't valid on the OV6620/OV7620/6630? */ i2c_w_mask(ov, 0x0e, 0x40, 0x40); } i2c_w_mask(ov, 0x13, 0x20, 0x20); } else { if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { /* not valid on the OV6620/OV7620/6630? */ i2c_w_mask(ov, 0x0e, 0x00, 0x40); } i2c_w_mask(ov, 0x13, 0x00, 0x20); } /******** Clock programming ********/ // FIXME: Test this with OV6630 /* The OV6620 needs special handling. This prevents the * severe banding that normally occurs */ if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) { /* Clock down */ i2c_w(ov, 0x2a, 0x04); if (ov->compress) { // clock = 0; /* This ensures the highest frame rate */ clock = 3; } else if (clockdiv == -1) { /* If user didn't override it */ clock = 3; /* Gives better exposure time */ } else { clock = clockdiv; } PDEBUG(4, "Setting clock divisor to %d", clock); i2c_w(ov, 0x11, clock); i2c_w(ov, 0x2a, 0x84); /* This next setting is critical. It seems to improve * the gain or the contrast. The "reserved" bits seem * to have some effect in this case. */ i2c_w(ov, 0x2d, 0x85); } else { if (ov->compress) { clock = 1; /* This ensures the highest frame rate */ } else if (clockdiv == -1) { /* If user didn't override it */ /* Calculate and set the clock divisor */ clock = ((sub_flag ? ov->subw * ov->subh : width * height) * (mode == VIDEO_PALETTE_GREY ? 2 : 3) / 2) / 66000; } else { clock = clockdiv; } PDEBUG(4, "Setting clock divisor to %d", clock); i2c_w(ov, 0x11, clock); } /******** Special Features ********/ if (framedrop >= 0) i2c_w(ov, 0x16, framedrop); if (sensor_gbr) i2c_w_mask(ov, 0x12, 0x08, 0x08); else i2c_w_mask(ov, 0x12, 0x00, 0x08); /* Test Pattern */ i2c_w_mask(ov, 0x12, (testpat?0x02:0x00), 0x02); /* Auto white balance */ // if (awb) i2c_w_mask(ov, 0x12, 0x04, 0x04); // else // i2c_w_mask(ov, 0x12, 0x00, 0x04); // This will go away as soon as ov51x_mode_init_sensor_regs() // is fully tested. /* 7620/6620/6630? don't have register 0x35, so play it safe */ if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { if (width == 640 && height == 480) i2c_w(ov, 0x35, 0x9e); else i2c_w(ov, 0x35, 0x1e); } return 0; } static int set_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag) { int ret; int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; int hoffset, voffset, hwscale = 0, vwscale = 0; /* The different sensor ICs handle setting up of window differently. * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */ switch (ov->sensor) { case SEN_OV7610: case SEN_OV76BE: hwsbase = 0x38; hwebase = 0x3a; vwsbase = vwebase = 0x05; break; case SEN_OV6620: case SEN_OV6630: hwsbase = 0x38; hwebase = 0x3a; vwsbase = 0x05; vwebase = 0x06; break; case SEN_OV7620: hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ hwebase = 0x2f; vwsbase = vwebase = 0x05; break; default: err("Invalid sensor"); return -EINVAL; } if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) { /* Note: OV518(+) does downsample on its own) */ if ((width > 176 && height > 144) || ov->bclass == BCL_OV518) { /* CIF */ ret = mode_init_ov_sensor_regs(ov, width, height, mode, sub_flag, 0); if (ret < 0) return ret; hwscale = 1; vwscale = 1; /* The datasheet says 0; it's wrong */ hwsize = 352; vwsize = 288; } else if (width > 176 || height > 144) { err("Illegal dimensions"); return -EINVAL; } else { /* QCIF */ ret = mode_init_ov_sensor_regs(ov, width, height, mode, sub_flag, 1); if (ret < 0) return ret; hwsize = 176; vwsize = 144; } } else { if (width > 320 && height > 240) { /* VGA */ ret = mode_init_ov_sensor_regs(ov, width, height, mode, sub_flag, 0); if (ret < 0) return ret; hwscale = 2; vwscale = 1; hwsize = 640; vwsize = 480; } else if (width > 320 || height > 240) { err("Ill
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -