diff options
Diffstat (limited to 'drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.c')
-rwxr-xr-x | drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.c b/drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.c new file mode 100755 index 00000000..3b913277 --- /dev/null +++ b/drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.c @@ -0,0 +1,209 @@ +/* + * Driver for ov3660 CMOS Image Sensor from Omnivision + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <asm/errno.h> +#include "../cmos-subdev.h" +#include "../../wmt-vid.h" +#include "ov3660.h" + +#define sensor_write_array(sd, arr, sz) \ + cmos_init_16bit_addr(arr, sz, (sd)->i2c_addr) + +#define sensor_read(sd, reg) \ + wmt_vid_i2c_read16addr(sd->i2c_addr, reg) + +#define sensor_write(sd, reg, val) \ + wmt_vid_i2c_write16addr(sd->i2c_addr, reg, val) + +struct cmos_win_size { + char *name; + int width; + int height; + uint32_t *regs; + size_t size; +}; + +#define CMOS_WIN_SIZE(n, w, h, r) \ + {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) } + + +static const struct cmos_win_size cmos_supported_win_sizes[] = { + CMOS_WIN_SIZE("VGA", 640, 480, ov3660_640_480_regs), + CMOS_WIN_SIZE("QXGA", 2048, 1536, ov3660_2048_1536_regs), +}; + +static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) { + if (cmos_supported_win_sizes[i].width == *width && + cmos_supported_win_sizes[i].height == *height) { + *width = cmos_supported_win_sizes[i].width; + *height = cmos_supported_win_sizes[i].height; + return &cmos_supported_win_sizes[i]; + } + } + return NULL; +} + +static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value) +{ + uint32_t *regs; + size_t size; + + switch (value) { + case WHITE_BALANCE_AUTO: + regs = ov3660_wb_auto; + size = ARRAY_SIZE(ov3660_wb_auto); + break; + case WHITE_BALANCE_INCANDESCENCE: + regs = ov3660_wb_incandescent; + size = ARRAY_SIZE(ov3660_wb_incandescent); + break; + case WHITE_BALANCE_DAYLIGHT: + regs = ov3660_wb_daylight; + size = ARRAY_SIZE(ov3660_wb_daylight); + break; + case WHITE_BALANCE_CLOUDY: + regs = ov3660_wb_cloudy; + size = ARRAY_SIZE(ov3660_wb_cloudy); + break; + case WHITE_BALANCE_FLUORESCENT: + regs = ov3660_wb_fluorescent; + size = ARRAY_SIZE(ov3660_wb_fluorescent); + break; + default: + return -EINVAL; + } + + sensor_write_array(sd, regs, size); + return 0; +} + +static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + case V4L2_CID_DO_WHITE_BALANCE: + return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0); + } + return -EINVAL; +} + +static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + case V4L2_CID_DO_WHITE_BALANCE: + return sensor_s_wb(sd, ctrl->value); + } + return -EINVAL; +} + +static int sensor_g_mbus_fmt(struct cmos_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + return -EINVAL; +} + +static int sensor_s_mbus_fmt(struct cmos_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + const struct cmos_win_size *win; + + win = cmos_select_win(&mf->width, &mf->height); + if (!win) + return -EINVAL; + + sensor_write_array(sd, win->regs, win->size); + return 0; +} + +static int sensor_try_mbus_fmt(struct cmos_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + const struct cmos_win_size *win; + + /* + * select suitable win + */ + win = cmos_select_win(&mf->width, &mf->height); + + return 0; +} + +static int sensor_identify(struct cmos_subdev *sd) +{ + uint32_t data; + + data = sensor_read(sd, 0x300a); + data <<= 8; + data |= sensor_read(sd, 0x300b); + + return (data == sd->id) ? 0 : 1; +} + +static int sensor_init(struct cmos_subdev *sd) +{ + if (!sensor_identify(sd)) + return -1; + + sensor_write(sd, 0x3103, 0x11); + sensor_write(sd, 0x3008, 0x82); + msleep(5); + sensor_write_array(sd, ov3660_default_regs_init, + ARRAY_SIZE(ov3660_default_regs_init)); + return 0; +} + +static int sensor_exit(struct cmos_subdev *sd) +{ + sensor_write(sd, 0x3017, 0x80); + sensor_write(sd, 0x3018, 0x03); + return 0; +} + +static struct cmos_subdev_ops ov3660_ops = { + .identify = sensor_identify, + .init = sensor_init, + .exit = sensor_exit, + .queryctrl = sensor_queryctrl, + .s_ctrl = sensor_s_ctrl, + .s_mbus_fmt = sensor_s_mbus_fmt, + .g_mbus_fmt = sensor_g_mbus_fmt, + .try_mbus_fmt = sensor_try_mbus_fmt, +}; + +struct cmos_subdev ov3660 = { + .name = "ov3660", + .i2c_addr = 0x3c, + .id = 0x3660, + .max_width = 2048, + .max_height = 1536, + .ops = &ov3660_ops, +}; + +static int __init ov3660_init(void) +{ + return cmos_register_sudbdev(&ov3660); +} + +static void __exit ov3660_exit(void) +{ + cmos_unregister_subdev(&ov3660); + return; +} + +module_init(ov3660_init); +module_exit(ov3660_exit); + +MODULE_LICENSE("GPL"); |