From 08a66aea55154b50f9e9e2e89cc85d8b75121568 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 24 Oct 2008 15:43:07 -0300 Subject: V4L/DVB (9488): Add ov772x driver This patch adds ov772x driver that use soc_camera framework. It was tested on SH Migo-r board. Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/media/ov772x.h | 21 +++++++++++++++++++++ include/media/v4l2-chip-ident.h | 1 + 2 files changed, 22 insertions(+) create mode 100644 include/media/ov772x.h (limited to 'include') diff --git a/include/media/ov772x.h b/include/media/ov772x.h new file mode 100644 index 000000000000..e391d55edb95 --- /dev/null +++ b/include/media/ov772x.h @@ -0,0 +1,21 @@ +/* ov772x Camera + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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. + */ + +#ifndef __OV772X_H__ +#define __OV772X_H__ + +#include + +struct ov772x_camera_info { + unsigned long buswidth; + struct soc_camera_link link; +}; + +#endif /* __OV772X_H__ */ diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index d73a8e9028a5..bfe5142e6672 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -60,6 +60,7 @@ enum { /* OmniVision sensors: reserved range 250-299 */ V4L2_IDENT_OV7670 = 250, + V4L2_IDENT_OV772X = 251, /* Conexant MPEG encoder/decoders: reserved range 410-420 */ V4L2_IDENT_CX23415 = 415, -- cgit v1.2.3-55-g7522 From f473bf76c71ca734a16f9331ce6b6e9603641888 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 1 Nov 2008 08:25:11 -0300 Subject: V4L/DVB (9503): v4l: remove inode argument from video_usercopy The inode argument was never used. Removing it from video_usercopy brings the function pointer type of video_usercopy in line with similar v4l2 functions, thus simplifying several drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 2 +- drivers/media/common/saa7146_video.c | 10 ++-------- drivers/media/video/arv.c | 5 ++--- drivers/media/video/bw-qcam.c | 5 ++--- drivers/media/video/c-qcam.c | 7 +++---- drivers/media/video/cpia.c | 9 ++++----- drivers/media/video/cpia2/cpia2_v4l.c | 13 ++++++------- drivers/media/video/ov511.c | 5 ++--- drivers/media/video/pms.c | 5 ++--- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 13 +++---------- drivers/media/video/pwc/pwc-if.c | 2 +- drivers/media/video/pwc/pwc-v4l.c | 3 +-- drivers/media/video/pwc/pwc.h | 3 +-- drivers/media/video/saa5246a.c | 5 ++--- drivers/media/video/saa5249.c | 5 ++--- drivers/media/video/se401.c | 5 ++--- drivers/media/video/stv680.c | 5 ++--- drivers/media/video/usbvideo/usbvideo.c | 5 ++--- drivers/media/video/usbvision/usbvision-video.c | 4 ++-- drivers/media/video/uvc/uvc_v4l2.c | 13 +++---------- drivers/media/video/v4l2-ioctl.c | 8 +++----- drivers/media/video/vino.c | 5 ++--- drivers/media/video/w9966.c | 5 ++--- drivers/media/video/zoran/zoran_driver.c | 8 ++------ include/media/saa7146_vv.h | 3 +-- include/media/v4l2-ioctl.h | 6 ++---- 26 files changed, 57 insertions(+), 102 deletions(-) (limited to 'include') diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 127b0526a727..7d844af88384 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -313,7 +313,7 @@ static int fops_ioctl(struct inode *inode, struct file *file, unsigned int cmd, /* DEB_EE(("inode:%p, file:%p, cmd:%d, arg:%li\n",inode, file, cmd, arg)); */ - return video_usercopy(inode, file, cmd, arg, saa7146_video_do_ioctl); + return video_usercopy(file, cmd, arg, saa7146_video_do_ioctl); } static int fops_mmap(struct file *file, struct vm_area_struct * vma) diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index fe0bd55977e3..101b01dbb8ea 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -834,7 +834,7 @@ static int video_end(struct saa7146_fh *fh, struct file *file) * copying is done already, arg is a kernel pointer. */ -static int __saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) +int saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct saa7146_fh *fh = file->private_data; struct saa7146_dev *dev = fh->dev; @@ -1216,17 +1216,11 @@ static int __saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *a #endif default: return v4l_compat_translate_ioctl(file, cmd, arg, - __saa7146_video_do_ioctl); + saa7146_video_do_ioctl); } return 0; } -int saa7146_video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - return __saa7146_video_do_ioctl(file, cmd, arg); -} - /*********************************************************************************/ /* buffer handling functions */ diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c index e09b00693230..2ba6abd92b6f 100644 --- a/drivers/media/video/arv.c +++ b/drivers/media/video/arv.c @@ -396,8 +396,7 @@ out_up: return ret; } -static int ar_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int ar_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); struct ar_device *ar = video_get_drvdata(dev); @@ -543,7 +542,7 @@ static int ar_do_ioctl(struct inode *inode, struct file *file, static int ar_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, ar_do_ioctl); + return video_usercopy(file, cmd, arg, ar_do_ioctl); } #if USE_INT diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index ace4ff9ea023..17f80d03f38e 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -706,8 +706,7 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l * Video4linux interfacing */ -static int qcam_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); struct qcam_device *qcam=(struct qcam_device *)dev; @@ -867,7 +866,7 @@ static int qcam_do_ioctl(struct inode *inode, struct file *file, static int qcam_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl); + return video_usercopy(file, cmd, arg, qcam_do_ioctl); } static ssize_t qcam_read(struct file *file, char __user *buf, diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index 0f930d351466..21c71eb085db 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -500,8 +500,7 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le * Video4linux interfacing */ -static int qcam_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); struct qcam_device *qcam=(struct qcam_device *)dev; @@ -667,9 +666,9 @@ static int qcam_do_ioctl(struct inode *inode, struct file *file, } static int qcam_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl); + return video_usercopy(file, cmd, arg, qcam_do_ioctl); } static ssize_t qcam_read(struct file *file, char __user *buf, diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index 16c094f77852..028a400d2453 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -3333,8 +3333,7 @@ static ssize_t cpia_read(struct file *file, char __user *buf, return cam->decompressed_frame.count; } -static int cpia_do_ioctl(struct inode *inode, struct file *file, - unsigned int ioctlnr, void *arg) +static int cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = file->private_data; struct cam_data *cam = video_get_drvdata(dev); @@ -3347,9 +3346,9 @@ static int cpia_do_ioctl(struct inode *inode, struct file *file, if (mutex_lock_interruptible(&cam->busy_lock)) return -EINTR; - //DBG("cpia_ioctl: %u\n", ioctlnr); + /* DBG("cpia_ioctl: %u\n", cmd); */ - switch (ioctlnr) { + switch (cmd) { /* query capabilities */ case VIDIOCGCAP: { @@ -3724,7 +3723,7 @@ static int cpia_do_ioctl(struct inode *inode, struct file *file, static int cpia_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl); + return video_usercopy(file, cmd, arg, cpia_do_ioctl); } diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index d9de2e8e00d4..3c2d7eac1197 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -1572,8 +1572,7 @@ static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file) * cpia2_ioctl * *****************************************************************************/ -static int cpia2_do_ioctl(struct inode *inode, struct file *file, - unsigned int ioctl_nr, void *arg) +static int cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct camera_data *cam = video_drvdata(file); int retval = 0; @@ -1591,7 +1590,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file, } /* Priority check */ - switch (ioctl_nr) { + switch (cmd) { case VIDIOCSWIN: case VIDIOCMCAPTURE: case VIDIOC_S_FMT: @@ -1618,7 +1617,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file, break; } - switch (ioctl_nr) { + switch (cmd) { case VIDIOCGCAP: /* query capabilities */ retval = ioctl_cap_query(arg, cam); break; @@ -1683,7 +1682,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMINPUT: case VIDIOC_G_INPUT: case VIDIOC_S_INPUT: - retval = ioctl_input(ioctl_nr, arg,cam); + retval = ioctl_input(cmd, arg, cam); break; case VIDIOC_ENUM_FMT: @@ -1843,9 +1842,9 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file, } static int cpia2_ioctl(struct inode *inode, struct file *file, - unsigned int ioctl_nr, unsigned long iarg) + unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, ioctl_nr, iarg, cpia2_do_ioctl); + return video_usercopy(file, cmd, arg, cpia2_do_ioctl); } /****************************************************************************** diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c index 210f1240b331..6ee9b69cc4a9 100644 --- a/drivers/media/video/ov511.c +++ b/drivers/media/video/ov511.c @@ -4011,8 +4011,7 @@ ov51x_v4l1_close(struct inode *inode, struct file *file) /* Do not call this function directly! */ static int -ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +ov51x_v4l1_ioctl_internal(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = file->private_data; struct usb_ov511 *ov = video_get_drvdata(vdev); @@ -4461,7 +4460,7 @@ ov51x_v4l1_ioctl(struct inode *inode, struct file *file, if (mutex_lock_interruptible(&ov->lock)) return -EINTR; - rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal); + rc = video_usercopy(file, cmd, arg, ov51x_v4l1_ioctl_internal); mutex_unlock(&ov->lock); return rc; diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index 8062d0016997..45730fac1570 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -680,8 +680,7 @@ static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int * Video4linux interfacing */ -static int pms_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int pms_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); struct pms_device *pd=(struct pms_device *)dev; @@ -866,7 +865,7 @@ static int pms_do_ioctl(struct inode *inode, struct file *file, static int pms_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, pms_do_ioctl); + return video_usercopy(file, cmd, arg, pms_do_ioctl); } static ssize_t pms_read(struct file *file, char __user *buf, diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 97ed95957992..52af1c435965 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -168,8 +168,7 @@ static const char *get_v4l_name(int v4l_type) * This is part of Video 4 Linux API. The procedure handles ioctl() calls. * */ -static int __pvr2_v4l2_do_ioctl(struct file *file, - unsigned int cmd, void *arg) +static int pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_v4l2 *vp = fh->vhead; @@ -864,7 +863,7 @@ static int __pvr2_v4l2_do_ioctl(struct file *file, default : ret = v4l_compat_translate_ioctl(file, cmd, - arg, __pvr2_v4l2_do_ioctl); + arg, pvr2_v4l2_do_ioctl); } pvr2_hdw_commit_ctl(hdw); @@ -890,12 +889,6 @@ static int __pvr2_v4l2_do_ioctl(struct file *file, return ret; } -static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - return __pvr2_v4l2_do_ioctl(file, cmd, arg); -} - static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) { int num = dip->devbase.num; @@ -963,7 +956,7 @@ static int pvr2_v4l2_ioctl(struct inode *inode, struct file *file, #define IVTV_IOC_G_CODEC 0xFFEE7703 #define IVTV_IOC_S_CODEC 0xFFEE7704 if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0; - return video_usercopy(inode, file, cmd, arg, pvr2_v4l2_do_ioctl); + return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl); } diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index f3897a3fdb75..1ce9da167b7e 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -1412,7 +1412,7 @@ static int pwc_video_ioctl(struct inode *inode, struct file *file, mutex_lock(&pdev->modlock); if (!pdev->unplugged) - r = video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); + r = video_usercopy(file, cmd, arg, pwc_video_do_ioctl); mutex_unlock(&pdev->modlock); out: return r; diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 76a1376c9751..d7c147328e35 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -337,8 +337,7 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f) } -int pwc_video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +int pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct pwc_device *pdev; diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 74178754b39b..c046a2535668 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -340,8 +340,7 @@ extern int pwc_camera_power(struct pwc_device *pdev, int power); extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); /** Functions in pwc-v4l.c */ -extern int pwc_video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg); +extern int pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg); /** pwc-uncompress.c */ /* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c index 9d4582651467..f159441e9375 100644 --- a/drivers/media/video/saa5246a.c +++ b/drivers/media/video/saa5246a.c @@ -804,8 +804,7 @@ static inline int saa5246a_stop_dau(struct saa5246a_device *t, * * Returns 0 if successful */ -static int do_saa5246a_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int do_saa5246a_ioctl(struct file *file, unsigned int cmd, void *arg) { struct saa5246a_device *t = video_drvdata(file); @@ -953,7 +952,7 @@ static int saa5246a_ioctl(struct inode *inode, struct file *file, cmd = vtx_fix_command(cmd); mutex_lock(&t->lock); - err = video_usercopy(inode, file, cmd, arg, do_saa5246a_ioctl); + err = video_usercopy(file, cmd, arg, do_saa5246a_ioctl); mutex_unlock(&t->lock); return err; } diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index 1594773e02d0..6ef3affb97f1 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -190,8 +190,7 @@ static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf) * Standard character-device-driver functions */ -static int do_saa5249_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int do_saa5249_ioctl(struct file *file, unsigned int cmd, void *arg) { static int virtual_mode = false; struct saa5249_device *t = video_drvdata(file); @@ -488,7 +487,7 @@ static int saa5249_ioctl(struct inode *inode, struct file *file, cmd = vtx_fix_command(cmd); mutex_lock(&t->lock); - err = video_usercopy(inode,file,cmd,arg,do_saa5249_ioctl); + err = video_usercopy(file, cmd, arg, do_saa5249_ioctl); mutex_unlock(&t->lock); return err; } diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c index 044a2e94c34d..d652f25eef0e 100644 --- a/drivers/media/video/se401.c +++ b/drivers/media/video/se401.c @@ -975,8 +975,7 @@ static int se401_close(struct inode *inode, struct file *file) return 0; } -static int se401_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int se401_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = file->private_data; struct usb_se401 *se401 = (struct usb_se401 *)vdev; @@ -1142,7 +1141,7 @@ static int se401_do_ioctl(struct inode *inode, struct file *file, static int se401_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, se401_do_ioctl); + return video_usercopy(file, cmd, arg, se401_do_ioctl); } static ssize_t se401_read(struct file *file, char __user *buf, diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c index 328c41b1517d..42acc92c182d 100644 --- a/drivers/media/video/stv680.c +++ b/drivers/media/video/stv680.c @@ -1132,8 +1132,7 @@ static int stv_close (struct inode *inode, struct file *file) return 0; } -static int stv680_do_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int stv680_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = file->private_data; struct usb_stv *stv680 = video_get_drvdata(vdev); @@ -1303,7 +1302,7 @@ static int stv680_do_ioctl (struct inode *inode, struct file *file, static int stv680_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, stv680_do_ioctl); + return video_usercopy(file, cmd, arg, stv680_do_ioctl); } static int stv680_mmap (struct file *file, struct vm_area_struct *vma) diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c index 7c575bb8184f..a42049de2bb6 100644 --- a/drivers/media/video/usbvideo/usbvideo.c +++ b/drivers/media/video/usbvideo/usbvideo.c @@ -1281,8 +1281,7 @@ static int usbvideo_v4l_close(struct inode *inode, struct file *file) * History: * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings. */ -static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int usbvideo_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct uvd *uvd = file->private_data; @@ -1505,7 +1504,7 @@ static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file, static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl); + return video_usercopy(file, cmd, arg, usbvideo_v4l_do_ioctl); } /* diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index d185b57fdcd0..062819cabec4 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -1278,7 +1278,7 @@ static int usbvision_vbi_close(struct inode *inode, struct file *file) return -ENODEV; } -static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file, +static int usbvision_do_vbi_ioctl(struct file *file, unsigned int cmd, void *arg) { /* TODO */ @@ -1288,7 +1288,7 @@ static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file, static int usbvision_vbi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, usbvision_do_vbi_ioctl); + return video_usercopy(file, cmd, arg, usbvision_do_vbi_ioctl); } diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 758dfefaba8d..8361367806a9 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -464,8 +464,7 @@ static int uvc_v4l2_release(struct inode *inode, struct file *file) return 0; } -static int __uvc_v4l2_do_ioctl(struct file *file, - unsigned int cmd, void *arg) +static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct uvc_video_device *video = video_get_drvdata(vdev); @@ -979,7 +978,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file, default: if ((ret = v4l_compat_translate_ioctl(file, cmd, arg, - __uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD) + uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD) uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); return ret; @@ -988,17 +987,11 @@ static int __uvc_v4l2_do_ioctl(struct file *file, return ret; } -static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - return __uvc_v4l2_do_ioctl(file, cmd, arg); -} - static int uvc_v4l2_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n"); - return video_usercopy(inode, file, cmd, arg, uvc_v4l2_do_ioctl); + return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); } static ssize_t uvc_v4l2_read(struct file *file, char __user *data, diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index eef5814aebe5..0e648cb3b399 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -393,10 +393,8 @@ video_fix_command(unsigned int cmd) * Obsolete usercopy function - Should be removed soon */ int -video_usercopy(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, - int (*func)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg)) +video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, + v4l2_kioctl func) { char sbuf[128]; void *mbuf = NULL; @@ -458,7 +456,7 @@ video_usercopy(struct inode *inode, struct file *file, } /* call driver */ - err = func(inode, file, cmd, parg); + err = func(file, cmd, parg); if (err == -ENOIOCTLCMD) err = -EINVAL; if (is_ext_ctrl) { diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 1efc5f3462c6..a72a361daade 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -4237,8 +4237,7 @@ error: return ret; } -static int vino_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vino_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct vino_channel_settings *vcs = video_drvdata(file); @@ -4353,7 +4352,7 @@ static int vino_ioctl(struct inode *inode, struct file *file, if (mutex_lock_interruptible(&vcs->mutex)) return -EINTR; - ret = video_usercopy(inode, file, cmd, arg, vino_do_ioctl); + ret = video_usercopy(file, cmd, arg, vino_do_ioctl); mutex_unlock(&vcs->mutex); diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index b2dbe48a92bb..56c570c267ea 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -727,8 +727,7 @@ static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data) * Video4linux interfacing */ -static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int w9966_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct w9966_dev *cam = video_drvdata(file); @@ -881,7 +880,7 @@ static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file, static int w9966_v4l_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, w9966_v4l_do_ioctl); + return video_usercopy(file, cmd, arg, w9966_v4l_do_ioctl); } // Capture data diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index db11ab9e60da..00b97d97aeaa 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -1940,11 +1940,7 @@ zoran_set_input (struct zoran *zr, * ioctl routine */ -static int -zoran_do_ioctl (struct inode *inode, - struct file *file, - unsigned int cmd, - void *arg) +static int zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; @@ -4201,7 +4197,7 @@ zoran_ioctl (struct inode *inode, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, zoran_do_ioctl); + return video_usercopy(file, cmd, arg, zoran_do_ioctl); } static unsigned int diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 1d104096619c..6bbb0d93bb5f 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -216,8 +216,7 @@ void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data); extern struct saa7146_use_ops saa7146_video_uops; int saa7146_start_preview(struct saa7146_fh *fh); int saa7146_stop_preview(struct saa7146_fh *fh); -int saa7146_video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg); +int saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg); /* from saa7146_vbi.c */ extern struct saa7146_use_ops saa7146_vbi_uops; diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index e6ba25b3d7c8..f5985724c456 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -285,10 +285,8 @@ extern long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg); /* Include support for obsoleted stuff */ -extern int video_usercopy(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, - int (*func)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg)); +extern int video_usercopy(struct file *file, unsigned int cmd, + unsigned long arg, v4l2_kioctl func); /* Standard handlers for V4L ioctl's */ -- cgit v1.2.3-55-g7522 From af128a102c4aee994b4ff6e422b3cfab17127578 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Thu, 30 Oct 2008 00:51:46 -0300 Subject: V4L/DVB (9521): V4L: struct device - replace bus_id with dev_name(), dev_set_name() This patch is part of a larger patch series which will remove the "char bus_id[20]" name string from struct device. The device name is managed in the kobject anyway, and without any size limitation, and just needlessly copied into "struct device". To set and read the device name dev_name(dev) and dev_set_name(dev) must be used. If your code uses static kobjects, which it shouldn't do, "const char *init_name" can be used to statically provide the name the registered device should have. At registration time, the init_name field is cleared, to enforce the use of dev_name(dev) to access the device name at a later time. We need to get rid of all occurrences of bus_id in the entire tree to be able to enable the new interface. Please apply this patch, and possibly convert any remaining remaining occurrences of bus_id. We want to submit a patch to -next, which will remove bus_id from "struct device", to find the remaining pieces to convert, and finally switch over to the new api, which will remove the 20 bytes array and does no longer have a size limitation. Thanks, Kay Signed-off-by: Kay Sievers Acked-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt8xx/bttv-gpio.c | 7 +++---- drivers/media/video/bt8xx/bttv.h | 2 +- drivers/media/video/em28xx/em28xx-video.c | 4 ++-- drivers/media/video/et61x251/et61x251_core.c | 2 +- drivers/media/video/ir-kbd-i2c.c | 6 +++--- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 2 +- drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 4 ++-- drivers/media/video/sh_mobile_ceu_camera.c | 6 +++--- drivers/media/video/sn9c102/sn9c102_core.c | 2 +- drivers/media/video/soc_camera.c | 5 ++--- drivers/media/video/usbvision/usbvision-video.c | 2 +- drivers/media/video/v4l2-dev.c | 2 +- drivers/media/video/zc0301/zc0301_core.c | 2 +- include/media/soc_camera.h | 4 ++-- 14 files changed, 24 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c index dce6dae5740e..74c325e594a2 100644 --- a/drivers/media/video/bt8xx/bttv-gpio.c +++ b/drivers/media/video/bt8xx/bttv-gpio.c @@ -42,7 +42,7 @@ static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv) struct bttv_sub_driver *sub = to_bttv_sub_drv(drv); int len = strlen(sub->wanted); - if (0 == strncmp(dev->bus_id, sub->wanted, len)) + if (0 == strncmp(dev_name(dev), sub->wanted, len)) return 1; return 0; } @@ -91,15 +91,14 @@ int bttv_sub_add_device(struct bttv_core *core, char *name) sub->dev.parent = &core->pci->dev; sub->dev.bus = &bttv_sub_bus_type; sub->dev.release = release_sub_device; - snprintf(sub->dev.bus_id,sizeof(sub->dev.bus_id),"%s%d", - name, core->nr); + dev_set_name(&sub->dev, "%s%d", name, core->nr); err = device_register(&sub->dev); if (0 != err) { kfree(sub); return err; } - printk("bttv%d: add subdevice \"%s\"\n", core->nr, sub->dev.bus_id); + printk("bttv%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev)); list_add_tail(&sub->list,&core->subs); return 0; } diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index 46cb90e0985b..bc2c88499ab9 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -308,7 +308,7 @@ struct bttv_sub_device { struct bttv_sub_driver { struct device_driver drv; - char wanted[BUS_ID_SIZE]; + char wanted[20]; int (*probe)(struct bttv_sub_device *sub); void (*remove)(struct bttv_sub_device *sub); }; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 4ea1f1e04897..415dff830f88 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1271,7 +1271,7 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info)); + strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info)); cap->version = EM28XX_VERSION_CODE; @@ -1424,7 +1424,7 @@ static int radio_querycap(struct file *file, void *priv, strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info)); + strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info)); cap->version = EM28XX_VERSION_CODE; cap->capabilities = V4L2_CAP_TUNER; diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index 9d0ef96c23ff..83c07112c59d 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -1581,7 +1581,7 @@ et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg) strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), sizeof(cap.bus_info)); if (copy_to_user(arg, &cap, sizeof(cap))) diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index efe849981ab7..d4658c56eddc 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -385,10 +385,10 @@ static int ir_attach(struct i2c_adapter *adap, int addr, goto err_out_detach; } - /* Phys addr can only be set after attaching (for ir->c.dev.bus_id) */ + /* Phys addr can only be set after attaching (for ir->c.dev) */ snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0", - ir->c.adapter->dev.bus_id, - ir->c.dev.bus_id); + dev_name(&ir->c.adapter->dev), + dev_name(&ir->c.dev)); /* init + register input device */ ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 5b81ba469641..4358079f1966 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2395,7 +2395,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, scnprintf(hdw->bus_info,sizeof(hdw->bus_info), "usb %s address %d", - hdw->usb_dev->dev.bus_id, + dev_name(&hdw->usb_dev->dev), hdw->usb_dev->devnum); ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber; diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 733680f21317..e641cd971453 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -628,10 +628,10 @@ static void class_dev_create(struct pvr2_sysfs *sfp, class_dev->class = &class_ptr->class; if (pvr2_hdw_get_sn(sfp->channel.hdw)) { - snprintf(class_dev->bus_id, BUS_ID_SIZE, "sn-%lu", + dev_set_name(class_dev, "sn-%lu", pvr2_hdw_get_sn(sfp->channel.hdw)); } else if (pvr2_hdw_get_unit_number(sfp->channel.hdw) >= 0) { - snprintf(class_dev->bus_id, BUS_ID_SIZE, "unit-%c", + dev_set_name(class_dev, "unit-%c", pvr2_hdw_get_unit_number(sfp->channel.hdw) + 'a'); } else { kfree(class_dev); diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 536b1a9b310c..63bc0a6e68b0 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -616,7 +616,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) /* request irq */ err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED, - pdev->dev.bus_id, pcdev); + dev_name(&pdev->dev), pcdev); if (err) { dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); goto exit_release_mem; @@ -633,8 +633,8 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->ici.priv = pcdev; pcdev->ici.dev.parent = &pdev->dev; pcdev->ici.nr = pdev->id; - pcdev->ici.drv_name = pdev->dev.bus_id, - pcdev->ici.ops = &sh_mobile_ceu_host_ops, + pcdev->ici.drv_name = dev_name(&pdev->dev); + pcdev->ici.ops = &sh_mobile_ceu_host_ops; err = soc_camera_host_register(&pcdev->ici); if (err) diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index fcd2b62f92c4..01a8efb8deb1 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -2162,7 +2162,7 @@ sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg) strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), sizeof(cap.bus_info)); if (copy_to_user(arg, &cap, sizeof(cap))) diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 66ebe5956a87..28cf5c94bd61 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -783,7 +783,7 @@ int soc_camera_host_register(struct soc_camera_host *ici) return -EINVAL; /* Number might be equal to the platform device ID */ - sprintf(ici->dev.bus_id, "camera_host%d", ici->nr); + dev_set_name(&ici->dev, "camera_host%d", ici->nr); mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { @@ -867,8 +867,7 @@ int soc_camera_device_register(struct soc_camera_device *icd) icd->devnum = num; icd->dev.bus = &soc_camera_bus_type; - snprintf(icd->dev.bus_id, sizeof(icd->dev.bus_id), - "%u-%u", icd->iface, icd->devnum); + dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum); icd->dev.release = dummy_release; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 062819cabec4..48c8c4399eb8 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -523,7 +523,7 @@ static int vidioc_querycap (struct file *file, void *priv, strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, sizeof(vc->card)); - strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, + strlcpy(vc->bus_info, dev_name(&usbvision->dev->dev), sizeof(vc->bus_info)); vc->version = USBVISION_DRIVER_VERSION; vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index ead6d18e51ad..682f48aadcac 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -339,7 +339,7 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); if (vfd->parent) vfd->dev.parent = vfd->parent; - sprintf(vfd->dev.bus_id, "%s%d", name_base, nr); + dev_set_name(&vfd->dev, "%s%d", name_base, nr); ret = device_register(&vfd->dev); if (ret < 0) { printk(KERN_ERR "%s: device_register failed\n", __func__); diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index 9fc581707638..9d00e6056491 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -1020,7 +1020,7 @@ zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg) strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), sizeof(cap.bus_info)); if (copy_to_user(arg, &cap, sizeof(cap))) diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index c5de7bb19fda..9231e2d908f2 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -56,7 +56,7 @@ struct soc_camera_host { struct device dev; unsigned char nr; /* Host number */ void *priv; - char *drv_name; + const char *drv_name; struct soc_camera_host_ops *ops; }; @@ -107,7 +107,7 @@ extern int soc_camera_video_start(struct soc_camera_device *icd); extern void soc_camera_video_stop(struct soc_camera_device *icd); struct soc_camera_data_format { - char *name; + const char *name; unsigned int depth; __u32 fourcc; enum v4l2_colorspace colorspace; -- cgit v1.2.3-55-g7522 From 69acdf1e5a9146ec6667f6c4b439acd38c18f5ea Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Tue, 4 Nov 2008 21:59:37 -0300 Subject: V4L/DVB (9530): Add new pixel format VYUY 16 bits wide. There were already 3 YUV formats defined : - YUYV - YVYU - UYVY The only left combination is VYUY, which is added in this patch. Signed-off-by: Robert Jarzmik Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 4669d7e72e75..ec311d4616cd 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -293,6 +293,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y', 'V', '1', '2') /* 12 YVU 4:2:0 */ #define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */ #define V4L2_PIX_FMT_UYVY v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_VYUY v4l2_fourcc('V', 'Y', 'U', 'Y') /* 16 YUV 4:2:2 */ #define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4', '2', '2', 'P') /* 16 YVU422 planar */ #define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4', '1', '1', 'P') /* 16 YVU411 planar */ #define V4L2_PIX_FMT_Y41P v4l2_fourcc('Y', '4', '1', 'P') /* 12 YUV 4:1:1 */ -- cgit v1.2.3-55-g7522 From 74d83fa0241f603a4067f071a88ef8b9a7c415a0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Nov 2008 21:13:47 -0300 Subject: V4L/DVB (9578): v4l core: add support for enumerating frame sizes and intervals video_ioctl2 lacks implementation of those two ioctls: - VIDIOC_ENUM_FRAMESIZES and VIDIOC_ENUM_FRAMEINTERVALS Adds implementation for those. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ioctl.c | 67 ++++++++++++++++++++++++++++++++++++++++ include/media/v4l2-ioctl.h | 6 ++++ 2 files changed, 73 insertions(+) (limited to 'include') diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 0e648cb3b399..213c4dd6f72c 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -1747,6 +1747,73 @@ static int __video_do_ioctl(struct file *file, ret = ops->vidioc_s_hw_freq_seek(file, fh, p); break; } + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *p = arg; + + if (!ops->vidioc_enum_framesizes) + break; + + memset(p, 0, sizeof(*p)); + + ret = ops->vidioc_enum_framesizes(file, fh, p); + dbgarg(cmd, + "index=%d, pixelformat=%d, type=%d ", + p->index, p->pixel_format, p->type); + switch (p->type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + dbgarg2("width = %d, height=%d\n", + p->discrete.width, p->discrete.height); + break; + case V4L2_FRMSIZE_TYPE_STEPWISE: + dbgarg2("min %dx%d, max %dx%d, step %dx%d\n", + p->stepwise.min_width, p->stepwise.min_height, + p->stepwise.step_width, p->stepwise.step_height, + p->stepwise.max_width, p->stepwise.max_height); + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + dbgarg2("continuous\n"); + break; + default: + dbgarg2("- Unknown type!\n"); + } + + break; + } + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *p = arg; + + if (!ops->vidioc_enum_frameintervals) + break; + + memset(p, 0, sizeof(*p)); + + ret = ops->vidioc_enum_frameintervals(file, fh, p); + dbgarg(cmd, + "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ", + p->index, p->pixel_format, + p->width, p->height, p->type); + switch (p->type) { + case V4L2_FRMIVAL_TYPE_DISCRETE: + dbgarg2("fps=%d/%d\n", + p->discrete.numerator, + p->discrete.denominator); + break; + case V4L2_FRMIVAL_TYPE_STEPWISE: + dbgarg2("min=%d, max=%d, step=%d\n", + p->stepwise.min, p->stepwise.max, + p->stepwise.step); + break; + case V4L2_FRMIVAL_TYPE_CONTINUOUS: + dbgarg2("continuous\n"); + break; + default: + dbgarg2("- Unknown type!\n"); + } + break; + } + default: { if (!ops->vidioc_default) diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index f5985724c456..c884432f9383 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -232,6 +232,12 @@ struct v4l2_ioctl_ops { int (*vidioc_g_chip_ident) (struct file *file, void *fh, struct v4l2_chip_ident *chip); + int (*vidioc_enum_framesizes) (struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize); + + int (*vidioc_enum_frameintervals) (struct file *file, void *fh, + struct v4l2_frmivalenum *fival); + /* For other private ioctls */ int (*vidioc_default) (struct file *file, void *fh, int cmd, void *arg); -- cgit v1.2.3-55-g7522 From 60245e858ddb2255d458868be16ba814f6f8d446 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 16 Nov 2008 08:03:53 -0300 Subject: V4L/DVB (9629): Add support for the ATI TV Wonder HD 600 USB Remote Control Add support for the ATI TV Wonder HD 600 USB Remote Control (required a new keymap) [mchehab@redhat.com: Fix CodingStyle] Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-keymaps.c | 32 +++++++++++++++++++++++++++++++ drivers/media/video/em28xx/em28xx-cards.c | 1 + include/media/ir-common.h | 1 + 3 files changed, 34 insertions(+) (limited to 'include') diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 4952aeb5dd80..3534cdc1f953 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -2511,3 +2511,35 @@ IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys); + +/* ATI TV Wonder HD 600 USB + Devin Heitmueller + */ +IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE] = { + [0x00] = KEY_RECORD, /* Row 1 */ + [0x01] = KEY_PLAYPAUSE, + [0x02] = KEY_STOP, + [0x03] = KEY_POWER, + [0x04] = KEY_PREVIOUS, /* Row 2 */ + [0x05] = KEY_REWIND, + [0x06] = KEY_FORWARD, + [0x07] = KEY_NEXT, + [0x08] = KEY_EPG, /* Row 3 */ + [0x09] = KEY_HOME, + [0x0a] = KEY_MENU, + [0x0b] = KEY_CHANNELUP, + [0x0c] = KEY_BACK, /* Row 4 */ + [0x0d] = KEY_UP, + [0x0e] = KEY_INFO, + [0x0f] = KEY_CHANNELDOWN, + [0x10] = KEY_LEFT, /* Row 5 */ + [0x11] = KEY_SELECT, + [0x12] = KEY_RIGHT, + [0x13] = KEY_VOLUMEUP, + [0x14] = KEY_LAST, /* Row 6 */ + [0x15] = KEY_DOWN, + [0x16] = KEY_MUTE, + [0x17] = KEY_VOLUMEDOWN, +}; + +EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 3b585486f7fe..75b2af2bf4cf 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -640,6 +640,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_12mhz_i2s = 1, .has_dvb = 1, + .ir_codes = ir_codes_ati_tv_wonder_hd_600, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 38f2d93c3957..3a88e13a20e9 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -157,6 +157,7 @@ extern IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE]; #endif /* -- cgit v1.2.3-55-g7522 From 26d5f3a3fe917232cb77e2e3450f7d7f8698259c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 7 Dec 2008 13:19:29 -0300 Subject: V4L/DVB (9772): saa7134: Add support for Kworld Plus TV Analog Lite PCI Thanks to Sistema Fenix (http://www.sistemafenix.com.br/) for sponsoring this development. Signed-off-by: Gilberto Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 1 + drivers/media/common/ir-keymaps.c | 61 +++++++++++++++++++++++++++++ drivers/media/video/saa7134/saa7134-cards.c | 44 +++++++++++++++++++++ drivers/media/video/saa7134/saa7134-input.c | 14 +++++++ drivers/media/video/saa7134/saa7134.h | 1 + include/media/ir-common.h | 1 + 6 files changed, 122 insertions(+) (limited to 'include') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index dc67eef38ff9..dd979dca8af6 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -151,3 +151,4 @@ 150 -> Zogis Real Angel 220 151 -> ADS Tech Instant HDTV [1421:0380] 152 -> Asus Tiger Rev:1.00 [1043:4857] +153 -> Kworld Plus TV Analog Lite PCI [17de:7128] diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 3534cdc1f953..d8229a0e9a9c 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -2391,6 +2391,67 @@ IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel); +/* Kworld Plus TV Analog Lite PCI IR + Mauro Carvalho Chehab + */ +IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE] = { + [0x0c] = KEY_PROG1, /* Kworld key */ + [0x16] = KEY_CLOSECD, /* -> ) */ + [0x1d] = KEY_POWER2, + + [0x00] = KEY_1, + [0x01] = KEY_2, + [0x02] = KEY_3, /* Two keys have the same code: 3 and left */ + [0x03] = KEY_4, /* Two keys have the same code: 3 and right */ + [0x04] = KEY_5, + [0x05] = KEY_6, + [0x06] = KEY_7, + [0x07] = KEY_8, + [0x08] = KEY_9, + [0x0a] = KEY_0, + + [0x09] = KEY_AGAIN, + [0x14] = KEY_MUTE, + + [0x20] = KEY_UP, + [0x21] = KEY_DOWN, + [0x0b] = KEY_ENTER, + + [0x10] = KEY_CHANNELUP, + [0x11] = KEY_CHANNELDOWN, + + /* Couldn't map key left/key right since those + conflict with '3' and '4' scancodes + I dunno what the original driver does + */ + + [0x13] = KEY_VOLUMEUP, + [0x12] = KEY_VOLUMEDOWN, + + /* The lower part of the IR + There are several duplicated keycodes there. + Most of them conflict with digits. + Add mappings just to the unused scancodes. + Somehow, the original driver has a way to know, + but this doesn't seem to be on some GPIO. + Also, it is not related to the time between keyup + and keydown. + */ + [0x19] = KEY_PAUSE, /* Timeshift */ + [0x1a] = KEY_STOP, + [0x1b] = KEY_RECORD, + + [0x22] = KEY_TEXT, + + [0x15] = KEY_AUDIO, /* ((*)) */ + [0x0f] = KEY_ZOOM, + [0x1c] = KEY_SHUFFLE, /* snapshot */ + + [0x18] = KEY_RED, /* B */ + [0x23] = KEY_GREEN, /* C */ +}; +EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog); + IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = { [0x20] = KEY_LIST, [0x00] = KEY_POWER, diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 863522899e85..f0b95804e414 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -4606,6 +4606,43 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200000, }, }, + [SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG] = { + .name = "Kworld Plus TV Analog Lite PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_YMEC_TVF_5533MF, + .radio_type = TUNER_TEA5767, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x80000700, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + .gpio = 0x100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x200, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x200, + } }, + .radio = { + .name = name_radio, + .vmux = 1, + .amux = LINE1, + .gpio = 0x100, + }, + .mute = { + .name = name_mute, + .vmux = 8, + .amux = 2, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -5652,6 +5689,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1043, .subdevice = 0x4878, /* REV:1.02G */ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x17de, + .subdevice = 0x7128, + .driver_data = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG, }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -5880,6 +5923,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_507_9FM: case SAA7134_BOARD_GENIUS_TVGO_A11MCE: case SAA7134_BOARD_REAL_ANGEL_220: + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index c53fd5f9f6b5..d2124f64e4e2 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -97,6 +97,15 @@ static int build_key(struct saa7134_dev *dev) dprintk("build_key gpio=0x%x mask=0x%x data=%d\n", gpio, ir->mask_keycode, data); + switch (dev->board) { + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: + if (data == ir->mask_keycode) + ir_input_nokey(ir->dev, &ir->ir); + else + ir_input_keydown(ir->dev, &ir->ir, data, data); + return 0; + } + if (ir->polling) { if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { @@ -586,6 +595,11 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keyup = 0x4000; polling = 50; /* ms */ break; + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: + ir_codes = ir_codes_kworld_plus_tv_analog; + mask_keycode = 0x7f; + polling = 40; /* ms */ + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 9070efe4a4d0..f6c1fcc72070 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -275,6 +275,7 @@ struct saa7134_format { #define SAA7134_BOARD_REAL_ANGEL_220 150 #define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151 #define SAA7134_BOARD_ASUSTeK_TIGER 152 +#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 3a88e13a20e9..5bf2ea00678c 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -158,6 +158,7 @@ extern IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE]; #endif /* -- cgit v1.2.3-55-g7522 From a2c8c68cca3dbb0c87f5034ab8ea29350174ec4a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 09:44:53 -0300 Subject: V4L/DVB (9785): soc-camera: merge .try_bus_param() into .try_fmt_cap() .try_bus_param() method from struct soc_camera_host_ops is only called at one location immediately before .try_fmt_cap(), there is no value in keeping these two methods separate, merge them. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pxa_camera.c | 6 +++++- drivers/media/video/sh_mobile_ceu_camera.c | 6 +++++- drivers/media/video/soc_camera.c | 5 ----- include/media/soc_camera.h | 1 - 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index eb6be5802928..2a811f8584b5 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -913,6 +913,11 @@ static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, struct v4l2_format *f) { + int ret = pxa_camera_try_bus_param(icd, f->fmt.pix.pixelformat); + + if (ret < 0) + return ret; + /* limit to pxa hardware capabilities */ if (f->fmt.pix.height < 32) f->fmt.pix.height = 32; @@ -1039,7 +1044,6 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .reqbufs = pxa_camera_reqbufs, .poll = pxa_camera_poll, .querycap = pxa_camera_querycap, - .try_bus_param = pxa_camera_try_bus_param, .set_bus_param = pxa_camera_set_bus_param, }; diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 63bc0a6e68b0..87d0f3075811 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -459,6 +459,11 @@ static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, struct v4l2_format *f) { + int ret = sh_mobile_ceu_try_bus_param(icd, f->fmt.pix.pixelformat); + + if (ret < 0) + return ret; + /* FIXME: calculate using depth and bus width */ if (f->fmt.pix.height < 4) @@ -546,7 +551,6 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .reqbufs = sh_mobile_ceu_reqbufs, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, - .try_bus_param = sh_mobile_ceu_try_bus_param, .set_bus_param = sh_mobile_ceu_set_bus_param, .init_videobuf = sh_mobile_ceu_init_videobuf, }; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 28cf5c94bd61..2ebdf4d667e3 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -77,11 +77,6 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - /* test physical bus parameters */ - ret = ici->ops->try_bus_param(icd, f->fmt.pix.pixelformat); - if (ret) - return ret; - /* limit format to hardware capabilities */ ret = ici->ops->try_fmt_cap(icd, f); diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 9231e2d908f2..ee0e6b4bed33 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -73,7 +73,6 @@ struct soc_camera_host_ops { struct soc_camera_device *); int (*reqbufs)(struct soc_camera_file *, struct v4l2_requestbuffers *); int (*querycap)(struct soc_camera_host *, struct v4l2_capability *); - int (*try_bus_param)(struct soc_camera_device *, __u32); int (*set_bus_param)(struct soc_camera_device *, __u32); unsigned int (*poll)(struct file *, poll_table *); }; -- cgit v1.2.3-55-g7522 From 25c4d74ea6f07f2aaa3df537619680ba967043f5 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 09:44:59 -0300 Subject: V4L/DVB (9787): soc-camera: let camera host drivers decide upon pixel format Pixel format requested by the user is not necessarily the same, as what a sensor driver provides. There are situations, when a camera host driver provides the required format, but requires a different format from the sensor. Further, the list of formats, supported by sensors is pretty static and can be pretty good described with a constant list of structures. Whereas decisions, made by camera host drivers to support requested formats can be quite complex, therefore it is better to let the host driver do the work. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pxa_camera.c | 32 ++++++++++++++++- drivers/media/video/sh_mobile_ceu_camera.c | 32 ++++++++++++++++- drivers/media/video/soc_camera.c | 58 ++++++++++++------------------ include/media/soc_camera.h | 3 ++ 4 files changed, 87 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 2a811f8584b5..a375872b1342 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -907,17 +907,43 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt) static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, __u32 pixfmt, struct v4l2_rect *rect) { - return icd->ops->set_fmt_cap(icd, pixfmt, rect); + const struct soc_camera_data_format *cam_fmt; + int ret; + + /* + * TODO: find a suitable supported by the SoC output format, check + * whether the sensor supports one of acceptable input formats. + */ + if (pixfmt) { + cam_fmt = soc_camera_format_by_fourcc(icd, pixfmt); + if (!cam_fmt) + return -EINVAL; + } + + ret = icd->ops->set_fmt_cap(icd, pixfmt, rect); + if (pixfmt && !ret) + icd->current_fmt = cam_fmt; + + return ret; } static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, struct v4l2_format *f) { + const struct soc_camera_data_format *cam_fmt; int ret = pxa_camera_try_bus_param(icd, f->fmt.pix.pixelformat); if (ret < 0) return ret; + /* + * TODO: find a suitable supported by the SoC output format, check + * whether the sensor supports one of acceptable input formats. + */ + cam_fmt = soc_camera_format_by_fourcc(icd, f->fmt.pix.pixelformat); + if (!cam_fmt) + return -EINVAL; + /* limit to pxa hardware capabilities */ if (f->fmt.pix.height < 32) f->fmt.pix.height = 32; @@ -929,6 +955,10 @@ static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, f->fmt.pix.width = 2048; f->fmt.pix.width &= ~0x01; + f->fmt.pix.bytesperline = f->fmt.pix.width * + DIV_ROUND_UP(cam_fmt->depth, 8); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + /* limit to sensor capabilities */ return icd->ops->try_fmt_cap(icd, f); } diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 87d0f3075811..02f846d1908b 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -453,17 +453,43 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, __u32 pixfmt, struct v4l2_rect *rect) { - return icd->ops->set_fmt_cap(icd, pixfmt, rect); + const struct soc_camera_data_format *cam_fmt; + int ret; + + /* + * TODO: find a suitable supported by the SoC output format, check + * whether the sensor supports one of acceptable input formats. + */ + if (pixfmt) { + cam_fmt = soc_camera_format_by_fourcc(icd, pixfmt); + if (!cam_fmt) + return -EINVAL; + } + + ret = icd->ops->set_fmt_cap(icd, pixfmt, rect); + if (pixfmt && !ret) + icd->current_fmt = cam_fmt; + + return ret; } static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, struct v4l2_format *f) { + const struct soc_camera_data_format *cam_fmt; int ret = sh_mobile_ceu_try_bus_param(icd, f->fmt.pix.pixelformat); if (ret < 0) return ret; + /* + * TODO: find a suitable supported by the SoC output format, check + * whether the sensor supports one of acceptable input formats. + */ + cam_fmt = soc_camera_format_by_fourcc(icd, f->fmt.pix.pixelformat); + if (!cam_fmt) + return -EINVAL; + /* FIXME: calculate using depth and bus width */ if (f->fmt.pix.height < 4) @@ -477,6 +503,10 @@ static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, f->fmt.pix.width &= ~0x01; f->fmt.pix.height &= ~0x03; + f->fmt.pix.bytesperline = f->fmt.pix.width * + DIV_ROUND_UP(cam_fmt->depth, 8); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + /* limit to sensor capabilities */ return icd->ops->try_fmt_cap(icd, f); } diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 7217de21e76b..01c33841d1c6 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -35,7 +35,7 @@ static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); static DEFINE_MUTEX(video_lock); -const static struct soc_camera_data_format *format_by_fourcc( +const struct soc_camera_data_format *soc_camera_format_by_fourcc( struct soc_camera_device *icd, unsigned int fourcc) { unsigned int i; @@ -45,6 +45,7 @@ const static struct soc_camera_data_format *format_by_fourcc( return icd->formats + i; return NULL; } +EXPORT_SYMBOL(soc_camera_format_by_fourcc); static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) @@ -54,25 +55,19 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); enum v4l2_field field; - const struct soc_camera_data_format *fmt; int ret; WARN_ON(priv != file->private_data); - fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat); - if (!fmt) { - dev_dbg(&icd->dev, "invalid format 0x%08x\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } - - dev_dbg(&icd->dev, "fmt: 0x%08x\n", fmt->fourcc); - + /* + * TODO: this might also have to migrate to host-drivers, if anyone + * wishes to support other fields + */ field = f->fmt.pix.field; if (field == V4L2_FIELD_ANY) { - field = V4L2_FIELD_NONE; - } else if (V4L2_FIELD_NONE != field) { + f->fmt.pix.field = V4L2_FIELD_NONE; + } else if (field != V4L2_FIELD_NONE) { dev_err(&icd->dev, "Field type invalid.\n"); return -EINVAL; } @@ -80,13 +75,6 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, /* limit format to hardware capabilities */ ret = ici->ops->try_fmt_cap(icd, f); - /* calculate missing fields */ - f->fmt.pix.field = field; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - return ret; } @@ -325,18 +313,10 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, to_soc_camera_host(icd->dev.parent); int ret; struct v4l2_rect rect; - const static struct soc_camera_data_format *data_fmt; WARN_ON(priv != file->private_data); - data_fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat); - if (!data_fmt) - return -EINVAL; - - /* buswidth may be further adjusted by the ici */ - icd->buswidth = data_fmt->depth; - - ret = soc_camera_try_fmt_vid_cap(file, icf, f); + ret = soc_camera_try_fmt_vid_cap(file, priv, f); if (ret < 0) return ret; @@ -345,14 +325,21 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, rect.width = f->fmt.pix.width; rect.height = f->fmt.pix.height; ret = ici->ops->set_fmt_cap(icd, f->fmt.pix.pixelformat, &rect); - if (ret < 0) + if (ret < 0) { return ret; + } else if (!icd->current_fmt || + icd->current_fmt->fourcc != f->fmt.pix.pixelformat) { + dev_err(&ici->dev, "Host driver hasn't set up current " + "format correctly!\n"); + return -EINVAL; + } - icd->current_fmt = data_fmt; + /* buswidth may be further adjusted by the ici */ + icd->buswidth = icd->current_fmt->depth; icd->width = rect.width; icd->height = rect.height; icf->vb_vidq.field = f->fmt.pix.field; - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", f->type); @@ -394,10 +381,9 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.height = icd->height; f->fmt.pix.field = icf->vb_vidq.field; f->fmt.pix.pixelformat = icd->current_fmt->fourcc; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * icd->current_fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.bytesperline = f->fmt.pix.width * + DIV_ROUND_UP(icd->current_fmt->depth, 8); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n", icd->current_fmt->fourcc); return 0; diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index ee0e6b4bed33..8e8fcb75dacb 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -105,6 +105,9 @@ extern void soc_camera_device_unregister(struct soc_camera_device *icd); extern int soc_camera_video_start(struct soc_camera_device *icd); extern void soc_camera_video_stop(struct soc_camera_device *icd); +extern const struct soc_camera_data_format *soc_camera_format_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc); + struct soc_camera_data_format { const char *name; unsigned int depth; -- cgit v1.2.3-55-g7522 From d8fac217c58f0101a351b9c8c80f1665bd9efef9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 09:45:21 -0300 Subject: V4L/DVB (9788): soc-camera: simplify naming We anyway don't follow the s_fmt_vid_cap / g_fmt_vid_cap / try_fmt_vid_cap naming, and soc-camera is so far only about video capture, let's simplify operation names a bit further. set_fmt_cap / try_fmt_cap wasn't a very good choice too. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9m001.c | 14 +++++++------- drivers/media/video/mt9m111.c | 12 ++++++------ drivers/media/video/mt9v022.c | 14 +++++++------- drivers/media/video/ov772x.c | 14 +++++++------- drivers/media/video/pxa_camera.c | 16 ++++++++-------- drivers/media/video/sh_mobile_ceu_camera.c | 16 ++++++++-------- drivers/media/video/soc_camera.c | 6 +++--- drivers/media/video/soc_camera_platform.c | 12 ++++++------ include/media/soc_camera.h | 10 ++++------ 9 files changed, 56 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 0c524376b67e..0bcfef7b1c17 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -285,8 +285,8 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) width_flag; } -static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9m001_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); int ret; @@ -298,7 +298,7 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank); /* The caller provides a supported format, as verified per - * call to icd->try_fmt_cap() */ + * call to icd->try_fmt() */ if (!ret) ret = reg_write(icd, MT9M001_COLUMN_START, rect->left); if (!ret) @@ -325,8 +325,8 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, return ret; } -static int mt9m001_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m001_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { if (f->fmt.pix.height < 32 + icd->y_skip_top) f->fmt.pix.height = 32 + icd->y_skip_top; @@ -447,8 +447,8 @@ static struct soc_camera_ops mt9m001_ops = { .release = mt9m001_release, .start_capture = mt9m001_start_capture, .stop_capture = mt9m001_stop_capture, - .set_fmt_cap = mt9m001_set_fmt_cap, - .try_fmt_cap = mt9m001_try_fmt_cap, + .set_fmt = mt9m001_set_fmt, + .try_fmt = mt9m001_try_fmt, .set_bus_param = mt9m001_set_bus_param, .query_bus_param = mt9m001_query_bus_param, .controls = mt9m001_controls, diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index da0b2d553fd0..63c52c0b3e72 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -452,8 +452,8 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) return ret; } -static int mt9m111_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9m111_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); int ret; @@ -473,8 +473,8 @@ static int mt9m111_set_fmt_cap(struct soc_camera_device *icd, return ret; } -static int mt9m111_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m111_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { if (f->fmt.pix.height > MT9M111_MAX_HEIGHT) f->fmt.pix.height = MT9M111_MAX_HEIGHT; @@ -597,8 +597,8 @@ static struct soc_camera_ops mt9m111_ops = { .release = mt9m111_release, .start_capture = mt9m111_start_capture, .stop_capture = mt9m111_stop_capture, - .set_fmt_cap = mt9m111_set_fmt_cap, - .try_fmt_cap = mt9m111_try_fmt_cap, + .set_fmt = mt9m111_set_fmt, + .try_fmt = mt9m111_try_fmt, .query_bus_param = mt9m111_query_bus_param, .set_bus_param = mt9m111_set_bus_param, .controls = mt9m111_controls, diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 2584201059d8..3a39f0288599 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -337,14 +337,14 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) width_flag; } -static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9v022_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); int ret; /* The caller provides a supported format, as verified per call to - * icd->try_fmt_cap(), datawidth is from our supported format list */ + * icd->try_fmt(), datawidth is from our supported format list */ switch (pixfmt) { case V4L2_PIX_FMT_GREY: case V4L2_PIX_FMT_Y16: @@ -400,8 +400,8 @@ static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, return 0; } -static int mt9v022_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9v022_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { if (f->fmt.pix.height < 32 + icd->y_skip_top) f->fmt.pix.height = 32 + icd->y_skip_top; @@ -538,8 +538,8 @@ static struct soc_camera_ops mt9v022_ops = { .release = mt9v022_release, .start_capture = mt9v022_start_capture, .stop_capture = mt9v022_stop_capture, - .set_fmt_cap = mt9v022_set_fmt_cap, - .try_fmt_cap = mt9v022_try_fmt_cap, + .set_fmt = mt9v022_set_fmt, + .try_fmt = mt9v022_try_fmt, .set_bus_param = mt9v022_set_bus_param, .query_bus_param = mt9v022_query_bus_param, .controls = mt9v022_controls, diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 96964b79afac..76f296642db6 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -755,9 +755,9 @@ static int ov772x_set_register(struct soc_camera_device *icd, } #endif -static int ov772x_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, - struct v4l2_rect *rect) +static int ov772x_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, + struct v4l2_rect *rect) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); int ret = -EINVAL; @@ -778,8 +778,8 @@ static int ov772x_set_fmt_cap(struct soc_camera_device *icd, return ret; } -static int ov772x_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int ov772x_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; struct ov772x_priv *priv; @@ -868,8 +868,8 @@ static struct soc_camera_ops ov772x_ops = { .release = ov772x_release, .start_capture = ov772x_start_capture, .stop_capture = ov772x_stop_capture, - .set_fmt_cap = ov772x_set_fmt_cap, - .try_fmt_cap = ov772x_try_fmt_cap, + .set_fmt = ov772x_set_fmt, + .try_fmt = ov772x_try_fmt, .set_bus_param = ov772x_set_bus_param, .query_bus_param = ov772x_query_bus_param, .get_chip_id = ov772x_get_chip_id, diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index a375872b1342..665eef236f5b 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -904,8 +904,8 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt) return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL; } -static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int pxa_camera_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { const struct soc_camera_data_format *cam_fmt; int ret; @@ -920,15 +920,15 @@ static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, return -EINVAL; } - ret = icd->ops->set_fmt_cap(icd, pixfmt, rect); + ret = icd->ops->set_fmt(icd, pixfmt, rect); if (pixfmt && !ret) icd->current_fmt = cam_fmt; return ret; } -static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int pxa_camera_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { const struct soc_camera_data_format *cam_fmt; int ret = pxa_camera_try_bus_param(icd, f->fmt.pix.pixelformat); @@ -960,7 +960,7 @@ static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; /* limit to sensor capabilities */ - return icd->ops->try_fmt_cap(icd, f); + return icd->ops->try_fmt(icd, f); } static int pxa_camera_reqbufs(struct soc_camera_file *icf, @@ -1068,8 +1068,8 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .remove = pxa_camera_remove_device, .suspend = pxa_camera_suspend, .resume = pxa_camera_resume, - .set_fmt_cap = pxa_camera_set_fmt_cap, - .try_fmt_cap = pxa_camera_try_fmt_cap, + .set_fmt = pxa_camera_set_fmt, + .try_fmt = pxa_camera_try_fmt, .init_videobuf = pxa_camera_init_videobuf, .reqbufs = pxa_camera_reqbufs, .poll = pxa_camera_poll, diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 02f846d1908b..d284d5d1dd01 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -450,8 +450,8 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, return 0; } -static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { const struct soc_camera_data_format *cam_fmt; int ret; @@ -466,15 +466,15 @@ static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, return -EINVAL; } - ret = icd->ops->set_fmt_cap(icd, pixfmt, rect); + ret = icd->ops->set_fmt(icd, pixfmt, rect); if (pixfmt && !ret) icd->current_fmt = cam_fmt; return ret; } -static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { const struct soc_camera_data_format *cam_fmt; int ret = sh_mobile_ceu_try_bus_param(icd, f->fmt.pix.pixelformat); @@ -508,7 +508,7 @@ static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; /* limit to sensor capabilities */ - return icd->ops->try_fmt_cap(icd, f); + return icd->ops->try_fmt(icd, f); } static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf, @@ -576,8 +576,8 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .owner = THIS_MODULE, .add = sh_mobile_ceu_add_device, .remove = sh_mobile_ceu_remove_device, - .set_fmt_cap = sh_mobile_ceu_set_fmt_cap, - .try_fmt_cap = sh_mobile_ceu_try_fmt_cap, + .set_fmt = sh_mobile_ceu_set_fmt, + .try_fmt = sh_mobile_ceu_try_fmt, .reqbufs = sh_mobile_ceu_reqbufs, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 01c33841d1c6..9db66a4fd1a3 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -73,7 +73,7 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, } /* limit format to hardware capabilities */ - ret = ici->ops->try_fmt_cap(icd, f); + ret = ici->ops->try_fmt(icd, f); return ret; } @@ -324,7 +324,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, rect.top = icd->y_current; rect.width = f->fmt.pix.width; rect.height = f->fmt.pix.height; - ret = ici->ops->set_fmt_cap(icd, f->fmt.pix.pixelformat, &rect); + ret = ici->ops->set_fmt(icd, f->fmt.pix.pixelformat, &rect); if (ret < 0) { return ret; } else if (!icd->current_fmt || @@ -553,7 +553,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - ret = ici->ops->set_fmt_cap(icd, 0, &a->c); + ret = ici->ops->set_fmt(icd, 0, &a->c); if (!ret) { icd->width = a->c.width; icd->height = a->c.height; diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index bb7a9d480e8f..c23871e4c1e6 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -79,14 +79,14 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *icd) return p->bus_param; } -static int soc_camera_platform_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int soc_camera_platform_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { return 0; } -static int soc_camera_platform_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int soc_camera_platform_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); @@ -124,8 +124,8 @@ static struct soc_camera_ops soc_camera_platform_ops = { .release = soc_camera_platform_release, .start_capture = soc_camera_platform_start_capture, .stop_capture = soc_camera_platform_stop_capture, - .set_fmt_cap = soc_camera_platform_set_fmt_cap, - .try_fmt_cap = soc_camera_platform_try_fmt_cap, + .set_fmt = soc_camera_platform_set_fmt, + .try_fmt = soc_camera_platform_try_fmt, .set_bus_param = soc_camera_platform_set_bus_param, .query_bus_param = soc_camera_platform_query_bus_param, }; diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 8e8fcb75dacb..b14f6ddc9e18 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -66,9 +66,8 @@ struct soc_camera_host_ops { void (*remove)(struct soc_camera_device *); int (*suspend)(struct soc_camera_device *, pm_message_t state); int (*resume)(struct soc_camera_device *); - int (*set_fmt_cap)(struct soc_camera_device *, __u32, - struct v4l2_rect *); - int (*try_fmt_cap)(struct soc_camera_device *, struct v4l2_format *); + int (*set_fmt)(struct soc_camera_device *, __u32, struct v4l2_rect *); + int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); void (*init_videobuf)(struct videobuf_queue *, struct soc_camera_device *); int (*reqbufs)(struct soc_camera_file *, struct v4l2_requestbuffers *); @@ -125,9 +124,8 @@ struct soc_camera_ops { int (*release)(struct soc_camera_device *); int (*start_capture)(struct soc_camera_device *); int (*stop_capture)(struct soc_camera_device *); - int (*set_fmt_cap)(struct soc_camera_device *, __u32, - struct v4l2_rect *); - int (*try_fmt_cap)(struct soc_camera_device *, struct v4l2_format *); + int (*set_fmt)(struct soc_camera_device *, __u32, struct v4l2_rect *); + int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); unsigned long (*query_bus_param)(struct soc_camera_device *); int (*set_bus_param)(struct soc_camera_device *, unsigned long); int (*get_chip_id)(struct soc_camera_device *, -- cgit v1.2.3-55-g7522 From d2e3dce083bc96b3bfb71603eaa1726181f7184b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 09:45:24 -0300 Subject: V4L/DVB (9789): soc-camera: add a per-camera device host private data pointer This pointer will be used by pxa_camera.c to point to its pixel format data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/media/soc_camera.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index b14f6ddc9e18..dddaf45c9583 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -42,6 +42,7 @@ struct soc_camera_device { const struct soc_camera_data_format *formats; int num_formats; struct module *owner; + void *host_priv; /* per-device host private data */ /* soc_camera.c private count. Only accessed with video_lock held */ int use_count; }; -- cgit v1.2.3-55-g7522 From c2786ad27104c558b92343e8816e18654aae1759 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 09:45:27 -0300 Subject: V4L/DVB (9790): soc-camera: pixel format negotiation - core support Allocate and fill a list of formats, supported by this specific camera-host combination. Use it for format enumeration. Take care to stay backwards-compatible. Camera hosts rely on sensor formats available, as well as host specific translations. We add a structure so that hosts can define a translation table and use it for format check and setup. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Robert Jarzmik Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 93 ++++++++++++++++++++++++++++++++++------ include/media/soc_camera.h | 25 ++++++++++- 2 files changed, 105 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 9db66a4fd1a3..5e48c2cc1a44 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -47,6 +47,18 @@ const struct soc_camera_data_format *soc_camera_format_by_fourcc( } EXPORT_SYMBOL(soc_camera_format_by_fourcc); +const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < icd->num_user_formats; i++) + if (icd->user_formats[i].host_fmt->fourcc == fourcc) + return icd->user_formats + i; + return NULL; +} +EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); + static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -161,6 +173,59 @@ static int soc_camera_dqbuf(struct file *file, void *priv, return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK); } +static int soc_camera_init_user_formats(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + int i, fmts = 0; + + if (!ici->ops->get_formats) + /* + * Fallback mode - the host will have to serve all + * sensor-provided formats one-to-one to the user + */ + fmts = icd->num_formats; + else + /* + * First pass - only count formats this host-sensor + * configuration can provide + */ + for (i = 0; i < icd->num_formats; i++) + fmts += ici->ops->get_formats(icd, i, NULL); + + if (!fmts) + return -ENXIO; + + icd->user_formats = + vmalloc(fmts * sizeof(struct soc_camera_format_xlate)); + if (!icd->user_formats) + return -ENOMEM; + + icd->num_user_formats = fmts; + fmts = 0; + + dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts); + + /* Second pass - actually fill data formats */ + for (i = 0; i < icd->num_formats; i++) + if (!ici->ops->get_formats) { + icd->user_formats[i].host_fmt = icd->formats + i; + icd->user_formats[i].cam_fmt = icd->formats + i; + icd->user_formats[i].buswidth = icd->formats[i].depth; + } else { + fmts += ici->ops->get_formats(icd, i, + &icd->user_formats[fmts]); + } + + icd->current_fmt = icd->user_formats[0].host_fmt; + + return 0; +} + +static void soc_camera_free_user_formats(struct soc_camera_device *icd) +{ + vfree(icd->user_formats); +} + static int soc_camera_open(struct inode *inode, struct file *file) { struct video_device *vdev; @@ -197,10 +262,12 @@ static int soc_camera_open(struct inode *inode, struct file *file) /* Now we really have to activate the camera */ if (icd->use_count == 1) { + ret = soc_camera_init_user_formats(icd); + if (ret < 0) + goto eiufmt; ret = ici->ops->add(icd); if (ret < 0) { dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); - icd->use_count--; goto eiciadd; } } @@ -216,6 +283,9 @@ static int soc_camera_open(struct inode *inode, struct file *file) /* All errors are entered with the video_lock held */ eiciadd: + soc_camera_free_user_formats(icd); +eiufmt: + icd->use_count--; module_put(ici->ops->owner); emgi: module_put(icd->ops->owner); @@ -234,8 +304,10 @@ static int soc_camera_close(struct inode *inode, struct file *file) mutex_lock(&video_lock); icd->use_count--; - if (!icd->use_count) + if (!icd->use_count) { ici->ops->remove(icd); + soc_camera_free_user_formats(icd); + } module_put(icd->ops->owner); module_put(ici->ops->owner); mutex_unlock(&video_lock); @@ -311,6 +383,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, struct soc_camera_device *icd = icf->icd; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + __u32 pixfmt = f->fmt.pix.pixelformat; int ret; struct v4l2_rect rect; @@ -328,14 +401,12 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, if (ret < 0) { return ret; } else if (!icd->current_fmt || - icd->current_fmt->fourcc != f->fmt.pix.pixelformat) { - dev_err(&ici->dev, "Host driver hasn't set up current " - "format correctly!\n"); + icd->current_fmt->fourcc != pixfmt) { + dev_err(&ici->dev, + "Host driver hasn't set up current format correctly!\n"); return -EINVAL; } - /* buswidth may be further adjusted by the ici */ - icd->buswidth = icd->current_fmt->depth; icd->width = rect.width; icd->height = rect.height; icf->vb_vidq.field = f->fmt.pix.field; @@ -347,7 +418,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, icd->width, icd->height); /* set physical bus parameters */ - return ici->ops->set_bus_param(icd, f->fmt.pix.pixelformat); + return ici->ops->set_bus_param(icd, pixfmt); } static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, @@ -359,10 +430,10 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, WARN_ON(priv != file->private_data); - if (f->index >= icd->num_formats) + if (f->index >= icd->num_user_formats) return -EINVAL; - format = &icd->formats[f->index]; + format = icd->user_formats[f->index].host_fmt; strlcpy(f->description, format->name, sizeof(f->description)); f->pixelformat = format->fourcc; @@ -919,8 +990,6 @@ int soc_camera_video_start(struct soc_camera_device *icd) vdev->minor = -1; vdev->tvnorms = V4L2_STD_UNKNOWN, - icd->current_fmt = &icd->formats[0]; - err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor); if (err < 0) { dev_err(vdev->parent, "video_register_device failed\n"); diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index dddaf45c9583..da57ffdaec4d 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -41,6 +41,8 @@ struct soc_camera_device { const struct soc_camera_data_format *current_fmt; const struct soc_camera_data_format *formats; int num_formats; + struct soc_camera_format_xlate *user_formats; + int num_user_formats; struct module *owner; void *host_priv; /* per-device host private data */ /* soc_camera.c private count. Only accessed with video_lock held */ @@ -65,8 +67,10 @@ struct soc_camera_host_ops { struct module *owner; int (*add)(struct soc_camera_device *); void (*remove)(struct soc_camera_device *); - int (*suspend)(struct soc_camera_device *, pm_message_t state); + int (*suspend)(struct soc_camera_device *, pm_message_t); int (*resume)(struct soc_camera_device *); + int (*get_formats)(struct soc_camera_device *, int, + struct soc_camera_format_xlate *); int (*set_fmt)(struct soc_camera_device *, __u32, struct v4l2_rect *); int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); void (*init_videobuf)(struct videobuf_queue *, @@ -107,6 +111,8 @@ extern void soc_camera_video_stop(struct soc_camera_device *icd); extern const struct soc_camera_data_format *soc_camera_format_by_fourcc( struct soc_camera_device *icd, unsigned int fourcc); +extern const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc); struct soc_camera_data_format { const char *name; @@ -115,6 +121,23 @@ struct soc_camera_data_format { enum v4l2_colorspace colorspace; }; +/** + * struct soc_camera_format_xlate - match between host and sensor formats + * @cam_fmt: sensor format provided by the sensor + * @host_fmt: host format after host translation from cam_fmt + * @buswidth: bus width for this format + * + * Host and sensor translation structure. Used in table of host and sensor + * formats matchings in soc_camera_device. A host can override the generic list + * generation by implementing get_formats(), and use it for format checks and + * format setup. + */ +struct soc_camera_format_xlate { + const struct soc_camera_data_format *cam_fmt; + const struct soc_camera_data_format *host_fmt; + unsigned char buswidth; +}; + struct soc_camera_ops { struct module *owner; int (*probe)(struct soc_camera_device *); -- cgit v1.2.3-55-g7522 From 67bc04dd04bfe6a6337a9c6773e4c36645360332 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Fri, 5 Dec 2008 10:13:40 -0300 Subject: V4L/DVB (9816): v4l2-int-if: add three new ioctls for std handling and routing These changes are needed for the new TVP514x driver. Signed-off-by: Brijesh Jadav Signed-off-by: Hardik Shah Signed-off-by: Manjunath Hadli Signed-off-by: R Sivaraj Signed-off-by: Vaibhav Hiremath Signed-off-by: Karicheri Muralidharan Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-int-device.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/media/v4l2-int-device.h b/include/media/v4l2-int-device.h index 9c2df41dbf92..ecda3c725837 100644 --- a/include/media/v4l2-int-device.h +++ b/include/media/v4l2-int-device.h @@ -183,6 +183,9 @@ enum v4l2_int_ioctl_num { vidioc_int_s_crop_num, vidioc_int_g_parm_num, vidioc_int_s_parm_num, + vidioc_int_querystd_num, + vidioc_int_s_std_num, + vidioc_int_s_video_routing_num, /* * @@ -284,6 +287,9 @@ V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *); V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *); V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *); V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *); +V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *); +V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *); +V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *); V4L2_INT_WRAPPER_0(dev_init); V4L2_INT_WRAPPER_0(dev_exit); -- cgit v1.2.3-55-g7522 From 07b1747c8d0bb463311f9dd05d4c013765abe2eb Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Fri, 5 Dec 2008 10:19:36 -0300 Subject: V4L/DVB (9817): v4l: add new tvp514x I2C video decoder driver Signed-off-by: Brijesh Jadav Signed-off-by: Hardik Shah Signed-off-by: Manjunath Hadli Signed-off-by: R Sivaraj Signed-off-by: Vaibhav Hiremath Signed-off-by: Karicheri Muralidharan Signed-off-by: Hans Verkuil Reviewed-by: Hans Verkuil Reviewed-by: David Brownell Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 11 + drivers/media/video/Makefile | 1 + drivers/media/video/tvp514x.c | 1569 ++++++++++++++++++++++++++++++++++++ drivers/media/video/tvp514x_regs.h | 297 +++++++ include/media/tvp514x.h | 118 +++ 5 files changed, 1996 insertions(+) create mode 100644 drivers/media/video/tvp514x.c create mode 100644 drivers/media/video/tvp514x_regs.h create mode 100644 include/media/tvp514x.h (limited to 'include') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index c487d2a1285f..eb9ff7e107d6 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -361,6 +361,17 @@ config VIDEO_SAA7191 To compile this driver as a module, choose M here: the module will be called saa7191. +config VIDEO_TVP514X + tristate "Texas Instruments TVP514x video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 + decoder. It is currently working with the TI OMAP3 camera + controller. + + To compile this driver as a module, choose M here: the + module will be called tvp514x. + config VIDEO_TVP5150 tristate "Texas Instruments TVP5150 video decoder" depends on VIDEO_V4L2 && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index ac147b1aea36..492ab3dce71b 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o +obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_CS5345) += cs5345.o diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c new file mode 100644 index 000000000000..ac9aa40d09f6 --- /dev/null +++ b/drivers/media/video/tvp514x.c @@ -0,0 +1,1569 @@ +/* + * drivers/media/video/tvp514x.c + * + * TI TVP5146/47 decoder driver + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * This package 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +#include "tvp514x_regs.h" + +/* Module Name */ +#define TVP514X_MODULE_NAME "tvp514x" + +/* Private macros for TVP */ +#define I2C_RETRY_COUNT (5) +#define LOCK_RETRY_COUNT (5) +#define LOCK_RETRY_DELAY (200) + +/* Debug functions */ +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dump_reg(client, reg, val) \ + do { \ + val = tvp514x_read_reg(client, reg); \ + v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \ + } while (0) + +/** + * enum tvp514x_std - enum for supported standards + */ +enum tvp514x_std { + STD_NTSC_MJ = 0, + STD_PAL_BDGHIN, + STD_INVALID +}; + +/** + * enum tvp514x_state - enum for different decoder states + */ +enum tvp514x_state { + STATE_NOT_DETECTED, + STATE_DETECTED +}; + +/** + * struct tvp514x_std_info - Structure to store standard informations + * @width: Line width in pixels + * @height:Number of active lines + * @video_std: Value to write in REG_VIDEO_STD register + * @standard: v4l2 standard structure information + */ +struct tvp514x_std_info { + unsigned long width; + unsigned long height; + u8 video_std; + struct v4l2_standard standard; +}; + +/** + * struct tvp514x_decoded - TVP5146/47 decoder object + * @v4l2_int_device: Slave handle + * @pdata: Board specific + * @client: I2C client data + * @id: Entry from I2C table + * @ver: Chip version + * @state: TVP5146/47 decoder state - detected or not-detected + * @pix: Current pixel format + * @num_fmts: Number of formats + * @fmt_list: Format list + * @current_std: Current standard + * @num_stds: Number of standards + * @std_list: Standards list + * @route: input and output routing at chip level + */ +struct tvp514x_decoder { + struct v4l2_int_device *v4l2_int_device; + const struct tvp514x_platform_data *pdata; + struct i2c_client *client; + + struct i2c_device_id *id; + + int ver; + enum tvp514x_state state; + + struct v4l2_pix_format pix; + int num_fmts; + const struct v4l2_fmtdesc *fmt_list; + + enum tvp514x_std current_std; + int num_stds; + struct tvp514x_std_info *std_list; + + struct v4l2_routing route; +}; + +/* TVP514x default register values */ +static struct tvp514x_reg tvp514x_reg_list[] = { + {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */ + {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, + {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */ + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, + {TOK_WRITE, REG_COLOR_KILLER, 0x10}, + {TOK_WRITE, REG_LUMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL2, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL3, 0x02}, + {TOK_WRITE, REG_BRIGHTNESS, 0x80}, + {TOK_WRITE, REG_CONTRAST, 0x80}, + {TOK_WRITE, REG_SATURATION, 0x80}, + {TOK_WRITE, REG_HUE, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, + {TOK_SKIP, 0x0F, 0x00}, /* Reserved */ + {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, + {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, + {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, + {TOK_SKIP, 0x13, 0x00}, /* Reserved */ + {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, + {TOK_SKIP, 0x15, 0x00}, /* Reserved */ + {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */ + {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, + {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */ + {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */ + {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */ + {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, + {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, + {TOK_SKIP, 0x26, 0x00}, /* Reserved */ + {TOK_SKIP, 0x27, 0x00}, /* Reserved */ + {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, + {TOK_SKIP, 0x29, 0x00}, /* Reserved */ + {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, + {TOK_SKIP, 0x2B, 0x00}, /* Reserved */ + {TOK_SKIP, REG_SCART_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_CONTROL, 0x00}, + {TOK_SKIP, 0x2F, 0x00}, /* Reserved */ + {TOK_SKIP, 0x30, 0x00}, /* Reserved */ + {TOK_SKIP, 0x31, 0x00}, /* Reserved */ + {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */ + {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */ + {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */ + {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */ + {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */ + {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, + {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, + {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */ + {TOK_TERM, 0, 0}, +}; + +/* List of image formats supported by TVP5146/47 decoder + * Currently we are using 8 bit mode only, but can be + * extended to 10/20 bit mode. + */ +static const struct v4l2_fmtdesc tvp514x_fmt_list[] = { + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = 0, + .description = "8-bit UYVY 4:2:2 Format", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, +}; + +/* + * Supported standards - + * + * Currently supports two standards only, need to add support for rest of the + * modes, like SECAM, etc... + */ +static struct tvp514x_std_info tvp514x_std_list[] = { + /* Standard: STD_NTSC_MJ */ + [STD_NTSC_MJ] = { + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_NTSC_MJ_BIT, + .standard = { + .index = 0, + .id = V4L2_STD_NTSC, + .name = "NTSC", + .frameperiod = {1001, 30000}, + .framelines = 525 + }, + /* Standard: STD_PAL_BDGHIN */ + }, + [STD_PAL_BDGHIN] = { + .width = PAL_NUM_ACTIVE_PIXELS, + .height = PAL_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_PAL_BDGHIN_BIT, + .standard = { + .index = 1, + .id = V4L2_STD_PAL, + .name = "PAL", + .frameperiod = {1, 25}, + .framelines = 625 + }, + }, + /* Standard: need to add for additional standard */ +}; +/* + * Control structure for Auto Gain + * This is temporary data, will get replaced once + * v4l2_ctrl_query_fill supports it. + */ +static const struct v4l2_queryctrl tvp514x_autogain_ctrl = { + .id = V4L2_CID_AUTOGAIN, + .name = "Gain, Automatic", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, +}; + +/* + * Read a value from a register in an TVP5146/47 decoder device. + * Returns value read if successful, or non-zero (-1) otherwise. + */ +static int tvp514x_read_reg(struct i2c_client *client, u8 reg) +{ + int err; + int retry = 0; +read_again: + + err = i2c_smbus_read_byte_data(client, reg); + if (err == -1) { + if (retry <= I2C_RETRY_COUNT) { + v4l_warn(client, "Read: retry ... %d\n", retry); + retry++; + msleep_interruptible(10); + goto read_again; + } + } + + return err; +} + +/* + * Write a value to a register in an TVP5146/47 decoder device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + int retry = 0; +write_again: + + err = i2c_smbus_write_byte_data(client, reg, val); + if (err) { + if (retry <= I2C_RETRY_COUNT) { + v4l_warn(client, "Write: retry ... %d\n", retry); + retry++; + msleep_interruptible(10); + goto write_again; + } + } + + return err; +} + +/* + * tvp514x_write_regs : Initializes a list of TVP5146/47 registers + * if token is TOK_TERM, then entire write operation terminates + * if token is TOK_DELAY, then a delay of 'val' msec is introduced + * if token is TOK_SKIP, then the register write is skipped + * if token is TOK_WRITE, then the register write is performed + * + * reglist - list of registers to be written + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_regs(struct i2c_client *client, + const struct tvp514x_reg reglist[]) +{ + int err; + const struct tvp514x_reg *next = reglist; + + for (; next->token != TOK_TERM; next++) { + if (next->token == TOK_DELAY) { + msleep(next->val); + continue; + } + + if (next->token == TOK_SKIP) + continue; + + err = tvp514x_write_reg(client, next->reg, (u8) next->val); + if (err) { + v4l_err(client, "Write failed. Err[%d]\n", err); + return err; + } + } + return 0; +} + +/* + * tvp514x_get_current_std: + * Returns the current standard detected by TVP5146/47 + */ +static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder + *decoder) +{ + u8 std, std_status; + + std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD); + if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) { + /* use the standard status register */ + std_status = tvp514x_read_reg(decoder->client, + REG_VIDEO_STD_STATUS); + } else + std_status = std; /* use the standard register itself */ + + switch (std_status & VIDEO_STD_MASK) { + case VIDEO_STD_NTSC_MJ_BIT: + return STD_NTSC_MJ; + + case VIDEO_STD_PAL_BDGHIN_BIT: + return STD_PAL_BDGHIN; + + default: + return STD_INVALID; + } + + return STD_INVALID; +} + +/* + * TVP5146/47 register dump function + */ +static void tvp514x_reg_dump(struct tvp514x_decoder *decoder) +{ + u8 value; + + dump_reg(decoder->client, REG_INPUT_SEL, value); + dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value); + dump_reg(decoder->client, REG_VIDEO_STD, value); + dump_reg(decoder->client, REG_OPERATION_MODE, value); + dump_reg(decoder->client, REG_COLOR_KILLER, value); + dump_reg(decoder->client, REG_LUMA_CONTROL1, value); + dump_reg(decoder->client, REG_LUMA_CONTROL2, value); + dump_reg(decoder->client, REG_LUMA_CONTROL3, value); + dump_reg(decoder->client, REG_BRIGHTNESS, value); + dump_reg(decoder->client, REG_CONTRAST, value); + dump_reg(decoder->client, REG_SATURATION, value); + dump_reg(decoder->client, REG_HUE, value); + dump_reg(decoder->client, REG_CHROMA_CONTROL1, value); + dump_reg(decoder->client, REG_CHROMA_CONTROL2, value); + dump_reg(decoder->client, REG_COMP_PR_SATURATION, value); + dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value); + dump_reg(decoder->client, REG_COMP_PB_SATURATION, value); + dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value); + dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value); + dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value); + dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value); + dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value); + dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value); + dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value); + dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value); + dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value); + dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value); + dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value); + dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value); + dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value); + dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value); + dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value); + dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value); + dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value); + dump_reg(decoder->client, REG_SYNC_CONTROL, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value); + dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value); +} + +/* + * Configure the TVP5146/47 with the current register settings + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_configure(struct tvp514x_decoder *decoder) +{ + int err; + + /* common register initialization */ + err = + tvp514x_write_regs(decoder->client, tvp514x_reg_list); + if (err) + return err; + + if (debug) + tvp514x_reg_dump(decoder); + + return 0; +} + +/* + * Detect if an tvp514x is present, and if so which revision. + * A device is considered to be detected if the chip ID (LSB and MSB) + * registers match the expected values. + * Any value of the rom version register is accepted. + * Returns ENODEV error number if no device is detected, or zero + * if a device is detected. + */ +static int tvp514x_detect(struct tvp514x_decoder *decoder) +{ + u8 chip_id_msb, chip_id_lsb, rom_ver; + + chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB); + chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB); + rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION); + + v4l_dbg(1, debug, decoder->client, + "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", + chip_id_msb, chip_id_lsb, rom_ver); + if ((chip_id_msb != TVP514X_CHIP_ID_MSB) + || ((chip_id_lsb != TVP5146_CHIP_ID_LSB) + && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) { + /* We didn't read the values we expected, so this must not be + * an TVP5146/47. + */ + v4l_err(decoder->client, + "chip id mismatch msb:0x%x lsb:0x%x\n", + chip_id_msb, chip_id_lsb); + return -ENODEV; + } + + decoder->ver = rom_ver; + decoder->state = STATE_DETECTED; + + v4l_info(decoder->client, + "%s found at 0x%x (%s)\n", decoder->client->name, + decoder->client->addr << 1, + decoder->client->adapter->name); + return 0; +} + +/* + * Following are decoder interface functions implemented by + * TVP5146/47 decoder driver. + */ + +/** + * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl + * @s: pointer to standard V4L2 device structure + * @std_id: standard V4L2 std_id ioctl enum + * + * Returns the current standard detected by TVP5146/47. If no active input is + * detected, returns -EINVAL + */ +static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) +{ + struct tvp514x_decoder *decoder = s->priv; + enum tvp514x_std current_std; + enum tvp514x_input input_sel; + u8 sync_lock_status, lock_mask; + + if (std_id == NULL) + return -EINVAL; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + input_sel = decoder->route.input; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /*Need to add other interfaces*/ + default: + return -EINVAL; + } + /* check whether signal is locked */ + sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1); + if (lock_mask != (sync_lock_status & lock_mask)) + return -EINVAL; /* No input detected */ + + decoder->current_std = current_std; + *std_id = decoder->std_list[current_std].standard.id; + + v4l_dbg(1, debug, decoder->client, "Current STD: %s", + decoder->std_list[current_std].standard.name); + return 0; +} + +/** + * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl + * @s: pointer to standard V4L2 device structure + * @std_id: standard V4L2 v4l2_std_id ioctl enum + * + * If std_id is supported, sets the requested standard. Otherwise, returns + * -EINVAL + */ +static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) +{ + struct tvp514x_decoder *decoder = s->priv; + int err, i; + + if (std_id == NULL) + return -EINVAL; + + for (i = 0; i < decoder->num_stds; i++) + if (*std_id & decoder->std_list[i].standard.id) + break; + + if ((i == decoder->num_stds) || (i == STD_INVALID)) + return -EINVAL; + + err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD, + decoder->std_list[i].video_std); + if (err) + return err; + + decoder->current_std = i; + tvp514x_reg_list[REG_VIDEO_STD].val = decoder->std_list[i].video_std; + + v4l_dbg(1, debug, decoder->client, "Standard set to: %s", + decoder->std_list[i].standard.name); + return 0; +} + +/** + * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl + * @s: pointer to standard V4L2 device structure + * @index: number of the input + * + * If index is valid, selects the requested input. Otherwise, returns -EINVAL if + * the input is not supported or there is no active signal present in the + * selected input. + */ +static int ioctl_s_routing(struct v4l2_int_device *s, + struct v4l2_routing *route) +{ + struct tvp514x_decoder *decoder = s->priv; + int err; + enum tvp514x_input input_sel; + enum tvp514x_output output_sel; + enum tvp514x_std current_std = STD_INVALID; + u8 sync_lock_status, lock_mask; + int try_count = LOCK_RETRY_COUNT; + + if ((!route) || (route->input >= INPUT_INVALID) || + (route->output >= OUTPUT_INVALID)) + return -EINVAL; /* Index out of bound */ + + input_sel = route->input; + output_sel = route->output; + + err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel); + if (err) + return err; + + output_sel |= tvp514x_read_reg(decoder->client, + REG_OUTPUT_FORMATTER1) & 0x7; + err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1, + output_sel); + if (err) + return err; + + tvp514x_reg_list[REG_INPUT_SEL].val = input_sel; + tvp514x_reg_list[REG_OUTPUT_FORMATTER1].val = output_sel; + + /* Clear status */ + msleep(LOCK_RETRY_DELAY); + err = + tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01); + if (err) + return err; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /*Need to add other interfaces*/ + default: + return -EINVAL; + } + + while (try_count-- > 0) { + /* Allow decoder to sync up with new input */ + msleep(LOCK_RETRY_DELAY); + + /* get the current standard for future reference */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + continue; + + sync_lock_status = tvp514x_read_reg(decoder->client, + REG_STATUS1); + if (lock_mask == (sync_lock_status & lock_mask)) + break; /* Input detected */ + } + + if ((current_std == STD_INVALID) || (try_count < 0)) + return -EINVAL; + + decoder->current_std = current_std; + decoder->route.input = route->input; + decoder->route.output = route->output; + + v4l_dbg(1, debug, decoder->client, + "Input set to: %d, std : %d", + input_sel, current_std); + + return 0; +} + +/** + * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qctrl: standard V4L2 v4l2_queryctrl structure + * + * If the requested control is supported, returns the control information. + * Otherwise, returns -EINVAL if the control is not supported. + */ +static int +ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = -EINVAL; + + if (qctrl == NULL) + return err; + + switch (qctrl->id) { + case V4L2_CID_BRIGHTNESS: + /* Brightness supported is same as standard one (0-255), + * so make use of standard API provided. + */ + err = v4l2_ctrl_query_fill_std(qctrl); + break; + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + /* Saturation and Contrast supported is - + * Contrast: 0 - 255 (Default - 128) + * Saturation: 0 - 255 (Default - 128) + */ + err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); + break; + case V4L2_CID_HUE: + /* Hue Supported is - + * Hue - -180 - +180 (Default - 0, Step - +180) + */ + err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0); + break; + case V4L2_CID_AUTOGAIN: + /* Autogain is either 0 or 1*/ + memcpy(qctrl, &tvp514x_autogain_ctrl, + sizeof(struct v4l2_queryctrl)); + err = 0; + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", qctrl->id); + return err; + } + + v4l_dbg(1, debug, decoder->client, + "Query Control: %s : Min - %d, Max - %d, Def - %d", + qctrl->name, + qctrl->minimum, + qctrl->maximum, + qctrl->default_value); + + return err; +} + +/** + * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @ctrl: pointer to v4l2_control structure + * + * If the requested control is supported, returns the control's current + * value from the decoder. Otherwise, returns -EINVAL if the control is not + * supported. + */ +static int +ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (ctrl == NULL) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = tvp514x_reg_list[REG_BRIGHTNESS].val; + break; + case V4L2_CID_CONTRAST: + ctrl->value = tvp514x_reg_list[REG_CONTRAST].val; + break; + case V4L2_CID_SATURATION: + ctrl->value = tvp514x_reg_list[REG_SATURATION].val; + break; + case V4L2_CID_HUE: + ctrl->value = tvp514x_reg_list[REG_HUE].val; + if (ctrl->value == 0x7F) + ctrl->value = 180; + else if (ctrl->value == 0x80) + ctrl->value = -180; + else + ctrl->value = 0; + + break; + case V4L2_CID_AUTOGAIN: + ctrl->value = tvp514x_reg_list[REG_AFE_GAIN_CTRL].val; + if ((ctrl->value & 0x3) == 3) + ctrl->value = 1; + else + ctrl->value = 0; + + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", ctrl->id); + return -EINVAL; + } + + v4l_dbg(1, debug, decoder->client, + "Get Control: ID - %d - %d", + ctrl->id, ctrl->value); + return 0; +} + +/** + * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @ctrl: pointer to v4l2_control structure + * + * If the requested control is supported, sets the control's current + * value in HW. Otherwise, returns -EINVAL if the control is not supported. + */ +static int +ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = -EINVAL, value; + + if (ctrl == NULL) + return err; + + value = (__s32) ctrl->value; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid brightness setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS, + value); + if (err) + return err; + tvp514x_reg_list[REG_BRIGHTNESS].val = value; + break; + case V4L2_CID_CONTRAST: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid contrast setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_CONTRAST, + value); + if (err) + return err; + tvp514x_reg_list[REG_CONTRAST].val = value; + break; + case V4L2_CID_SATURATION: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid saturation setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_SATURATION, + value); + if (err) + return err; + tvp514x_reg_list[REG_SATURATION].val = value; + break; + case V4L2_CID_HUE: + if (value == 180) + value = 0x7F; + else if (value == -180) + value = 0x80; + else if (value == 0) + value = 0; + else { + v4l_err(decoder->client, + "invalid hue setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_HUE, + value); + if (err) + return err; + tvp514x_reg_list[REG_HUE].val = value; + break; + case V4L2_CID_AUTOGAIN: + if (value == 1) + value = 0x0F; + else if (value == 0) + value = 0x0C; + else { + v4l_err(decoder->client, + "invalid auto gain setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL, + value); + if (err) + return err; + tvp514x_reg_list[REG_AFE_GAIN_CTRL].val = value; + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", ctrl->id); + return err; + } + + v4l_dbg(1, debug, decoder->client, + "Set Control: ID - %d - %d", + ctrl->id, ctrl->value); + + return err; +} + +/** + * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure + * + * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats + */ +static int +ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) +{ + struct tvp514x_decoder *decoder = s->priv; + int index; + + if (fmt == NULL) + return -EINVAL; + + index = fmt->index; + if ((index >= decoder->num_fmts) || (index < 0)) + return -EINVAL; /* Index out of bound */ + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + memcpy(fmt, &decoder->fmt_list[index], + sizeof(struct v4l2_fmtdesc)); + + v4l_dbg(1, debug, decoder->client, + "Current FMT: index - %d (%s)", + decoder->fmt_list[index].index, + decoder->fmt_list[index].description); + return 0; +} + +/** + * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure + * + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This + * ioctl is used to negotiate the image capture size and pixel format + * without actually making it take effect. + */ +static int +ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + int ifmt; + struct v4l2_pix_format *pix; + enum tvp514x_std current_std; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + pix = &f->fmt.pix; + + /* Calculate height and width based on current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + pix->width = decoder->std_list[current_std].width; + pix->height = decoder->std_list[current_std].height; + + for (ifmt = 0; ifmt < decoder->num_fmts; ifmt++) { + if (pix->pixelformat == + decoder->fmt_list[ifmt].pixelformat) + break; + } + if (ifmt == decoder->num_fmts) + ifmt = 0; /* None of the format matched, select default */ + pix->pixelformat = decoder->fmt_list[ifmt].pixelformat; + + pix->field = V4L2_FIELD_INTERLACED; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->priv = 0; + + v4l_dbg(1, debug, decoder->client, + "Try FMT: pixelformat - %s, bytesperline - %d" + "Width - %d, Height - %d", + decoder->fmt_list[ifmt].description, pix->bytesperline, + pix->width, pix->height); + return 0; +} + +/** + * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure + * + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ +static int +ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_pix_format *pix; + int rval; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + pix = &f->fmt.pix; + rval = ioctl_try_fmt_cap(s, f); + if (rval) + return rval; + + decoder->pix = *pix; + + return rval; +} + +/** + * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the decoder's current pixel format in the v4l2_format + * parameter. + */ +static int +ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + f->fmt.pix = decoder->pix; + + v4l_dbg(1, debug, decoder->client, + "Current FMT: bytesperline - %d" + "Width - %d, Height - %d", + decoder->pix.bytesperline, + decoder->pix.width, decoder->pix.height); + return 0; +} + +/** + * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the decoder's video CAPTURE parameters. + */ +static int +ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_captureparm *cparm; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + + cparm = &a->parm.capture; + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the decoder to use the input parameters, if possible. If + * not possible, returns the appropriate error code. + */ +static int +ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_fract *timeperframe; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + timeperframe = &a->parm.capture.timeperframe; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + + *timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num + * @s: pointer to standard V4L2 device structure + * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure + * + * Gets slave interface parameters. + * Calculates the required xclk value to support the requested + * clock parameters in p. This value is returned in the p + * parameter. + */ +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + struct tvp514x_decoder *decoder = s->priv; + int rval; + + if (p == NULL) + return -EINVAL; + + if (NULL == decoder->pdata->ifparm) + return -EINVAL; + + rval = decoder->pdata->ifparm(p); + if (rval) { + v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval); + return rval; + } + + p->u.bt656.clock_curr = TVP514X_XCLK_BT656; + + return 0; +} + +/** + * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold decoder's private data address + * + * Returns device's (decoder's) private data area address in p parameter + */ +static int ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (NULL == decoder->pdata->priv_data_set) + return -EINVAL; + + return decoder->pdata->priv_data_set(p); +} + +/** + * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requrested state, if possible. + */ +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = 0; + + switch (on) { + case V4L2_POWER_OFF: + /* Power Down Sequence */ + err = + tvp514x_write_reg(decoder->client, REG_OPERATION_MODE, + 0x01); + /* Disable mux for TVP5146/47 decoder data path */ + if (decoder->pdata->power_set) + err |= decoder->pdata->power_set(on); + decoder->state = STATE_NOT_DETECTED; + break; + + case V4L2_POWER_STANDBY: + if (decoder->pdata->power_set) + err = decoder->pdata->power_set(on); + break; + + case V4L2_POWER_ON: + /* Enable mux for TVP5146/47 decoder data path */ + if ((decoder->pdata->power_set) && + (decoder->state == STATE_NOT_DETECTED)) { + int i; + struct tvp514x_init_seq *int_seq = + (struct tvp514x_init_seq *) + decoder->id->driver_data; + + err = decoder->pdata->power_set(on); + + /* Power Up Sequence */ + for (i = 0; i < int_seq->no_regs; i++) { + err |= tvp514x_write_reg(decoder->client, + int_seq->init_reg_seq[i].reg, + int_seq->init_reg_seq[i].val); + } + /* Detect the sensor is not already detected */ + err |= tvp514x_detect(decoder); + if (err) { + v4l_err(decoder->client, + "Unable to detect decoder\n"); + return err; + } + } + err |= tvp514x_configure(decoder); + break; + + default: + err = -ENODEV; + break; + } + + return err; +} + +/** + * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + * + * Initialize the decoder device (calls tvp514x_configure()) + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + struct tvp514x_decoder *decoder = s->priv; + + /* Set default standard to auto */ + tvp514x_reg_list[REG_VIDEO_STD].val = + VIDEO_STD_AUTO_SWITCH_BIT; + + return tvp514x_configure(decoder); +} + +/** + * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. Returns 0 if + * TVP5146/47 device could be found, otherwise returns appropriate error. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct tvp514x_decoder *decoder = s->priv; + int err; + + err = tvp514x_detect(decoder); + if (err < 0) { + v4l_err(decoder->client, + "Unable to detect decoder\n"); + return err; + } + + v4l_info(decoder->client, + "chip version 0x%.2x detected\n", decoder->ver); + + return 0; +} + +static struct v4l2_int_ioctl_desc tvp514x_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_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv}, + {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm}, + {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}, + {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd}, + {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std}, + {vidioc_int_s_video_routing_num, + (v4l2_int_ioctl_func *) ioctl_s_routing}, +}; + +static struct v4l2_int_slave tvp514x_slave = { + .ioctls = tvp514x_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc), +}; + +static struct tvp514x_decoder tvp514x_dev = { + .state = STATE_NOT_DETECTED, + + .fmt_list = tvp514x_fmt_list, + .num_fmts = ARRAY_SIZE(tvp514x_fmt_list), + + .pix = { /* Default to NTSC 8-bit YUV 422 */ + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = NTSC_NUM_ACTIVE_PIXELS * 2, + .sizeimage = + NTSC_NUM_ACTIVE_PIXELS * 2 * NTSC_NUM_ACTIVE_LINES, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + }, + + .current_std = STD_NTSC_MJ, + .std_list = tvp514x_std_list, + .num_stds = ARRAY_SIZE(tvp514x_std_list), + +}; + +static struct v4l2_int_device tvp514x_int_device = { + .module = THIS_MODULE, + .name = TVP514X_MODULE_NAME, + .priv = &tvp514x_dev, + .type = v4l2_int_type_slave, + .u = { + .slave = &tvp514x_slave, + }, +}; + +/** + * tvp514x_probe - decoder driver i2c probe handler + * @client: i2c driver client device structure + * + * Register decoder as an i2c client device and V4L2 + * device. + */ +static int +tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct tvp514x_decoder *decoder = &tvp514x_dev; + int err; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + decoder->pdata = client->dev.platform_data; + if (!decoder->pdata) { + v4l_err(client, "No platform data\n!!"); + return -ENODEV; + } + /* + * Fetch platform specific data, and configure the + * tvp514x_reg_list[] accordingly. Since this is one + * time configuration, no need to preserve. + */ + tvp514x_reg_list[REG_OUTPUT_FORMATTER2].val |= + (decoder->pdata->clk_polarity << 1); + tvp514x_reg_list[REG_SYNC_CONTROL].val |= + ((decoder->pdata->hs_polarity << 2) | + (decoder->pdata->vs_polarity << 3)); + /* + * Save the id data, required for power up sequence + */ + decoder->id = (struct i2c_device_id *)id; + /* Attach to Master */ + strcpy(tvp514x_int_device.u.slave->attach_to, decoder->pdata->master); + decoder->v4l2_int_device = &tvp514x_int_device; + decoder->client = client; + i2c_set_clientdata(client, decoder); + + /* Register with V4L2 layer as slave device */ + err = v4l2_int_device_register(decoder->v4l2_int_device); + if (err) { + i2c_set_clientdata(client, NULL); + v4l_err(client, + "Unable to register to v4l2. Err[%d]\n", err); + + } else + v4l_info(client, "Registered to v4l2 master %s!!\n", + decoder->pdata->master); + + return 0; +} + +/** + * tvp514x_remove - decoder driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister decoder as an i2c client device and V4L2 + * device. Complement of tvp514x_probe(). + */ +static int __exit tvp514x_remove(struct i2c_client *client) +{ + struct tvp514x_decoder *decoder = i2c_get_clientdata(client); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + v4l2_int_device_unregister(decoder->v4l2_int_device); + i2c_set_clientdata(client, NULL); + + return 0; +} +/* + * TVP5146 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp5146_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp5146_init = { + .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq), + .init_reg_seq = tvp5146_init_reg_seq, +}; +/* + * TVP5147 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp5147_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp5147_init = { + .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq), + .init_reg_seq = tvp5147_init_reg_seq, +}; +/* + * TVP5146M2/TVP5147M1 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp514xm_init = { + .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq), + .init_reg_seq = tvp514xm_init_reg_seq, +}; +/* + * I2C Device Table - + * + * name - Name of the actual device/chip. + * driver_data - Driver data + */ +static const struct i2c_device_id tvp514x_id[] = { + {"tvp5146", (unsigned long)&tvp5146_init}, + {"tvp5146m2", (unsigned long)&tvp514xm_init}, + {"tvp5147", (unsigned long)&tvp5147_init}, + {"tvp5147m1", (unsigned long)&tvp514xm_init}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tvp514x_id); + +static struct i2c_driver tvp514x_i2c_driver = { + .driver = { + .name = TVP514X_MODULE_NAME, + .owner = THIS_MODULE, + }, + .probe = tvp514x_probe, + .remove = __exit_p(tvp514x_remove), + .id_table = tvp514x_id, +}; + +/** + * tvp514x_init + * + * Module init function + */ +static int __init tvp514x_init(void) +{ + return i2c_add_driver(&tvp514x_i2c_driver); +} + +/** + * tvp514x_cleanup + * + * Module exit function + */ +static void __exit tvp514x_cleanup(void) +{ + i2c_del_driver(&tvp514x_i2c_driver); +} + +module_init(tvp514x_init); +module_exit(tvp514x_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TVP514X linux decoder driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tvp514x_regs.h b/drivers/media/video/tvp514x_regs.h new file mode 100644 index 000000000000..351620aeecc2 --- /dev/null +++ b/drivers/media/video/tvp514x_regs.h @@ -0,0 +1,297 @@ +/* + * drivers/media/video/tvp514x_regs.h + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * This package 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _TVP514X_REGS_H +#define _TVP514X_REGS_H + +/* + * TVP5146/47 registers + */ +#define REG_INPUT_SEL (0x00) +#define REG_AFE_GAIN_CTRL (0x01) +#define REG_VIDEO_STD (0x02) +#define REG_OPERATION_MODE (0x03) +#define REG_AUTOSWITCH_MASK (0x04) + +#define REG_COLOR_KILLER (0x05) +#define REG_LUMA_CONTROL1 (0x06) +#define REG_LUMA_CONTROL2 (0x07) +#define REG_LUMA_CONTROL3 (0x08) + +#define REG_BRIGHTNESS (0x09) +#define REG_CONTRAST (0x0A) +#define REG_SATURATION (0x0B) +#define REG_HUE (0x0C) + +#define REG_CHROMA_CONTROL1 (0x0D) +#define REG_CHROMA_CONTROL2 (0x0E) + +/* 0x0F Reserved */ + +#define REG_COMP_PR_SATURATION (0x10) +#define REG_COMP_Y_CONTRAST (0x11) +#define REG_COMP_PB_SATURATION (0x12) + +/* 0x13 Reserved */ + +#define REG_COMP_Y_BRIGHTNESS (0x14) + +/* 0x15 Reserved */ + +#define REG_AVID_START_PIXEL_LSB (0x16) +#define REG_AVID_START_PIXEL_MSB (0x17) +#define REG_AVID_STOP_PIXEL_LSB (0x18) +#define REG_AVID_STOP_PIXEL_MSB (0x19) + +#define REG_HSYNC_START_PIXEL_LSB (0x1A) +#define REG_HSYNC_START_PIXEL_MSB (0x1B) +#define REG_HSYNC_STOP_PIXEL_LSB (0x1C) +#define REG_HSYNC_STOP_PIXEL_MSB (0x1D) + +#define REG_VSYNC_START_LINE_LSB (0x1E) +#define REG_VSYNC_START_LINE_MSB (0x1F) +#define REG_VSYNC_STOP_LINE_LSB (0x20) +#define REG_VSYNC_STOP_LINE_MSB (0x21) + +#define REG_VBLK_START_LINE_LSB (0x22) +#define REG_VBLK_START_LINE_MSB (0x23) +#define REG_VBLK_STOP_LINE_LSB (0x24) +#define REG_VBLK_STOP_LINE_MSB (0x25) + +/* 0x26 - 0x27 Reserved */ + +#define REG_FAST_SWTICH_CONTROL (0x28) + +/* 0x29 Reserved */ + +#define REG_FAST_SWTICH_SCART_DELAY (0x2A) + +/* 0x2B Reserved */ + +#define REG_SCART_DELAY (0x2C) +#define REG_CTI_DELAY (0x2D) +#define REG_CTI_CONTROL (0x2E) + +/* 0x2F - 0x31 Reserved */ + +#define REG_SYNC_CONTROL (0x32) +#define REG_OUTPUT_FORMATTER1 (0x33) +#define REG_OUTPUT_FORMATTER2 (0x34) +#define REG_OUTPUT_FORMATTER3 (0x35) +#define REG_OUTPUT_FORMATTER4 (0x36) +#define REG_OUTPUT_FORMATTER5 (0x37) +#define REG_OUTPUT_FORMATTER6 (0x38) +#define REG_CLEAR_LOST_LOCK (0x39) + +#define REG_STATUS1 (0x3A) +#define REG_STATUS2 (0x3B) + +#define REG_AGC_GAIN_STATUS_LSB (0x3C) +#define REG_AGC_GAIN_STATUS_MSB (0x3D) + +/* 0x3E Reserved */ + +#define REG_VIDEO_STD_STATUS (0x3F) +#define REG_GPIO_INPUT1 (0x40) +#define REG_GPIO_INPUT2 (0x41) + +/* 0x42 - 0x45 Reserved */ + +#define REG_AFE_COARSE_GAIN_CH1 (0x46) +#define REG_AFE_COARSE_GAIN_CH2 (0x47) +#define REG_AFE_COARSE_GAIN_CH3 (0x48) +#define REG_AFE_COARSE_GAIN_CH4 (0x49) + +#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A) +#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B) +#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C) +#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D) +#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E) +#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F) +#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50) +#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51) + +/* 0x52 - 0x68 Reserved */ + +#define REG_FBIT_VBIT_CONTROL1 (0x69) + +/* 0x6A - 0x6B Reserved */ + +#define REG_BACKEND_AGC_CONTROL (0x6C) + +/* 0x6D - 0x6E Reserved */ + +#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F) +#define REG_ROM_VERSION (0x70) + +/* 0x71 - 0x73 Reserved */ + +#define REG_AGC_WHITE_PEAK_PROCESSING (0x74) +#define REG_FBIT_VBIT_CONTROL2 (0x75) +#define REG_VCR_TRICK_MODE_CONTROL (0x76) +#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77) +#define REG_AGC_INCREMENT_SPEED (0x78) +#define REG_AGC_INCREMENT_DELAY (0x79) + +/* 0x7A - 0x7F Reserved */ + +#define REG_CHIP_ID_MSB (0x80) +#define REG_CHIP_ID_LSB (0x81) + +/* 0x82 Reserved */ + +#define REG_CPLL_SPEED_CONTROL (0x83) + +/* 0x84 - 0x96 Reserved */ + +#define REG_STATUS_REQUEST (0x97) + +/* 0x98 - 0x99 Reserved */ + +#define REG_VERTICAL_LINE_COUNT_LSB (0x9A) +#define REG_VERTICAL_LINE_COUNT_MSB (0x9B) + +/* 0x9C - 0x9D Reserved */ + +#define REG_AGC_DECREMENT_DELAY (0x9E) + +/* 0x9F - 0xB0 Reserved */ + +#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1) +#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2) +#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3) +#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4) +#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5) +#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6) +#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7) +#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8) +#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9) +#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA) +#define REG_VDP_TTX_FILTER_CONTROL (0xBB) +#define REG_VDP_FIFO_WORD_COUNT (0xBC) +#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD) + +/* 0xBE Reserved */ + +#define REG_VDP_FIFO_RESET (0xBF) +#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0) +#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1) +#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2) +#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3) + +/* 0xC4 - 0xD5 Reserved */ + +#define REG_VDP_LINE_START (0xD6) +#define REG_VDP_LINE_STOP (0xD7) +#define REG_VDP_GLOBAL_LINE_MODE (0xD8) +#define REG_VDP_FULL_FIELD_ENABLE (0xD9) +#define REG_VDP_FULL_FIELD_MODE (0xDA) + +/* 0xDB - 0xDF Reserved */ + +#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0) +#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1) +#define REG_FIFO_READ_DATA (0xE2) + +/* 0xE3 - 0xE7 Reserved */ + +#define REG_VBUS_ADDRESS_ACCESS1 (0xE8) +#define REG_VBUS_ADDRESS_ACCESS2 (0xE9) +#define REG_VBUS_ADDRESS_ACCESS3 (0xEA) + +/* 0xEB - 0xEF Reserved */ + +#define REG_INTERRUPT_RAW_STATUS0 (0xF0) +#define REG_INTERRUPT_RAW_STATUS1 (0xF1) +#define REG_INTERRUPT_STATUS0 (0xF2) +#define REG_INTERRUPT_STATUS1 (0xF3) +#define REG_INTERRUPT_MASK0 (0xF4) +#define REG_INTERRUPT_MASK1 (0xF5) +#define REG_INTERRUPT_CLEAR0 (0xF6) +#define REG_INTERRUPT_CLEAR1 (0xF7) + +/* 0xF8 - 0xFF Reserved */ + +/* + * Mask and bit definitions of TVP5146/47 registers + */ +/* The ID values we are looking for */ +#define TVP514X_CHIP_ID_MSB (0x51) +#define TVP5146_CHIP_ID_LSB (0x46) +#define TVP5147_CHIP_ID_LSB (0x47) + +#define VIDEO_STD_MASK (0x07) +#define VIDEO_STD_AUTO_SWITCH_BIT (0x00) +#define VIDEO_STD_NTSC_MJ_BIT (0x01) +#define VIDEO_STD_PAL_BDGHIN_BIT (0x02) +#define VIDEO_STD_PAL_M_BIT (0x03) +#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04) +#define VIDEO_STD_NTSC_4_43_BIT (0x05) +#define VIDEO_STD_SECAM_BIT (0x06) +#define VIDEO_STD_PAL_60_BIT (0x07) + +/* + * Status bit + */ +#define STATUS_TV_VCR_BIT (1<<0) +#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1) +#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2) +#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3) +#define STATUS_LOST_LOCK_DETECT_BIT (1<<4) +#define STATUS_FEILD_RATE_BIT (1<<5) +#define STATUS_LINE_ALTERNATING_BIT (1<<6) +#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7) + +/* Tokens for register write */ +#define TOK_WRITE (0) /* token for write operation */ +#define TOK_TERM (1) /* terminating token */ +#define TOK_DELAY (2) /* delay token for reg list */ +#define TOK_SKIP (3) /* token to skip a register */ +/** + * struct tvp514x_reg - Structure for TVP5146/47 register initialization values + * @token - Token: TOK_WRITE, TOK_TERM etc.. + * @reg - Register offset + * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY + */ +struct tvp514x_reg { + u8 token; + u8 reg; + u32 val; +}; + +/** + * struct tvp514x_init_seq - Structure for TVP5146/47/46M2/47M1 power up + * Sequence. + * @ no_regs - Number of registers to write for power up sequence. + * @ init_reg_seq - Array of registers and respective value to write. + */ +struct tvp514x_init_seq { + unsigned int no_regs; + const struct tvp514x_reg *init_reg_seq; +}; +#endif /* ifndef _TVP514X_REGS_H */ diff --git a/include/media/tvp514x.h b/include/media/tvp514x.h new file mode 100644 index 000000000000..5e7ee968c6dc --- /dev/null +++ b/include/media/tvp514x.h @@ -0,0 +1,118 @@ +/* + * drivers/media/video/tvp514x.h + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * This package 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _TVP514X_H +#define _TVP514X_H + +/* + * Other macros + */ +#define TVP514X_MODULE_NAME "tvp514x" + +#define TVP514X_XCLK_BT656 (27000000) + +/* Number of pixels and number of lines per frame for different standards */ +#define NTSC_NUM_ACTIVE_PIXELS (720) +#define NTSC_NUM_ACTIVE_LINES (480) +#define PAL_NUM_ACTIVE_PIXELS (720) +#define PAL_NUM_ACTIVE_LINES (576) + +/** + * enum tvp514x_input - enum for different decoder input pin + * configuration. + */ +enum tvp514x_input { + /* + * CVBS input selection + */ + INPUT_CVBS_VI1A = 0x0, + INPUT_CVBS_VI1B, + INPUT_CVBS_VI1C, + INPUT_CVBS_VI2A = 0x04, + INPUT_CVBS_VI2B, + INPUT_CVBS_VI2C, + INPUT_CVBS_VI3A = 0x08, + INPUT_CVBS_VI3B, + INPUT_CVBS_VI3C, + INPUT_CVBS_VI4A = 0x0C, + /* + * S-Video input selection + */ + INPUT_SVIDEO_VI2A_VI1A = 0x44, + INPUT_SVIDEO_VI2B_VI1B, + INPUT_SVIDEO_VI2C_VI1C, + INPUT_SVIDEO_VI2A_VI3A = 0x54, + INPUT_SVIDEO_VI2B_VI3B, + INPUT_SVIDEO_VI2C_VI3C, + INPUT_SVIDEO_VI4A_VI1A = 0x4C, + INPUT_SVIDEO_VI4A_VI1B, + INPUT_SVIDEO_VI4A_VI1C, + INPUT_SVIDEO_VI4A_VI3A = 0x5C, + INPUT_SVIDEO_VI4A_VI3B, + INPUT_SVIDEO_VI4A_VI3C, + + /* Need to add entries for + * RGB, YPbPr and SCART. + */ + INPUT_INVALID +}; + +/** + * enum tvp514x_output - enum for output format + * supported. + * + */ +enum tvp514x_output { + OUTPUT_10BIT_422_EMBEDDED_SYNC = 0, + OUTPUT_20BIT_422_SEPERATE_SYNC, + OUTPUT_10BIT_422_SEPERATE_SYNC = 3, + OUTPUT_INVALID +}; + +/** + * struct tvp514x_platform_data - Platform data values and access functions. + * @power_set: Power state access function, zero is off, non-zero is on. + * @ifparm: Interface parameters access function. + * @priv_data_set: Device private data (pointer) access function. + * @clk_polarity: Clock polarity of the current interface. + * @ hs_polarity: HSYNC Polarity configuration for current interface. + * @ vs_polarity: VSYNC Polarity configuration for current interface. + */ +struct tvp514x_platform_data { + char *master; + int (*power_set) (enum v4l2_power on); + int (*ifparm) (struct v4l2_ifparm *p); + int (*priv_data_set) (void *); + /* Interface control params */ + bool clk_polarity; + bool hs_polarity; + bool vs_polarity; +}; + + +#endif /* ifndef _TVP514X_H */ -- cgit v1.2.3-55-g7522 From 2a1fcdf08230522bd5024f91da24aaa6e8d81f59 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 21:36:58 -0300 Subject: V4L/DVB (9820): v4l2: add v4l2_device and v4l2_subdev structs to the v4l2 framework. Start implementing a proper v4l2 framework as discussed during the Linux Plumbers Conference 2008. Introduces v4l2_device (for device instances) and v4l2_subdev (representing sub-device instances). Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Reviewed-by: Guennadi Liakhovetski Reviewed-by: Andy Walls Reviewed-by: David Brownell Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 362 +++++++++++++++++++++++++++ drivers/media/video/Makefile | 2 +- drivers/media/video/v4l2-device.c | 86 +++++++ drivers/media/video/v4l2-subdev.c | 108 ++++++++ include/media/v4l2-device.h | 109 ++++++++ include/media/v4l2-subdev.h | 188 ++++++++++++++ 6 files changed, 854 insertions(+), 1 deletion(-) create mode 100644 Documentation/video4linux/v4l2-framework.txt create mode 100644 drivers/media/video/v4l2-device.c create mode 100644 drivers/media/video/v4l2-subdev.c create mode 100644 include/media/v4l2-device.h create mode 100644 include/media/v4l2-subdev.h (limited to 'include') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt new file mode 100644 index 000000000000..60eaf54e7eff --- /dev/null +++ b/Documentation/video4linux/v4l2-framework.txt @@ -0,0 +1,362 @@ +Overview of the V4L2 driver framework +===================================== + +This text documents the various structures provided by the V4L2 framework and +their relationships. + + +Introduction +------------ + +The V4L2 drivers tend to be very complex due to the complexity of the +hardware: most devices have multiple ICs, export multiple device nodes in +/dev, and create also non-V4L2 devices such as DVB, ALSA, FB, I2C and input +(IR) devices. + +Especially the fact that V4L2 drivers have to setup supporting ICs to +do audio/video muxing/encoding/decoding makes it more complex than most. +Usually these ICs are connected to the main bridge driver through one or +more I2C busses, but other busses can also be used. Such devices are +called 'sub-devices'. + +For a long time the framework was limited to the video_device struct for +creating V4L device nodes and video_buf for handling the video buffers +(note that this document does not discuss the video_buf framework). + +This meant that all drivers had to do the setup of device instances and +connecting to sub-devices themselves. Some of this is quite complicated +to do right and many drivers never did do it correctly. + +There is also a lot of common code that could never be refactored due to +the lack of a framework. + +So this framework sets up the basic building blocks that all drivers +need and this same framework should make it much easier to refactor +common code into utility functions shared by all drivers. + + +Structure of a driver +--------------------- + +All drivers have the following structure: + +1) A struct for each device instance containing the device state. + +2) A way of initializing and commanding sub-devices (if any). + +3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX, /dev/radioX and + /dev/vtxX) and keeping track of device-node specific data. + +4) Filehandle-specific structs containing per-filehandle data. + +This is a rough schematic of how it all relates: + + device instances + | + +-sub-device instances + | + \-V4L2 device nodes + | + \-filehandle instances + + +Structure of the framework +-------------------------- + +The framework closely resembles the driver structure: it has a v4l2_device +struct for the device instance data, a v4l2_subdev struct to refer to +sub-device instances, the video_device struct stores V4L2 device node data +and in the future a v4l2_fh struct will keep track of filehandle instances +(this is not yet implemented). + + +struct v4l2_device +------------------ + +Each device instance is represented by a struct v4l2_device (v4l2-device.h). +Very simple devices can just allocate this struct, but most of the time you +would embed this struct inside a larger struct. + +You must register the device instance: + + v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); + +Registration will initialize the v4l2_device struct and link dev->driver_data +to v4l2_dev. Registration will also set v4l2_dev->name to a value derived from +dev (driver name followed by the bus_id, to be precise). You may change the +name after registration if you want. + +You unregister with: + + v4l2_device_unregister(struct v4l2_device *v4l2_dev); + +Unregistering will also automatically unregister all subdevs from the device. + +Sometimes you need to iterate over all devices registered by a specific +driver. This is usually the case if multiple device drivers use the same +hardware. E.g. the ivtvfb driver is a framebuffer driver that uses the ivtv +hardware. The same is true for alsa drivers for example. + +You can iterate over all registered devices as follows: + +static int callback(struct device *dev, void *p) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + + /* test if this device was inited */ + if (v4l2_dev == NULL) + return 0; + ... + return 0; +} + +int iterate(void *p) +{ + struct device_driver *drv; + int err; + + /* Find driver 'ivtv' on the PCI bus. + pci_bus_type is a global. For USB busses use usb_bus_type. */ + drv = driver_find("ivtv", &pci_bus_type); + /* iterate over all ivtv device instances */ + err = driver_for_each_device(drv, NULL, p, callback); + put_driver(drv); + return err; +} + +Sometimes you need to keep a running counter of the device instance. This is +commonly used to map a device instance to an index of a module option array. + +The recommended approach is as follows: + +static atomic_t drv_instance = ATOMIC_INIT(0); + +static int __devinit drv_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + ... + state->instance = atomic_inc_return(&drv_instance) - 1; +} + + +struct v4l2_subdev +------------------ + +Many drivers need to communicate with sub-devices. These devices can do all +sort of tasks, but most commonly they handle audio and/or video muxing, +encoding or decoding. For webcams common sub-devices are sensors and camera +controllers. + +Usually these are I2C devices, but not necessarily. In order to provide the +driver with a consistent interface to these sub-devices the v4l2_subdev struct +(v4l2-subdev.h) was created. + +Each sub-device driver must have a v4l2_subdev struct. This struct can be +stand-alone for simple sub-devices or it might be embedded in a larger struct +if more state information needs to be stored. Usually there is a low-level +device struct (e.g. i2c_client) that contains the device data as setup +by the kernel. It is recommended to store that pointer in the private +data of v4l2_subdev using v4l2_set_subdevdata(). That makes it easy to go +from a v4l2_subdev to the actual low-level bus-specific device data. + +You also need a way to go from the low-level struct to v4l2_subdev. For the +common i2c_client struct the i2c_set_clientdata() call is used to store a +v4l2_subdev pointer, for other busses you may have to use other methods. + +From the bridge driver perspective you load the sub-device module and somehow +obtain the v4l2_subdev pointer. For i2c devices this is easy: you call +i2c_get_clientdata(). For other busses something similar needs to be done. +Helper functions exists for sub-devices on an I2C bus that do most of this +tricky work for you. + +Each v4l2_subdev contains function pointers that sub-device drivers can +implement (or leave NULL if it is not applicable). Since sub-devices can do +so many different things and you do not want to end up with a huge ops struct +of which only a handful of ops are commonly implemented, the function pointers +are sorted according to category and each category has its own ops struct. + +The top-level ops struct contains pointers to the category ops structs, which +may be NULL if the subdev driver does not support anything from that category. + +It looks like this: + +struct v4l2_subdev_core_ops { + int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip); + int (*log_status)(struct v4l2_subdev *sd); + int (*init)(struct v4l2_subdev *sd, u32 val); + ... +}; + +struct v4l2_subdev_tuner_ops { + ... +}; + +struct v4l2_subdev_audio_ops { + ... +}; + +struct v4l2_subdev_video_ops { + ... +}; + +struct v4l2_subdev_ops { + const struct v4l2_subdev_core_ops *core; + const struct v4l2_subdev_tuner_ops *tuner; + const struct v4l2_subdev_audio_ops *audio; + const struct v4l2_subdev_video_ops *video; +}; + +The core ops are common to all subdevs, the other categories are implemented +depending on the sub-device. E.g. a video device is unlikely to support the +audio ops and vice versa. + +This setup limits the number of function pointers while still making it easy +to add new ops and categories. + +A sub-device driver initializes the v4l2_subdev struct using: + + v4l2_subdev_init(subdev, &ops); + +Afterwards you need to initialize subdev->name with a unique name and set the +module owner. This is done for you if you use the i2c helper functions. + +A device (bridge) driver needs to register the v4l2_subdev with the +v4l2_device: + + int err = v4l2_device_register_subdev(device, subdev); + +This can fail if the subdev module disappeared before it could be registered. +After this function was called successfully the subdev->dev field points to +the v4l2_device. + +You can unregister a sub-device using: + + v4l2_device_unregister_subdev(subdev); + +Afterwards the subdev module can be unloaded and subdev->dev == NULL. + +You can call an ops function either directly: + + err = subdev->ops->core->g_chip_ident(subdev, &chip); + +but it is better and easier to use this macro: + + err = v4l2_subdev_call(subdev, core, g_chip_ident, &chip); + +The macro will to the right NULL pointer checks and returns -ENODEV if subdev +is NULL, -ENOIOCTLCMD if either subdev->core or subdev->core->g_chip_ident is +NULL, or the actual result of the subdev->ops->core->g_chip_ident ops. + +It is also possible to call all or a subset of the sub-devices: + + v4l2_device_call_all(dev, 0, core, g_chip_ident, &chip); + +Any subdev that does not support this ops is skipped and error results are +ignored. If you want to check for errors use this: + + err = v4l2_device_call_until_err(dev, 0, core, g_chip_ident, &chip); + +Any error except -ENOIOCTLCMD will exit the loop with that error. If no +errors (except -ENOIOCTLCMD) occured, then 0 is returned. + +The second argument to both calls is a group ID. If 0, then all subdevs are +called. If non-zero, then only those whose group ID match that value will +be called. Before a bridge driver registers a subdev it can set subdev->grp_id +to whatever value it wants (it's 0 by default). This value is owned by the +bridge driver and the sub-device driver will never modify or use it. + +The group ID gives the bridge driver more control how callbacks are called. +For example, there may be multiple audio chips on a board, each capable of +changing the volume. But usually only one will actually be used when the +user want to change the volume. You can set the group ID for that subdev to +e.g. AUDIO_CONTROLLER and specify that as the group ID value when calling +v4l2_device_call_all(). That ensures that it will only go to the subdev +that needs it. + +The advantage of using v4l2_subdev is that it is a generic struct and does +not contain any knowledge about the underlying hardware. So a driver might +contain several subdevs that use an I2C bus, but also a subdev that is +controlled through GPIO pins. This distinction is only relevant when setting +up the device, but once the subdev is registered it is completely transparent. + + +I2C sub-device drivers +---------------------- + +Since these drivers are so common, special helper functions are available to +ease the use of these drivers (v4l2-common.h). + +The recommended method of adding v4l2_subdev support to an I2C driver is to +embed the v4l2_subdev struct into the state struct that is created for each +I2C device instance. Very simple devices have no state struct and in that case +you can just create a v4l2_subdev directly. + +A typical state struct would look like this (where 'chipname' is replaced by +the name of the chip): + +struct chipname_state { + struct v4l2_subdev sd; + ... /* additional state fields */ +}; + +Initialize the v4l2_subdev struct as follows: + + v4l2_i2c_subdev_init(&state->sd, client, subdev_ops); + +This function will fill in all the fields of v4l2_subdev and ensure that the +v4l2_subdev and i2c_client both point to one another. + +You should also add a helper inline function to go from a v4l2_subdev pointer +to a chipname_state struct: + +static inline struct chipname_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct chipname_state, sd); +} + +Use this to go from the v4l2_subdev struct to the i2c_client struct: + + struct i2c_client *client = v4l2_get_subdevdata(sd); + +And this to go from an i2c_client to a v4l2_subdev struct: + + struct v4l2_subdev *sd = i2c_get_clientdata(client); + +Finally you need to make a command function to make driver->command() +call the right subdev_ops functions: + +static int subdev_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +If driver->command is never used then you can leave this out. Eventually the +driver->command usage should be removed from v4l. + +Make sure to call v4l2_device_unregister_subdev(sd) when the remove() callback +is called. This will unregister the sub-device from the bridge driver. It is +safe to call this even if the sub-device was never registered. + + +The bridge driver also has some helper functions it can use: + +struct v4l2_subdev *sd = v4l2_i2c_new_subdev(adapter, "module_foo", "chipid", 0x36); + +This loads the given module (can be NULL if no module needs to be loaded) and +calls i2c_new_device() with the given i2c_adapter and chip/address arguments. +If all goes well, then it registers the subdev with the v4l2_device. It gets +the v4l2_device by calling i2c_get_adapdata(adapter), so you should make sure +that adapdata is set to v4l2_device when you setup the i2c_adapter in your +driver. + +You can also use v4l2_i2c_new_probed_subdev() which is very similar to +v4l2_i2c_new_subdev(), except that it has an array of possible I2C addresses +that it should probe. Internally it calls i2c_new_probed_device(). + +Both functions return NULL if something went wrong. + + +struct video_device +------------------- + +Not yet documented. diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 492ab3dce71b..84a2be0cbbe7 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -10,7 +10,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o -videodev-objs := v4l2-dev.o v4l2-ioctl.o +videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-subdev.o obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-compat-ioctl32.o v4l2-int-device.o diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c new file mode 100644 index 000000000000..9eefde031597 --- /dev/null +++ b/drivers/media/video/v4l2-device.c @@ -0,0 +1,86 @@ +/* + V4L2 device support. + + Copyright (C) 2008 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) +{ + if (dev == NULL || v4l2_dev == NULL) + return -EINVAL; + /* Warn if we apparently re-register a device */ + WARN_ON(dev_get_drvdata(dev)); + INIT_LIST_HEAD(&v4l2_dev->subdevs); + spin_lock_init(&v4l2_dev->lock); + v4l2_dev->dev = dev; + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", + dev->driver->name, dev->bus_id); + dev_set_drvdata(dev, v4l2_dev); + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register); + +void v4l2_device_unregister(struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd, *next; + + if (v4l2_dev == NULL || v4l2_dev->dev == NULL) + return; + dev_set_drvdata(v4l2_dev->dev, NULL); + /* unregister subdevs */ + list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) + v4l2_device_unregister_subdev(sd); + + v4l2_dev->dev = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_device_unregister); + +int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd) +{ + /* Check for valid input */ + if (dev == NULL || sd == NULL || !sd->name[0]) + return -EINVAL; + /* Warn if we apparently re-register a subdev */ + WARN_ON(sd->dev); + if (!try_module_get(sd->owner)) + return -ENODEV; + sd->dev = dev; + spin_lock(&dev->lock); + list_add_tail(&sd->list, &dev->subdevs); + spin_unlock(&dev->lock); + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); + +void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) +{ + /* return if it isn't registered */ + if (sd == NULL || sd->dev == NULL) + return; + spin_lock(&sd->dev->lock); + list_del(&sd->list); + spin_unlock(&sd->dev->lock); + sd->dev = NULL; + module_put(sd->owner); +} +EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c new file mode 100644 index 000000000000..fe1f01c970ac --- /dev/null +++ b/drivers/media/video/v4l2-subdev.c @@ -0,0 +1,108 @@ +/* + V4L2 sub-device support. + + Copyright (C) 2008 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) +{ + switch (cmd) { + case VIDIOC_QUERYCTRL: + return v4l2_subdev_call(sd, core, querymenu, arg); + case VIDIOC_G_CTRL: + return v4l2_subdev_call(sd, core, g_ctrl, arg); + case VIDIOC_S_CTRL: + return v4l2_subdev_call(sd, core, s_ctrl, arg); + case VIDIOC_QUERYMENU: + return v4l2_subdev_call(sd, core, queryctrl, arg); + case VIDIOC_LOG_STATUS: + return v4l2_subdev_call(sd, core, log_status); + case VIDIOC_G_CHIP_IDENT: + return v4l2_subdev_call(sd, core, g_chip_ident, arg); + case VIDIOC_INT_S_STANDBY: + return v4l2_subdev_call(sd, core, s_standby, *(u32 *)arg); + case VIDIOC_INT_RESET: + return v4l2_subdev_call(sd, core, reset, *(u32 *)arg); + case VIDIOC_INT_S_GPIO: + return v4l2_subdev_call(sd, core, s_gpio, *(u32 *)arg); + case VIDIOC_INT_INIT: + return v4l2_subdev_call(sd, core, init, *(u32 *)arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + return v4l2_subdev_call(sd, core, g_register, arg); + case VIDIOC_DBG_S_REGISTER: + return v4l2_subdev_call(sd, core, s_register, arg); +#endif + + case VIDIOC_INT_S_TUNER_MODE: + return v4l2_subdev_call(sd, tuner, s_mode, *(enum v4l2_tuner_type *)arg); + case AUDC_SET_RADIO: + return v4l2_subdev_call(sd, tuner, s_radio); + case VIDIOC_S_TUNER: + return v4l2_subdev_call(sd, tuner, s_tuner, arg); + case VIDIOC_G_TUNER: + return v4l2_subdev_call(sd, tuner, g_tuner, arg); + case VIDIOC_S_STD: + return v4l2_subdev_call(sd, tuner, s_std, *(v4l2_std_id *)arg); + case VIDIOC_S_FREQUENCY: + return v4l2_subdev_call(sd, tuner, s_frequency, arg); + case VIDIOC_G_FREQUENCY: + return v4l2_subdev_call(sd, tuner, g_frequency, arg); + case TUNER_SET_TYPE_ADDR: + return v4l2_subdev_call(sd, tuner, s_type_addr, arg); + case TUNER_SET_CONFIG: + return v4l2_subdev_call(sd, tuner, s_config, arg); + + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + return v4l2_subdev_call(sd, audio, s_clock_freq, *(u32 *)arg); + case VIDIOC_INT_S_AUDIO_ROUTING: + return v4l2_subdev_call(sd, audio, s_routing, arg); + case VIDIOC_INT_I2S_CLOCK_FREQ: + return v4l2_subdev_call(sd, audio, s_i2s_clock_freq, *(u32 *)arg); + + case VIDIOC_INT_S_VIDEO_ROUTING: + return v4l2_subdev_call(sd, video, s_routing, arg); + case VIDIOC_INT_S_CRYSTAL_FREQ: + return v4l2_subdev_call(sd, video, s_crystal_freq, arg); + case VIDIOC_INT_DECODE_VBI_LINE: + return v4l2_subdev_call(sd, video, decode_vbi_line, arg); + case VIDIOC_INT_S_VBI_DATA: + return v4l2_subdev_call(sd, video, s_vbi_data, arg); + case VIDIOC_INT_G_VBI_DATA: + return v4l2_subdev_call(sd, video, g_vbi_data, arg); + case VIDIOC_S_FMT: + return v4l2_subdev_call(sd, video, s_fmt, arg); + case VIDIOC_G_FMT: + return v4l2_subdev_call(sd, video, g_fmt, arg); + case VIDIOC_INT_S_STD_OUTPUT: + return v4l2_subdev_call(sd, video, s_std_output, *(v4l2_std_id *)arg); + case VIDIOC_STREAMON: + return v4l2_subdev_call(sd, video, s_stream, 1); + case VIDIOC_STREAMOFF: + return v4l2_subdev_call(sd, video, s_stream, 0); + + default: + return v4l2_subdev_call(sd, core, ioctl, cmd, arg); + } +} +EXPORT_SYMBOL_GPL(v4l2_subdev_command); diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h new file mode 100644 index 000000000000..97b283a04289 --- /dev/null +++ b/include/media/v4l2-device.h @@ -0,0 +1,109 @@ +/* + V4L2 device support header. + + Copyright (C) 2008 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _V4L2_DEVICE_H +#define _V4L2_DEVICE_H + +#include + +/* Each instance of a V4L2 device should create the v4l2_device struct, + either stand-alone or embedded in a larger struct. + + It allows easy access to sub-devices (see v4l2-subdev.h) and provides + basic V4L2 device-level support. + */ + +#define V4L2_DEVICE_NAME_SIZE (BUS_ID_SIZE + 16) + +struct v4l2_device { + /* dev->driver_data points to this struct */ + struct device *dev; + /* used to keep track of the registered subdevs */ + struct list_head subdevs; + /* lock this struct; can be used by the driver as well if this + struct is embedded into a larger struct. */ + spinlock_t lock; + /* unique device name, by default the driver name + bus ID */ + char name[V4L2_DEVICE_NAME_SIZE]; +}; + +/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev */ +int __must_check v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); +/* Set v4l2_dev->dev->driver_data to NULL and unregister all sub-devices */ +void v4l2_device_unregister(struct v4l2_device *v4l2_dev); + +/* Register a subdev with a v4l2 device. While registered the subdev module + is marked as in-use. An error is returned if the module is no longer + loaded when you attempt to register it. */ +int __must_check v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd); +/* Unregister a subdev with a v4l2 device. Can also be called if the subdev + wasn't registered. In that case it will do nothing. */ +void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); + +/* Iterate over all subdevs. */ +#define v4l2_device_for_each_subdev(sd, dev) \ + list_for_each_entry(sd, &(dev)->subdevs, list) + +/* Call the specified callback for all subdevs matching the condition. + Ignore any errors. Note that you cannot add or delete a subdev + while walking the subdevs list. */ +#define __v4l2_device_call_subdevs(dev, cond, o, f, args...) \ + do { \ + struct v4l2_subdev *sd; \ + \ + list_for_each_entry(sd, &(dev)->subdevs, list) \ + if ((cond) && sd->ops->o && sd->ops->o->f) \ + sd->ops->o->f(sd , ##args); \ + } while (0) + +/* Call the specified callback for all subdevs matching the condition. + If the callback returns an error other than 0 or -ENOIOCTLCMD, then + return with that error code. Note that you cannot add or delete a + subdev while walking the subdevs list. */ +#define __v4l2_device_call_subdevs_until_err(dev, cond, o, f, args...) \ +({ \ + struct v4l2_subdev *sd; \ + int err = 0; \ + \ + list_for_each_entry(sd, &(dev)->subdevs, list) { \ + if ((cond) && sd->ops->o && sd->ops->o->f) \ + err = sd->ops->o->f(sd , ##args); \ + if (err && err != -ENOIOCTLCMD) \ + break; \ + } \ + (err == -ENOIOCTLCMD) ? 0 : err; \ +}) + +/* Call the specified callback for all subdevs matching grp_id (if 0, then + match them all). Ignore any errors. Note that you cannot add or delete + a subdev while walking the subdevs list. */ +#define v4l2_device_call_all(dev, grp_id, o, f, args...) \ + __v4l2_device_call_subdevs(dev, \ + !(grp_id) || sd->grp_id == (grp_id), o, f , ##args) + +/* Call the specified callback for all subdevs matching grp_id (if 0, then + match them all). If the callback returns an error other than 0 or + -ENOIOCTLCMD, then return with that error code. Note that you cannot + add or delete a subdev while walking the subdevs list. */ +#define v4l2_device_call_until_err(dev, grp_id, o, f, args...) \ + __v4l2_device_call_subdevs_until_err(dev, \ + !(grp_id) || sd->grp_id == (grp_id), o, f , ##args) + +#endif diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h new file mode 100644 index 000000000000..bc9e0fbf2822 --- /dev/null +++ b/include/media/v4l2-subdev.h @@ -0,0 +1,188 @@ +/* + V4L2 sub-device support header. + + Copyright (C) 2008 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _V4L2_SUBDEV_H +#define _V4L2_SUBDEV_H + +#include + +struct v4l2_device; +struct v4l2_subdev; +struct tuner_setup; + +/* Sub-devices are devices that are connected somehow to the main bridge + device. These devices are usually audio/video muxers/encoders/decoders or + sensors and webcam controllers. + + Usually these devices are controlled through an i2c bus, but other busses + may also be used. + + The v4l2_subdev struct provides a way of accessing these devices in a + generic manner. Most operations that these sub-devices support fall in + a few categories: core ops, audio ops, video ops and tuner ops. + + More categories can be added if needed, although this should remain a + limited set (no more than approx. 8 categories). + + Each category has its own set of ops that subdev drivers can implement. + + A subdev driver can leave the pointer to the category ops NULL if + it does not implement them (e.g. an audio subdev will generally not + implement the video category ops). The exception is the core category: + this must always be present. + + These ops are all used internally so it is no problem to change, remove + or add ops or move ops from one to another category. Currently these + ops are based on the original ioctls, but since ops are not limited to + one argument there is room for improvement here once all i2c subdev + drivers are converted to use these ops. + */ + +/* Core ops: it is highly recommended to implement at least these ops: + + g_chip_ident + log_status + g_register + s_register + + This provides basic debugging support. + + The ioctl ops is meant for generic ioctl-like commands. Depending on + the use-case it might be better to use subdev-specific ops (currently + not yet implemented) since ops provide proper type-checking. + */ +struct v4l2_subdev_core_ops { + int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip); + int (*log_status)(struct v4l2_subdev *sd); + int (*init)(struct v4l2_subdev *sd, u32 val); + int (*s_standby)(struct v4l2_subdev *sd, u32 standby); + int (*reset)(struct v4l2_subdev *sd, u32 val); + int (*s_gpio)(struct v4l2_subdev *sd, u32 val); + int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc); + int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); + int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); + int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm); + int (*ioctl)(struct v4l2_subdev *sd, int cmd, void *arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG + int (*g_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); + int (*s_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); +#endif +}; + +struct v4l2_subdev_tuner_ops { + int (*s_mode)(struct v4l2_subdev *sd, enum v4l2_tuner_type); + int (*s_radio)(struct v4l2_subdev *sd); + int (*s_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq); + int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq); + int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt); + int (*s_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt); + int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm); + int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type); + int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config); +}; + +struct v4l2_subdev_audio_ops { + int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq); + int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq); + int (*s_routing)(struct v4l2_subdev *sd, const struct v4l2_routing *route); +}; + +struct v4l2_subdev_video_ops { + int (*s_routing)(struct v4l2_subdev *sd, const struct v4l2_routing *route); + int (*s_crystal_freq)(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq); + int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line); + int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data); + int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data); + int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); + int (*s_stream)(struct v4l2_subdev *sd, int enable); + int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); + int (*g_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); +}; + +struct v4l2_subdev_ops { + const struct v4l2_subdev_core_ops *core; + const struct v4l2_subdev_tuner_ops *tuner; + const struct v4l2_subdev_audio_ops *audio; + const struct v4l2_subdev_video_ops *video; +}; + +#define V4L2_SUBDEV_NAME_SIZE 32 + +/* Each instance of a subdev driver should create this struct, either + stand-alone or embedded in a larger struct. + */ +struct v4l2_subdev { + struct list_head list; + struct module *owner; + struct v4l2_device *dev; + const struct v4l2_subdev_ops *ops; + /* name must be unique */ + char name[V4L2_SUBDEV_NAME_SIZE]; + /* can be used to group similar subdevs, value is driver-specific */ + u32 grp_id; + /* pointer to private data */ + void *priv; +}; + +static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) +{ + sd->priv = p; +} + +static inline void *v4l2_get_subdevdata(const struct v4l2_subdev *sd) +{ + return sd->priv; +} + +/* Convert an ioctl-type command to the proper v4l2_subdev_ops function call. + This is used by subdev modules that can be called by both old-style ioctl + commands and through the v4l2_subdev_ops. + + The ioctl API of the subdev driver can call this function to call the + right ops based on the ioctl cmd and arg. + + Once all subdev drivers have been converted and all drivers no longer + use the ioctl interface, then this function can be removed. + */ +int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg); + +static inline void v4l2_subdev_init(struct v4l2_subdev *sd, + const struct v4l2_subdev_ops *ops) +{ + INIT_LIST_HEAD(&sd->list); + /* ops->core MUST be set */ + BUG_ON(!ops || !ops->core); + sd->ops = ops; + sd->dev = NULL; + sd->name[0] = '\0'; + sd->grp_id = 0; + sd->priv = NULL; +} + +/* Call an ops of a v4l2_subdev, doing the right checks against + NULL pointers. + + Example: err = v4l2_subdev_call(sd, core, g_chip_ident, &chip); + */ +#define v4l2_subdev_call(sd, o, f, args...) \ + (!(sd) ? -ENODEV : (((sd) && (sd)->ops->o && (sd)->ops->o->f) ? \ + (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD)) + +#endif -- cgit v1.2.3-55-g7522 From dd99120c7165c6873a423977d1eaa41b6e2d1ffc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 23 Nov 2008 12:19:45 -0300 Subject: V4L/DVB (9821): v4l2-common: add i2c helper functions Add helper functions to load i2c sub-devices, integrating them into the v4l2-framework. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-common.c | 113 ++++++++++++++++++++++++++++++++++++++ include/media/v4l2-common.h | 41 ++++++++++++++ 2 files changed, 154 insertions(+) (limited to 'include') diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index fdc8871f631e..26f3254337da 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -58,6 +58,7 @@ #include #define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include +#include #include #include @@ -801,4 +802,116 @@ int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver return err != -ENOMEM ? 0 : err; } EXPORT_SYMBOL(v4l2_i2c_attach); + +void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, + const struct v4l2_subdev_ops *ops) +{ + v4l2_subdev_init(sd, ops); + /* the owner is the same as the i2c_client's driver owner */ + sd->owner = client->driver->driver.owner; + /* i2c_client and v4l2_subdev point to one another */ + v4l2_set_subdevdata(sd, client); + i2c_set_clientdata(client, sd); + /* initialize name */ + snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", + client->driver->driver.name, i2c_adapter_id(client->adapter), + client->addr); +} +EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); + + + +/* Load an i2c sub-device. It assumes that i2c_get_adapdata(adapter) + returns the v4l2_device and that i2c_get_clientdata(client) + returns the v4l2_subdev. */ +struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, u8 addr) +{ + struct v4l2_device *dev = i2c_get_adapdata(adapter); + struct v4l2_subdev *sd = NULL; + struct i2c_client *client; + struct i2c_board_info info; + + BUG_ON(!dev); +#ifdef MODULE + if (module_name) + request_module(module_name); +#endif + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); + strlcpy(info.type, client_type, sizeof(info.type)); + info.addr = addr; + + /* Create the i2c client */ + client = i2c_new_device(adapter, &info); + /* Note: it is possible in the future that + c->driver is NULL if the driver is still being loaded. + We need better support from the kernel so that we + can easily wait for the load to finish. */ + if (client == NULL || client->driver == NULL) + return NULL; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + return NULL; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + return sd; + +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); + +/* Probe and load an i2c sub-device. It assumes that i2c_get_adapdata(adapter) + returns the v4l2_device and that i2c_get_clientdata(client) + returns the v4l2_subdev. */ +struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + const unsigned short *addrs) +{ + struct v4l2_device *dev = i2c_get_adapdata(adapter); + struct v4l2_subdev *sd = NULL; + struct i2c_client *client = NULL; + struct i2c_board_info info; + + BUG_ON(!dev); +#ifdef MODULE + if (module_name) + request_module(module_name); +#endif + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); + strlcpy(info.type, client_type, sizeof(info.type)); + + /* Probe and create the i2c client */ + client = i2c_new_probed_device(adapter, &info, addrs); + /* Note: it is possible in the future that + c->driver is NULL if the driver is still being loaded. + We need better support from the kernel so that we + can easily wait for the load to finish. */ + if (client == NULL || client->driver == NULL) + return NULL; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + return NULL; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + return sd; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev); + #endif diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 2f8719abf5cb..f99c866d8c37 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -57,6 +57,29 @@ /* ------------------------------------------------------------------------- */ +/* These printk constructs can be used with v4l2_device and v4l2_subdev */ +#define v4l2_printk(level, dev, fmt, arg...) \ + printk(level "%s: " fmt, (dev)->name , ## arg) + +#define v4l2_err(dev, fmt, arg...) \ + v4l2_printk(KERN_ERR, dev, fmt , ## arg) + +#define v4l2_warn(dev, fmt, arg...) \ + v4l2_printk(KERN_WARNING, dev, fmt , ## arg) + +#define v4l2_info(dev, fmt, arg...) \ + v4l2_printk(KERN_INFO, dev, fmt , ## arg) + +/* These three macros assume that the debug level is set with a module + parameter called 'debug'. */ +#define v4l2_dbg(level, debug, dev, fmt, arg...) \ + do { \ + if (debug >= (level)) \ + v4l2_printk(KERN_DEBUG, dev, fmt , ## arg); \ + } while (0) + +/* ------------------------------------------------------------------------- */ + /* Priority helper functions */ struct v4l2_prio_state { @@ -104,11 +127,29 @@ struct i2c_driver; struct i2c_adapter; struct i2c_client; struct i2c_device_id; +struct v4l2_device; +struct v4l2_subdev; +struct v4l2_subdev_ops; int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver *driver, const char *name, int (*probe)(struct i2c_client *, const struct i2c_device_id *)); +/* Load an i2c module and return an initialized v4l2_subdev struct. + Only call request_module if module_name != NULL. + The client_type argument is the name of the chip that's on the adapter. */ +struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, u8 addr); +/* Probe and load an i2c module and return an initialized v4l2_subdev struct. + Only call request_module if module_name != NULL. + The client_type argument is the name of the chip that's on the adapter. */ +struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + const unsigned short *addrs); +/* Initialize an v4l2_subdev with data from an i2c_client struct */ +void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, + const struct v4l2_subdev_ops *ops); + /* ------------------------------------------------------------------------- */ /* Internal ioctls */ -- cgit v1.2.3-55-g7522 From 0877258d98154565def498833ccb208234c85084 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 14 Dec 2008 16:21:16 -0300 Subject: V4L/DVB (9897): v4l2: Add camera zoom controls The zoom controls move the zoom lens group to a an absolute position, as a relative displacement or at a given speed until reaching physical device limits. Positive values move the zoom lens group towards the telephoto direction, negative values towards the wide-angle direction. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index ec311d4616cd..e450a92a622b 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1118,6 +1118,10 @@ enum v4l2_exposure_auto_type { #define V4L2_CID_FOCUS_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+11) #define V4L2_CID_FOCUS_AUTO (V4L2_CID_CAMERA_CLASS_BASE+12) +#define V4L2_CID_ZOOM_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+13) +#define V4L2_CID_ZOOM_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+14) +#define V4L2_CID_ZOOM_CONTINUOUS (V4L2_CID_CAMERA_CLASS_BASE+15) + /* * T U N I N G */ -- cgit v1.2.3-55-g7522 From 046425f8c4ac431db00c09a6d9fba16560b8e5b9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 14 Dec 2008 16:22:05 -0300 Subject: V4L/DVB (9898): v4l2: Add privacy control The privacy control prevents video from being acquired by the camera. A true value indicates that no image can be captured. Devices that implement the privacy control must support read access and may support write access. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e450a92a622b..6e6743bd0f92 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1122,6 +1122,8 @@ enum v4l2_exposure_auto_type { #define V4L2_CID_ZOOM_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+14) #define V4L2_CID_ZOOM_CONTINUOUS (V4L2_CID_CAMERA_CLASS_BASE+15) +#define V4L2_CID_PRIVACY (V4L2_CID_CAMERA_CLASS_BASE+16) + /* * T U N I N G */ -- cgit v1.2.3-55-g7522 From 92f45badbbaccdbc1be25085292a1e258948e221 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 21 Dec 2008 10:35:25 -0300 Subject: V4L/DVB (9932): v4l2-compat32: fix 32-64 compatibility module Added all missing v4l1/2 ioctls and fix several broken conversions. Partially based on work done by Cody Pisto . Tested-by: Brandon Jenkins Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-compat-ioctl32.c | 781 ++++++++++++++++++------------ include/linux/videodev2.h | 2 + 2 files changed, 467 insertions(+), 316 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 9b50b43b76a2..5084773d4f10 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -7,12 +7,14 @@ * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be) + * Copyright (C) 2008 Hans Verkuil * * These routines maintain argument size conversion between 32bit and 64bit * ioctls. */ #include +#define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include #include #include @@ -58,7 +60,6 @@ static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user return 0; } - struct video_buffer32 { compat_caddr_t base; compat_int_t height, width, depth, bytesperline; @@ -99,7 +100,7 @@ static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 __u } struct video_clip32 { - s32 x, y, width, height; /* Its really s32 in videodev.h */ + s32 x, y, width, height; /* It's really s32 in videodev.h */ compat_caddr_t next; }; @@ -108,25 +109,72 @@ struct video_window32 { compat_caddr_t clips; compat_int_t clipcount; }; -#endif -static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static int get_video_window32(struct video_window *kp, struct video_window32 __user *up) { - int ret = -ENOIOCTLCMD; + struct video_clip __user *uclips; + struct video_clip __user *kclips; + compat_caddr_t p; + int nclips; - if (file->f_op->unlocked_ioctl) - ret = file->f_op->unlocked_ioctl(file, cmd, arg); - else if (file->f_op->ioctl) { - lock_kernel(); - ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg); - unlock_kernel(); + if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32))) + return -EFAULT; + + if (get_user(nclips, &up->clipcount)) + return -EFAULT; + + if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32)) || + get_user(kp->x, &up->x) || + get_user(kp->y, &up->y) || + get_user(kp->width, &up->width) || + get_user(kp->height, &up->height) || + get_user(kp->chromakey, &up->chromakey) || + get_user(kp->flags, &up->flags) || + get_user(kp->clipcount, &up->clipcount)) + return -EFAULT; + + nclips = kp->clipcount; + kp->clips = NULL; + + if (nclips == 0) + return 0; + if (get_user(p, &up->clips)) + return -EFAULT; + uclips = compat_ptr(p); + + /* If nclips < 0, then it is a clipping bitmap of size + VIDEO_CLIPMAP_SIZE */ + if (nclips < 0) { + if (!access_ok(VERIFY_READ, uclips, VIDEO_CLIPMAP_SIZE)) + return -EFAULT; + kp->clips = compat_alloc_user_space(VIDEO_CLIPMAP_SIZE); + if (copy_in_user(kp->clips, uclips, VIDEO_CLIPMAP_SIZE)) + return -EFAULT; + return 0; } - return ret; -} + /* Otherwise it is an array of video_clip structs. */ + if (!access_ok(VERIFY_READ, uclips, nclips * sizeof(struct video_clip))) + return -EFAULT; + kp->clips = compat_alloc_user_space(nclips * sizeof(struct video_clip)); + kclips = kp->clips; + while (nclips--) { + int err; + + err = copy_in_user(&kclips->x, &uclips->x, sizeof(kclips->x)); + err |= copy_in_user(&kclips->y, &uclips->y, sizeof(kclips->y)); + err |= copy_in_user(&kclips->width, &uclips->width, sizeof(kclips->width)); + err |= copy_in_user(&kclips->height, &uclips->height, sizeof(kclips->height)); + kclips->next = NULL; + if (err) + return -EFAULT; + kclips++; + uclips++; + } + return 0; +} -#ifdef CONFIG_VIDEO_V4L1_COMPAT /* You get back everything except the clips... */ static int put_video_window32(struct video_window *kp, struct video_window32 __user *up) { @@ -141,8 +189,55 @@ static int put_video_window32(struct video_window *kp, struct video_window32 __u return -EFAULT; return 0; } + +struct video_code32 { + char loadwhat[16]; /* name or tag of file being passed */ + compat_int_t datasize; + unsigned char *data; +}; + +static int get_microcode32(struct video_code *kp, struct video_code32 __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) || + copy_from_user(kp->loadwhat, up->loadwhat, sizeof(up->loadwhat)) || + get_user(kp->datasize, &up->datasize) || + copy_from_user(kp->data, up->data, up->datasize)) + return -EFAULT; + return 0; +} + +#define VIDIOCGTUNER32 _IOWR('v', 4, struct video_tuner32) +#define VIDIOCSTUNER32 _IOW('v', 5, struct video_tuner32) +#define VIDIOCGWIN32 _IOR('v', 9, struct video_window32) +#define VIDIOCSWIN32 _IOW('v', 10, struct video_window32) +#define VIDIOCGFBUF32 _IOR('v', 11, struct video_buffer32) +#define VIDIOCSFBUF32 _IOW('v', 12, struct video_buffer32) +#define VIDIOCGFREQ32 _IOR('v', 14, u32) +#define VIDIOCSFREQ32 _IOW('v', 15, u32) +#define VIDIOCSMICROCODE32 _IOW('v', 27, struct video_code32) + +#define VIDIOCCAPTURE32 _IOW('v', 8, s32) +#define VIDIOCSYNC32 _IOW('v', 18, s32) +#define VIDIOCSWRITEMODE32 _IOW('v', 25, s32) + #endif +static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = -ENOIOCTLCMD; + + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, cmd, arg); + else if (file->f_op->ioctl) { + lock_kernel(); + ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg); + unlock_kernel(); + } + + return ret; +} + + struct v4l2_clip32 { struct v4l2_rect c; compat_caddr_t next; @@ -229,12 +324,27 @@ static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vb return 0; } +static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up) +{ + if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format))) + return -EFAULT; + return 0; +} + +static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up) +{ + if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format))) + return -EFAULT; + return 0; +} + struct v4l2_format32 { enum v4l2_buf_type type; union { - struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ - struct v4l2_window32 win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ - struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */ + struct v4l2_pix_format pix; + struct v4l2_window32 win; + struct v4l2_vbi_format vbi; + struct v4l2_sliced_vbi_format sliced; __u8 raw_data[200]; /* user-defined */ } fmt; }; @@ -246,15 +356,27 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user return -EFAULT; switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: return get_v4l2_window32(&kp->fmt.win, &up->fmt.win); case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced); + case V4L2_BUF_TYPE_PRIVATE: + if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data))) + return -EFAULT; + return 0; + case 0: + return -EINVAL; default: - printk(KERN_INFO "compat_ioctl: unexpected VIDIOC_FMT type %d\n", + printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n", kp->type); - return -ENXIO; + return -EINVAL; } } @@ -265,31 +387,30 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user return -EFAULT; switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: return put_v4l2_window32(&kp->fmt.win, &up->fmt.win); case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced); + case V4L2_BUF_TYPE_PRIVATE: + if (copy_to_user(up, kp, sizeof(up->fmt.raw_data))) + return -EFAULT; + return 0; + case 0: + return -EINVAL; default: - return -ENXIO; + printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n", + kp->type); + return -EINVAL; } } -static inline int get_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_standard))) - return -EFAULT; - return 0; - -} - -static inline int put_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_standard))) - return -EFAULT; - return 0; -} - struct v4l2_standard32 { __u32 index; __u32 id[2]; /* __u64 would get the alignment wrong */ @@ -321,21 +442,6 @@ static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 return 0; } -static inline int get_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_tuner))) - return -EFAULT; - return 0; - -} - -static inline int put_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_tuner))) - return -EFAULT; - return 0; -} - struct v4l2_buffer32 { __u32 index; enum v4l2_buf_type type; @@ -459,149 +565,143 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame return 0; } -static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_input) - 4)) - return -EFAULT; - return 0; -} - -static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up) +struct v4l2_input32 { + __u32 index; /* Which input */ + __u8 name[32]; /* Label */ + __u32 type; /* Type of input */ + __u32 audioset; /* Associated audios (bitfield) */ + __u32 tuner; /* Associated tuner */ + v4l2_std_id std; + __u32 status; + __u32 reserved[4]; +} __attribute__ ((packed)); + +/* The 64-bit v4l2_input struct has extra padding at the end of the struct. + Otherwise it is identical to the 32-bit version. */ +static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up) { - if (copy_to_user(up, kp, sizeof(struct v4l2_input) - 4)) + if (copy_from_user(kp, up, sizeof(struct v4l2_input32))) return -EFAULT; return 0; } -static inline int get_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up) +static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up) { - if (copy_from_user(kp, up, sizeof(struct v4l2_input))) + if (copy_to_user(up, kp, sizeof(struct v4l2_input32))) return -EFAULT; return 0; } -static inline int put_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_input))) - return -EFAULT; - return 0; -} - -#ifdef CONFIG_VIDEO_V4L1_COMPAT -struct video_code32 { - char loadwhat[16]; /* name or tag of file being passed */ - compat_int_t datasize; - unsigned char *data; +struct v4l2_ext_controls32 { + __u32 ctrl_class; + __u32 count; + __u32 error_idx; + __u32 reserved[2]; + compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ }; -static inline int microcode32(struct video_code *kp, struct video_code32 __user *up) +static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - if (!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) || - copy_from_user(kp->loadwhat, up->loadwhat, sizeof(up->loadwhat)) || - get_user(kp->datasize, &up->datasize) || - copy_from_user(kp->data, up->data, up->datasize)) + struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control __user *kcontrols; + int n; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) || + get_user(kp->ctrl_class, &up->ctrl_class) || + get_user(kp->count, &up->count) || + get_user(kp->error_idx, &up->error_idx) || + copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved))) + return -EFAULT; + n = kp->count; + if (n == 0) { + kp->controls = NULL; + return 0; + } + if (get_user(p, &up->controls)) + return -EFAULT; + ucontrols = compat_ptr(p); + if (!access_ok(VERIFY_READ, ucontrols, n * sizeof(struct v4l2_ext_control))) + return -EFAULT; + kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); + kp->controls = kcontrols; + while (--n >= 0) { + if (copy_in_user(&kcontrols->id, &ucontrols->id, sizeof(__u32))) + return -EFAULT; + if (copy_in_user(&kcontrols->reserved2, &ucontrols->reserved2, sizeof(ucontrols->reserved2))) return -EFAULT; + /* Note: if the void * part of the union ever becomes relevant + then we need to know the type of the control in order to do + the right thing here. Luckily, that is not yet an issue. */ + if (copy_in_user(&kcontrols->value, &ucontrols->value, sizeof(ucontrols->value))) + return -EFAULT; + ucontrols++; + kcontrols++; + } return 0; } -#define VIDIOCGTUNER32 _IOWR('v', 4, struct video_tuner32) -#define VIDIOCSTUNER32 _IOW('v', 5, struct video_tuner32) -#define VIDIOCGWIN32 _IOR('v', 9, struct video_window32) -#define VIDIOCSWIN32 _IOW('v', 10, struct video_window32) -#define VIDIOCGFBUF32 _IOR('v', 11, struct video_buffer32) -#define VIDIOCSFBUF32 _IOW('v', 12, struct video_buffer32) -#define VIDIOCGFREQ32 _IOR('v', 14, u32) -#define VIDIOCSFREQ32 _IOW('v', 15, u32) -#define VIDIOCSMICROCODE32 _IOW('v', 27, struct video_code32) - -#endif - -/* VIDIOC_ENUMINPUT32 is VIDIOC_ENUMINPUT minus 4 bytes of padding alignement */ -#define VIDIOC_ENUMINPUT32 (VIDIOC_ENUMINPUT - _IOC(0, 0, 0, 4)) -#define VIDIOC_G_FMT32 _IOWR ('V', 4, struct v4l2_format32) -#define VIDIOC_S_FMT32 _IOWR ('V', 5, struct v4l2_format32) -#define VIDIOC_QUERYBUF32 _IOWR ('V', 9, struct v4l2_buffer32) -#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) -#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32) -/* VIDIOC_OVERLAY is now _IOW, but was _IOWR */ -#define VIDIOC_OVERLAY32 _IOWR ('V', 14, compat_int_t) -#define VIDIOC_QBUF32 _IOWR ('V', 15, struct v4l2_buffer32) -#define VIDIOC_DQBUF32 _IOWR ('V', 17, struct v4l2_buffer32) -#define VIDIOC_STREAMON32 _IOW ('V', 18, compat_int_t) -#define VIDIOC_STREAMOFF32 _IOW ('V', 19, compat_int_t) -#define VIDIOC_ENUMSTD32 _IOWR ('V', 25, struct v4l2_standard32) -/* VIDIOC_S_CTRL is now _IOWR, but was _IOW */ -#define VIDIOC_S_CTRL32 _IOW ('V', 28, struct v4l2_control) -#define VIDIOC_G_INPUT32 _IOR ('V', 38, compat_int_t) -#define VIDIOC_S_INPUT32 _IOWR ('V', 39, compat_int_t) -#define VIDIOC_TRY_FMT32 _IOWR ('V', 64, struct v4l2_format32) - -#ifdef CONFIG_VIDEO_V4L1_COMPAT -enum { - MaxClips = (~0U-sizeof(struct video_window))/sizeof(struct video_clip) -}; - -static int do_set_window(struct file *file, unsigned int cmd, unsigned long arg) +static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - struct video_window32 __user *up = compat_ptr(arg); - struct video_window __user *vw; - struct video_clip __user *p; - int nclips; - u32 n; - - if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32))) - return -EFAULT; + struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control __user *kcontrols = kp->controls; + int n = kp->count; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) || + put_user(kp->ctrl_class, &up->ctrl_class) || + put_user(kp->count, &up->count) || + put_user(kp->error_idx, &up->error_idx) || + copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved))) + return -EFAULT; + if (!kp->count) + return 0; - if (get_user(nclips, &up->clipcount)) + if (get_user(p, &up->controls)) return -EFAULT; - - /* Peculiar interface... */ - if (nclips < 0) - nclips = VIDEO_CLIPMAP_SIZE; - - if (nclips > MaxClips) - return -ENOMEM; - - vw = compat_alloc_user_space(sizeof(struct video_window) + - nclips * sizeof(struct video_clip)); - - p = nclips ? (struct video_clip __user *)(vw + 1) : NULL; - - if (get_user(n, &up->x) || put_user(n, &vw->x) || - get_user(n, &up->y) || put_user(n, &vw->y) || - get_user(n, &up->width) || put_user(n, &vw->width) || - get_user(n, &up->height) || put_user(n, &vw->height) || - get_user(n, &up->chromakey) || put_user(n, &vw->chromakey) || - get_user(n, &up->flags) || put_user(n, &vw->flags) || - get_user(n, &up->clipcount) || put_user(n, &vw->clipcount) || - get_user(n, &up->clips) || put_user(p, &vw->clips)) + ucontrols = compat_ptr(p); + if (!access_ok(VERIFY_WRITE, ucontrols, n * sizeof(struct v4l2_ext_control))) return -EFAULT; - if (nclips) { - struct video_clip32 __user *u = compat_ptr(n); - int i; - if (!u) - return -EINVAL; - for (i = 0; i < nclips; i++, u++, p++) { - s32 v; - if (!access_ok(VERIFY_READ, u, sizeof(struct video_clip32)) || - !access_ok(VERIFY_WRITE, p, sizeof(struct video_clip32)) || - get_user(v, &u->x) || - put_user(v, &p->x) || - get_user(v, &u->y) || - put_user(v, &p->y) || - get_user(v, &u->width) || - put_user(v, &p->width) || - get_user(v, &u->height) || - put_user(v, &p->height) || - put_user(NULL, &p->next)) - return -EFAULT; - } + while (--n >= 0) { + if (copy_in_user(&ucontrols->id, &kcontrols->id, sizeof(__u32))) + return -EFAULT; + if (copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2, + sizeof(ucontrols->reserved2))) + return -EFAULT; + /* Note: if the void * part of the union ever becomes relevant + then we need to know the type of the control in order to do + the right thing here. Luckily, that is not yet an issue. */ + if (copy_in_user(&ucontrols->value, &kcontrols->value, sizeof(ucontrols->value))) + return -EFAULT; + ucontrols++; + kcontrols++; } - - return native_ioctl(file, VIDIOCSWIN, (unsigned long)vw); + return 0; } -#endif + +#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32) +#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32) +#define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32) +#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) +#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32) +#define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32) +#define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32) +#define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32) +#define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32) +#define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32) +#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32) +#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32) +#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) + +#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) +#define VIDIOC_OVERLAY32_OLD _IOWR('V', 14, s32) +#define VIDIOC_STREAMON32 _IOW ('V', 18, s32) +#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) +#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) +#define VIDIOC_S_INPUT32 _IOWR('V', 39, s32) +#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32) +#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32) static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -616,45 +716,51 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg struct v4l2_format v2f; struct v4l2_buffer v2b; struct v4l2_framebuffer v2fb; - struct v4l2_standard v2s; struct v4l2_input v2i; - struct v4l2_tuner v2t; + struct v4l2_standard v2s; + struct v4l2_ext_controls v2ecs; unsigned long vx; + int vi; } karg; void __user *up = compat_ptr(arg); int compatible_arg = 1; int err = 0; - int realcmd = cmd; /* First, convert the command. */ switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGTUNER32: realcmd = cmd = VIDIOCGTUNER; break; - case VIDIOCSTUNER32: realcmd = cmd = VIDIOCSTUNER; break; - case VIDIOCGWIN32: realcmd = cmd = VIDIOCGWIN; break; - case VIDIOCGFBUF32: realcmd = cmd = VIDIOCGFBUF; break; - case VIDIOCSFBUF32: realcmd = cmd = VIDIOCSFBUF; break; - case VIDIOCGFREQ32: realcmd = cmd = VIDIOCGFREQ; break; - case VIDIOCSFREQ32: realcmd = cmd = VIDIOCSFREQ; break; - case VIDIOCSMICROCODE32: realcmd = cmd = VIDIOCSMICROCODE; break; + case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; + case VIDIOCSWIN32: cmd = VIDIOCSWIN; break; + case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + case VIDIOCSMICROCODE32: cmd = VIDIOCSMICROCODE; break; #endif - case VIDIOC_G_FMT32: realcmd = cmd = VIDIOC_G_FMT; break; - case VIDIOC_S_FMT32: realcmd = cmd = VIDIOC_S_FMT; break; - case VIDIOC_QUERYBUF32: realcmd = cmd = VIDIOC_QUERYBUF; break; - case VIDIOC_QBUF32: realcmd = cmd = VIDIOC_QBUF; break; - case VIDIOC_DQBUF32: realcmd = cmd = VIDIOC_DQBUF; break; - case VIDIOC_STREAMON32: realcmd = cmd = VIDIOC_STREAMON; break; - case VIDIOC_STREAMOFF32: realcmd = cmd = VIDIOC_STREAMOFF; break; - case VIDIOC_G_FBUF32: realcmd = cmd = VIDIOC_G_FBUF; break; - case VIDIOC_S_FBUF32: realcmd = cmd = VIDIOC_S_FBUF; break; - case VIDIOC_OVERLAY32: realcmd = cmd = VIDIOC_OVERLAY; break; - case VIDIOC_ENUMSTD32: realcmd = VIDIOC_ENUMSTD; break; - case VIDIOC_ENUMINPUT32: realcmd = VIDIOC_ENUMINPUT; break; - case VIDIOC_S_CTRL32: realcmd = cmd = VIDIOC_S_CTRL; break; - case VIDIOC_G_INPUT32: realcmd = cmd = VIDIOC_G_INPUT; break; - case VIDIOC_S_INPUT32: realcmd = cmd = VIDIOC_S_INPUT; break; - case VIDIOC_TRY_FMT32: realcmd = cmd = VIDIOC_TRY_FMT; break; - }; + case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; + case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break; + case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break; + case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break; + case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break; + case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break; + case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break; + case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break; + case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break; + case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break; + case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break; + case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break; + case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break; + case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; + case VIDIOC_OVERLAY32_OLD: cmd = VIDIOC_OVERLAY; break; + case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; + case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; + case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; + case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; + case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break; + case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break; + } switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT @@ -662,7 +768,6 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg case VIDIOCGTUNER: err = get_video_tuner32(&karg.vt, up); compatible_arg = 0; - break; case VIDIOCSFBUF: @@ -670,19 +775,42 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg compatible_arg = 0; break; + case VIDIOCSWIN: + err = get_video_window32(&karg.vw, up); + compatible_arg = 0; + break; + + case VIDIOCGWIN: + case VIDIOCGFBUF: + case VIDIOCGFREQ: + compatible_arg = 0; + break; + + case VIDIOCSMICROCODE: + err = get_microcode32(&karg.vc, up); + compatible_arg = 0; + break; case VIDIOCSFREQ: + err = get_user(karg.vx, (u32 __user *)up); + compatible_arg = 0; + break; + + case VIDIOCCAPTURE: + case VIDIOCSYNC: + case VIDIOCSWRITEMODE: #endif - case VIDIOC_S_INPUT: case VIDIOC_OVERLAY: case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: - err = get_user(karg.vx, (u32 __user *)up); - compatible_arg = 1; + case VIDIOC_S_INPUT: + case VIDIOC_S_OUTPUT: + err = get_user(karg.vi, (s32 __user *)up); + compatible_arg = 0; break; - case VIDIOC_S_FBUF: - err = get_v4l2_framebuffer32(&karg.v2fb, up); + case VIDIOC_G_INPUT: + case VIDIOC_G_OUTPUT: compatible_arg = 0; break; @@ -700,122 +828,108 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg compatible_arg = 0; break; - case VIDIOC_ENUMSTD: - err = get_v4l2_standard(&karg.v2s, up); + case VIDIOC_S_FBUF: + err = get_v4l2_framebuffer32(&karg.v2fb, up); compatible_arg = 0; break; - case VIDIOC_ENUMSTD32: - err = get_v4l2_standard32(&karg.v2s, up); + case VIDIOC_G_FBUF: compatible_arg = 0; break; - case VIDIOC_ENUMINPUT: - err = get_v4l2_input(&karg.v2i, up); + case VIDIOC_ENUMSTD: + err = get_v4l2_standard32(&karg.v2s, up); compatible_arg = 0; break; - case VIDIOC_ENUMINPUT32: + case VIDIOC_ENUMINPUT: err = get_v4l2_input32(&karg.v2i, up); compatible_arg = 0; break; - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - err = get_v4l2_tuner(&karg.v2t, up); + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + err = get_v4l2_ext_controls32(&karg.v2ecs, up); compatible_arg = 0; break; - -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGWIN: - case VIDIOCGFBUF: - case VIDIOCGFREQ: -#endif - case VIDIOC_G_FBUF: - case VIDIOC_G_INPUT: - compatible_arg = 0; - break; -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCSMICROCODE: - err = microcode32(&karg.vc, up); - compatible_arg = 0; - break; -#endif - }; + } if (err) - goto out; + return err; if (compatible_arg) - err = native_ioctl(file, realcmd, (unsigned long)up); + err = native_ioctl(file, cmd, (unsigned long)up); else { mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); - err = native_ioctl(file, realcmd, (unsigned long)&karg); + err = native_ioctl(file, cmd, (unsigned long)&karg); set_fs(old_fs); } - if (err == 0) { - switch (cmd) { + + /* Special case: even after an error we need to put the + results back for these ioctls since the error_idx will + contain information on which control failed. */ + switch (cmd) { + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + if (put_v4l2_ext_controls32(&karg.v2ecs, up)) + err = -EFAULT; + break; + } + if (err) + return err; + + switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGTUNER: - err = put_video_tuner32(&karg.vt, up); - break; + case VIDIOCGTUNER: + err = put_video_tuner32(&karg.vt, up); + break; - case VIDIOCGWIN: - err = put_video_window32(&karg.vw, up); - break; + case VIDIOCGWIN: + err = put_video_window32(&karg.vw, up); + break; - case VIDIOCGFBUF: - err = put_video_buffer32(&karg.vb, up); - break; + case VIDIOCGFBUF: + err = put_video_buffer32(&karg.vb, up); + break; + case VIDIOCGFREQ: + err = put_user(((u32)karg.vx), (u32 __user *)up); + break; #endif - case VIDIOC_G_FBUF: - err = put_v4l2_framebuffer32(&karg.v2fb, up); - break; - - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: - err = put_v4l2_format32(&karg.v2f, up); - break; - - case VIDIOC_QUERYBUF: - case VIDIOC_QBUF: - case VIDIOC_DQBUF: - err = put_v4l2_buffer32(&karg.v2b, up); - break; - - case VIDIOC_ENUMSTD: - err = put_v4l2_standard(&karg.v2s, up); - break; - - case VIDIOC_ENUMSTD32: - err = put_v4l2_standard32(&karg.v2s, up); - break; - - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - err = put_v4l2_tuner(&karg.v2t, up); - break; - - case VIDIOC_ENUMINPUT: - err = put_v4l2_input(&karg.v2i, up); - break; - - case VIDIOC_ENUMINPUT32: - err = put_v4l2_input32(&karg.v2i, up); - break; + case VIDIOC_S_INPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_G_INPUT: + case VIDIOC_G_OUTPUT: + err = put_user(((s32)karg.vi), (s32 __user *)up); + break; -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGFREQ: -#endif - case VIDIOC_G_INPUT: - err = put_user(((u32)karg.vx), (u32 __user *)up); - break; - }; + case VIDIOC_G_FBUF: + err = put_v4l2_framebuffer32(&karg.v2fb, up); + break; + + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + err = put_v4l2_format32(&karg.v2f, up); + break; + + case VIDIOC_QUERYBUF: + case VIDIOC_QBUF: + case VIDIOC_DQBUF: + err = put_v4l2_buffer32(&karg.v2b, up); + break; + + case VIDIOC_ENUMSTD: + err = put_v4l2_standard32(&karg.v2s, up); + break; + + case VIDIOC_ENUMINPUT: + err = put_v4l2_input32(&karg.v2i, up); + break; } -out: return err; } @@ -828,26 +942,48 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCSWIN32: - ret = do_set_window(file, cmd, arg); - break; + case VIDIOCGCAP: + case VIDIOCGCHAN: + case VIDIOCSCHAN: case VIDIOCGTUNER32: case VIDIOCSTUNER32: + case VIDIOCGPICT: + case VIDIOCSPICT: + case VIDIOCCAPTURE32: case VIDIOCGWIN32: + case VIDIOCSWIN32: case VIDIOCGFBUF32: case VIDIOCSFBUF32: + case VIDIOCKEY: case VIDIOCGFREQ32: case VIDIOCSFREQ32: case VIDIOCGAUDIO: case VIDIOCSAUDIO: + case VIDIOCSYNC32: + case VIDIOCMCAPTURE: + case VIDIOCGMBUF: + case VIDIOCGUNIT: + case VIDIOCGCAPTURE: + case VIDIOCSCAPTURE: + case VIDIOCSPLAYMODE: + case VIDIOCSWRITEMODE32: + case VIDIOCGPLAYINFO: + case VIDIOCSMICROCODE32: case VIDIOCGVBIFMT: case VIDIOCSVBIFMT: +#endif +#ifdef __OLD_VIDIOC_ + case VIDIOC_OVERLAY32_OLD: + case VIDIOC_S_PARM_OLD: + case VIDIOC_S_CTRL_OLD: + case VIDIOC_G_AUDIO_OLD: + case VIDIOC_G_AUDOUT_OLD: + case VIDIOC_CROPCAP_OLD: #endif case VIDIOC_QUERYCAP: + case VIDIOC_RESERVED: case VIDIOC_ENUM_FMT: case VIDIOC_G_FMT32: - case VIDIOC_CROPCAP: - case VIDIOC_S_CROP: case VIDIOC_S_FMT32: case VIDIOC_REQBUFS: case VIDIOC_QUERYBUF32: @@ -862,43 +998,56 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_S_PARM: case VIDIOC_G_STD: case VIDIOC_S_STD: - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - case VIDIOC_ENUMSTD: case VIDIOC_ENUMSTD32: - case VIDIOC_ENUMINPUT: case VIDIOC_ENUMINPUT32: case VIDIOC_G_CTRL: case VIDIOC_S_CTRL: - case VIDIOC_S_CTRL32: - case VIDIOC_S_FREQUENCY: - case VIDIOC_G_FREQUENCY: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_AUDIO: + case VIDIOC_S_AUDIO: case VIDIOC_QUERYCTRL: + case VIDIOC_QUERYMENU: case VIDIOC_G_INPUT32: case VIDIOC_S_INPUT32: + case VIDIOC_G_OUTPUT32: + case VIDIOC_S_OUTPUT32: + case VIDIOC_ENUMOUTPUT: + case VIDIOC_G_AUDOUT: + case VIDIOC_S_AUDOUT: + case VIDIOC_G_MODULATOR: + case VIDIOC_S_MODULATOR: + case VIDIOC_S_FREQUENCY: + case VIDIOC_G_FREQUENCY: + case VIDIOC_CROPCAP: + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + case VIDIOC_G_JPEGCOMP: + case VIDIOC_S_JPEGCOMP: + case VIDIOC_QUERYSTD: case VIDIOC_TRY_FMT32: - case VIDIOC_S_HW_FREQ_SEEK: + case VIDIOC_ENUMAUDIO: + case VIDIOC_ENUMAUDOUT: + case VIDIOC_G_PRIORITY: + case VIDIOC_S_PRIORITY: + case VIDIOC_G_SLICED_VBI_CAP: + case VIDIOC_LOG_STATUS: + case VIDIOC_G_EXT_CTRLS32: + case VIDIOC_S_EXT_CTRLS32: + case VIDIOC_TRY_EXT_CTRLS32: case VIDIOC_ENUM_FRAMESIZES: case VIDIOC_ENUM_FRAMEINTERVALS: + case VIDIOC_G_ENC_INDEX: + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: + case VIDIOC_DBG_S_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_G_CHIP_IDENT: + case VIDIOC_S_HW_FREQ_SEEK: ret = do_video_ioctl(file, cmd, arg); break; #ifdef CONFIG_VIDEO_V4L1_COMPAT - /* Little v, the video4linux ioctls (conflict?) */ - case VIDIOCGCAP: - case VIDIOCGCHAN: - case VIDIOCSCHAN: - case VIDIOCGPICT: - case VIDIOCSPICT: - case VIDIOCCAPTURE: - case VIDIOCKEY: - case VIDIOCSYNC: - case VIDIOCMCAPTURE: - case VIDIOCGMBUF: - case VIDIOCGUNIT: - case VIDIOCGCAPTURE: - case VIDIOCSCAPTURE: - /* BTTV specific... */ case _IOW('v', BASE_VIDIOCPRIVATE+0, char [256]): case _IOR('v', BASE_VIDIOCPRIVATE+1, char [256]): diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 6e6743bd0f92..e2cfd6add78c 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1465,6 +1465,8 @@ struct v4l2_chip_ident { #define VIDIOC_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_chip_ident) #endif #define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek) +/* Reminder: when adding new ioctls please add support for them to + drivers/media/video/v4l2-compat-ioctl32.c as well! */ #ifdef __OLD_VIDIOC_ /* for compatibility, will go away some day */ -- cgit v1.2.3-55-g7522 From 77587c5627aab50636ea0f93c28d2013cd0b7004 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Dec 2008 07:18:38 -0300 Subject: V4L/DVB (9940): bt832: remove this driver The bt832 i2c driver was never used or even compiled and is no longer maintained. It is now removed completely. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt8xx/bt832.c | 274 ----------------------------- drivers/media/video/bt8xx/bt832.h | 305 --------------------------------- drivers/media/video/bt8xx/bttv-cards.c | 12 -- include/media/i2c-addr.h | 2 - 4 files changed, 593 deletions(-) delete mode 100644 drivers/media/video/bt8xx/bt832.c delete mode 100644 drivers/media/video/bt8xx/bt832.h (limited to 'include') diff --git a/drivers/media/video/bt8xx/bt832.c b/drivers/media/video/bt8xx/bt832.c deleted file mode 100644 index 216fc9680e80..000000000000 --- a/drivers/media/video/bt8xx/bt832.c +++ /dev/null @@ -1,274 +0,0 @@ -/* Driver for Bt832 CMOS Camera Video Processor - i2c-addresses: 0x88 or 0x8a - - The BT832 interfaces to a Quartzsight Digital Camera (352x288, 25 or 30 fps) - via a 9 pin connector ( 4-wire SDATA, 2-wire i2c, SCLK, VCC, GND). - It outputs an 8-bit 4:2:2 YUV or YCrCb video signal which can be directly - connected to bt848/bt878 GPIO pins on this purpose. - (see: VLSI Vision Ltd. www.vvl.co.uk for camera datasheets) - - Supported Cards: - - Pixelview Rev.4E: 0x8a - GPIO 0x400000 toggles Bt832 RESET, and the chip changes to i2c 0x88 ! - - (c) Gunther Mayer, 2002 - - STATUS: - - detect chip and hexdump - - reset chip and leave low power mode - - detect camera present - - TODO: - - make it work (find correct setup for Bt832 and Bt878) -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bttv.h" -#include "bt832.h" - -MODULE_LICENSE("GPL"); - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { I2C_ADDR_BT832_ALT1>>1, I2C_ADDR_BT832_ALT2>>1, - I2C_CLIENT_END }; -I2C_CLIENT_INSMOD; - -int debug; /* debug output */ -module_param(debug, int, 0644); - -/* ---------------------------------------------------------------------- */ - -static int bt832_detach(struct i2c_client *client); - - -static struct i2c_driver driver; -static struct i2c_client client_template; - -struct bt832 { - struct i2c_client client; -}; - -int bt832_hexdump(struct i2c_client *i2c_client_s, unsigned char *buf) -{ - int i,rc; - buf[0]=0x80; // start at register 0 with auto-increment - if (1 != (rc = i2c_master_send(i2c_client_s,buf,1))) - v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 1)\n",rc); - - for(i=0;i<65;i++) - buf[i]=0; - if (65 != (rc=i2c_master_recv(i2c_client_s,buf,65))) - v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 65)\n",rc); - - // Note: On READ the first byte is the current index - // (e.g. 0x80, what we just wrote) - - if(debug>1) { - int i; - v4l_dbg(2, debug,i2c_client_s,"hexdump:"); - for(i=1;i<65;i++) { - if(i!=1) { - if(((i-1)%8)==0) printk(" "); - if(((i-1)%16)==0) { - printk("\n"); - v4l_dbg(2, debug,i2c_client_s,"hexdump:"); - } - } - printk(" %02x",buf[i]); - } - printk("\n"); - } - return 0; -} - -// Return: 1 (is a bt832), 0 (No bt832 here) -int bt832_init(struct i2c_client *i2c_client_s) -{ - unsigned char *buf; - int rc; - - buf=kmalloc(65,GFP_KERNEL); - if (!buf) { - v4l_err(&t->client, - "Unable to allocate memory. Detaching.\n"); - return 0; - } - bt832_hexdump(i2c_client_s,buf); - - if(buf[0x40] != 0x31) { - v4l_err(i2c_client_s,"This i2c chip is no bt832 (id=%02x). Detaching.\n",buf[0x40]); - kfree(buf); - return 0; - } - - v4l_err(i2c_client_s,"Write 0 tp VPSTATUS\n"); - buf[0]=BT832_VP_STATUS; // Reg.52 - buf[1]= 0x00; - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - - // Leave low power mode: - v4l_err(i2c_client_s,"leave low power mode.\n"); - buf[0]=BT832_CAM_SETUP0; //0x39 57 - buf[1]=0x08; - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error LLPM: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - v4l_info(i2c_client_s,"Write 0 tp VPSTATUS\n"); - buf[0]=BT832_VP_STATUS; // Reg.52 - buf[1]= 0x00; - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - - // Enable Output - v4l_info(i2c_client_s,"Enable Output\n"); - buf[0]=BT832_VP_CONTROL1; // Reg.40 - buf[1]= 0x27 & (~0x01); // Default | !skip - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error EO: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - - // for testing (even works when no camera attached) - v4l_info(i2c_client_s,"*** Generate NTSC M Bars *****\n"); - buf[0]=BT832_VP_TESTCONTROL0; // Reg. 42 - buf[1]=3; // Generate NTSC System M bars, Generate Frame timing internally - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_info(i2c_client_s,"i2c i/o error MBAR: rc == %d (should be 2)\n",rc); - - v4l_info(i2c_client_s,"Camera Present: %s\n", - (buf[1+BT832_CAM_STATUS] & BT832_56_CAMERA_PRESENT) ? "yes":"no"); - - bt832_hexdump(i2c_client_s,buf); - kfree(buf); - return 1; -} - - - -static int bt832_attach(struct i2c_adapter *adap, int addr, int kind) -{ - struct bt832 *t; - - client_template.adapter = adap; - client_template.addr = addr; - - if (NULL == (t = kzalloc(sizeof(*t), GFP_KERNEL))) - return -ENOMEM; - t->client = client_template; - i2c_set_clientdata(&t->client, t); - i2c_attach_client(&t->client); - - v4l_info(&t->client,"chip found @ 0x%x\n", addr<<1); - - if(! bt832_init(&t->client)) { - bt832_detach(&t->client); - return -1; - } - - return 0; -} - -static int bt832_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, bt832_attach); - return 0; -} - -static int bt832_detach(struct i2c_client *client) -{ - struct bt832 *t = i2c_get_clientdata(client); - - v4l_info(&t->client,"dettach\n"); - i2c_detach_client(client); - kfree(t); - return 0; -} - -static int -bt832_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct bt832 *t = i2c_get_clientdata(client); - - if (debug>1) - v4l_i2c_print_ioctl(&t->client,cmd); - - switch (cmd) { - case BT832_HEXDUMP: { - unsigned char *buf; - buf = kmalloc(65, GFP_KERNEL); - if (!buf) { - v4l_err(&t->client, - "Unable to allocate memory\n"); - break; - } - bt832_hexdump(&t->client,buf); - kfree(buf); - } - break; - case BT832_REATTACH: - v4l_info(&t->client,"re-attach\n"); - i2c_del_driver(&driver); - i2c_add_driver(&driver); - break; - } - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static struct i2c_driver driver = { - .driver = { - .name = "bt832", - }, - .id = 0, /* FIXME */ - .attach_adapter = bt832_probe, - .detach_client = bt832_detach, - .command = bt832_command, -}; -static struct i2c_client client_template = -{ - .name = "bt832", - .driver = &driver, -}; - - -static int __init bt832_init_module(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit bt832_cleanup_module(void) -{ - i2c_del_driver(&driver); -} - -module_init(bt832_init_module); -module_exit(bt832_cleanup_module); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/bt8xx/bt832.h b/drivers/media/video/bt8xx/bt832.h deleted file mode 100644 index 1ce8fa71f7db..000000000000 --- a/drivers/media/video/bt8xx/bt832.h +++ /dev/null @@ -1,305 +0,0 @@ -/* Bt832 CMOS Camera Video Processor (VP) - - The Bt832 CMOS Camera Video Processor chip connects a Quartsight CMOS - color digital camera directly to video capture devices via an 8-bit, - 4:2:2 YUV or YCrCb video interface. - - i2c addresses: 0x88 or 0x8a - */ - -/* The 64 registers: */ - -// Input Processor -#define BT832_OFFSET 0 -#define BT832_RCOMP 1 -#define BT832_G1COMP 2 -#define BT832_G2COMP 3 -#define BT832_BCOMP 4 -// Exposures: -#define BT832_FINEH 5 -#define BT832_FINEL 6 -#define BT832_COARSEH 7 -#define BT832_COARSEL 8 -#define BT832_CAMGAIN 9 -// Main Processor: -#define BT832_M00 10 -#define BT832_M01 11 -#define BT832_M02 12 -#define BT832_M10 13 -#define BT832_M11 14 -#define BT832_M12 15 -#define BT832_M20 16 -#define BT832_M21 17 -#define BT832_M22 18 -#define BT832_APCOR 19 -#define BT832_GAMCOR 20 -// Level Accumulator Inputs -#define BT832_VPCONTROL2 21 -#define BT832_ZONECODE0 22 -#define BT832_ZONECODE1 23 -#define BT832_ZONECODE2 24 -#define BT832_ZONECODE3 25 -// Level Accumulator Outputs: -#define BT832_RACC 26 -#define BT832_GACC 27 -#define BT832_BACC 28 -#define BT832_BLACKACC 29 -#define BT832_EXP_AGC 30 -#define BT832_LACC0 31 -#define BT832_LACC1 32 -#define BT832_LACC2 33 -#define BT832_LACC3 34 -#define BT832_LACC4 35 -#define BT832_LACC5 36 -#define BT832_LACC6 37 -#define BT832_LACC7 38 -// System: -#define BT832_VP_CONTROL0 39 -#define BT832_VP_CONTROL1 40 -#define BT832_THRESH 41 -#define BT832_VP_TESTCONTROL0 42 -#define BT832_VP_DMCODE 43 -#define BT832_ACB_CONFIG 44 -#define BT832_ACB_GNBASE 45 -#define BT832_ACB_MU 46 -#define BT832_CAM_TEST0 47 -#define BT832_AEC_CONFIG 48 -#define BT832_AEC_TL 49 -#define BT832_AEC_TC 50 -#define BT832_AEC_TH 51 -// Status: -#define BT832_VP_STATUS 52 -#define BT832_VP_LINECOUNT 53 -#define BT832_CAM_DEVICEL 54 // e.g. 0x19 -#define BT832_CAM_DEVICEH 55 // e.g. 0x40 == 0x194 Mask0, 0x194 = 404 decimal (VVL-404 camera) -#define BT832_CAM_STATUS 56 - #define BT832_56_CAMERA_PRESENT 0x20 -//Camera Setups: -#define BT832_CAM_SETUP0 57 -#define BT832_CAM_SETUP1 58 -#define BT832_CAM_SETUP2 59 -#define BT832_CAM_SETUP3 60 -// System: -#define BT832_DEFCOR 61 -#define BT832_VP_TESTCONTROL1 62 -#define BT832_DEVICE_ID 63 -# define BT832_DEVICE_ID__31 0x31 // Bt832 has ID 0x31 - -/* STMicroelectronivcs VV5404 camera module - i2c: 0x20: sensor address - i2c: 0xa0: eeprom for ccd defect map - */ -#define VV5404_device_h 0x00 // 0x19 -#define VV5404_device_l 0x01 // 0x40 -#define VV5404_status0 0x02 -#define VV5404_linecountc 0x03 // current line counter -#define VV5404_linecountl 0x04 -#define VV5404_setup0 0x10 -#define VV5404_setup1 0x11 -#define VV5404_setup2 0x12 -#define VV5404_setup4 0x14 -#define VV5404_setup5 0x15 -#define VV5404_fine_h 0x20 // fine exposure -#define VV5404_fine_l 0x21 -#define VV5404_coarse_h 0x22 //coarse exposure -#define VV5404_coarse_l 0x23 -#define VV5404_gain 0x24 // ADC pre-amp gain setting -#define VV5404_clk_div 0x25 -#define VV5404_cr 0x76 // control register -#define VV5404_as0 0x77 // ADC setup register - - -// IOCTL -#define BT832_HEXDUMP _IOR('b',1,int) -#define BT832_REATTACH _IOR('b',2,int) - -/* from BT8x8VXD/capdrv/dialogs.cpp */ - -/* -typedef enum { SVI, Logitech, Rockwell } CAMERA; - -static COMBOBOX_ENTRY gwCameraOptions[] = -{ - { SVI, "Silicon Vision 512N" }, - { Logitech, "Logitech VideoMan 1.3" }, - { Rockwell, "Rockwell QuartzSight PCI 1.0" } -}; - -// SRAM table values -//=========================================================================== -typedef enum { TGB_NTSC624, TGB_NTSC780, TGB_NTSC858, TGB_NTSC392 } TimeGenByte; - -BYTE SRAMTable[][ 60 ] = -{ - // TGB_NTSC624 - { - 0x33, // size of table = 51 - 0x0E, 0xC0, 0x00, 0x00, 0x90, 0x02, 0x03, 0x10, 0x03, 0x06, - 0x10, 0x04, 0x12, 0x12, 0x05, 0x02, 0x13, 0x04, 0x19, 0x00, - 0x04, 0x39, 0x00, 0x06, 0x59, 0x08, 0x03, 0x85, 0x08, 0x07, - 0x03, 0x50, 0x00, 0x91, 0x40, 0x00, 0x11, 0x01, 0x01, 0x4D, - 0x0D, 0x02, 0x03, 0x11, 0x01, 0x05, 0x37, 0x00, 0x37, 0x21, 0x00 - }, - // TGB_NTSC780 - { - 0x33, // size of table = 51 - 0x0e, 0xc0, 0x00, 0x00, 0x90, 0xe2, 0x03, 0x10, 0x03, 0x06, - 0x10, 0x34, 0x12, 0x12, 0x65, 0x02, 0x13, 0x24, 0x19, 0x00, - 0x24, 0x39, 0x00, 0x96, 0x59, 0x08, 0x93, 0x85, 0x08, 0x97, - 0x03, 0x50, 0x50, 0xaf, 0x40, 0x30, 0x5f, 0x01, 0xf1, 0x7f, - 0x0d, 0xf2, 0x03, 0x11, 0xf1, 0x05, 0x37, 0x30, 0x85, 0x21, 0x50 - }, - // TGB_NTSC858 - { - 0x33, // size of table = 51 - 0x0c, 0xc0, 0x00, 0x00, 0x90, 0xc2, 0x03, 0x10, 0x03, 0x06, - 0x10, 0x34, 0x12, 0x12, 0x65, 0x02, 0x13, 0x24, 0x19, 0x00, - 0x24, 0x39, 0x00, 0x96, 0x59, 0x08, 0x93, 0x83, 0x08, 0x97, - 0x03, 0x50, 0x30, 0xc0, 0x40, 0x30, 0x86, 0x01, 0x01, 0xa6, - 0x0d, 0x62, 0x03, 0x11, 0x61, 0x05, 0x37, 0x30, 0xac, 0x21, 0x50 - }, - // TGB_NTSC392 - // This table has been modified to be used for Fusion Rev D - { - 0x2A, // size of table = 42 - 0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24, - 0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10, - 0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00, - 0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3, - 0x20, 0x00 - } -}; - -//=========================================================================== -// This is the structure of the camera specifications -//=========================================================================== -typedef struct tag_cameraSpec -{ - SignalFormat signal; // which digital signal format the camera has - VideoFormat vidFormat; // video standard - SyncVideoRef syncRef; // which sync video reference is used - State syncOutput; // enable sync output for sync video input? - DecInputClk iClk; // which input clock is used - TimeGenByte tgb; // which timing generator byte does the camera use - int HReset; // select 64, 48, 32, or 16 CLKx1 for HReset - PLLFreq pllFreq; // what synthesized frequency to set PLL to - VSIZEPARMS vSize; // video size the camera produces - int lineCount; // expected total number of half-line per frame - 1 - BOOL interlace; // interlace signal? -} CameraSpec; - -//=========================================================================== -// -// Camera specifications database. Update this table whenever camera spec -// has been changed or added/deleted supported camera models -//=========================================================================== -static CameraSpec dbCameraSpec[ N_CAMERAOPTIONS ] = -{ // Silicon Vision 512N - { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC624, 64, KHz19636, - // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace - { 512, 0x64, 480, 0x13, 240 }, 0, TRUE - }, - // Logitech VideoMan 1.3 - { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC780, 64, KHz24545, - // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace - { 640, 0x80, 480, 0x1A, 240 }, 0, TRUE - }, - // Rockwell QuartzSight - // Note: Fusion Rev D (rev ID 0x02) and later supports 16 pixels for HReset which is preferable. - // Use 32 for earlier version of hardware. Clkx1_HDELAY also changed from 0x27 to 0x20. - { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC392, 16, KHz28636, - // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace - { 352, 0x20, 576, 0x08, 288 }, 607, FALSE - } -}; -*/ - -/* -The corresponding APIs required to be invoked are: -SetConnector( ConCamera, TRUE/FALSE ); -SetSignalFormat( spec.signal ); -SetVideoFormat( spec.vidFormat ); -SetSyncVideoRef( spec.syncRef ); -SetEnableSyncOutput( spec.syncOutput ); -SetTimGenByte( SRAMTable[ spec.tgb ], SRAMTableSize[ spec.tgb ] ); -SetHReset( spec.HReset ); -SetPLL( spec.pllFreq ); -SetDecInputClock( spec.iClk ); -SetVideoInfo( spec.vSize ); -SetTotalLineCount( spec.lineCount ); -SetInterlaceMode( spec.interlace ); -*/ - -/* from web: - Video Sampling -Digital video is a sampled form of analog video. The most common sampling schemes in use today are: - Pixel Clock Horiz Horiz Vert - Rate Total Active -NTSC square pixel 12.27 MHz 780 640 525 -NTSC CCIR-601 13.5 MHz 858 720 525 -NTSC 4FSc 14.32 MHz 910 768 525 -PAL square pixel 14.75 MHz 944 768 625 -PAL CCIR-601 13.5 MHz 864 720 625 -PAL 4FSc 17.72 MHz 1135 948 625 - -For the CCIR-601 standards, the sampling is based on a static orthogonal sampling grid. The luminance component (Y) is sampled at 13.5 MHz, while the two color difference signals, Cr and Cb are sampled at half that, or 6.75 MHz. The Cr and Cb samples are colocated with alternate Y samples, and they are taken at the same position on each line, such that one sample is coincident with the 50% point of the falling edge of analog sync. The samples are coded to either 8 or 10 bits per component. -*/ - -/* from DScaler:*/ -/* -//=========================================================================== -// CCIR656 Digital Input Support: The tables were taken from DScaler proyect -// -// 13 Dec 2000 - Michael Eskin, Conexant Systems - Initial version -// - -//=========================================================================== -// Timing generator SRAM table values for CCIR601 720x480 NTSC -//=========================================================================== -// For NTSC CCIR656 -BYTE BtCard::SRAMTable_NTSC[] = -{ - // SRAM Timing Table for NTSC - 0x0c, 0xc0, 0x00, - 0x00, 0x90, 0xc2, - 0x03, 0x10, 0x03, - 0x06, 0x10, 0x34, - 0x12, 0x12, 0x65, - 0x02, 0x13, 0x24, - 0x19, 0x00, 0x24, - 0x39, 0x00, 0x96, - 0x59, 0x08, 0x93, - 0x83, 0x08, 0x97, - 0x03, 0x50, 0x30, - 0xc0, 0x40, 0x30, - 0x86, 0x01, 0x01, - 0xa6, 0x0d, 0x62, - 0x03, 0x11, 0x61, - 0x05, 0x37, 0x30, - 0xac, 0x21, 0x50 -}; - -//=========================================================================== -// Timing generator SRAM table values for CCIR601 720x576 NTSC -//=========================================================================== -// For PAL CCIR656 -BYTE BtCard::SRAMTable_PAL[] = -{ - // SRAM Timing Table for PAL - 0x36, 0x11, 0x01, - 0x00, 0x90, 0x02, - 0x05, 0x10, 0x04, - 0x16, 0x14, 0x05, - 0x11, 0x00, 0x04, - 0x12, 0xc0, 0x00, - 0x31, 0x00, 0x06, - 0x51, 0x08, 0x03, - 0x89, 0x08, 0x07, - 0xc0, 0x44, 0x00, - 0x81, 0x01, 0x01, - 0xa9, 0x0d, 0x02, - 0x02, 0x50, 0x03, - 0x37, 0x3d, 0x00, - 0xaf, 0x21, 0x00, -}; -*/ diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 8629b77666f6..d24dcc025e37 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -44,7 +44,6 @@ /* fwd decl */ static void boot_msp34xx(struct bttv *btv, int pin); -static void boot_bt832(struct bttv *btv); static void hauppauge_eeprom(struct bttv *btv); static void avermedia_eeprom(struct bttv *btv); static void osprey_eeprom(struct bttv *btv, const u8 ee[256]); @@ -3721,13 +3720,6 @@ void __devinit bttv_init_card2(struct bttv *btv) if (bttv_tvcards[btv->c.type].audio_mode_gpio) btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio; - if (bttv_tvcards[btv->c.type].digital_mode == DIGITAL_MODE_CAMERA) { - /* detect Bt832 chip for quartzsight digital camera */ - if ((bttv_I2CRead(btv, I2C_ADDR_BT832_ALT1, "Bt832") >=0) || - (bttv_I2CRead(btv, I2C_ADDR_BT832_ALT2, "Bt832") >=0)) - boot_bt832(btv); - } - if (!autoload) return; @@ -4123,10 +4115,6 @@ static void __devinit boot_msp34xx(struct bttv *btv, int pin) "init [%d]\n", btv->c.nr, pin); } -static void __devinit boot_bt832(struct bttv *btv) -{ -} - /* ----------------------------------------------------------------------- */ /* Imagenation L-Model PXC200 Framegrabber */ /* This is basically the same procedure as diff --git a/include/media/i2c-addr.h b/include/media/i2c-addr.h index e7ff44a35ca0..5d0f56054d26 100644 --- a/include/media/i2c-addr.h +++ b/include/media/i2c-addr.h @@ -12,8 +12,6 @@ /* bttv address list */ #define I2C_ADDR_TSA5522 0xc2 #define I2C_ADDR_TDA7432 0x8a -#define I2C_ADDR_BT832_ALT1 0x88 -#define I2C_ADDR_BT832_ALT2 0x8a // alternate setting #define I2C_ADDR_TDA8425 0x82 #define I2C_ADDR_TDA9840 0x84 #define I2C_ADDR_TDA9850 0xb6 /* also used by 9855,9873 */ -- cgit v1.2.3-55-g7522 From 4b00eb25340c1a9b9eedaf0bc5b0f0d18eddb028 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Dec 2008 11:17:56 -0300 Subject: V4L/DVB (9944): videodev2.h: fix typo. The comment said CX2584X instead of CX2341X. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e2cfd6add78c..754c8d9685a4 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1051,7 +1051,7 @@ enum v4l2_mpeg_video_bitrate_mode { #define V4L2_CID_MPEG_VIDEO_MUTE (V4L2_CID_MPEG_BASE+210) #define V4L2_CID_MPEG_VIDEO_MUTE_YUV (V4L2_CID_MPEG_BASE+211) -/* MPEG-class control IDs specific to the CX2584x driver as defined by V4L2 */ +/* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0) enum v4l2_mpeg_cx2341x_video_spatial_filter_mode { -- cgit v1.2.3-55-g7522 From 531c98e71805b32e9ea35a218119100bbd2b7615 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Dec 2008 13:18:27 -0300 Subject: V4L/DVB (9953): em28xx: Add suport for debugging AC97 anciliary chips The em28xx driver can be coupled to an anciliary AC97 chip. This patch allows read/write AC97 registers directly. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-core.c | 4 ++-- drivers/media/video/em28xx/em28xx-video.c | 19 +++++++++++++++++++ drivers/media/video/em28xx/em28xx.h | 3 +++ include/linux/videodev2.h | 1 + 4 files changed, 25 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index c647dcb20a57..5964998daf2e 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -259,7 +259,7 @@ static int em28xx_is_ac97_ready(struct em28xx *dev) * em28xx_read_ac97() * write a 16 bit value to the specified AC97 address (LSB first!) */ -static int em28xx_read_ac97(struct em28xx *dev, u8 reg) +int em28xx_read_ac97(struct em28xx *dev, u8 reg) { int ret; u8 addr = (reg & 0x7f) | 0x80; @@ -285,7 +285,7 @@ static int em28xx_read_ac97(struct em28xx *dev, u8 reg) * em28xx_write_ac97() * write a 16 bit value to the specified AC97 address (LSB first!) */ -static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) +int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) { int ret; u8 addr = reg & 0x7f; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 4701b6589b10..4ea4920c927a 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1221,6 +1221,17 @@ static int vidioc_g_register(struct file *file, void *priv, struct em28xx *dev = fh->dev; int ret; + if (reg->match_type == V4L2_CHIP_MATCH_AC97) { + mutex_lock(&dev->lock); + ret = em28xx_read_ac97(dev, reg->reg); + mutex_unlock(&dev->lock); + if (ret < 0) + return ret; + + reg->val = ret; + return 0; + } + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) return -EINVAL; @@ -1256,6 +1267,14 @@ static int vidioc_s_register(struct file *file, void *priv, __le64 buf; int rc; + if (reg->match_type == V4L2_CHIP_MATCH_AC97) { + mutex_lock(&dev->lock); + rc = em28xx_write_ac97(dev, reg->reg, reg->val); + mutex_unlock(&dev->lock); + + return rc; + } + buf = cpu_to_le64(reg->val); mutex_lock(&dev->lock); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 80d1d55d80f9..100f27819cdc 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -564,6 +564,9 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val); +int em28xx_read_ac97(struct em28xx *dev, u8 reg); +int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val); + int em28xx_audio_analog_set(struct em28xx *dev); int em28xx_audio_setup(struct em28xx *dev); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 754c8d9685a4..e31144d22237 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1376,6 +1376,7 @@ struct v4l2_streamparm { #define V4L2_CHIP_MATCH_HOST 0 /* Match against chip ID on host (0 for the host) */ #define V4L2_CHIP_MATCH_I2C_DRIVER 1 /* Match against I2C driver ID */ #define V4L2_CHIP_MATCH_I2C_ADDR 2 /* Match against I2C 7-bit address */ +#define V4L2_CHIP_MATCH_AC97 3 /* Match against anciliary AC97 chip */ struct v4l2_register { __u32 match_type; /* Match type */ -- cgit v1.2.3-55-g7522 From 1fe2740ff10b3de1aab8f88f2f05547e5f369035 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 11:16:24 -0300 Subject: V4L/DVB (9957): v4l2-subdev: add g_sliced_vbi_cap and add NULL pointer checks Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-subdev.c | 10 ++++++---- include/media/v4l2-subdev.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index fe1f01c970ac..e3612f29d0df 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -40,13 +40,13 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) case VIDIOC_G_CHIP_IDENT: return v4l2_subdev_call(sd, core, g_chip_ident, arg); case VIDIOC_INT_S_STANDBY: - return v4l2_subdev_call(sd, core, s_standby, *(u32 *)arg); + return v4l2_subdev_call(sd, core, s_standby, arg ? (*(u32 *)arg) : 0); case VIDIOC_INT_RESET: - return v4l2_subdev_call(sd, core, reset, *(u32 *)arg); + return v4l2_subdev_call(sd, core, reset, arg ? (*(u32 *)arg) : 0); case VIDIOC_INT_S_GPIO: - return v4l2_subdev_call(sd, core, s_gpio, *(u32 *)arg); + return v4l2_subdev_call(sd, core, s_gpio, arg ? (*(u32 *)arg) : 0); case VIDIOC_INT_INIT: - return v4l2_subdev_call(sd, core, init, *(u32 *)arg); + return v4l2_subdev_call(sd, core, init, arg ? (*(u32 *)arg) : 0); #ifdef CONFIG_VIDEO_ADV_DEBUG case VIDIOC_DBG_G_REGISTER: return v4l2_subdev_call(sd, core, g_register, arg); @@ -90,6 +90,8 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) return v4l2_subdev_call(sd, video, s_vbi_data, arg); case VIDIOC_INT_G_VBI_DATA: return v4l2_subdev_call(sd, video, g_vbi_data, arg); + case VIDIOC_G_SLICED_VBI_CAP: + return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg); case VIDIOC_S_FMT: return v4l2_subdev_call(sd, video, s_fmt, arg); case VIDIOC_G_FMT: diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index bc9e0fbf2822..bca25e8eab62 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -110,6 +110,7 @@ struct v4l2_subdev_video_ops { int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line); int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data); int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data); + int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap); int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); int (*s_stream)(struct v4l2_subdev *sd, int enable); int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); -- cgit v1.2.3-55-g7522 From 035f8dc1e478c67ea2682fde8f26ee9d0982a2e7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 12:27:28 -0300 Subject: V4L/DVB (9960): v4l2-subdev: ioctl ops should use unsigned for cmd arg. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/msp3400-driver.c | 2 +- drivers/media/video/tuner-core.c | 2 +- include/media/v4l2-subdev.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 79ae7bd23528..a622dbb72ed8 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -483,7 +483,7 @@ static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) } #ifdef CONFIG_VIDEO_ALLOW_V4L1 -static int msp_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) +static int msp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct msp_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 694087bfa77d..97d7509d212f 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -800,7 +800,7 @@ static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby) } #ifdef CONFIG_VIDEO_ALLOW_V4L1 -static int tuner_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) +static int tuner_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index bca25e8eab62..ceef016bb0b7 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -79,7 +79,7 @@ struct v4l2_subdev_core_ops { int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm); - int (*ioctl)(struct v4l2_subdev *sd, int cmd, void *arg); + int (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg); #ifdef CONFIG_VIDEO_ADV_DEBUG int (*g_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); int (*s_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); -- cgit v1.2.3-55-g7522 From bc97430510960846b6e8f2d62c503653031991e1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Dec 2008 20:34:18 -0300 Subject: V4L/DVB (9969): tvp5150: add support for VIDIOC_G_CHIP_IDENT ioctl Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tvp5150.c | 16 ++++++++++++++++ include/media/v4l2-chip-ident.h | 3 +++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index e8a4637350ec..a388a9f0cb18 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "tvp5150_reg.h" @@ -961,6 +962,20 @@ static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) } +static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 | + tvp5150_read(sd, TVP5150_ROM_MINOR_VER); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150, + rev); +} + + #ifdef CONFIG_VIDEO_ADV_DEBUG static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) { @@ -1026,6 +1041,7 @@ static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .s_ctrl = tvp5150_s_ctrl, .queryctrl = tvp5150_queryctrl, .reset = tvp5150_reset, + .g_chip_ident = tvp5150_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tvp5150_g_register, .s_register = tvp5150_s_register, diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index bfe5142e6672..e3e5b53931de 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -70,6 +70,9 @@ enum { /* module vp27smpx: just ident 2700 */ V4L2_IDENT_VP27SMPX = 2700, + /* module tvp5150 */ + V4L2_IDENT_TVP5150 = 5150, + /* module cs5345: just ident 5345 */ V4L2_IDENT_CS5345 = 5345, -- cgit v1.2.3-55-g7522 From dc93a70cc7f92e1dbaf29fa7dfd914b0f618fb31 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Dec 2008 21:28:27 -0300 Subject: V4L/DVB (9973): v4l2-dev: use the release callback from device instead of cdev Instead of relying on the cdev release callback we should rely on the release callback from the device struct. This requires that we use get_device/put_device to do proper refcounting. In order to do this safely v4l2-dev.c now sets up its own file_operations that call out to the driver's ops. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-dev.c | 356 ++++++++++++++++++++++++++++++----------- include/media/v4l2-dev.h | 47 ++++-- 2 files changed, 295 insertions(+), 108 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index c5ca51a9020a..4e0db8845e04 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -41,17 +41,17 @@ static ssize_t show_index(struct device *cd, struct device_attribute *attr, char *buf) { - struct video_device *vfd = container_of(cd, struct video_device, dev); + struct video_device *vdev = to_video_device(cd); - return sprintf(buf, "%i\n", vfd->index); + return sprintf(buf, "%i\n", vdev->index); } static ssize_t show_name(struct device *cd, struct device_attribute *attr, char *buf) { - struct video_device *vfd = container_of(cd, struct video_device, dev); + struct video_device *vdev = to_video_device(cd); - return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); + return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name); } static struct device_attribute video_device_attrs[] = { @@ -73,64 +73,64 @@ struct video_device *video_device_alloc(void) } EXPORT_SYMBOL(video_device_alloc); -void video_device_release(struct video_device *vfd) +void video_device_release(struct video_device *vdev) { - kfree(vfd); + kfree(vdev); } EXPORT_SYMBOL(video_device_release); -void video_device_release_empty(struct video_device *vfd) +void video_device_release_empty(struct video_device *vdev) { /* Do nothing */ /* Only valid when the video_device struct is a static. */ } EXPORT_SYMBOL(video_device_release_empty); -/* Called when the last user of the character device is gone. */ -static void v4l2_chardev_release(struct kobject *kobj) +static inline void video_get(struct video_device *vdev) { - struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj); + get_device(&vdev->dev); +} + +static inline void video_put(struct video_device *vdev) +{ + put_device(&vdev->dev); +} + +/* Called when the last user of the video device exits. */ +static void v4l2_device_release(struct device *cd) +{ + struct video_device *vdev = to_video_device(cd); mutex_lock(&videodev_lock); - if (video_device[vfd->minor] != vfd) { + if (video_device[vdev->minor] != vdev) { mutex_unlock(&videodev_lock); - BUG(); + /* should not happen */ + WARN_ON(1); return; } /* Free up this device for reuse */ - video_device[vfd->minor] = NULL; - clear_bit(vfd->num, video_nums[vfd->vfl_type]); - mutex_unlock(&videodev_lock); + video_device[vdev->minor] = NULL; - /* Release the character device */ - vfd->cdev_release(kobj); - /* Release video_device and perform other - cleanups as needed. */ - if (vfd->release) - vfd->release(vfd); -} + /* Delete the cdev on this minor as well */ + cdev_del(vdev->cdev); + /* Just in case some driver tries to access this from + the release() callback. */ + vdev->cdev = NULL; -/* The new kobj_type for the character device */ -static struct kobj_type v4l2_ktype_cdev_default = { - .release = v4l2_chardev_release, -}; + /* Mark minor as free */ + clear_bit(vdev->num, video_nums[vdev->vfl_type]); -static void video_release(struct device *cd) -{ - struct video_device *vfd = container_of(cd, struct video_device, dev); + mutex_unlock(&videodev_lock); - /* It's now safe to delete the char device. - This will either trigger the v4l2_chardev_release immediately (if - the refcount goes to 0) or later when the last user of the - character device closes it. */ - cdev_del(&vfd->cdev); + /* Release video_device and perform other + cleanups as needed. */ + vdev->release(vdev); } static struct class video_class = { .name = VIDEO_NAME, .dev_attrs = video_device_attrs, - .dev_release = video_release, }; struct video_device *video_devdata(struct file *file) @@ -139,13 +139,163 @@ struct video_device *video_devdata(struct file *file) } EXPORT_SYMBOL(video_devdata); +static ssize_t v4l2_read(struct file *filp, char __user *buf, + size_t sz, loff_t *off) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->read) + return -EINVAL; + if (video_is_unregistered(vdev)) + return -EIO; + return vdev->fops->read(filp, buf, sz, off); +} + +static ssize_t v4l2_write(struct file *filp, const char __user *buf, + size_t sz, loff_t *off) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->write) + return -EINVAL; + if (video_is_unregistered(vdev)) + return -EIO; + return vdev->fops->write(filp, buf, sz, off); +} + +static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->poll || video_is_unregistered(vdev)) + return DEFAULT_POLLMASK; + return vdev->fops->poll(filp, poll); +} + +static int v4l2_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->ioctl) + return -ENOTTY; + /* Allow ioctl to continue even if the device was unregistered. + Things like dequeueing buffers might still be useful. */ + return vdev->fops->ioctl(inode, filp, cmd, arg); +} + +static long v4l2_unlocked_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->unlocked_ioctl) + return -ENOTTY; + /* Allow ioctl to continue even if the device was unregistered. + Things like dequeueing buffers might still be useful. */ + return vdev->fops->unlocked_ioctl(filp, cmd, arg); +} + +#ifdef CONFIG_COMPAT +static long v4l2_compat_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->compat_ioctl) + return -ENOIOCTLCMD; + /* Allow ioctl to continue even if the device was unregistered. + Things like dequeueing buffers might still be useful. */ + return vdev->fops->compat_ioctl(filp, cmd, arg); +} +#endif + +static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->mmap || + video_is_unregistered(vdev)) + return -ENODEV; + return vdev->fops->mmap(filp, vm); +} + +/* Override for the open function */ +static int v4l2_open(struct inode *inode, struct file *filp) +{ + struct video_device *vdev; + int ret; + + /* Check if the video device is available */ + mutex_lock(&videodev_lock); + vdev = video_devdata(filp); + /* return ENODEV if the video device has been removed + already or if it is not registered anymore. */ + if (vdev == NULL || video_is_unregistered(vdev)) { + mutex_unlock(&videodev_lock); + return -ENODEV; + } + /* and increase the device refcount */ + video_get(vdev); + mutex_unlock(&videodev_lock); + ret = vdev->fops->open(inode, filp); + /* decrease the refcount in case of an error */ + if (ret) + video_put(vdev); + return ret; +} + +/* Override for the release function */ +static int v4l2_release(struct inode *inode, struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + int ret = vdev->fops->release(inode, filp); + + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + video_put(vdev); + return ret; +} + +static const struct file_operations v4l2_unlocked_fops = { + .owner = THIS_MODULE, + .read = v4l2_read, + .write = v4l2_write, + .open = v4l2_open, + .mmap = v4l2_mmap, + .unlocked_ioctl = v4l2_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l2_compat_ioctl, +#endif + .release = v4l2_release, + .poll = v4l2_poll, + .llseek = no_llseek, +}; + +static const struct file_operations v4l2_fops = { + .owner = THIS_MODULE, + .read = v4l2_read, + .write = v4l2_write, + .open = v4l2_open, + .mmap = v4l2_mmap, + .ioctl = v4l2_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l2_compat_ioctl, +#endif + .release = v4l2_release, + .poll = v4l2_poll, + .llseek = no_llseek, +}; + /** * get_index - assign stream number based on parent device - * @vdev: video_device to assign index number to, vdev->dev should be assigned - * @num: -1 if auto assign, requested number otherwise + * @vdev: video_device to assign index number to, vdev->parent should be assigned + * @num: -1 if auto assign, requested number otherwise * + * Note that when this is called the new device has not yet been registered + * in the video_device array. * - * returns -ENFILE if num is already in use, a free index number if + * Returns -ENFILE if num is already in use, a free index number if * successful. */ static int get_index(struct video_device *vdev, int num) @@ -168,7 +318,6 @@ static int get_index(struct video_device *vdev, int num) for (i = 0; i < VIDEO_NUM_DEVICES; i++) { if (video_device[i] != NULL && - video_device[i] != vdev && video_device[i]->parent == vdev->parent) { used |= 1 << video_device[i]->index; } @@ -184,17 +333,15 @@ static int get_index(struct video_device *vdev, int num) return i > max_index ? -ENFILE : i; } -static const struct file_operations video_fops; - -int video_register_device(struct video_device *vfd, int type, int nr) +int video_register_device(struct video_device *vdev, int type, int nr) { - return video_register_device_index(vfd, type, nr, -1); + return video_register_device_index(vdev, type, nr, -1); } EXPORT_SYMBOL(video_register_device); /** * video_register_device_index - register video4linux devices - * @vfd: video device structure we want to register + * @vdev: video device structure we want to register * @type: type of device to register * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... * -1 == first free) @@ -218,8 +365,7 @@ EXPORT_SYMBOL(video_register_device); * * %VFL_TYPE_RADIO - A radio card */ - -int video_register_device_index(struct video_device *vfd, int type, int nr, +int video_register_device_index(struct video_device *vdev, int type, int nr, int index) { int i = 0; @@ -227,14 +373,19 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, int minor_offset = 0; int minor_cnt = VIDEO_NUM_DEVICES; const char *name_base; - void *priv = video_get_drvdata(vfd); + void *priv = video_get_drvdata(vdev); - /* the release callback MUST be present */ - BUG_ON(!vfd->release); + /* A minor value of -1 marks this video device as never + having been registered */ + if (vdev) + vdev->minor = -1; - if (vfd == NULL) + /* the release callback MUST be present */ + WARN_ON(!vdev || !vdev->release); + if (!vdev || !vdev->release) return -EINVAL; + /* Part 1: check device type */ switch (type) { case VFL_TYPE_GRABBER: name_base = "video"; @@ -254,8 +405,10 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, return -EINVAL; } - vfd->vfl_type = type; + vdev->vfl_type = type; + vdev->cdev = NULL; + /* Part 2: find a free minor, kernel number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* Keep the ranges for the first four types for historical * reasons. @@ -286,10 +439,7 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, } #endif - /* Initialize the character device */ - cdev_init(&vfd->cdev, vfd->fops); - vfd->cdev.owner = vfd->fops->owner; - /* pick a minor number */ + /* Pick a minor number */ mutex_lock(&videodev_lock); nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr); if (nr == minor_cnt) @@ -313,72 +463,92 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, return -ENFILE; } #endif - vfd->minor = i + minor_offset; - vfd->num = nr; + vdev->minor = i + minor_offset; + vdev->num = nr; set_bit(nr, video_nums[type]); - BUG_ON(video_device[vfd->minor]); - video_device[vfd->minor] = vfd; - - ret = get_index(vfd, index); - vfd->index = ret; - + /* Should not happen since we thought this minor was free */ + WARN_ON(video_device[vdev->minor] != NULL); + ret = vdev->index = get_index(vdev, index); mutex_unlock(&videodev_lock); if (ret < 0) { printk(KERN_ERR "%s: get_index failed\n", __func__); - goto fail_minor; + goto cleanup; } - ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1); + /* Part 3: Initialize the character device */ + vdev->cdev = cdev_alloc(); + if (vdev->cdev == NULL) { + ret = -ENOMEM; + goto cleanup; + } + if (vdev->fops->unlocked_ioctl) + vdev->cdev->ops = &v4l2_unlocked_fops; + else + vdev->cdev->ops = &v4l2_fops; + vdev->cdev->owner = vdev->fops->owner; + ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); if (ret < 0) { printk(KERN_ERR "%s: cdev_add failed\n", __func__); - goto fail_minor; + kfree(vdev->cdev); + vdev->cdev = NULL; + goto cleanup; } - /* sysfs class */ - memset(&vfd->dev, 0, sizeof(vfd->dev)); + + /* Part 4: register the device with sysfs */ + memset(&vdev->dev, 0, sizeof(vdev->dev)); /* The memset above cleared the device's drvdata, so put back the copy we made earlier. */ - video_set_drvdata(vfd, priv); - vfd->dev.class = &video_class; - vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); - if (vfd->parent) - vfd->dev.parent = vfd->parent; - dev_set_name(&vfd->dev, "%s%d", name_base, nr); - ret = device_register(&vfd->dev); + video_set_drvdata(vdev, priv); + vdev->dev.class = &video_class; + vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); + if (vdev->parent) + vdev->dev.parent = vdev->parent; + dev_set_name(&vdev->dev, "%s%d", name_base, nr); + ret = device_register(&vdev->dev); if (ret < 0) { printk(KERN_ERR "%s: device_register failed\n", __func__); - goto del_cdev; + goto cleanup; } - /* Remember the cdev's release function */ - vfd->cdev_release = vfd->cdev.kobj.ktype->release; - /* Install our own */ - vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default; - return 0; + /* Register the release callback that will be called when the last + reference to the device goes away. */ + vdev->dev.release = v4l2_device_release; -del_cdev: - cdev_del(&vfd->cdev); + /* Part 5: Activate this minor. The char device can now be used. */ + mutex_lock(&videodev_lock); + video_device[vdev->minor] = vdev; + mutex_unlock(&videodev_lock); + return 0; -fail_minor: +cleanup: mutex_lock(&videodev_lock); - video_device[vfd->minor] = NULL; - clear_bit(vfd->num, video_nums[type]); + if (vdev->cdev) + cdev_del(vdev->cdev); + clear_bit(vdev->num, video_nums[type]); mutex_unlock(&videodev_lock); - vfd->minor = -1; + /* Mark this video device as never having been registered. */ + vdev->minor = -1; return ret; } EXPORT_SYMBOL(video_register_device_index); /** * video_unregister_device - unregister a video4linux device - * @vfd: the device to unregister + * @vdev: the device to unregister * - * This unregisters the passed device and deassigns the minor - * number. Future open calls will be met with errors. + * This unregisters the passed device. Future open calls will + * be met with errors. */ - -void video_unregister_device(struct video_device *vfd) +void video_unregister_device(struct video_device *vdev) { - device_unregister(&vfd->dev); + /* Check if vdev was ever registered at all */ + if (!vdev || vdev->minor < 0) + return; + + mutex_lock(&videodev_lock); + set_bit(V4L2_FL_UNREGISTERED, &vdev->flags); + mutex_unlock(&videodev_lock); + device_unregister(&vdev->dev); } EXPORT_SYMBOL(video_unregister_device); diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index a0a6b41c5e09..e0d72d2c6f0e 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -26,6 +26,11 @@ struct v4l2_ioctl_callbacks; +/* Flag to mark the video_device struct as unregistered. + Drivers can set this flag if they want to block all future + device access. It is set by video_unregister_device. */ +#define V4L2_FL_UNREGISTERED (0) + /* * Newer version of video_device, handled by videodev2.c * This version moves redundant code from video device code to @@ -39,15 +44,17 @@ struct video_device /* sysfs */ struct device dev; /* v4l device */ - struct cdev cdev; /* character device */ - void (*cdev_release)(struct kobject *kobj); + struct cdev *cdev; /* character device */ struct device *parent; /* device parent */ /* device info */ char name[32]; int vfl_type; + /* 'minor' is set to -1 if the registration failed */ int minor; u16 num; + /* use bitops to set/clear/test flags */ + unsigned long flags; /* attribute to differentiate multiple indices on one physical device */ int index; @@ -58,7 +65,7 @@ struct video_device v4l2_std_id current_norm; /* Current tvnorm */ /* callbacks */ - void (*release)(struct video_device *vfd); + void (*release)(struct video_device *vdev); /* ioctl callbacks */ const struct v4l2_ioctl_ops *ioctl_ops; @@ -67,36 +74,41 @@ struct video_device /* dev to video-device */ #define to_video_device(cd) container_of(cd, struct video_device, dev) -/* Register and unregister devices. Note that if video_register_device fails, +/* Register video devices. Note that if video_register_device fails, the release() callback of the video_device structure is *not* called, so the caller is responsible for freeing any data. Usually that means that - you call video_device_release() on failure. */ -int __must_check video_register_device(struct video_device *vfd, int type, int nr); -int __must_check video_register_device_index(struct video_device *vfd, + you call video_device_release() on failure. + + Also note that vdev->minor is set to -1 if the registration failed. */ +int __must_check video_register_device(struct video_device *vdev, int type, int nr); +int __must_check video_register_device_index(struct video_device *vdev, int type, int nr, int index); -void video_unregister_device(struct video_device *vfd); + +/* Unregister video devices. Will do nothing if vdev == NULL or + vdev->minor < 0. */ +void video_unregister_device(struct video_device *vdev); /* helper functions to alloc/release struct video_device, the latter can also be used for video_device->release(). */ struct video_device * __must_check video_device_alloc(void); -/* this release function frees the vfd pointer */ -void video_device_release(struct video_device *vfd); +/* this release function frees the vdev pointer */ +void video_device_release(struct video_device *vdev); /* this release function does nothing, use when the video_device is a static global struct. Note that having a static video_device is a dubious construction at best. */ -void video_device_release_empty(struct video_device *vfd); +void video_device_release_empty(struct video_device *vdev); /* helper functions to access driver private data. */ -static inline void *video_get_drvdata(struct video_device *dev) +static inline void *video_get_drvdata(struct video_device *vdev) { - return dev_get_drvdata(&dev->dev); + return dev_get_drvdata(&vdev->dev); } -static inline void video_set_drvdata(struct video_device *dev, void *data) +static inline void video_set_drvdata(struct video_device *vdev, void *data) { - dev_set_drvdata(&dev->dev, data); + dev_set_drvdata(&vdev->dev, data); } struct video_device *video_devdata(struct file *file); @@ -108,4 +120,9 @@ static inline void *video_drvdata(struct file *file) return video_get_drvdata(video_devdata(file)); } +static inline int video_is_unregistered(struct video_device *vdev) +{ + return test_bit(V4L2_FL_UNREGISTERED, &vdev->flags); +} + #endif /* _V4L2_DEV_H */ -- cgit v1.2.3-55-g7522 From 9bea3514dd4a44490b53cc52498b2967e48056dd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 23 Dec 2008 07:35:17 -0300 Subject: V4L/DVB (9974): v4l2-dev: allow drivers to pass v4l2_device as parent Drivers that use v4l2_device can set that as parent pointer in the v4l2_dev field instead of using the struct device parent field. This allows v4l2-dev.c to check whether this driver is v4l2_device based, and if so then it can offer additional services. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-dev.c | 3 +++ include/media/v4l2-dev.h | 4 ++++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 4e0db8845e04..7ad6711ee327 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -30,6 +30,7 @@ #include #include +#include #define VIDEO_NUM_DEVICES 256 #define VIDEO_NAME "video4linux" @@ -407,6 +408,8 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, vdev->vfl_type = type; vdev->cdev = NULL; + if (vdev->v4l2_dev) + vdev->parent = vdev->v4l2_dev->dev; /* Part 2: find a free minor, kernel number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index e0d72d2c6f0e..0a88d1d17d30 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -25,6 +25,7 @@ #define VFL_TYPE_MAX 4 struct v4l2_ioctl_callbacks; +struct v4l2_device; /* Flag to mark the video_device struct as unregistered. Drivers can set this flag if they want to block all future @@ -45,7 +46,10 @@ struct video_device /* sysfs */ struct device dev; /* v4l device */ struct cdev *cdev; /* character device */ + + /* Set either parent or v4l2_dev if your driver uses v4l2_device */ struct device *parent; /* device parent */ + struct v4l2_device *v4l2_dev; /* v4l2_device parent */ /* device info */ char name[32]; -- cgit v1.2.3-55-g7522 From aeabc882a3ad9a320783815e0446b12526fd2102 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Dec 2008 11:05:49 -0300 Subject: V4L/DVB (10068): Change device ID selection method on ov772x driver Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ov772x.c | 27 +++++++++++++++++++++------ include/media/v4l2-chip-ident.h | 2 +- 2 files changed, 22 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index a2d51e27e48d..305befab8943 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -345,6 +345,12 @@ #define OP_UV 0x00000001 #define OP_SWAP_RGB 0x00000002 +/* + * ID + */ +#define OV7720 0x7720 +#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF)) + /* * struct */ @@ -374,6 +380,7 @@ struct ov772x_priv { struct soc_camera_device icd; const struct ov772x_color_format *fmt; const struct ov772x_win_size *win; + int model; }; #define ENDMARKER { 0xff, 0xff } @@ -702,7 +709,9 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) static int ov772x_get_chip_id(struct soc_camera_device *icd, struct v4l2_chip_ident *id) { - id->ident = V4L2_IDENT_OV772X; + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + + id->ident = priv->model; id->revision = 0; return 0; @@ -796,6 +805,7 @@ static int ov772x_video_probe(struct soc_camera_device *icd) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); u8 pid, ver; + const char *devname; /* * We must have a parent by now. And it cannot be a wrong one. @@ -822,15 +832,21 @@ static int ov772x_video_probe(struct soc_camera_device *icd) */ pid = i2c_smbus_read_byte_data(priv->client, PID); ver = i2c_smbus_read_byte_data(priv->client, VER); - if (pid != 0x77 || - ver != 0x21) { + + switch (VERSION(pid, ver)) { + case OV7720: + devname = "ov7720"; + priv->model = V4L2_IDENT_OV7720; + break; + default: dev_err(&icd->dev, "Product ID error %x:%x\n", pid, ver); return -ENODEV; } dev_info(&icd->dev, - "ov772x Product ID %0x:%0x Manufacturer ID %x:%x\n", + "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", + devname, pid, ver, i2c_smbus_read_byte_data(priv->client, MIDH), @@ -921,7 +937,7 @@ static int ov772x_remove(struct i2c_client *client) } static const struct i2c_device_id ov772x_id[] = { - {"ov772x", 0}, + { "ov772x", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ov772x_id); @@ -941,7 +957,6 @@ static struct i2c_driver ov772x_i2c_driver = { static int __init ov772x_module_init(void) { - printk(KERN_INFO "ov772x driver\n"); return i2c_add_driver(&ov772x_i2c_driver); } diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index e3e5b53931de..56974e689a4d 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -60,7 +60,7 @@ enum { /* OmniVision sensors: reserved range 250-299 */ V4L2_IDENT_OV7670 = 250, - V4L2_IDENT_OV772X = 251, + V4L2_IDENT_OV7720 = 251, /* Conexant MPEG encoder/decoders: reserved range 410-420 */ V4L2_IDENT_CX23415 = 415, -- cgit v1.2.3-55-g7522 From 3cac2cab4f5b7eb7d9f7afc42cb251c45b96be36 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Dec 2008 11:07:11 -0300 Subject: V4L/DVB (10069): Add ov7725 support to ov772x driver Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ov772x.c | 22 ++++++++++++++++++++++ include/media/v4l2-chip-ident.h | 1 + 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 305befab8943..99dd943aacf3 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -51,6 +51,7 @@ #define COM8 0x13 /* Common control 8 */ #define COM9 0x14 /* Common control 9 */ #define COM10 0x15 /* Common control 10 */ +#define REG16 0x16 /* Register 16 */ #define HSTART 0x17 /* Horizontal sensor size */ #define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */ #define VSTART 0x19 /* Vertical frame (row) start high 8-bit */ @@ -65,6 +66,7 @@ #define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ #define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ #define VPT 0x26 /* AGC/AEC Fast mode operating region */ +#define REG28 0x28 /* Register 28 */ #define HOUTSIZE 0x29 /* Horizontal data output size MSBs */ #define EXHCH 0x2A /* Dummy pixel insert MSB */ #define EXHCL 0x2B /* Dummy pixel insert LSB */ @@ -94,6 +96,7 @@ #define TGT_R 0x43 /* BLC red channel target value */ #define TGT_GB 0x44 /* BLC Gb channel target value */ #define TGT_GR 0x45 /* BLC Gr channel target value */ +/* for ov7720 */ #define LCC0 0x46 /* Lens correction control 0 */ #define LCC1 0x47 /* Lens correction option 1 - X coordinate */ #define LCC2 0x48 /* Lens correction option 2 - Y coordinate */ @@ -101,6 +104,15 @@ #define LCC4 0x4A /* Lens correction option 4 - radius of the circular */ #define LCC5 0x4B /* Lens correction option 5 */ #define LCC6 0x4C /* Lens correction option 6 */ +/* for ov7725 */ +#define LC_CTR 0x46 /* Lens correction control */ +#define LC_XC 0x47 /* X coordinate of lens correction center relative */ +#define LC_YC 0x48 /* Y coordinate of lens correction center relative */ +#define LC_COEF 0x49 /* Lens correction coefficient */ +#define LC_RADI 0x4A /* Lens correction radius */ +#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */ +#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */ + #define FIXGAIN 0x4D /* Analog fix gain amplifer */ #define AREF0 0x4E /* Sensor reference control */ #define AREF1 0x4F /* Sensor reference current control */ @@ -182,8 +194,13 @@ #define SDE 0xA6 /* Special digital effect control */ #define USAT 0xA7 /* U component saturation control */ #define VSAT 0xA8 /* V component saturation control */ +/* for ov7720 */ #define HUE0 0xA9 /* Hue control 0 */ #define HUE1 0xAA /* Hue control 1 */ +/* for ov7725 */ +#define HUECOS 0xA9 /* Cosine value */ +#define HUESIN 0xAA /* Sine value */ + #define SIGN 0xAB /* Sign bit for Hue and contrast */ #define DSPAUTO 0xAC /* DSP auto function ON/OFF control */ @@ -349,6 +366,7 @@ * ID */ #define OV7720 0x7720 +#define OV7725 0x7721 #define VERSION(pid, ver) ((pid<<8)|(ver&0xFF)) /* @@ -838,6 +856,10 @@ static int ov772x_video_probe(struct soc_camera_device *icd) devname = "ov7720"; priv->model = V4L2_IDENT_OV7720; break; + case OV7725: + devname = "ov7725"; + priv->model = V4L2_IDENT_OV7725; + break; default: dev_err(&icd->dev, "Product ID error %x:%x\n", pid, ver); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 56974e689a4d..15fd93caaf6c 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -61,6 +61,7 @@ enum { /* OmniVision sensors: reserved range 250-299 */ V4L2_IDENT_OV7670 = 250, V4L2_IDENT_OV7720 = 251, + V4L2_IDENT_OV7725 = 252, /* Conexant MPEG encoder/decoders: reserved range 410-420 */ V4L2_IDENT_CX23415 = 415, -- cgit v1.2.3-55-g7522 From bd73b36f0c41b0c02ef4b10a307db1c43537e006 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 23 Dec 2008 05:54:45 -0300 Subject: V4L/DVB (10072): soc-camera: Add signal inversion flags to be used by camera drivers As reported by Antonio Ospite two platforms with a mt9m111 camera require opposite pixel clock polarity, which means one of them inverts it. This patch adds support for inversion flags and switches all available camera drivers to using them. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9m001.c | 15 +++++++-------- drivers/media/video/mt9m111.c | 6 +++++- drivers/media/video/mt9v022.c | 3 +++ drivers/media/video/ov772x.c | 10 +++++----- drivers/media/video/soc_camera.c | 34 ++++++++++++++++++++++++++++++++++ include/media/soc_camera.h | 11 +++++++++++ 6 files changed, 65 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index a7f0e6971fe1..b58f0f85e30f 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -272,17 +272,16 @@ static int mt9m001_set_bus_param(struct soc_camera_device *icd, static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - unsigned int width_flag = SOCAM_DATAWIDTH_10; + struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + /* MT9M001 has all capture_format parameters fixed */ + unsigned long flags = SOCAM_DATAWIDTH_10 | SOCAM_PCLK_SAMPLE_RISING | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_MASTER; if (bus_switch_possible(mt9m001)) - width_flag |= SOCAM_DATAWIDTH_8; + flags |= SOCAM_DATAWIDTH_8; - /* MT9M001 has all capture_format parameters fixed */ - return SOCAM_PCLK_SAMPLE_RISING | - SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_MASTER | - width_flag; + return soc_camera_apply_sensor_flags(icl, flags); } static int mt9m001_set_fmt(struct soc_camera_device *icd, diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index b4a238f49600..b0e6046ea967 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -415,9 +415,13 @@ static int mt9m111_stop_capture(struct soc_camera_device *icd) static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd) { - return SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; + + return soc_camera_apply_sensor_flags(icl, flags); } static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f) diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 82e1a3381a7a..3b3a6a027b1d 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -273,6 +273,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct soc_camera_link *icl = mt9v022->client->dev.platform_data; unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; int ret; u16 pixclk = 0; @@ -296,6 +297,8 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; } + flags = soc_camera_apply_sensor_flags(icl, flags); + if (flags & SOCAM_PCLK_SAMPLE_RISING) pixclk |= 0x10; diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 99dd943aacf3..110cb9be09d6 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -716,12 +716,12 @@ static int ov772x_set_bus_param(struct soc_camera_device *icd, static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - - return SOCAM_PCLK_SAMPLE_RISING | - SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_MASTER | + struct soc_camera_link *icl = priv->client->dev.platform_data; + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | priv->info->buswidth; + + return soc_camera_apply_sensor_flags(icl, flags); } static int ov772x_get_chip_id(struct soc_camera_device *icd, diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 5e48c2cc1a44..176017501055 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -59,6 +59,40 @@ const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( } EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); +/** + * soc_camera_apply_sensor_flags() - apply platform SOCAM_SENSOR_INVERT_* flags + * @icl: camera platform parameters + * @flags: flags to be inverted according to platform configuration + * @return: resulting flags + */ +unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl, + unsigned long flags) +{ + unsigned long f; + + /* If only one of the two polarities is supported, switch to the opposite */ + if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) { + f = flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW); + if (f == SOCAM_HSYNC_ACTIVE_HIGH || f == SOCAM_HSYNC_ACTIVE_LOW) + flags ^= SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW; + } + + if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) { + f = flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW); + if (f == SOCAM_VSYNC_ACTIVE_HIGH || f == SOCAM_VSYNC_ACTIVE_LOW) + flags ^= SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW; + } + + if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) { + f = flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING); + if (f == SOCAM_PCLK_SAMPLE_RISING || f == SOCAM_PCLK_SAMPLE_FALLING) + flags ^= SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING; + } + + return flags; +} +EXPORT_SYMBOL(soc_camera_apply_sensor_flags); + static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index da57ffdaec4d..e6ed0d94ac1b 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -81,11 +81,19 @@ struct soc_camera_host_ops { unsigned int (*poll)(struct file *, poll_table *); }; +#define SOCAM_SENSOR_INVERT_PCLK (1 << 0) +#define SOCAM_SENSOR_INVERT_MCLK (1 << 1) +#define SOCAM_SENSOR_INVERT_HSYNC (1 << 2) +#define SOCAM_SENSOR_INVERT_VSYNC (1 << 3) +#define SOCAM_SENSOR_INVERT_DATA (1 << 4) + struct soc_camera_link { /* Camera bus id, used to match a camera and a bus */ int bus_id; /* GPIO number to switch between 8 and 10 bit modes */ unsigned int gpio; + /* Per camera SOCAM_SENSOR_* bus flags */ + unsigned long flags; /* Optional callbacks to power on or off and reset the sensor */ int (*power)(struct device *, int); int (*reset)(struct device *); @@ -206,4 +214,7 @@ static inline unsigned long soc_camera_bus_param_compatible( return (!hsync || !vsync || !pclk) ? 0 : common_flags; } +extern unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl, + unsigned long flags); + #endif -- cgit v1.2.3-55-g7522 From a9bef518cd78d569a3ff0b1ac2afa5e2d8b3573a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 18 Dec 2008 11:34:20 -0300 Subject: V4L/DVB (10074): soc-camera: add camera sense data Add a struct soc_camera_sense, that can be used by camera host drivers to request additional information from a camera driver, for example, when changing data format. This struct can be extended in the future, its first use is to request the camera driver whether the pixel-clock frequency has changed. Tested-by: Robert Jarzmik Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/media/soc_camera.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'include') diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index e6ed0d94ac1b..38b826c608be 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -36,6 +36,7 @@ struct soc_camera_device { unsigned char iface; /* Host number */ unsigned char devnum; /* Device number per host */ unsigned char buswidth; /* See comment in .c */ + struct soc_camera_sense *sense; /* See comment in struct definition */ struct soc_camera_ops *ops; struct video_device *vdev; const struct soc_camera_data_format *current_fmt; @@ -172,6 +173,32 @@ struct soc_camera_ops { int num_controls; }; +#define SOCAM_SENSE_PCLK_CHANGED (1 << 0) + +/** + * This struct can be attached to struct soc_camera_device by the host driver + * to request sense from the camera, for example, when calling .set_fmt(). The + * host then can check which flags are set and verify respective values if any. + * For example, if SOCAM_SENSE_PCLK_CHANGED is set, it means, pixclock has + * changed during this operation. After completion the host should detach sense. + * + * @flags ored SOCAM_SENSE_* flags + * @master_clock if the host wants to be informed about pixel-clock + * change, it better set master_clock. + * @pixel_clock_max maximum pixel clock frequency supported by the host, + * camera is not allowed to exceed this. + * @pixel_clock if the camera driver changed pixel clock during this + * operation, it sets SOCAM_SENSE_PCLK_CHANGED, uses + * master_clock to calculate the new pixel-clock and + * sets this field. + */ +struct soc_camera_sense { + unsigned long flags; + unsigned long master_clock; + unsigned long pixel_clock_max; + unsigned long pixel_clock; +}; + static inline struct v4l2_queryctrl const *soc_camera_find_qctrl( struct soc_camera_ops *ops, int id) { -- cgit v1.2.3-55-g7522 From 9b9fd6c71fb46a4a710040c8ef28f1e84b88a830 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 18 Dec 2008 11:42:54 -0300 Subject: V4L/DVB (10076): v4l: add chip ID for MT9M112 camera sensor from Micron The chip is largely compatible with MT9M111 and is going to be supported by the same driver. Signed-off-by: Mike Rapoport Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-chip-ident.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 15fd93caaf6c..30448cd51751 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -171,6 +171,7 @@ enum { V4L2_IDENT_MT9M001C12ST = 45000, V4L2_IDENT_MT9M001C12STM = 45005, V4L2_IDENT_MT9M111 = 45007, + V4L2_IDENT_MT9M112 = 45008, V4L2_IDENT_MT9V022IX7ATC = 45010, /* No way to detect "normal" I77ATx */ V4L2_IDENT_MT9V022IX7ATM = 45015, /* and "lead free" IA7ATx chips */ }; -- cgit v1.2.3-55-g7522 From 91962fa713bd8bf47434b02ac661fdc201365fa5 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 18 Dec 2008 11:45:00 -0300 Subject: V4L/DVB (10078): video: add NV16 and NV61 pixel formats This patch adds support for NV16 and NV61 pixel formats. These pixel formats use two planes; one for 8-bit Y values and one for interleaved 8-bit U and V values. NV16/NV61 formats are very similar to NV12/NV21 with the exception that NV16/NV61 are using the same number of lines for both planes. The difference between NV16 and NV61 is the U and V byte order. The fourcc values are extrapolated from the NV12/NV21 case. Signed-off-by: Magnus Damm Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e31144d22237..1f126e30766c 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -305,6 +305,8 @@ struct v4l2_pix_format { /* two planes -- one Y, one Cr + Cb interleaved */ #define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */ #define V4L2_PIX_FMT_NV21 v4l2_fourcc('N', 'V', '2', '1') /* 12 Y/CrCb 4:2:0 */ +#define V4L2_PIX_FMT_NV16 v4l2_fourcc('N', 'V', '1', '6') /* 16 Y/CbCr 4:2:2 */ +#define V4L2_PIX_FMT_NV61 v4l2_fourcc('N', 'V', '6', '1') /* 16 Y/CrCb 4:2:2 */ /* The following formats are not defined in the V4L2 specification */ #define V4L2_PIX_FMT_YUV410 v4l2_fourcc('Y', 'U', 'V', '9') /* 9 YUV 4:1:0 */ -- cgit v1.2.3-55-g7522 From 1c3bb7431d16f7486a8523d54380bad89c485dc8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 18 Dec 2008 12:28:54 -0300 Subject: V4L/DVB (10083): soc-camera: unify locking, play nicer with videobuf locking Move mutex from host drivers to camera device object, take into account videobuf locking. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pxa_camera.c | 15 ++--- drivers/media/video/sh_mobile_ceu_camera.c | 9 +-- drivers/media/video/soc_camera.c | 99 +++++++++++++++++++++++++----- include/media/soc_camera.h | 8 ++- 4 files changed, 96 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index aa7efc45d364..c3c50de0aa50 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -164,8 +163,6 @@ CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \ CICR0_EOFM | CICR0_FOM) -static DEFINE_MUTEX(camera_lock); - /* * Structures */ @@ -813,16 +810,17 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) return IRQ_HANDLED; } -/* The following two functions absolutely depend on the fact, that - * there can be only one camera on PXA quick capture interface */ +/* + * The following two functions absolutely depend on the fact, that + * there can be only one camera on PXA quick capture interface + * Called with .video_lock held + */ static int pxa_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; int ret; - mutex_lock(&camera_lock); - if (pcdev->icd) { ret = -EBUSY; goto ebusy; @@ -838,11 +836,10 @@ static int pxa_camera_add_device(struct soc_camera_device *icd) pcdev->icd = icd; ebusy: - mutex_unlock(&camera_lock); - return ret; } +/* Called with .video_lock held */ static void pxa_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index a49bec1509f4..47ffa441c869 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include @@ -75,8 +74,6 @@ #define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */ #define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */ -static DEFINE_MUTEX(camera_lock); - /* per video frame buffer */ struct sh_mobile_ceu_buffer { struct videobuf_buffer vb; /* v4l buffer must be first */ @@ -292,14 +289,13 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) return IRQ_HANDLED; } +/* Called with .video_lock held */ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; int ret = -EBUSY; - mutex_lock(&camera_lock); - if (pcdev->icd) goto err; @@ -319,11 +315,10 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) pcdev->icd = icd; err: - mutex_unlock(&camera_lock); - return ret; } +/* Called with .video_lock held */ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index d5613cdd93a6..e869670dbae5 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -33,7 +33,6 @@ static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); -static DEFINE_MUTEX(video_lock); const struct soc_camera_data_format *soc_camera_format_by_fourcc( struct soc_camera_device *icd, unsigned int fourcc) @@ -270,8 +269,10 @@ static int soc_camera_open(struct inode *inode, struct file *file) if (!icf) return -ENOMEM; - /* Protect against icd->remove() until we module_get() both drivers. */ - mutex_lock(&video_lock); + /* + * It is safe to dereference these pointers now as long as a user has + * the video device open - we are protected by the held cdev reference. + */ vdev = video_devdata(file); icd = container_of(vdev->parent, struct soc_camera_device, dev); @@ -289,6 +290,9 @@ static int soc_camera_open(struct inode *inode, struct file *file) goto emgi; } + /* Protect against icd->remove() until we module_get() both drivers. */ + mutex_lock(&icd->video_lock); + icf->icd = icd; icd->use_count++; @@ -304,7 +308,7 @@ static int soc_camera_open(struct inode *inode, struct file *file) } } - mutex_unlock(&video_lock); + mutex_unlock(&icd->video_lock); file->private_data = icf; dev_dbg(&icd->dev, "camera device open\n"); @@ -313,16 +317,16 @@ static int soc_camera_open(struct inode *inode, struct file *file) return 0; - /* All errors are entered with the video_lock held */ + /* First two errors are entered with the .video_lock held */ eiciadd: soc_camera_free_user_formats(icd); eiufmt: icd->use_count--; + mutex_unlock(&icd->video_lock); module_put(ici->ops->owner); emgi: module_put(icd->ops->owner); emgd: - mutex_unlock(&video_lock); vfree(icf); return ret; } @@ -334,15 +338,16 @@ static int soc_camera_close(struct inode *inode, struct file *file) struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct video_device *vdev = icd->vdev; - mutex_lock(&video_lock); + mutex_lock(&icd->video_lock); icd->use_count--; if (!icd->use_count) { ici->ops->remove(icd); soc_camera_free_user_formats(icd); } + mutex_unlock(&icd->video_lock); + module_put(icd->ops->owner); module_put(ici->ops->owner); - mutex_unlock(&video_lock); vfree(icf); @@ -424,18 +429,27 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, if (ret < 0) return ret; + mutex_lock(&icf->vb_vidq.vb_lock); + + if (videobuf_queue_is_busy(&icf->vb_vidq)) { + dev_err(&icd->dev, "S_FMT denied: queue busy\n"); + ret = -EBUSY; + goto unlock; + } + rect.left = icd->x_current; rect.top = icd->y_current; rect.width = pix->width; rect.height = pix->height; ret = ici->ops->set_fmt(icd, pix->pixelformat, &rect); if (ret < 0) { - return ret; + goto unlock; } else if (!icd->current_fmt || icd->current_fmt->fourcc != pixfmt) { dev_err(&ici->dev, "Host driver hasn't set up current format correctly!\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } icd->width = rect.width; @@ -449,7 +463,12 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, icd->width, icd->height); /* set physical bus parameters */ - return ici->ops->set_bus_param(icd, pixfmt); + ret = ici->ops->set_bus_param(icd, pixfmt); + +unlock: + mutex_unlock(&icf->vb_vidq.vb_lock); + + return ret; } static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, @@ -510,6 +529,7 @@ static int soc_camera_streamon(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + int ret; WARN_ON(priv != file->private_data); @@ -518,10 +538,16 @@ static int soc_camera_streamon(struct file *file, void *priv, if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + mutex_lock(&icd->video_lock); + icd->ops->start_capture(icd); /* This calls buf_queue from host driver's videobuf_queue_ops */ - return videobuf_streamon(&icf->vb_vidq); + ret = videobuf_streamon(&icf->vb_vidq); + + mutex_unlock(&icd->video_lock); + + return ret; } static int soc_camera_streamoff(struct file *file, void *priv, @@ -537,12 +563,16 @@ static int soc_camera_streamoff(struct file *file, void *priv, if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + mutex_lock(&icd->video_lock); + /* This calls buf_release from host driver's videobuf_queue_ops for all * remaining buffers. When the last buffer is freed, stop capture */ videobuf_streamoff(&icf->vb_vidq); icd->ops->stop_capture(icd); + mutex_unlock(&icd->video_lock); + return 0; } @@ -654,6 +684,9 @@ static int soc_camera_s_crop(struct file *file, void *fh, if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + /* Cropping is allowed during a running capture, guard consistency */ + mutex_lock(&icf->vb_vidq.vb_lock); + ret = ici->ops->set_fmt(icd, 0, &a->c); if (!ret) { icd->width = a->c.width; @@ -662,6 +695,8 @@ static int soc_camera_s_crop(struct file *file, void *fh, icd->y_current = a->c.top; } + mutex_unlock(&icf->vb_vidq.vb_lock); + return ret; } @@ -775,11 +810,32 @@ static int soc_camera_probe(struct device *dev) struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); int ret; + /* + * Possible race scenario: + * modprobe triggers __func__ + * at this moment respective gets rmmod'ed + * to protect take module references. + */ + + if (!try_module_get(icd->ops->owner)) { + dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); + ret = -EINVAL; + goto emgd; + } + + if (!try_module_get(ici->ops->owner)) { + dev_err(&icd->dev, "Couldn't lock capture bus driver.\n"); + ret = -EINVAL; + goto emgi; + } + + mutex_lock(&icd->video_lock); + /* We only call ->add() here to activate and probe the camera. * We shall ->remove() and deactivate it immediately afterwards. */ ret = ici->ops->add(icd); if (ret < 0) - return ret; + goto eiadd; ret = icd->ops->probe(icd); if (ret >= 0) { @@ -793,6 +849,12 @@ static int soc_camera_probe(struct device *dev) } ici->ops->remove(icd); +eiadd: + mutex_unlock(&icd->video_lock); + module_put(ici->ops->owner); +emgi: + module_put(icd->ops->owner); +emgd: return ret; } @@ -966,6 +1028,7 @@ int soc_camera_device_register(struct soc_camera_device *icd) icd->dev.release = dummy_release; icd->use_count = 0; icd->host_priv = NULL; + mutex_init(&icd->video_lock); return scan_add_device(icd); } @@ -1012,6 +1075,10 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { #endif }; +/* + * Usually called from the struct soc_camera_ops .probe() method, i.e., from + * soc_camera_probe() above with .video_lock held + */ int soc_camera_video_start(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); @@ -1027,7 +1094,7 @@ int soc_camera_video_start(struct soc_camera_device *icd) dev_dbg(&ici->dev, "Allocated video_device %p\n", vdev); strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); - /* Maybe better &ici->dev */ + vdev->parent = &icd->dev; vdev->current_norm = V4L2_STD_UNKNOWN; vdev->fops = &soc_camera_fops; @@ -1061,10 +1128,10 @@ void soc_camera_video_stop(struct soc_camera_device *icd) if (!icd->dev.parent || !vdev) return; - mutex_lock(&video_lock); + mutex_lock(&icd->video_lock); video_unregister_device(vdev); icd->vdev = NULL; - mutex_unlock(&video_lock); + mutex_unlock(&icd->video_lock); } EXPORT_SYMBOL(soc_camera_video_stop); diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 38b826c608be..8bae9a359d93 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -12,9 +12,10 @@ #ifndef SOC_CAMERA_H #define SOC_CAMERA_H +#include +#include #include #include -#include struct soc_camera_device { struct list_head list; @@ -45,9 +46,10 @@ struct soc_camera_device { struct soc_camera_format_xlate *user_formats; int num_user_formats; struct module *owner; - void *host_priv; /* per-device host private data */ - /* soc_camera.c private count. Only accessed with video_lock held */ + void *host_priv; /* Per-device host private data */ + /* soc_camera.c private count. Only accessed with .video_lock held */ int use_count; + struct mutex video_lock; /* Protects device data */ }; struct soc_camera_file { -- cgit v1.2.3-55-g7522 From 513791aba6266e0c15d5b697b97e956e83537f5a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Dec 2008 12:46:45 -0300 Subject: V4L/DVB (10086): Add new set_std function on soc_camera This patch presents new method to be able to check v4l2_std_id Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 9 ++++++++- include/media/soc_camera.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index e869670dbae5..b12d11f213bd 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -152,7 +152,14 @@ static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) { - return 0; + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + int ret = 0; + + if (icd->ops->set_std) + ret = icd->ops->set_std(icd, a); + + return ret; } static int soc_camera_reqbufs(struct file *file, void *priv, diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 8bae9a359d93..26dede82057c 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -165,6 +165,7 @@ struct soc_camera_ops { int (*set_bus_param)(struct soc_camera_device *, unsigned long); int (*get_chip_id)(struct soc_camera_device *, struct v4l2_chip_ident *); + int (*set_std)(struct soc_camera_device *, v4l2_std_id *); #ifdef CONFIG_VIDEO_ADV_DEBUG int (*get_register)(struct soc_camera_device *, struct v4l2_register *); int (*set_register)(struct soc_camera_device *, struct v4l2_register *); -- cgit v1.2.3-55-g7522 From 34d359db7d683e227f27595ad3702fb2ae96108a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Dec 2008 12:47:46 -0300 Subject: V4L/DVB (10087): Add new enum_input function on soc_camera This patch presents new method to be able to select V4L2 input type Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 17 +++++++++++++---- include/media/soc_camera.h | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index b12d11f213bd..e86e6bda1b7f 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -125,14 +125,23 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, static int soc_camera_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + int ret = 0; + if (inp->index != 0) return -EINVAL; - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_UNKNOWN; - strcpy(inp->name, "Camera"); + if (icd->ops->enum_input) + ret = icd->ops->enum_input(icd, inp); + else { + /* default is camera */ + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_UNKNOWN; + strcpy(inp->name, "Camera"); + } - return 0; + return ret; } static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i) diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 26dede82057c..50f4447566de 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -166,6 +166,7 @@ struct soc_camera_ops { int (*get_chip_id)(struct soc_camera_device *, struct v4l2_chip_ident *); int (*set_std)(struct soc_camera_device *, v4l2_std_id *); + int (*enum_input)(struct soc_camera_device *, struct v4l2_input *); #ifdef CONFIG_VIDEO_ADV_DEBUG int (*get_register)(struct soc_camera_device *, struct v4l2_register *); int (*set_register)(struct soc_camera_device *, struct v4l2_register *); -- cgit v1.2.3-55-g7522 From 042d87900217228f865654fa70fade8139bd42cf Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 19 Dec 2008 10:07:49 -0300 Subject: V4L/DVB (10093): soc-camera: add new bus width and signal polarity flags In preparation for i.MX31 camera host driver add flags for 4 and 15 bit bus widths and for data lines polarity inversion. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/media/soc_camera.h | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 50f4447566de..425b6a98c95c 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -221,15 +221,20 @@ static inline struct v4l2_queryctrl const *soc_camera_find_qctrl( #define SOCAM_HSYNC_ACTIVE_LOW (1 << 3) #define SOCAM_VSYNC_ACTIVE_HIGH (1 << 4) #define SOCAM_VSYNC_ACTIVE_LOW (1 << 5) -#define SOCAM_DATAWIDTH_8 (1 << 6) -#define SOCAM_DATAWIDTH_9 (1 << 7) -#define SOCAM_DATAWIDTH_10 (1 << 8) -#define SOCAM_DATAWIDTH_16 (1 << 9) -#define SOCAM_PCLK_SAMPLE_RISING (1 << 10) -#define SOCAM_PCLK_SAMPLE_FALLING (1 << 11) - -#define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_9 | \ - SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_16) +#define SOCAM_DATAWIDTH_4 (1 << 6) +#define SOCAM_DATAWIDTH_8 (1 << 7) +#define SOCAM_DATAWIDTH_9 (1 << 8) +#define SOCAM_DATAWIDTH_10 (1 << 9) +#define SOCAM_DATAWIDTH_15 (1 << 10) +#define SOCAM_DATAWIDTH_16 (1 << 11) +#define SOCAM_PCLK_SAMPLE_RISING (1 << 12) +#define SOCAM_PCLK_SAMPLE_FALLING (1 << 13) +#define SOCAM_DATA_ACTIVE_HIGH (1 << 14) +#define SOCAM_DATA_ACTIVE_LOW (1 << 15) + +#define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_4 | SOCAM_DATAWIDTH_8 | \ + SOCAM_DATAWIDTH_9 | SOCAM_DATAWIDTH_10 | \ + SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_16) static inline unsigned long soc_camera_bus_param_compatible( unsigned long camera_flags, unsigned long bus_flags) -- cgit v1.2.3-55-g7522 From ed922a892e535c14035210b5be328af1f49561c8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 29 Dec 2008 06:04:06 -0300 Subject: V4L/DVB (10094): Add tw9910 driver This patch adds tw9910 driver that use soc_camera framework. It was tested on SH Migo-r board and mplayer. create mode 100644 drivers/media/video/tw9910.c create mode 100644 include/media/tw9910.h Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 6 + drivers/media/video/Makefile | 1 + drivers/media/video/tw9910.c | 941 ++++++++++++++++++++++++++++++++++++++++ include/media/tw9910.h | 39 ++ include/media/v4l2-chip-ident.h | 3 + 5 files changed, 990 insertions(+) create mode 100644 drivers/media/video/tw9910.c create mode 100644 include/media/tw9910.h (limited to 'include') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index da538d9ca303..9785eeb1ed44 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -755,6 +755,12 @@ config MT9V022_PCA9536_SWITCH Select this if your MT9V022 camera uses a PCA9536 I2C GPIO extender to switch between 8 and 10 bit datawidth modes +config SOC_CAMERA_TW9910 + tristate "tw9910 support" + depends on SOC_CAMERA && I2C + help + This is a tw9910 video driver + config SOC_CAMERA_PLATFORM tristate "platform camera support" depends on SOC_CAMERA diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 84a2be0cbbe7..40c69afcde7a 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -140,6 +140,7 @@ obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o +obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o obj-$(CONFIG_VIDEO_AU0828) += au0828/ diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c new file mode 100644 index 000000000000..75cfde02d026 --- /dev/null +++ b/drivers/media/video/tw9910.c @@ -0,0 +1,941 @@ +/* + * tw9910 Video Driver + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on ov772x driver, + * + * Copyright (C) 2008 Kuninori Morimoto + * Copyright 2006-7 Jonathan Corbet + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GET_ID(val) ((val & 0xF8) >> 3) +#define GET_ReV(val) (val & 0x07) + +/* + * register offset + */ +#define ID 0x00 /* Product ID Code Register */ +#define STATUS1 0x01 /* Chip Status Register I */ +#define INFORM 0x02 /* Input Format */ +#define OPFORM 0x03 /* Output Format Control Register */ +#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */ +#define OUTCTR1 0x05 /* Output Control I */ +#define ACNTL1 0x06 /* Analog Control Register 1 */ +#define CROP_HI 0x07 /* Cropping Register, High */ +#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */ +#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */ +#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */ +#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */ +#define CNTRL1 0x0C /* Control Register I */ +#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */ +#define SCALE_HI 0x0E /* Scaling Register, High */ +#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */ +#define BRIGHT 0x10 /* BRIGHTNESS Control Register */ +#define CONTRAST 0x11 /* CONTRAST Control Register */ +#define SHARPNESS 0x12 /* SHARPNESS Control Register I */ +#define SAT_U 0x13 /* Chroma (U) Gain Register */ +#define SAT_V 0x14 /* Chroma (V) Gain Register */ +#define HUE 0x15 /* Hue Control Register */ +#define CORING1 0x17 +#define CORING2 0x18 /* Coring and IF compensation */ +#define VBICNTL 0x19 /* VBI Control Register */ +#define ACNTL2 0x1A /* Analog Control 2 */ +#define OUTCTR2 0x1B /* Output Control 2 */ +#define SDT 0x1C /* Standard Selection */ +#define SDTR 0x1D /* Standard Recognition */ +#define TEST 0x1F /* Test Control Register */ +#define CLMPG 0x20 /* Clamping Gain */ +#define IAGC 0x21 /* Individual AGC Gain */ +#define AGCGAIN 0x22 /* AGC Gain */ +#define PEAKWT 0x23 /* White Peak Threshold */ +#define CLMPL 0x24 /* Clamp level */ +#define SYNCT 0x25 /* Sync Amplitude */ +#define MISSCNT 0x26 /* Sync Miss Count Register */ +#define PCLAMP 0x27 /* Clamp Position Register */ +#define VCNTL1 0x28 /* Vertical Control I */ +#define VCNTL2 0x29 /* Vertical Control II */ +#define CKILL 0x2A /* Color Killer Level Control */ +#define COMB 0x2B /* Comb Filter Control */ +#define LDLY 0x2C /* Luma Delay and H Filter Control */ +#define MISC1 0x2D /* Miscellaneous Control I */ +#define LOOP 0x2E /* LOOP Control Register */ +#define MISC2 0x2F /* Miscellaneous Control II */ +#define MVSN 0x30 /* Macrovision Detection */ +#define STATUS2 0x31 /* Chip STATUS II */ +#define HFREF 0x32 /* H monitor */ +#define CLMD 0x33 /* CLAMP MODE */ +#define IDCNTL 0x34 /* ID Detection Control */ +#define CLCNTL1 0x35 /* Clamp Control I */ +#define ANAPLLCTL 0x4C +#define VBIMIN 0x4D +#define HSLOWCTL 0x4E +#define WSS3 0x4F +#define FILLDATA 0x50 +#define SDID 0x51 +#define DID 0x52 +#define WSS1 0x53 +#define WSS2 0x54 +#define VVBI 0x55 +#define LCTL6 0x56 +#define LCTL7 0x57 +#define LCTL8 0x58 +#define LCTL9 0x59 +#define LCTL10 0x5A +#define LCTL11 0x5B +#define LCTL12 0x5C +#define LCTL13 0x5D +#define LCTL14 0x5E +#define LCTL15 0x5F +#define LCTL16 0x60 +#define LCTL17 0x61 +#define LCTL18 0x62 +#define LCTL19 0x63 +#define LCTL20 0x64 +#define LCTL21 0x65 +#define LCTL22 0x66 +#define LCTL23 0x67 +#define LCTL24 0x68 +#define LCTL25 0x69 +#define LCTL26 0x6A +#define HSGEGIN 0x6B +#define HSEND 0x6C +#define OVSDLY 0x6D +#define OVSEND 0x6E +#define VBIDELAY 0x6F + +/* + * register detail + */ + +/* INFORM */ +#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */ +#define FC27_FF 0x00 /* 0 : Square pixel mode. */ + /* Must use 24.54MHz for 60Hz field rate */ + /* source or 29.5MHz for 50Hz field rate */ +#define IFSEL_S 0x10 /* 01 : S-video decoding */ +#define IFSEL_C 0x00 /* 00 : Composite video decoding */ + /* Y input video selection */ +#define YSEL_M0 0x00 /* 00 : Mux0 selected */ +#define YSEL_M1 0x04 /* 01 : Mux1 selected */ +#define YSEL_M2 0x08 /* 10 : Mux2 selected */ +#define YSEL_M3 0x10 /* 11 : Mux3 selected */ + +/* OPFORM */ +#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */ + /* 1 : ITU-R-656 compatible data sequence format */ +#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */ + /* 1 : 16-bit YCrCb 4:2:2 output format.*/ +#define LLCMODE 0x20 /* 1 : LLC output mode. */ + /* 0 : free-run output mode */ +#define AINC 0x10 /* Serial interface auto-indexing control */ + /* 0 : auto-increment */ + /* 1 : non-auto */ +#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */ + /* 0 : Vertical out ctrl by HACTIVE and DVALID */ +#define OEN 0x04 /* Output Enable together with TRI_SEL. */ + +/* OUTCTR1 */ +#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */ +#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */ + /* VS pin output control */ +#define VSSL_VSYNC 0x00 /* 0 : VSYNC */ +#define VSSL_VACT 0x10 /* 1 : VACT */ +#define VSSL_FIELD 0x20 /* 2 : FIELD */ +#define VSSL_VVALID 0x30 /* 3 : VVALID */ +#define VSSL_ZERO 0x70 /* 7 : 0 */ +#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */ +#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/ + /* HS pin output control */ +#define HSSL_HACT 0x00 /* 0 : HACT */ +#define HSSL_HSYNC 0x01 /* 1 : HSYNC */ +#define HSSL_DVALID 0x02 /* 2 : DVALID */ +#define HSSL_HLOCK 0x03 /* 3 : HLOCK */ +#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */ +#define HSSL_ZERO 0x07 /* 7 : 0 */ + +/* ACNTL1 */ +#define SRESET 0x80 /* resets the device to its default state + * but all register content remain unchanged. + * This bit is self-resetting. + */ + +/* VBICNTL */ +/* RTSEL : control the real time signal +* output from the MPOUT pin +*/ +#define RTSEL_MASK 0x07 +#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */ +#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */ +#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */ +#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */ +#define RTSEL_MONO 0x04 /* 0100 = MONO */ +#define RTSEL_DET50 0x05 /* 0101 = DET50 */ +#define RTSEL_FIELD 0x06 /* 0110 = FIELD */ +#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */ + +/* + * structure + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +struct tw9910_scale_ctrl { + char *name; + unsigned short width; + unsigned short height; + u16 hscale; + u16 vscale; +}; + +struct tw9910_cropping_ctrl { + u16 vdelay; + u16 vactive; + u16 hdelay; + u16 hactive; +}; + +struct tw9910_hsync_ctrl { + u16 start; + u16 end; +}; + +struct tw9910_priv { + struct tw9910_video_info *info; + struct i2c_client *client; + struct soc_camera_device icd; + const struct tw9910_scale_ctrl *scale; +}; + +/* + * register settings + */ + +#define ENDMARKER { 0xff, 0xff } + +static const struct regval_list tw9910_default_regs[] = +{ + { OPFORM, 0x00 }, + { OUTCTR1, VSP_LO | VSSL_VVALID | HSP_HI | HSSL_HSYNC }, + ENDMARKER, +}; + +static const struct soc_camera_data_format tw9910_color_fmt[] = { + { + .name = "VYUY", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + } +}; + +static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = { + { + .name = "NTSC SQ", + .width = 640, + .height = 480, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "NTSC CCIR601", + .width = 720, + .height = 480, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "NTSC SQ (CIF)", + .width = 320, + .height = 240, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "NTSC CCIR601 (CIF)", + .width = 360, + .height = 240, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "NTSC SQ (QCIF)", + .width = 160, + .height = 120, + .hscale = 0x0400, + .vscale = 0x0400, + }, + { + .name = "NTSC CCIR601 (QCIF)", + .width = 180, + .height = 120, + .hscale = 0x0400, + .vscale = 0x0400, + }, +}; + +static const struct tw9910_scale_ctrl tw9910_pal_scales[] = { + { + .name = "PAL SQ", + .width = 768, + .height = 576, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "PAL CCIR601", + .width = 720, + .height = 576, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "PAL SQ (CIF)", + .width = 384, + .height = 288, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "PAL CCIR601 (CIF)", + .width = 360, + .height = 288, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "PAL SQ (QCIF)", + .width = 192, + .height = 144, + .hscale = 0x0400, + .vscale = 0x0400, + }, + { + .name = "PAL CCIR601 (QCIF)", + .width = 180, + .height = 144, + .hscale = 0x0400, + .vscale = 0x0400, + }, +}; + +static const struct tw9910_cropping_ctrl tw9910_cropping_ctrl = { + .vdelay = 0x0012, + .vactive = 0x00F0, + .hdelay = 0x0010, + .hactive = 0x02D0, +}; + +static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = { + .start = 0x0260, + .end = 0x0300, +}; + +/* + * general function + */ +static int tw9910_set_scale(struct i2c_client *client, + const struct tw9910_scale_ctrl *scale) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, SCALE_HI, + (scale->vscale & 0x0F00) >> 4 | + (scale->hscale & 0x0F00) >> 8); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, HSCALE_LO, + scale->hscale & 0x00FF); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, VSCALE_LO, + scale->vscale & 0x00FF); + + return ret; +} + +static int tw9910_set_cropping(struct i2c_client *client, + const struct tw9910_cropping_ctrl *cropping) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, CROP_HI, + (cropping->vdelay & 0x0300) >> 2 | + (cropping->vactive & 0x0300) >> 4 | + (cropping->hdelay & 0x0300) >> 6 | + (cropping->hactive & 0x0300) >> 8); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, VDELAY_LO, + cropping->vdelay & 0x00FF); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, VACTIVE_LO, + cropping->vactive & 0x00FF); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, HDELAY_LO, + cropping->hdelay & 0x00FF); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, HACTIVE_LO, + cropping->hactive & 0x00FF); + + return ret; +} + +static int tw9910_set_hsync(struct i2c_client *client, + const struct tw9910_hsync_ctrl *hsync) +{ + int ret; + + /* bit 10 - 3 */ + ret = i2c_smbus_write_byte_data(client, HSGEGIN, + (hsync->start & 0x07F8) >> 3); + if (ret < 0) + return ret; + + /* bit 10 - 3 */ + ret = i2c_smbus_write_byte_data(client, HSEND, + (hsync->end & 0x07F8) >> 3); + if (ret < 0) + return ret; + + /* bit 2 - 0 */ + ret = i2c_smbus_read_byte_data(client, HSLOWCTL); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, HSLOWCTL, + (ret & 0x88) | + (hsync->start & 0x0007) << 4 | + (hsync->end & 0x0007)); + + return ret; +} + +static int tw9910_write_array(struct i2c_client *client, + const struct regval_list *vals) +{ + while (vals->reg_num != 0xff) { + int ret = i2c_smbus_write_byte_data(client, + vals->reg_num, + vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + +static int tw9910_mask_set(struct i2c_client *client, u8 command, + u8 mask, u8 set) +{ + s32 val = i2c_smbus_read_byte_data(client, command); + + val &= ~mask; + val |= set; + + return i2c_smbus_write_byte_data(client, command, val); +} + +static void tw9910_reset(struct i2c_client *client) +{ + i2c_smbus_write_byte_data(client, ACNTL1, SRESET); + msleep(1); +} + +static const struct tw9910_scale_ctrl* +tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height) +{ + const struct tw9910_scale_ctrl *scale; + const struct tw9910_scale_ctrl *ret = NULL; + v4l2_std_id norm = icd->vdev->current_norm; + __u32 diff = 0xffffffff, tmp; + int size, i; + + if (norm & V4L2_STD_NTSC) { + scale = tw9910_ntsc_scales; + size = ARRAY_SIZE(tw9910_ntsc_scales); + } else if (norm & V4L2_STD_PAL) { + scale = tw9910_pal_scales; + size = ARRAY_SIZE(tw9910_pal_scales); + } else { + return NULL; + } + + for (i = 0; i < size; i++) { + tmp = abs(width - scale[i].width) + + abs(height - scale[i].height); + if (tmp < diff) { + diff = tmp; + ret = scale + i; + } + } + + return ret; +} + +/* + * soc_camera_ops function + */ +static int tw9910_init(struct soc_camera_device *icd) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + int ret = 0; + + if (priv->info->link.power) { + ret = priv->info->link.power(&priv->client->dev, 1); + if (ret < 0) + return ret; + } + + if (priv->info->link.reset) + ret = priv->info->link.reset(&priv->client->dev); + + return ret; +} + +static int tw9910_release(struct soc_camera_device *icd) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + int ret = 0; + + if (priv->info->link.power) + ret = priv->info->link.power(&priv->client->dev, 0); + + return ret; +} + +static int tw9910_start_capture(struct soc_camera_device *icd) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + + if (!priv->scale) { + dev_err(&icd->dev, "norm select error\n"); + return -EPERM; + } + + dev_dbg(&icd->dev, "%s %dx%d\n", + priv->scale->name, + priv->scale->width, + priv->scale->height); + + return 0; +} + +static int tw9910_stop_capture(struct soc_camera_device *icd) +{ + return 0; +} + +static int tw9910_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + struct soc_camera_link *icl = priv->client->dev.platform_data; + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +static int tw9910_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + id->ident = V4L2_IDENT_TW9910; + id->revision = 0; + + return 0; +} + +static int tw9910_set_std(struct soc_camera_device *icd, + v4l2_std_id *a) +{ + int ret = -EINVAL; + + if (*a & (V4L2_STD_NTSC | V4L2_STD_PAL)) + ret = 0; + + return ret; +} + +static int tw9910_enum_input(struct soc_camera_device *icd, + struct v4l2_input *inp) +{ + inp->type = V4L2_INPUT_TYPE_TUNER; + inp->std = V4L2_STD_UNKNOWN; + strcpy(inp->name, "Video"); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int tw9910_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + int ret; + + if (reg->reg > 0xff) + return -EINVAL; + + ret = i2c_smbus_read_byte_data(priv->client, reg->reg); + if (ret < 0) + return ret; + + /* ret = int + * reg->val = __u64 + */ + reg->val = (__u64)ret; + + return 0; +} + +static int tw9910_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + + if (reg->reg > 0xff || + reg->val > 0xff) + return -EINVAL; + + return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val); +} +#endif + +static int tw9910_set_fmt(struct soc_camera_device *icd, __u32 pixfmt, + struct v4l2_rect *rect) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + int ret = -EINVAL; + u8 val; + + /* + * select suitable norm + */ + priv->scale = tw9910_select_norm(icd, rect->width, rect->height); + if (!priv->scale) + return ret; + + /* + * reset hardware + */ + tw9910_reset(priv->client); + ret = tw9910_write_array(priv->client, tw9910_default_regs); + if (ret < 0) + return ret; + /* + * set bus width + */ + val = 0x00; + if (SOCAM_DATAWIDTH_16 == priv->info->buswidth) + val = LEN; + + ret = tw9910_mask_set(priv->client, OPFORM, LEN, val); + if (ret < 0) + return ret; + + /* + * select MPOUT behavior + */ + switch (priv->info->mpout) { + case TW9910_MPO_VLOSS: + val = RTSEL_VLOSS; break; + case TW9910_MPO_HLOCK: + val = RTSEL_HLOCK; break; + case TW9910_MPO_SLOCK: + val = RTSEL_SLOCK; break; + case TW9910_MPO_VLOCK: + val = RTSEL_VLOCK; break; + case TW9910_MPO_MONO: + val = RTSEL_MONO; break; + case TW9910_MPO_DET50: + val = RTSEL_DET50; break; + case TW9910_MPO_FIELD: + val = RTSEL_FIELD; break; + case TW9910_MPO_RTCO: + val = RTSEL_RTCO; break; + default: + val = 0; + } + + ret = tw9910_mask_set(priv->client, VBICNTL, RTSEL_MASK, val); + if (ret < 0) + return ret; + + /* + * set scale + */ + ret = tw9910_set_scale(priv->client, priv->scale); + if (ret < 0) + return ret; + + /* + * set cropping + */ + ret = tw9910_set_cropping(priv->client, &tw9910_cropping_ctrl); + if (ret < 0) + return ret; + + /* + * set hsync + */ + ret = tw9910_set_hsync(priv->client, &tw9910_hsync_ctrl); + + return ret; +} + +static int tw9910_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + const struct tw9910_scale_ctrl *scale; + + if (V4L2_FIELD_ANY == pix->field) { + pix->field = V4L2_FIELD_INTERLACED; + } else if (V4L2_FIELD_INTERLACED != pix->field) { + dev_err(&icd->dev, "Field type invalid.\n"); + return -EINVAL; + } + + /* + * select suitable norm + */ + scale = tw9910_select_norm(icd, pix->width, pix->height); + if (!scale) + return -EINVAL; + + pix->width = scale->width; + pix->height = scale->height; + + return 0; +} + +static int tw9910_video_probe(struct soc_camera_device *icd) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + s32 val; + int ret; + + /* + * We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. + */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* + * tw9910 only use 8 or 16 bit bus width + */ + if (SOCAM_DATAWIDTH_16 != priv->info->buswidth && + SOCAM_DATAWIDTH_8 != priv->info->buswidth) { + dev_err(&icd->dev, "bus width error\n"); + return -ENODEV; + } + + icd->formats = tw9910_color_fmt; + icd->num_formats = ARRAY_SIZE(tw9910_color_fmt); + + /* + * check and show Product ID + */ + val = i2c_smbus_read_byte_data(priv->client, ID); + if (0x0B != GET_ID(val) || + 0x00 != GET_ReV(val)) { + dev_err(&icd->dev, + "Product ID error %x:%x\n", GET_ID(val), GET_ReV(val)); + return -ENODEV; + } + + dev_info(&icd->dev, + "tw9910 Product ID %0x:%0x\n", GET_ID(val), GET_ReV(val)); + + ret = soc_camera_video_start(icd); + if (ret < 0) + return ret; + + icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL; + icd->vdev->current_norm = V4L2_STD_NTSC; + + return ret; +} + +static void tw9910_video_remove(struct soc_camera_device *icd) +{ + soc_camera_video_stop(icd); +} + +static struct soc_camera_ops tw9910_ops = { + .owner = THIS_MODULE, + .probe = tw9910_video_probe, + .remove = tw9910_video_remove, + .init = tw9910_init, + .release = tw9910_release, + .start_capture = tw9910_start_capture, + .stop_capture = tw9910_stop_capture, + .set_fmt = tw9910_set_fmt, + .try_fmt = tw9910_try_fmt, + .set_bus_param = tw9910_set_bus_param, + .query_bus_param = tw9910_query_bus_param, + .get_chip_id = tw9910_get_chip_id, + .set_std = tw9910_set_std, + .enum_input = tw9910_enum_input, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = tw9910_get_register, + .set_register = tw9910_set_register, +#endif +}; + +/* + * i2c_driver function + */ + +static int tw9910_probe(struct i2c_client *client, + const struct i2c_device_id *did) + +{ + struct tw9910_priv *priv; + struct tw9910_video_info *info; + struct soc_camera_device *icd; + const struct tw9910_scale_ctrl *scale; + int i, ret; + + info = client->dev.platform_data; + if (!info) + return -EINVAL; + + if (!i2c_check_functionality(to_i2c_adapter(client->dev.parent), + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "I2C-Adapter doesn't support " + "I2C_FUNC_SMBUS_BYTE_DATA\n"); + return -EIO; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->info = info; + priv->client = client; + i2c_set_clientdata(client, priv); + + icd = &priv->icd; + icd->ops = &tw9910_ops; + icd->control = &client->dev; + icd->iface = info->link.bus_id; + + /* + * set width and height + */ + icd->width_max = tw9910_ntsc_scales[0].width; /* set default */ + icd->width_min = tw9910_ntsc_scales[0].width; + icd->height_max = tw9910_ntsc_scales[0].height; + icd->height_min = tw9910_ntsc_scales[0].height; + + scale = tw9910_ntsc_scales; + for (i = 0; i < ARRAY_SIZE(tw9910_ntsc_scales); i++) { + icd->width_max = max(scale[i].width, icd->width_max); + icd->width_min = min(scale[i].width, icd->width_min); + icd->height_max = max(scale[i].height, icd->height_max); + icd->height_min = min(scale[i].height, icd->height_min); + } + scale = tw9910_pal_scales; + for (i = 0; i < ARRAY_SIZE(tw9910_pal_scales); i++) { + icd->width_max = max(scale[i].width, icd->width_max); + icd->width_min = min(scale[i].width, icd->width_min); + icd->height_max = max(scale[i].height, icd->height_max); + icd->height_min = min(scale[i].height, icd->height_min); + } + + ret = soc_camera_device_register(icd); + + if (ret) { + i2c_set_clientdata(client, NULL); + kfree(priv); + } + + return ret; +} + +static int tw9910_remove(struct i2c_client *client) +{ + struct tw9910_priv *priv = i2c_get_clientdata(client); + + soc_camera_device_unregister(&priv->icd); + i2c_set_clientdata(client, NULL); + kfree(priv); + return 0; +} + +static const struct i2c_device_id tw9910_id[] = { + { "tw9910", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tw9910_id); + +static struct i2c_driver tw9910_i2c_driver = { + .driver = { + .name = "tw9910", + }, + .probe = tw9910_probe, + .remove = tw9910_remove, + .id_table = tw9910_id, +}; + +/* + * module function + */ +static int __init tw9910_module_init(void) +{ + return i2c_add_driver(&tw9910_i2c_driver); +} + +static void __exit tw9910_module_exit(void) +{ + i2c_del_driver(&tw9910_i2c_driver); +} + +module_init(tw9910_module_init); +module_exit(tw9910_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for tw9910"); +MODULE_AUTHOR("Kuninori Morimoto"); +MODULE_LICENSE("GPL v2"); diff --git a/include/media/tw9910.h b/include/media/tw9910.h new file mode 100644 index 000000000000..73231e7880d8 --- /dev/null +++ b/include/media/tw9910.h @@ -0,0 +1,39 @@ +/* + * tw9910 Driver header + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on ov772x.h + * + * Copyright (C) Kuninori Morimoto + * + * 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. + */ + +#ifndef __TW9910_H__ +#define __TW9910_H__ + +#include + +enum tw9910_mpout_pin { + TW9910_MPO_VLOSS, + TW9910_MPO_HLOCK, + TW9910_MPO_SLOCK, + TW9910_MPO_VLOCK, + TW9910_MPO_MONO, + TW9910_MPO_DET50, + TW9910_MPO_FIELD, + TW9910_MPO_RTCO, +}; + +struct tw9910_video_info { + unsigned long buswidth; + enum tw9910_mpout_pin mpout; + struct soc_camera_link link; +}; + + +#endif /* __TW9910_H__ */ diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 30448cd51751..f9d42ad99b4f 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -87,6 +87,9 @@ enum { /* module wm8775: just ident 8775 */ V4L2_IDENT_WM8775 = 8775, + /* module tw9910: just ident 9910 */ + V4L2_IDENT_TW9910 = 9910, + /* module cs53132a: just ident 53132 */ V4L2_IDENT_CS53l32A = 53132, -- cgit v1.2.3-55-g7522 From 4e96fd088cf6fb95ba4b212e5e72bac1e6d34e79 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 29 Dec 2008 06:04:59 -0300 Subject: V4L/DVB (10099): soc-camera: add support for MT9T031 CMOS camera sensor from Micron This camera is rather similar to MT9M001, but also has a couple of enhanced features, like pixel binning. create mode 100644 drivers/media/video/mt9t031.c Signed-off-by: Guennadi Liakhovetski Acked-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 6 + drivers/media/video/Makefile | 1 + drivers/media/video/mt9t031.c | 736 ++++++++++++++++++++++++++++++++++++++++ include/media/v4l2-chip-ident.h | 1 + 4 files changed, 744 insertions(+) create mode 100644 drivers/media/video/mt9t031.c (limited to 'include') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9785eeb1ed44..19cf3b8f67c4 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -741,6 +741,12 @@ config SOC_CAMERA_MT9M111 help This driver supports MT9M111 and MT9M112 cameras from Micron +config SOC_CAMERA_MT9T031 + tristate "mt9t031 support" + depends on SOC_CAMERA && I2C + help + This driver supports MT9T031 cameras from Micron. + config SOC_CAMERA_MT9V022 tristate "mt9v022 support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 40c69afcde7a..1611c33b1aee 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -137,6 +137,7 @@ obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o +obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c new file mode 100644 index 000000000000..1a9d53966d06 --- /dev/null +++ b/drivers/media/video/mt9t031.c @@ -0,0 +1,736 @@ +/* + * Driver for MT9T031 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering + * + * 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 +#include +#include +#include + +#include +#include +#include + +/* mt9t031 i2c address 0x5d + * The platform has to define i2c_board_info + * and call i2c_register_board_info() */ + +/* mt9t031 selected register addresses */ +#define MT9T031_CHIP_VERSION 0x00 +#define MT9T031_ROW_START 0x01 +#define MT9T031_COLUMN_START 0x02 +#define MT9T031_WINDOW_HEIGHT 0x03 +#define MT9T031_WINDOW_WIDTH 0x04 +#define MT9T031_HORIZONTAL_BLANKING 0x05 +#define MT9T031_VERTICAL_BLANKING 0x06 +#define MT9T031_OUTPUT_CONTROL 0x07 +#define MT9T031_SHUTTER_WIDTH_UPPER 0x08 +#define MT9T031_SHUTTER_WIDTH 0x09 +#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a +#define MT9T031_FRAME_RESTART 0x0b +#define MT9T031_SHUTTER_DELAY 0x0c +#define MT9T031_RESET 0x0d +#define MT9T031_READ_MODE_1 0x1e +#define MT9T031_READ_MODE_2 0x20 +#define MT9T031_READ_MODE_3 0x21 +#define MT9T031_ROW_ADDRESS_MODE 0x22 +#define MT9T031_COLUMN_ADDRESS_MODE 0x23 +#define MT9T031_GLOBAL_GAIN 0x35 +#define MT9T031_CHIP_ENABLE 0xF8 + +#define MT9T031_MAX_HEIGHT 1536 +#define MT9T031_MAX_WIDTH 2048 +#define MT9T031_MIN_HEIGHT 2 +#define MT9T031_MIN_WIDTH 2 +#define MT9T031_HORIZONTAL_BLANK 142 +#define MT9T031_VERTICAL_BLANK 25 +#define MT9T031_COLUMN_SKIP 32 +#define MT9T031_ROW_SKIP 20 + +#define MT9T031_BUS_PARAM (SOCAM_PCLK_SAMPLE_RISING | \ + SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | \ + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | \ + SOCAM_MASTER | SOCAM_DATAWIDTH_10) + +static const struct soc_camera_data_format mt9t031_colour_formats[] = { + { + .name = "Bayer (sRGB) 10 bit", + .depth = 10, + .fourcc = V4L2_PIX_FMT_SGRBG10, + .colorspace = V4L2_COLORSPACE_SRGB, + } +}; + +struct mt9t031 { + struct i2c_client *client; + struct soc_camera_device icd; + int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */ + unsigned char autoexposure; + u16 xskip; + u16 yskip; +}; + +static int reg_read(struct soc_camera_device *icd, const u8 reg) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = mt9t031->client; + s32 data = i2c_smbus_read_word_data(client, reg); + return data < 0 ? data : swab16(data); +} + +static int reg_write(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + return i2c_smbus_write_word_data(mt9t031->client, reg, swab16(data)); +} + +static int reg_set(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret | data); +} + +static int reg_clear(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret & ~data); +} + +static int set_shutter(struct soc_camera_device *icd, const u32 data) +{ + int ret; + + ret = reg_write(icd, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); + + if (ret >= 0) + ret = reg_write(icd, MT9T031_SHUTTER_WIDTH, data & 0xffff); + + return ret; +} + +static int get_shutter(struct soc_camera_device *icd, u32 *data) +{ + int ret; + + ret = reg_read(icd, MT9T031_SHUTTER_WIDTH_UPPER); + *data = ret << 16; + + if (ret >= 0) + ret = reg_read(icd, MT9T031_SHUTTER_WIDTH); + *data |= ret & 0xffff; + + return ret < 0 ? ret : 0; +} + +static int mt9t031_init(struct soc_camera_device *icd) +{ + int ret; + + /* Disable chip output, synchronous option update */ + dev_dbg(icd->vdev->parent, "%s\n", __func__); + + ret = reg_write(icd, MT9T031_RESET, 1); + if (ret >= 0) + ret = reg_write(icd, MT9T031_RESET, 0); + if (ret >= 0) + ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3); + + return ret >= 0 ? 0 : -EIO; +} + +static int mt9t031_release(struct soc_camera_device *icd) +{ + /* Disable the chip */ + reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3); + return 0; +} + +static int mt9t031_start_capture(struct soc_camera_device *icd) +{ + /* Switch to master "normal" mode */ + if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 3) < 0) + return -EIO; + return 0; +} + +static int mt9t031_stop_capture(struct soc_camera_device *icd) +{ + /* Stop sensor readout */ + if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3) < 0) + return -EIO; + return 0; +} + +static int mt9t031_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + /* The caller should have queried our parameters, check anyway */ + if (flags & ~MT9T031_BUS_PARAM) + return -EINVAL; + + if (flags & SOCAM_PCLK_SAMPLE_FALLING) + reg_set(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + else + reg_clear(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + + return 0; +} + +static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct soc_camera_link *icl = mt9t031->client->dev.platform_data; + + return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM); +} + +static int mt9t031_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + int ret; + const u16 hblank = MT9T031_HORIZONTAL_BLANK, + vblank = MT9T031_VERTICAL_BLANK; + u16 xbin, xskip = mt9t031->xskip, ybin, yskip = mt9t031->yskip, + width = rect->width * xskip, height = rect->height * yskip; + + if (pixfmt) { + /* S_FMT - use binning and skipping for scaling, recalculate */ + /* Is this more optimal than just a division? */ + for (xskip = 8; xskip > 1; xskip--) + if (rect->width * xskip <= icd->width_max) + break; + + for (yskip = 8; yskip > 1; yskip--) + if (rect->height * yskip <= icd->height_max) + break; + + width = rect->width * xskip; + height = rect->height * yskip; + + dev_dbg(&icd->dev, "xskip %u, width %u, yskip %u, height %u\n", + xskip, width, yskip, height); + } + + xbin = min(xskip, (u16)3); + ybin = min(yskip, (u16)3); + + /* Make sure we don't exceed frame limits */ + if (rect->left + width > icd->width_max) + rect->left = (icd->width_max - width) / 2; + + if (rect->top + height > icd->height_max) + rect->top = (icd->height_max - height) / 2; + + /* Could just do roundup(rect->left, [xy]bin); but this is cheaper */ + switch (xbin) { + case 2: + rect->left = (rect->left + 1) & ~1; + break; + case 3: + rect->left = roundup(rect->left, 3); + } + + switch (ybin) { + case 2: + rect->top = (rect->top + 1) & ~1; + break; + case 3: + rect->top = roundup(rect->top, 3); + } + + /* Blanking and start values - default... */ + ret = reg_write(icd, MT9T031_HORIZONTAL_BLANKING, hblank); + if (ret >= 0) + ret = reg_write(icd, MT9T031_VERTICAL_BLANKING, vblank); + + if (pixfmt) { + /* Binning, skipping */ + if (ret >= 0) + ret = reg_write(icd, MT9T031_COLUMN_ADDRESS_MODE, + ((xbin - 1) << 4) | (xskip - 1)); + if (ret >= 0) + ret = reg_write(icd, MT9T031_ROW_ADDRESS_MODE, + ((ybin - 1) << 4) | (yskip - 1)); + } + dev_dbg(&icd->dev, "new left %u, top %u\n", rect->left, rect->top); + + /* The caller provides a supported format, as guaranteed by + * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */ + if (ret >= 0) + ret = reg_write(icd, MT9T031_COLUMN_START, rect->left); + if (ret >= 0) + ret = reg_write(icd, MT9T031_ROW_START, rect->top); + if (ret >= 0) + ret = reg_write(icd, MT9T031_WINDOW_WIDTH, width - 1); + if (ret >= 0) + ret = reg_write(icd, MT9T031_WINDOW_HEIGHT, + height + icd->y_skip_top - 1); + if (ret >= 0 && mt9t031->autoexposure) { + ret = set_shutter(icd, height + icd->y_skip_top + vblank); + if (ret >= 0) { + const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; + const struct v4l2_queryctrl *qctrl = + soc_camera_find_qctrl(icd->ops, + V4L2_CID_EXPOSURE); + icd->exposure = (shutter_max / 2 + (height + + icd->y_skip_top + vblank - 1) * + (qctrl->maximum - qctrl->minimum)) / + shutter_max + qctrl->minimum; + } + } + + if (!ret && pixfmt) { + mt9t031->xskip = xskip; + mt9t031->yskip = yskip; + } + + return ret < 0 ? ret : 0; +} + +static int mt9t031_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (pix->height < icd->height_min) + pix->height = icd->height_min; + if (pix->height > icd->height_max) + pix->height = icd->height_max; + if (pix->width < icd->width_min) + pix->width = icd->width_min; + if (pix->width > icd->width_max) + pix->width = icd->width_max; + + pix->width &= ~0x01; /* has to be even */ + pix->height &= ~0x01; /* has to be even */ + + return 0; +} + +static int mt9t031_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + + if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match_chip != mt9t031->client->addr) + return -ENODEV; + + id->ident = mt9t031->model; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9t031_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9t031->client->addr) + return -ENODEV; + + reg->val = reg_read(icd, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9t031_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9t031->client->addr) + return -ENODEV; + + if (reg_write(icd, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static const struct v4l2_queryctrl mt9t031_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 1, + .maximum = 255, + .step = 1, + .default_value = 255, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + } +}; + +static int mt9t031_video_probe(struct soc_camera_device *); +static void mt9t031_video_remove(struct soc_camera_device *); +static int mt9t031_get_control(struct soc_camera_device *, struct v4l2_control *); +static int mt9t031_set_control(struct soc_camera_device *, struct v4l2_control *); + +static struct soc_camera_ops mt9t031_ops = { + .owner = THIS_MODULE, + .probe = mt9t031_video_probe, + .remove = mt9t031_video_remove, + .init = mt9t031_init, + .release = mt9t031_release, + .start_capture = mt9t031_start_capture, + .stop_capture = mt9t031_stop_capture, + .set_fmt = mt9t031_set_fmt, + .try_fmt = mt9t031_try_fmt, + .set_bus_param = mt9t031_set_bus_param, + .query_bus_param = mt9t031_query_bus_param, + .controls = mt9t031_controls, + .num_controls = ARRAY_SIZE(mt9t031_controls), + .get_control = mt9t031_get_control, + .set_control = mt9t031_set_control, + .get_chip_id = mt9t031_get_chip_id, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = mt9t031_get_register, + .set_register = mt9t031_set_register, +#endif +}; + +static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + data = reg_read(icd, MT9T031_READ_MODE_2); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x8000); + break; + case V4L2_CID_HFLIP: + data = reg_read(icd, MT9T031_READ_MODE_2); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x4000); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = mt9t031->autoexposure; + break; + } + return 0; +} + +static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + const struct v4l2_queryctrl *qctrl; + int data; + + qctrl = soc_camera_find_qctrl(&mt9t031_ops, ctrl->id); + + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->value) + data = reg_set(icd, MT9T031_READ_MODE_2, 0x8000); + else + data = reg_clear(icd, MT9T031_READ_MODE_2, 0x8000); + if (data < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + data = reg_set(icd, MT9T031_READ_MODE_2, 0x4000); + else + data = reg_clear(icd, MT9T031_READ_MODE_2, 0x4000); + if (data < 0) + return -EIO; + break; + case V4L2_CID_GAIN: + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + /* See Datasheet Table 7, Gain settings. */ + if (ctrl->value <= qctrl->default_value) { + /* Pack it into 0..1 step 0.125, register values 0..8 */ + unsigned long range = qctrl->default_value - qctrl->minimum; + data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range; + + dev_dbg(&icd->dev, "Setting gain %d\n", data); + data = reg_write(icd, MT9T031_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } else { + /* Pack it into 1.125..15 variable step, register values 9..67 */ + /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ + unsigned long range = qctrl->maximum - qctrl->default_value - 1; + unsigned long gain = ((ctrl->value - qctrl->default_value - 1) * + 111 + range / 2) / range + 9; + + if (gain <= 32) + data = gain; + else if (gain <= 64) + data = ((gain - 32) * 16 + 16) / 32 + 80; + else + data = ((gain - 64) * 7 + 28) / 56 + 96; + + dev_dbg(&icd->dev, "Setting gain from %d to %d\n", + reg_read(icd, MT9T031_GLOBAL_GAIN), data); + data = reg_write(icd, MT9T031_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } + + /* Success */ + icd->gain = ctrl->value; + break; + case V4L2_CID_EXPOSURE: + /* mt9t031 has maximum == default */ + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + else { + const unsigned long range = qctrl->maximum - qctrl->minimum; + const u32 shutter = ((ctrl->value - qctrl->minimum) * 1048 + + range / 2) / range + 1; + u32 old; + + get_shutter(icd, &old); + dev_dbg(&icd->dev, "Setting shutter width from %u to %u\n", + old, shutter); + if (set_shutter(icd, shutter) < 0) + return -EIO; + icd->exposure = ctrl->value; + mt9t031->autoexposure = 0; + } + break; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->value) { + const u16 vblank = MT9T031_VERTICAL_BLANK; + const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; + if (set_shutter(icd, icd->height + + icd->y_skip_top + vblank) < 0) + return -EIO; + qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); + icd->exposure = (shutter_max / 2 + (icd->height + + icd->y_skip_top + vblank - 1) * + (qctrl->maximum - qctrl->minimum)) / + shutter_max + qctrl->minimum; + mt9t031->autoexposure = 1; + } else + mt9t031->autoexposure = 0; + break; + } + return 0; +} + +/* Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one */ +static int mt9t031_video_probe(struct soc_camera_device *icd) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + s32 data; + int ret; + + /* We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* Enable the chip */ + data = reg_write(icd, MT9T031_CHIP_ENABLE, 1); + dev_dbg(&icd->dev, "write: %d\n", data); + + /* Read out the chip version register */ + data = reg_read(icd, MT9T031_CHIP_VERSION); + + switch (data) { + case 0x1621: + mt9t031->model = V4L2_IDENT_MT9T031; + icd->formats = mt9t031_colour_formats; + icd->num_formats = ARRAY_SIZE(mt9t031_colour_formats); + break; + default: + ret = -ENODEV; + dev_err(&icd->dev, + "No MT9T031 chip detected, register read %x\n", data); + goto ei2c; + } + + dev_info(&icd->dev, "Detected a MT9T031 chip ID %x\n", data); + + /* Now that we know the model, we can start video */ + ret = soc_camera_video_start(icd); + if (ret) + goto evstart; + + return 0; + +evstart: +ei2c: + return ret; +} + +static void mt9t031_video_remove(struct soc_camera_device *icd) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + + dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9t031->client->addr, + icd->dev.parent, icd->vdev); + soc_camera_video_stop(icd); +} + +static int mt9t031_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9t031 *mt9t031; + struct soc_camera_device *icd; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl = client->dev.platform_data; + int ret; + + if (!icl) { + dev_err(&client->dev, "MT9T031 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL); + if (!mt9t031) + return -ENOMEM; + + mt9t031->client = client; + i2c_set_clientdata(client, mt9t031); + + /* Second stage probe - when a capture adapter is there */ + icd = &mt9t031->icd; + icd->ops = &mt9t031_ops; + icd->control = &client->dev; + icd->x_min = MT9T031_COLUMN_SKIP; + icd->y_min = MT9T031_ROW_SKIP; + icd->x_current = icd->x_min; + icd->y_current = icd->y_min; + icd->width_min = MT9T031_MIN_WIDTH; + icd->width_max = MT9T031_MAX_WIDTH; + icd->height_min = MT9T031_MIN_HEIGHT; + icd->height_max = MT9T031_MAX_HEIGHT; + icd->y_skip_top = 0; + icd->iface = icl->bus_id; + /* Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width */ + mt9t031->autoexposure = 1; + + mt9t031->xskip = 1; + mt9t031->yskip = 1; + + ret = soc_camera_device_register(icd); + if (ret) + goto eisdr; + + return 0; + +eisdr: + i2c_set_clientdata(client, NULL); + kfree(mt9t031); + return ret; +} + +static int mt9t031_remove(struct i2c_client *client) +{ + struct mt9t031 *mt9t031 = i2c_get_clientdata(client); + + soc_camera_device_unregister(&mt9t031->icd); + i2c_set_clientdata(client, NULL); + kfree(mt9t031); + + return 0; +} + +static const struct i2c_device_id mt9t031_id[] = { + { "mt9t031", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9t031_id); + +static struct i2c_driver mt9t031_i2c_driver = { + .driver = { + .name = "mt9t031", + }, + .probe = mt9t031_probe, + .remove = mt9t031_remove, + .id_table = mt9t031_id, +}; + +static int __init mt9t031_mod_init(void) +{ + return i2c_add_driver(&mt9t031_i2c_driver); +} + +static void __exit mt9t031_mod_exit(void) +{ + i2c_del_driver(&mt9t031_i2c_driver); +} + +module_init(mt9t031_mod_init); +module_exit(mt9t031_mod_exit); + +MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL v2"); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index f9d42ad99b4f..43dbb659f1f5 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -177,6 +177,7 @@ enum { V4L2_IDENT_MT9M112 = 45008, V4L2_IDENT_MT9V022IX7ATC = 45010, /* No way to detect "normal" I77ATx */ V4L2_IDENT_MT9V022IX7ATM = 45015, /* and "lead free" IA7ATx chips */ + V4L2_IDENT_MT9T031 = 45020, }; #endif -- cgit v1.2.3-55-g7522 From 92ab7886119da6375a983713eedab444e32094f6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 29 Dec 2008 19:15:43 -0300 Subject: V4L/DVB (10110): v4l2-ioctl: Fix warnings when using .unlocked_ioctl = __video_ioctl2 This patch fixes this warning: drivers/media/video/gspca/gspca.c:1811: warning: initialization from incompatible pointer type The reason is that the returned argument should be a long, not an integer. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ioctl.c | 2 +- include/media/v4l2-ioctl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index bef4286933a1..b063381f4b3b 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -1852,7 +1852,7 @@ static int __video_do_ioctl(struct file *file, return ret; } -int __video_ioctl2(struct file *file, +long __video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg) { char sbuf[128]; diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index c884432f9383..fcdb58c4ce07 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -297,7 +297,7 @@ extern int video_usercopy(struct file *file, unsigned int cmd, /* Standard handlers for V4L ioctl's */ /* This prototype is used on fops.unlocked_ioctl */ -extern int __video_ioctl2(struct file *file, +extern long __video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg); /* This prototype is used on fops.ioctl -- cgit v1.2.3-55-g7522