📄 tcm825x.c
字号:
val |= 0x80; tcm825x_write_reg(sensor->i2c_client, 0x02, val); } return 0;}static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc){ struct vcontrol *control; control = find_vctrl(qc->id); if (control == NULL) return -EINVAL; *qc = control->qc; return 0;}static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc){ struct tcm825x_sensor *sensor = s->priv; struct i2c_client *client = sensor->i2c_client; int val, r; struct vcontrol *lvc; /* exposure time is special, spread accross 2 registers */ if (vc->id == V4L2_CID_EXPOSURE) { int val_lower, val_upper; val_upper = tcm825x_read_reg(client, TCM825X_ADDR(TCM825X_ESRSPD_U)); if (val_upper < 0) return val_upper; val_lower = tcm825x_read_reg(client, TCM825X_ADDR(TCM825X_ESRSPD_L)); if (val_lower < 0) return val_lower; vc->value = ((val_upper & 0x1f) << 8) | (val_lower); return 0; } lvc = find_vctrl(vc->id); if (lvc == NULL) return -EINVAL; r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg)); if (r < 0) return r; val = r & TCM825X_MASK(lvc->reg); val >>= lvc->start_bit; if (val < 0) return val; vc->value = val; return 0;}static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc){ struct tcm825x_sensor *sensor = s->priv; struct i2c_client *client = sensor->i2c_client; struct vcontrol *lvc; int val = vc->value; /* exposure time is special, spread accross 2 registers */ if (vc->id == V4L2_CID_EXPOSURE) { int val_lower, val_upper; val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L); val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U); if (tcm825x_write_reg_mask(client, TCM825X_ESRSPD_U, val_upper)) return -EIO; if (tcm825x_write_reg_mask(client, TCM825X_ESRSPD_L, val_lower)) return -EIO; return 0; } lvc = find_vctrl(vc->id); if (lvc == NULL) return -EINVAL; val = val << lvc->start_bit; if (tcm825x_write_reg_mask(client, lvc->reg, val)) return -EIO; return 0;}static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt){ int index = fmt->index; switch (fmt->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (index >= TCM825X_NUM_CAPTURE_FORMATS) return -EINVAL; break; default: return -EINVAL; } fmt->flags = tcm825x_formats[index].flags; strlcpy(fmt->description, tcm825x_formats[index].description, sizeof(fmt->description)); fmt->pixelformat = tcm825x_formats[index].pixelformat; return 0;}static int ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f){ struct tcm825x_sensor *sensor = s->priv; enum image_size isize; int ifmt; struct v4l2_pix_format *pix = &f->fmt.pix; isize = tcm825x_find_size(s, pix->width, pix->height); dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n", isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS); pix->width = tcm825x_sizes[isize].width; pix->height = tcm825x_sizes[isize].height; for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++) if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat) break; if (ifmt == TCM825X_NUM_CAPTURE_FORMATS) ifmt = 0; /* Default = YUV 4:2:2 */ pix->pixelformat = tcm825x_formats[ifmt].pixelformat; pix->field = V4L2_FIELD_NONE; pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL; pix->sizeimage = pix->bytesperline * pix->height; pix->priv = 0; dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n", pix->pixelformat); switch (pix->pixelformat) { case V4L2_PIX_FMT_UYVY: default: pix->colorspace = V4L2_COLORSPACE_JPEG; break; case V4L2_PIX_FMT_RGB565: pix->colorspace = V4L2_COLORSPACE_SRGB; break; } return 0;}static int ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f){ struct tcm825x_sensor *sensor = s->priv; struct v4l2_pix_format *pix = &f->fmt.pix; int rval; rval = ioctl_try_fmt_cap(s, f); if (rval) return rval; rval = tcm825x_configure(s); sensor->pix = *pix; return rval;}static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f){ struct tcm825x_sensor *sensor = s->priv; f->fmt.pix = sensor->pix; return 0;}static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a){ struct tcm825x_sensor *sensor = s->priv; struct v4l2_captureparm *cparm = &a->parm.capture; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; memset(a, 0, sizeof(*a)); a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cparm->capability = V4L2_CAP_TIMEPERFRAME; cparm->timeperframe = sensor->timeperframe; return 0;}static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a){ struct tcm825x_sensor *sensor = s->priv; struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; u32 tgt_fps; /* target frames per secound */ int rval; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if ((timeperframe->numerator == 0) || (timeperframe->denominator == 0)) { timeperframe->denominator = DEFAULT_FPS; timeperframe->numerator = 1; } tgt_fps = timeperframe->denominator / timeperframe->numerator; if (tgt_fps > MAX_FPS) { timeperframe->denominator = MAX_FPS; timeperframe->numerator = 1; } else if (tgt_fps < MIN_FPS) { timeperframe->denominator = MIN_FPS; timeperframe->numerator = 1; } sensor->timeperframe = *timeperframe; rval = tcm825x_configure(s); return rval;}static int ioctl_s_power(struct v4l2_int_device *s, int on){ struct tcm825x_sensor *sensor = s->priv; return sensor->platform_data->power_set(on);}/* * Given the image capture format in pix, the nominal frame period in * timeperframe, calculate the required xclk frequency. * * TCM825X input frequency characteristics are: * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz */static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p){ struct tcm825x_sensor *sensor = s->priv; struct v4l2_fract *timeperframe = &sensor->timeperframe; u32 tgt_xclk; /* target xclk */ u32 tgt_fps; /* target frames per secound */ int rval; rval = sensor->platform_data->ifparm(p); if (rval) return rval; tgt_fps = timeperframe->denominator / timeperframe->numerator; tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ? (2457 * tgt_fps) / MAX_HALF_FPS : (2457 * tgt_fps) / MAX_FPS; tgt_xclk *= 10000; tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX); tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN); p->u.bt656.clock_curr = tgt_xclk; return 0;}static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf){ struct tcm825x_sensor *sensor = s->priv; return sensor->platform_data->needs_reset(s, buf, &sensor->pix);}static int ioctl_reset(struct v4l2_int_device *s){ return -EBUSY;}static int ioctl_init(struct v4l2_int_device *s){ return tcm825x_configure(s);}static int ioctl_dev_exit(struct v4l2_int_device *s){ return 0;}static int ioctl_dev_init(struct v4l2_int_device *s){ struct tcm825x_sensor *sensor = s->priv; int r; r = tcm825x_read_reg(sensor->i2c_client, 0x01); if (r < 0) return r; if (r == 0) { dev_err(&sensor->i2c_client->dev, "device not detected\n"); return -EIO; } return 0;}static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = { { vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init }, { vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *)ioctl_dev_exit }, { vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power }, { vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm }, { vidioc_int_g_needs_reset_num, (v4l2_int_ioctl_func *)ioctl_g_needs_reset }, { vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset }, { vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init }, { vidioc_int_enum_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, { vidioc_int_try_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, { vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, { vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, { vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm }, { vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm }, { vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl }, { vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl }, { vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl },};static struct v4l2_int_slave tcm825x_slave = { .ioctls = tcm825x_ioctl_desc, .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc),};static struct tcm825x_sensor tcm825x;static struct v4l2_int_device tcm825x_int_device = { .module = THIS_MODULE, .name = TCM825X_NAME, .priv = &tcm825x, .type = v4l2_int_type_slave, .u = { .slave = &tcm825x_slave, },};static int tcm825x_probe(struct i2c_client *client){ struct tcm825x_sensor *sensor = &tcm825x; int rval; if (i2c_get_clientdata(client)) return -EBUSY; sensor->platform_data = client->dev.platform_data; if (sensor->platform_data == NULL && !sensor->platform_data->is_okay()) return -ENODEV; sensor->v4l2_int_device = &tcm825x_int_device; sensor->i2c_client = client; i2c_set_clientdata(client, sensor); /* Make the default capture format QVGA RGB565 */ sensor->pix.width = tcm825x_sizes[QVGA].width; sensor->pix.height = tcm825x_sizes[QVGA].height; sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565; rval = v4l2_int_device_register(sensor->v4l2_int_device); if (rval) i2c_set_clientdata(client, NULL); return rval;}static int __exit tcm825x_remove(struct i2c_client *client){ struct tcm825x_sensor *sensor = i2c_get_clientdata(client); if (!client->adapter) return -ENODEV; /* our client isn't attached */ v4l2_int_device_unregister(sensor->v4l2_int_device); i2c_set_clientdata(client, NULL); return 0;}static struct i2c_driver tcm825x_i2c_driver = { .driver = { .name = TCM825X_NAME, }, .probe = tcm825x_probe, .remove = __exit_p(tcm825x_remove),};static struct tcm825x_sensor tcm825x = { .timeperframe = { .numerator = 1, .denominator = DEFAULT_FPS, },};static int __init tcm825x_init(void){ int rval; rval = i2c_add_driver(&tcm825x_i2c_driver); if (rval) printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n", __FUNCTION__); return rval;}static void __exit tcm825x_exit(void){ i2c_del_driver(&tcm825x_i2c_driver);}/* * FIXME: Menelaus isn't ready (?) at module_init stage, so use * late_initcall for now. */late_initcall(tcm825x_init);module_exit(tcm825x_exit);MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");MODULE_DESCRIPTION("TCM825x camera sensor driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -