📄 ov511_core.c
字号:
static intsensor_get_picture(struct usb_ov511 *ov, struct video_picture *p){ int rc, v; PDEBUG(4, "sensor_get_picture"); if (ov->has_decoder) { call_i2c_clients(ov, VIDIOCGPICT, p); return 0; } /* Don't return error if a setting is unsupported, or rest of settings * will not be performed */ rc = sensor_get_control(ov, OVCAMCHIP_CID_CONT, &v); if (FATAL_ERROR(rc)) return rc; p->contrast = v; rc = sensor_get_control(ov, OVCAMCHIP_CID_BRIGHT, &v); if (FATAL_ERROR(rc)) return rc; p->brightness = v; rc = sensor_get_control(ov, OVCAMCHIP_CID_SAT, &v); if (FATAL_ERROR(rc)) return rc; p->colour = v; rc = sensor_get_control(ov, OVCAMCHIP_CID_HUE, &v); if (FATAL_ERROR(rc)) return rc; p->hue = v; p->whiteness = 105 << 8; return 0;}/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */static voidov51x_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 * * Returns: 0 for success */static intsensor_set_light_freq(struct usb_ov511 *ov, int freq){ if (freq != 50 && freq != 60) { err("Invalid light freq (%d Hz)", freq); return -EINVAL; } return sensor_set_control(ov, OVCAMCHIP_CID_FREQ, freq);}/* Returns number of bits per pixel (regardless of where they are located; * planar or not), or zero for unsupported format. */static inline intget_depth(int palette){ switch (palette) { case VIDEO_PALETTE_GREY: return 8; case VIDEO_PALETTE_YUV420: return 12; case VIDEO_PALETTE_YUV420P: return 12; /* Planar */ default: return 0; /* Invalid format */ }}/* Bytes per frame. Used by read(). Return of 0 indicates error */static inline long intget_frame_length(struct ov511_frame *frame){ if (!frame) return 0; else return ((frame->width * frame->height * get_depth(frame->format)) >> 3);}static intset_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode, int sub_flag){ struct ovcamchip_window win; int half_w = ov->maxwidth / 2; int half_h = ov->maxheight / 2; int rc; win.format = mode; /* Unless subcapture is enabled, center the image window and downsample * if possible to increase the field of view */ if (sub_flag) { win.x = ov->subx; win.y = ov->suby; win.width = ov->subw; win.height = ov->subh; win.quarter = 0; } else { /* NOTE: OV518(+) does downsampling on its own */ if ((width > half_w && height > half_h) || ov->bclass == BCL_OV518) { win.width = ov->maxwidth; win.height = ov->maxheight; win.quarter = 0; } else if (width > half_w || height > half_h) { err("Illegal dimensions"); return -EINVAL; } else { win.width = half_w; win.height = half_h; win.quarter = 1; } /* Center it */ win.x = (win.width - width) / 2; win.y = (win.height - height) / 2; } if (ov->clockdiv >= 0) { /* Manual override */ win.clockdiv = ov->clockdiv; } else if (ov->bridge == BRG_OV518) { /* OV518 controls the clock externally */ win.clockdiv = 0; } else if (ov->bridge == BRG_OV518PLUS) { /* OV518+ controls the clock externally */ win.clockdiv = 1; } else if (ov->compress) { /* Use the highest possible rate, to maximize FPS */ switch (ov->sensor) { case CC_OV6620: /* ...except with this sensor, which doesn't like * higher rates yet */ win.clockdiv = 3; break; case CC_OV6630: win.clockdiv = 0; break; case CC_OV76BE: case CC_OV7610: case CC_OV7620: win.clockdiv = 1; break; default: err("Invalid sensor"); return -EINVAL; } } else { switch (ov->sensor) { case CC_OV6620: case CC_OV6630: win.clockdiv = 3; break; case CC_OV76BE: case CC_OV7610: case CC_OV7620: /* Use slowest possible clock without sacrificing * frame rate */ win.clockdiv = ((sub_flag ? ov->subw * ov->subh : width * height) * (win.format == VIDEO_PALETTE_GREY ? 2 : 3) / 2) / 66000; break; default: err("Invalid sensor"); return -EINVAL; } } PDEBUG(4, "Setting clock divider to %d", win.clockdiv); rc = sensor_cmd(ov, OVCAMCHIP_CMD_S_MODE, &win); if (rc < 0) return rc; /* A setting of 0x06 reduces frame corruption with certain sensors, in * scenes with moving objects */ if (framedrop >= 0) { i2c_w(ov, 0x16, framedrop); } else if (ov->sensor == CC_OV6620 || ov->sensor == CC_OV76BE || ov->sensor == CC_OV7610) { i2c_w(ov, 0x16, 0x06); } 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) { 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 maximum) ********/ /* Mode independent, but framerate dependent, regs */ /* Clock divider; lower==faster */ if (ov->bridge == BRG_OV518PLUS) reg_w(ov, 0x51, 0x02); else reg_w(ov, 0x51, 0x04); reg_w(ov, 0x22, 0x18); reg_w(ov, 0x23, 0xff); if (ov->bridge == BRG_OV518PLUS) reg_w(ov, 0x21, 0x1f); else reg_w(ov, 0x71, 0x17); /* 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, 0x9f); reg_w(ov, 0x25, 0x90); ov518_reg_w32(ov, 0xc4, 700, 2); /* 2bch */ ov518_reg_w32(ov, 0xc6, 498, 2); /* 1f2h */ ov518_reg_w32(ov, 0xc7, 498, 2); /* 1f2h */ ov518_reg_w32(ov, 0xc8, 100, 2); /* 64h */ ov518_reg_w32(ov, 0xca, 183331, 3); /* 2cc23h */ ov518_reg_w32(ov, 0xcb, 895, 2); /* 37fh */ ov518_reg_w32(ov, 0xcc, 3000, 2); /* bb8h */ ov518_reg_w32(ov, 0xcd, 45, 2); /* 2dh */ ov518_reg_w32(ov, 0xce, 851, 2); /* 353h */ } else { reg_w(ov, 0x24, 0x9f); reg_w(ov, 0x25, 0x90); ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ ov518_reg_w32(ov, 0xc6, 381, 2); /* 17dh */ ov518_reg_w32(ov, 0xc7, 381, 2); /* 17dh */ ov518_reg_w32(ov, 0xc8, 128, 2); /* 80h */ ov518_reg_w32(ov, 0xca, 183331, 3); /* 2cc23h */ ov518_reg_w32(ov, 0xcb, 746, 2); /* 2eah */ ov518_reg_w32(ov, 0xcc, 1750, 2); /* 6d6h */ ov518_reg_w32(ov, 0xcd, 45, 2); /* 2dh */ ov518_reg_w32(ov, 0xce, 851, 2); /* 353h */ } 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->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 CC_OV7610: case CC_OV7620: case CC_OV76BE: case CC_OV6620: case CC_OV6630: { rc = set_ov_sensor_window(ov, width, height, mode, sub_flag); 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; return 0;}/* Set up the camera chip with the options provided by the module params */static intcamchip_init_settings(struct usb_ov511 *ov){ int rc; rc = sensor_set_control(ov, OVCAMCHIP_CID_AUTOBRIGHT, autobright); if (FATAL_ERROR(rc)) return rc; rc = sensor_set_control(ov, OVCAMCHIP_CID_AUTOEXP, autoexp); if (FATAL_ERROR(rc)) return rc; rc = sensor_set_control(ov, OVCAMCHIP_CID_BANDFILT, bandingfilter); if (FATAL_ERROR(rc)) return rc; if (lightfreq) { rc = sensor_set_light_freq(ov, lightfreq); if (FATAL_ERROR(rc)) return rc; } rc = sensor_set_control(ov, OVCAMCHIP_CID_BACKLIGHT, backlight); if (FATAL_ERROR(rc)) return rc; rc = sensor_set_control(ov, OVCAMCHIP_CID_MIRROR, 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -