static int mt9v011_remove(struct i2c_client *c) { struct v4l2_subdev *sd = i2c_get_clientdata(c); struct mt9v011 *core = to_mt9v011(sd); v4l2_dbg(1, debug, sd, "mt9v011.c: removing mt9v011 adapter on address 0x%x\n", c->addr << 1); v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(&core->ctrls); kfree(to_mt9v011(sd)); return 0; }
static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct mt9v011 *core = to_mt9v011(sd); v4l2_dbg(1, debug, sd, "g_ctrl called\n"); switch (ctrl->id) { case V4L2_CID_GAIN: ctrl->value = core->global_gain; return 0; case V4L2_CID_EXPOSURE: ctrl->value = core->exposure; return 0; case V4L2_CID_RED_BALANCE: ctrl->value = core->red_bal; return 0; case V4L2_CID_BLUE_BALANCE: ctrl->value = core->blue_bal; return 0; case V4L2_CID_HFLIP: ctrl->value = core->hflip ? 1 : 0; return 0; case V4L2_CID_VFLIP: ctrl->value = core->vflip ? 1 : 0; return 0; } return -EINVAL; }
static void set_res(struct v4l2_subdev *sd) { struct mt9v011 *core = to_mt9v011(sd); unsigned vstart, hstart; /* * The mt9v011 doesn't have scaling. So, in order to select the desired * resolution, we're cropping at the middle of the sensor. * hblank and vblank should be adjusted, in order to warrant that * we'll preserve the line timings for 30 fps, no matter what resolution * is selected. * NOTE: datasheet says that width (and height) should be filled with * width-1. However, this doesn't work, since one pixel per line will * be missing. */ hstart = 20 + (640 - core->width) / 2; mt9v011_write(sd, R02_MT9V011_COLSTART, hstart); mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); vstart = 8 + (480 - core->height) / 2; mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); calc_fps(sd, NULL, NULL); };
static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) { struct mt9v011 *core = to_mt9v011(sd); unsigned height, width, hblank, vblank, speed; unsigned row_time, t_time; u64 frames_per_ms; unsigned tmp; height = mt9v011_read(sd, R03_MT9V011_HEIGHT); width = mt9v011_read(sd, R04_MT9V011_WIDTH); hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); row_time = (width + 113 + hblank) * (speed + 2); t_time = row_time * (height + vblank + 1); frames_per_ms = core->xtal * 1000l; do_div(frames_per_ms, t_time); tmp = frames_per_ms; v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", tmp / 1000, tmp % 1000, t_time); if (numerator && denominator) { *numerator = 1000; *denominator = (u32)frames_per_ms; } }
static void set_balance(struct v4l2_subdev *sd) { struct mt9v011 *core = to_mt9v011(sd); u16 green_gain, blue_gain, red_gain; u16 exposure; s16 bal; exposure = core->exposure; green_gain = calc_mt9v011_gain(core->global_gain); bal = core->global_gain; bal += (core->blue_bal * core->global_gain / (1 << 7)); blue_gain = calc_mt9v011_gain(bal); bal = core->global_gain; bal += (core->red_bal * core->global_gain / (1 << 7)); red_gain = calc_mt9v011_gain(bal); mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain); mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain); mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain); mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure); }
static void set_read_mode(struct v4l2_subdev *sd) { struct mt9v011 *core = to_mt9v011(sd); unsigned mode = 0x1000; if (core->hflip) mode |= 0x4000; if (core->vflip) mode |= 0x8000; mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); }
static int mt9v011_s_config(struct v4l2_subdev *sd, int dumb, void *data) { struct mt9v011 *core = to_mt9v011(sd); unsigned *xtal = data; v4l2_dbg(1, debug, sd, "s_config called\n"); if (xtal) { core->xtal = *xtal; v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", *xtal / 1000000, (*xtal / 1000) % 1000); } return 0; }
static int mt9v011_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) { struct mt9v011 *core = to_mt9v011(sd); int rc; rc = mt9v011_try_mbus_fmt(sd, fmt); if (rc < 0) return -EINVAL; core->width = fmt->width; core->height = fmt->height; set_res(sd); return 0; }
static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct mt9v011 *core = to_mt9v011(sd); u8 i, n; n = ARRAY_SIZE(mt9v011_qctrl); for (i = 0; i < n; i++) { if (ctrl->id != mt9v011_qctrl[i].id) continue; if (ctrl->value < mt9v011_qctrl[i].minimum || ctrl->value > mt9v011_qctrl[i].maximum) return -ERANGE; v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", ctrl->id, ctrl->value); break; } switch (ctrl->id) { case V4L2_CID_GAIN: core->global_gain = ctrl->value; break; case V4L2_CID_EXPOSURE: core->exposure = ctrl->value; break; case V4L2_CID_RED_BALANCE: core->red_bal = ctrl->value; break; case V4L2_CID_BLUE_BALANCE: core->blue_bal = ctrl->value; break; case V4L2_CID_HFLIP: core->hflip = ctrl->value; set_read_mode(sd); return 0; case V4L2_CID_VFLIP: core->vflip = ctrl->value; set_read_mode(sd); return 0; default: return -EINVAL; } set_balance(sd); return 0; }
static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { struct v4l2_pix_format *pix = &fmt->fmt.pix; struct mt9v011 *core = to_mt9v011(sd); int rc; rc = mt9v011_try_fmt(sd, fmt); if (rc < 0) return -EINVAL; core->width = pix->width; core->height = pix->height; set_res(sd); return 0; }
static void set_res(struct v4l2_subdev *sd) { struct mt9v011 *core = to_mt9v011(sd); unsigned vstart, hstart; hstart = 20 + (640 - core->width) / 2; mt9v011_write(sd, R02_MT9V011_COLSTART, hstart); mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); vstart = 8 + (480 - core->height) / 2; mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); calc_fps(sd, NULL, NULL); };
static void set_balance(struct v4l2_subdev *sd) { struct mt9v011 *core = to_mt9v011(sd); u16 green1_gain, green2_gain, blue_gain, red_gain; green1_gain = core->global_gain; green2_gain = core->global_gain; blue_gain = core->global_gain + core->global_gain * core->blue_bal / (1 << 9); red_gain = core->global_gain + core->global_gain * core->blue_bal / (1 << 9); mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green1_gain); mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green1_gain); mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain); mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); }
static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) { struct mt9v011 *core = to_mt9v011(sd); unsigned height, width, hblank, vblank; unsigned row_time, line_time; u64 t_time, speed; /* Avoid bogus calculus */ if (!numerator || !denominator) return 0; height = mt9v011_read(sd, R03_MT9V011_HEIGHT); width = mt9v011_read(sd, R04_MT9V011_WIDTH); hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); row_time = width + 113 + hblank; line_time = height + vblank + 1; t_time = core->xtal * ((u64)numerator); /* round to the closest value */ t_time += denominator / 2; do_div(t_time, denominator); speed = t_time; do_div(speed, row_time * line_time); /* Avoid having a negative value for speed */ if (speed < 2) speed = 0; else speed -= 2; /* Avoid speed overflow */ if (speed > 15) return 15; return (u16)speed; }
static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) { struct mt9v011 *core = to_mt9v011(sd); unsigned height, width, hblank, vblank; unsigned row_time, line_time; u64 t_time, speed; if (!numerator || !denominator) return 0; height = mt9v011_read(sd, R03_MT9V011_HEIGHT); width = mt9v011_read(sd, R04_MT9V011_WIDTH); hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); row_time = width + 113 + hblank; line_time = height + vblank + 1; t_time = core->xtal * ((u64)numerator); t_time += denominator / 2; do_div(t_time, denominator); speed = t_time; do_div(speed, row_time * line_time); if (speed < 2) speed = 0; else speed -= 2; if (speed > 15) return 15; return (u16)speed; }