📄 ov511.c
字号:
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); } if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518 && ov518_color) { i2c_w_mask(ov, 0x12, 0x00, 0x10); i2c_w_mask(ov, 0x13, 0x00, 0x20); } else { 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); } /* The OV518 needs special treatment. Although both the OV518 * and the OV6630 support a 16-bit video bus, only the 8 bit Y * bus is actually used. The UV bus is tied to ground. * Therefore, the OV6630 needs to be in 8-bit multiplexed * output mode */ if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518 && ov518_color) { i2c_w_mask(ov, 0x12, 0x10, 0x10); i2c_w_mask(ov, 0x13, 0x20, 0x20); } else { i2c_w_mask(ov, 0x13, 0x00, 0x20); } } /******** Clock programming ********/ /* 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); /* Test Pattern */ i2c_w_mask(ov, 0x12, (testpat?0x02:0x00), 0x02); /* Enable auto white balance */ i2c_w_mask(ov, 0x12, 0x04, 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 intset_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("Illegal dimensions"); return -EINVAL; } else { /* QVGA */ ret = mode_init_ov_sensor_regs(ov, width, height, mode, sub_flag, 1); if (ret < 0) return ret; hwscale = 1; hwsize = 320; vwsize = 240; } } /* Center the window */ hoffset = ((hwsize - width) / 2) >> hwscale; voffset = ((vwsize - height) / 2) >> vwscale; /* FIXME! - This needs to be changed to support 160x120 and 6620!!! */ if (sub_flag) { i2c_w(ov, 0x17, hwsbase+(ov->subx>>hwscale)); i2c_w(ov, 0x18, hwebase+((ov->subx+ov->subw)>>hwscale)); i2c_w(ov, 0x19, vwsbase+(ov->suby>>vwscale)); i2c_w(ov, 0x1a, vwebase+((ov->suby+ov->subh)>>vwscale)); } else { i2c_w(ov, 0x17, hwsbase + hoffset); i2c_w(ov, 0x18, hwebase + hoffset + (hwsize>>hwscale)); i2c_w(ov, 0x19, vwsbase + voffset); i2c_w(ov, 0x1a, vwebase + voffset + (vwsize>>vwscale)); }#ifdef OV511_DEBUG if (dump_sensor) dump_i2c_regs(ov);#endif return 0;}/* Set up the OV511/OV511+ with the given image parameters. * * Do not put any sensor-specific code in here (including I2C I/O functions) */static intov511_mode_init_regs(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag){ int hsegs, vsegs; if (sub_flag) { width = ov->subw; height = ov->subh; } PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", width, height, mode, sub_flag); // FIXME: This should be moved to a 7111a-specific function once // subcapture is dealt with properly if (ov->sensor == SEN_SAA7111A) { if (width == 320 && height == 240) { /* No need to do anything special */ } else if (width == 640 && height == 480) { /* Set the OV511 up as 320x480, but keep the * V4L resolution as 640x480 */ width = 320; } else { err("SAA7111A only allows 320x240 or 640x480"); return -EINVAL; } } /* Make sure width and height are a multiple of 8 */ if (width % 8 || height % 8) { err("Invalid size (%d, %d) (mode = %d)", width, height, mode); return -EINVAL; } if (width < ov->minwidth || height < ov->minheight) { err("Requested dimensions are too small"); return -EINVAL; } if (ov51x_stop(ov) < 0) return -EIO; if (mode == VIDEO_PALETTE_GREY) { reg_w(ov, R511_CAM_UV_EN, 0x00); reg_w(ov, R511_SNAP_UV_EN, 0x00); reg_w(ov, R511_SNAP_OPTS, 0x01); } else { reg_w(ov, R511_CAM_UV_EN, 0x01); reg_w(ov, R511_SNAP_UV_EN, 0x01); reg_w(ov, R511_SNAP_OPTS, 0x03); } /* Here I'm assuming that snapshot size == image size. * I hope that's always true. --claudio */ hsegs = (width >> 3) - 1; vsegs = (height >> 3) - 1; reg_w(ov, R511_CAM_PXCNT, hsegs); reg_w(ov, R511_CAM_LNCNT, vsegs); reg_w(ov, R511_CAM_PXDIV, 0x00); reg_w(ov, R511_CAM_LNDIV, 0x00); /* YUV420, low pass filter on */ reg_w(ov, R511_CAM_OPTS, 0x03); /* Snapshot additions */ reg_w(ov, R511_SNAP_PXCNT, hsegs); reg_w(ov, R511_SNAP_LNCNT, vsegs); reg_w(ov, R511_SNAP_PXDIV, 0x00); reg_w(ov, R511_SNAP_LNDIV, 0x00); if (ov->compress) { /* Enable Y and UV quantization and compression */ reg_w(ov, R511_COMP_EN, 0x07); reg_w(ov, R511_COMP_LUT_EN, 0x03); ov51x_reset(ov, OV511_RESET_OMNICE); } if (ov51x_restart(ov) < 0) return -EIO; return 0;}/* Sets up the OV518/OV518+ with the given image parameters * * OV518 needs a completely different approach, until we can figure out what * the individual registers do. Also, only 15 FPS is supported now. * * Do not put any sensor-specific code in here (including I2C I/O functions) */static intov518_mode_init_regs(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag){ int hsegs, vsegs, hi_res; if (sub_flag) { width = ov->subw; height = ov->subh; } PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", width, height, mode, sub_flag); if (width % 16 || height % 8) { err("Invalid size (%d, %d)", width, height); return -EINVAL; } if (width < ov->minwidth || height < ov->minheight) { err("Requested dimensions are too small"); return -EINVAL; } if (width >= 320 && height >= 240) { hi_res = 1; } else if (width >= 320 || height >= 240) { err("Invalid width/height combination (%d, %d)", width, height); return -EINVAL; } else { hi_res = 0; } if (ov51x_stop(ov) < 0) return -EIO; /******** Set the mode ********/ reg_w(ov, 0x2b, 0); reg_w(ov, 0x2c, 0); reg_w(ov, 0x2d, 0); reg_w(ov, 0x2e, 0); reg_w(ov, 0x3b, 0); reg_w(ov, 0x3c, 0); reg_w(ov, 0x3d, 0); reg_w(ov, 0x3e, 0); if (ov->bridge == BRG_OV518 && ov518_color) { /* OV518 needs U and V swapped */ i2c_w_mask(ov, 0x15, 0x00, 0x01); if (mode == VIDEO_PALETTE_GREY) { /* Set 16-bit input format (UV data are ignored) */ reg_w_mask(ov, 0x20, 0x00, 0x08); /* Set 8-bit (4:0:0) output format */ reg_w_mask(ov, 0x28, 0x00, 0xf0); reg_w_mask(ov, 0x38, 0x00, 0xf0); } else { /* Set 8-bit (YVYU) input format */ reg_w_mask(ov, 0x20, 0x08, 0x08); /* Set 12-bit (4:2:0) output format */ reg_w_mask(ov, 0x28, 0x80, 0xf0); reg_w_mask(ov, 0x38, 0x80, 0xf0); } } else { reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80); reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80); } hsegs = width / 16; vsegs = height / 4; reg_w(ov, 0x29, hsegs); reg_w(ov, 0x2a, vsegs); reg_w(ov, 0x39, hsegs); reg_w(ov, 0x3a, vsegs); /* Windows driver does this here; who knows why */ reg_w(ov, 0x2f, 0x80); /******** Set the framerate (to 15 FPS) ********/ /* Mode independent, but framerate dependent, regs */ reg_w(ov, 0x51, 0x02); /* Clock divider; lower==faster */ reg_w(ov, 0x22, 0x18); reg_w(ov, 0x23, 0xff); if (ov->bridge == BRG_OV518PLUS) reg_w(ov, 0x21, 0x19); else reg_w(ov, 0x71, 0x19); /* Compression-related? */ // FIXME: Sensor-specific /* Bit 5 is what matters here. Of course, it is "reserved" */ i2c_w(ov, 0x54, 0x23); reg_w(ov, 0x2f, 0x80); if (ov->bridge == BRG_OV518PLUS) { reg_w(ov, 0x24, 0x94); reg_w(ov, 0x25, 0x90); ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ ov518_reg_w32(ov, 0xc6, 540, 2); /* 21ch */ ov518_reg_w32(ov, 0xc7, 540, 2); /* 21ch */ ov518_reg_w32(ov, 0xc8, 108, 2); /* 6ch */ ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */ ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */ ov518_reg_w32(ov, 0xcc, 2400, 2); /* 960h */ ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */ ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */ } else { reg_w(ov, 0x24, 0x9f); reg_w(ov, 0x25, 0x90); ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ ov518_reg_w32(ov, 0xc6, 500, 2); /* 1f4h */ ov518_reg_w32(ov, 0xc7, 500, 2); /* 1f4h */ ov518_reg_w32(ov, 0xc8, 142, 2); /* 8eh */ ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */ ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */ ov518_reg_w32(ov, 0xcc, 2000, 2); /* 7d0h */ ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */ ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */ } reg_w(ov, 0x2f, 0x80); if (ov51x_restart(ov) < 0) return -EIO; /* Reset it just for good measure */ if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) return -EIO; return 0;}/* This is a wrapper around the OV511, OV518, and sensor specific functions */static intmode_init_regs(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag){ int rc = 0; if (!ov || !ov->dev) return -EFAULT; if (ov->bclass == BCL_OV518) { rc = ov518_mode_init_regs(ov, width, height, mode, sub_flag); } else { rc = ov511_mode_init_regs(ov, width, height, mode, sub_flag); } if (FATAL_ERROR(rc)) return rc; switch (ov->sensor) { case SEN_OV7610: case SEN_OV7620: case SEN_OV76BE: case SEN_OV8600: case SEN_OV6620: case SEN_OV6630: rc = set_ov_sensor_window(ov, width, height, mode, sub_flag); break; case SEN_KS0127: case SEN_KS0127B: err("KS0127-series decoders not supported yet"); rc = -EINVAL; break; case SEN_SAA7111A:// rc = mode_init_saa_sensor_regs(ov, width, height, mode,// sub_flag); PDEBUG(1, "SAA status = 0x%02X", i2c_r(ov, 0x1f)); break; default: err("Unknown sensor"); rc = -EINVAL; } if (FATAL_ERROR(rc)) return rc; /* Sensor-independent settings */ rc = sensor_set_auto_brightness(ov, ov->auto_brt); if (FATAL_ERROR(rc)) return rc; rc = sensor_set_auto_exposure(ov, ov->auto_exp); if (FATAL_ERROR(rc)) return rc; rc = sensor_set_banding_filter(ov, bandingfilter); if (FATAL_ERROR(rc)) return rc; if (ov->lightfreq) { rc = sensor_set_light_freq(ov, lightfreq); if (FATAL_ERROR(rc)) return rc; } rc = sensor_set_backlight(ov, ov->backlight); if (FATAL_ERROR(rc)) return rc; rc = sensor_set_mirror(ov, ov->mirror); if (FATAL_ERROR(rc)) return rc; return 0;}/* This sets the default image parameters. This is useful for apps that use * read() and do not set these. */static intov51x_set_default_params(struct usb_ov511 *ov){ int i; /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used * (using read() instead). */ for (i = 0; i < OV511_NUMFRAMES; i++) { ov->frame[i].width = ov->maxwidth; ov->frame[i].height = ov->maxheight; ov->frame[i].bytes_read = 0; if (force_palette) ov->frame[i].format = force_palette; else ov->frame[i].format = VIDEO_PALETTE_YUV420; ov->frame[i].depth = get_depth(ov->frame[i].format); } PDEBUG(3, "%dx%d, %s
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -