blob: 5b17ca9cad1181ef8f465367e232b21bf8300eff [file] [log] [blame]
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001/*
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -08002 em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08003
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -08004 Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
5 Markus Rechberger <mrechberger@gmail.com>
Mauro Carvalho Chehab2e7c6dc2006-04-03 07:53:40 -03006 Mauro Carvalho Chehab <mchehab@infradead.org>
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -08007 Sascha Sommer <saschasommer@freenet.de>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08008
Mauro Carvalho Chehab439090d2006-01-23 17:10:54 -02009 Some parts based on SN9C10x PC Camera Controllers GPL driver made
10 by Luca Risolia <luca.risolia@studio.unibo.it>
11
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080012 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27#include <linux/init.h>
28#include <linux/list.h>
29#include <linux/module.h>
30#include <linux/kernel.h>
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -020031#include <linux/bitmap.h>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080032#include <linux/usb.h>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080033#include <linux/i2c.h>
Mauro Carvalho Chehabb296fc62005-11-08 21:38:37 -080034#include <linux/version.h>
Trent Piepho6d35c8f2007-11-01 01:16:09 -030035#include <linux/mm.h>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080036#include <linux/video_decoder.h>
Ingo Molnar1e4baed2006-01-15 07:52:23 -020037#include <linux/mutex.h>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080038
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -080039#include "em28xx.h"
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -020040#include <media/v4l2-common.h>
Hans Verkuil2474ed42006-03-19 12:35:57 -030041#include <media/msp3400.h>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080042
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -080043#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
44 "Markus Rechberger <mrechberger@gmail.com>, " \
Mauro Carvalho Chehab2e7c6dc2006-04-03 07:53:40 -030045 "Mauro Carvalho Chehab <mchehab@infradead.org>, " \
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -080046 "Sascha Sommer <saschasommer@freenet.de>"
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080047
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -080048#define DRIVER_NAME "em28xx"
49#define DRIVER_DESC "Empia em28xx based USB video device driver"
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -080050#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 0, 1)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080051
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -080052#define em28xx_videodbg(fmt, arg...) do {\
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -080053 if (video_debug) \
54 printk(KERN_INFO "%s %s :"fmt, \
Jean Delvaref85c6572005-12-19 08:53:59 -020055 dev->name, __FUNCTION__ , ##arg); } while (0)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080056
57MODULE_AUTHOR(DRIVER_AUTHOR);
58MODULE_DESCRIPTION(DRIVER_DESC);
59MODULE_LICENSE("GPL");
60
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -080061static LIST_HEAD(em28xx_devlist);
Markus Rechberger9c755412005-11-08 21:37:52 -080062
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -080063static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -020064static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
65static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -080066module_param_array(card, int, NULL, 0444);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -020067module_param_array(video_nr, int, NULL, 0444);
68module_param_array(vbi_nr, int, NULL, 0444);
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -080069MODULE_PARM_DESC(card,"card type");
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -020070MODULE_PARM_DESC(video_nr,"video device numbers");
71MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -080072
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080073static unsigned int video_debug = 0;
74module_param(video_debug,int,0644);
75MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
76
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -020077/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
78static unsigned long em28xx_devused;
79
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080080/* supported tv norms */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -080081static struct em28xx_tvnorm tvnorms[] = {
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080082 {
83 .name = "PAL",
84 .id = V4L2_STD_PAL,
85 .mode = VIDEO_MODE_PAL,
86 }, {
87 .name = "NTSC",
88 .id = V4L2_STD_NTSC,
89 .mode = VIDEO_MODE_NTSC,
90 }, {
91 .name = "SECAM",
92 .id = V4L2_STD_SECAM,
93 .mode = VIDEO_MODE_SECAM,
94 }, {
95 .name = "PAL-M",
96 .id = V4L2_STD_PAL_M,
97 .mode = VIDEO_MODE_PAL,
98 }
99};
100
101#define TVNORMS ARRAY_SIZE(tvnorms)
102
103/* supported controls */
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -0200104/* Common to all boards */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800105static struct v4l2_queryctrl em28xx_qctrl[] = {
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800106 {
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -0200107 .id = V4L2_CID_AUDIO_VOLUME,
108 .type = V4L2_CTRL_TYPE_INTEGER,
109 .name = "Volume",
110 .minimum = 0x0,
111 .maximum = 0x1f,
112 .step = 0x1,
113 .default_value = 0x1f,
114 .flags = 0,
115 },{
116 .id = V4L2_CID_AUDIO_MUTE,
117 .type = V4L2_CTRL_TYPE_BOOLEAN,
118 .name = "Mute",
119 .minimum = 0,
120 .maximum = 1,
121 .step = 1,
122 .default_value = 1,
123 .flags = 0,
124 }
125};
126
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800127static struct usb_driver em28xx_usb_driver;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800128
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800129
130/********************* v4l2 interface ******************************************/
131
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800132/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800133 * em28xx_config()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800134 * inits registers with sane defaults
135 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800136static int em28xx_config(struct em28xx *dev)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800137{
138
139 /* Sets I2C speed to 100 KHz */
Sascha Sommer2b2c93a2007-11-03 16:48:01 -0300140 if (!dev->is_em2800)
141 em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800142
143 /* enable vbi capturing */
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200144
Markus Rechberger9475fb12006-02-27 00:07:34 -0300145/* em28xx_write_regs_req(dev,0x00,0x0e,"\xC0",1); audio register */
146/* em28xx_write_regs_req(dev,0x00,0x0f,"\x80",1); clk register */
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200147 em28xx_write_regs_req(dev,0x00,0x11,"\x51",1);
148
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800149 em28xx_audio_usb_mute(dev, 1);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800150 dev->mute = 1; /* maybe not the right place... */
151 dev->volume = 0x1f;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800152 em28xx_audio_analog_set(dev);
153 em28xx_audio_analog_setup(dev);
154 em28xx_outfmt_set_yuv422(dev);
155 em28xx_colorlevels_set_default(dev);
156 em28xx_compression_disable(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800157
158 return 0;
159}
160
161/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800162 * em28xx_config_i2c()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800163 * configure i2c attached devices
164 */
Adrian Bunk943a4902005-12-01 00:51:35 -0800165static void em28xx_config_i2c(struct em28xx *dev)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800166{
Hans Verkuilc7c0b342006-04-02 13:35:00 -0300167 struct v4l2_routing route;
168
169 route.input = INPUT(dev->ctl_input)->vmux;
170 route.output = 0;
Al Viro663d1ba2006-10-10 22:48:37 +0100171 em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL);
Hans Verkuilc7c0b342006-04-02 13:35:00 -0300172 em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -0300173 em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800174}
175
176/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800177 * em28xx_empty_framequeues()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800178 * prepare queues for incoming and outgoing frames
179 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800180static void em28xx_empty_framequeues(struct em28xx *dev)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800181{
182 u32 i;
183
184 INIT_LIST_HEAD(&dev->inqueue);
185 INIT_LIST_HEAD(&dev->outqueue);
186
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800187 for (i = 0; i < EM28XX_NUM_FRAMES; i++) {
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800188 dev->frame[i].state = F_UNUSED;
189 dev->frame[i].buf.bytesused = 0;
190 }
191}
192
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800193static void video_mux(struct em28xx *dev, int index)
194{
Hans Verkuilc7c0b342006-04-02 13:35:00 -0300195 int ainput;
196 struct v4l2_routing route;
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800197
Hans Verkuilc7c0b342006-04-02 13:35:00 -0300198 route.input = INPUT(index)->vmux;
199 route.output = 0;
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800200 dev->ctl_input = index;
201 dev->ctl_ainput = INPUT(index)->amux;
202
Hans Verkuilc7c0b342006-04-02 13:35:00 -0300203 em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800204
Hans Verkuilc7c0b342006-04-02 13:35:00 -0300205 em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,route.input,dev->ctl_ainput);
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800206
207 if (dev->has_msp34xx) {
Mauro Carvalho Chehab9bb13a62006-01-09 15:25:37 -0200208 if (dev->i2s_speed)
209 em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed);
Hans Verkuil2474ed42006-03-19 12:35:57 -0300210 route.input = dev->ctl_ainput;
Hans Verkuil07151722006-04-01 18:03:23 -0300211 route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
Hans Verkuil2474ed42006-03-19 12:35:57 -0300212 /* Note: this is msp3400 specific */
213 em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route);
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800214 ainput = EM28XX_AUDIO_SRC_TUNER;
215 em28xx_audio_source(dev, ainput);
216 } else {
217 switch (dev->ctl_ainput) {
Markus Rechberger9475fb12006-02-27 00:07:34 -0300218 case 0:
219 ainput = EM28XX_AUDIO_SRC_TUNER;
220 break;
221 default:
222 ainput = EM28XX_AUDIO_SRC_LINE;
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800223 }
224 em28xx_audio_source(dev, ainput);
225 }
226}
227
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800228/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800229 * em28xx_v4l2_open()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800230 * inits the device and starts isoc transfer
231 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800232static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800233{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800234 int minor = iminor(inode);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800235 int errCode = 0;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800236 struct em28xx *h,*dev = NULL;
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300237 struct em28xx_fh *fh;
Markus Rechberger9c755412005-11-08 21:37:52 -0800238
Trent Piephoa991f442007-10-10 05:37:43 -0300239 list_for_each_entry(h, &em28xx_devlist, devlist) {
Markus Rechberger9c755412005-11-08 21:37:52 -0800240 if (h->vdev->minor == minor) {
241 dev = h;
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200242 dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
243 }
244 if (h->vbi_dev->minor == minor) {
245 dev = h;
246 dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
Markus Rechberger9c755412005-11-08 21:37:52 -0800247 }
248 }
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200249 if (NULL == dev)
250 return -ENODEV;
Markus Rechberger9c755412005-11-08 21:37:52 -0800251
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200252 em28xx_videodbg("open minor=%d type=%s users=%d\n",
253 minor,v4l2_type_names[dev->type],dev->users);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800254
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300255 fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800256
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300257 if (!fh) {
258 em28xx_errdev("em28xx-video.c: Out of memory?!\n");
259 return -ENOMEM;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800260 }
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300261 mutex_lock(&dev->lock);
262 fh->dev = dev;
263 filp->private_data = fh;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800264
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300265 if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200266 em28xx_set_alternate(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800267
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200268 dev->width = norm_maxw(dev);
269 dev->height = norm_maxh(dev);
270 dev->frame_size = dev->width * dev->height * 2;
271 dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
272 dev->bytesperline = dev->width * 2;
273 dev->hscale = 0;
274 dev->vscale = 0;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800275
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200276 em28xx_capture_start(dev, 1);
277 em28xx_resolution_set(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800278
Markus Rechbergere5d4a562006-02-07 06:49:13 -0200279
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200280 /* start the transfer */
281 errCode = em28xx_init_isoc(dev);
282 if (errCode)
283 goto err;
284
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300285 em28xx_empty_framequeues(dev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200286 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800287
288 dev->users++;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800289
Ingo Molnar3593cab2006-02-07 06:49:14 -0200290err:
291 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800292 return errCode;
293}
294
295/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800296 * em28xx_realease_resources()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800297 * unregisters the v4l2,i2c and usb devices
298 * called when the device gets disconected or at module unload
299*/
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800300static void em28xx_release_resources(struct em28xx *dev)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800301{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800302
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200303 /*FIXME: I2C IR should be disconnected */
304
305 em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
306 dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
307 dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
Markus Rechberger9c755412005-11-08 21:37:52 -0800308 list_del(&dev->devlist);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800309 video_unregister_device(dev->vdev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200310 video_unregister_device(dev->vbi_dev);
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800311 em28xx_i2c_unregister(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800312 usb_put_dev(dev->udev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200313
314
315 /* Mark device as unused */
316 em28xx_devused&=~(1<<dev->devno);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800317}
318
319/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800320 * em28xx_v4l2_close()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800321 * stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls
322 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800323static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800324{
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300325 struct em28xx_fh *fh = filp->private_data;
326 struct em28xx *dev = fh->dev;
327 int errCode;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800328
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800329 em28xx_videodbg("users=%d\n", dev->users);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800330
Ingo Molnar3593cab2006-02-07 06:49:14 -0200331 mutex_lock(&dev->lock);
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300332 if (fh->reader == 1)
333 fh->reader = 0;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800334
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300335 if (dev->users == 1) {
336 dev->reader = 0;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800337
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300338 em28xx_uninit_isoc(dev);
339 em28xx_release_buffers(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800340
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300341 /* the device is already disconnect,
342 free the remaining resources */
343 if (dev->state & DEV_DISCONNECTED) {
344 em28xx_release_resources(dev);
345 mutex_unlock(&dev->lock);
346 kfree(dev);
347 return 0;
348 }
349
350 /* set alternate 0 */
351 dev->alt = 0;
352 em28xx_videodbg("setting alternate 0\n");
353 errCode = usb_set_interface(dev->udev, 0, 0);
354 if (errCode < 0) {
355 em28xx_errdev("cannot change alternate number to "
356 "0 (error=%i)\n", errCode);
357 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800358 }
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300359 kfree(fh);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800360 dev->users--;
361 wake_up_interruptible_nr(&dev->open, 1);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200362 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800363 return 0;
364}
365
366/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800367 * em28xx_v4l2_read()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800368 * will allocate buffers when called for the first time
369 */
370static ssize_t
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800371em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800372 loff_t * f_pos)
373{
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800374 struct em28xx_frame_t *f, *i;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800375 unsigned long lock_flags;
376 int ret = 0;
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300377 struct em28xx_fh *fh = filp->private_data;
378 struct em28xx *dev = fh->dev;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800379
Sascha Sommer5a804152007-11-03 21:22:38 -0300380 mutex_lock(&dev->lock);
381
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300382 if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200383 em28xx_videodbg("V4l2_Buf_type_videocapture is set\n");
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300384
385 if (dev->reader > 0 && fh->reader == 0) {
386 mutex_unlock(&dev->lock);
387 return -EBUSY;
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200388 }
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300389
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200390 if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
391 em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n");
392 em28xx_videodbg("not supported yet! ...\n");
393 if (copy_to_user(buf, "", 1)) {
Sascha Sommer5a804152007-11-03 21:22:38 -0300394 mutex_unlock(&dev->lock);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200395 return -EFAULT;
396 }
Sascha Sommer5a804152007-11-03 21:22:38 -0300397 mutex_unlock(&dev->lock);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200398 return (1);
399 }
400 if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
401 em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n");
402 em28xx_videodbg("not supported yet! ...\n");
403 if (copy_to_user(buf, "", 1)) {
Sascha Sommer5a804152007-11-03 21:22:38 -0300404 mutex_unlock(&dev->lock);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200405 return -EFAULT;
406 }
Sascha Sommer5a804152007-11-03 21:22:38 -0300407 mutex_unlock(&dev->lock);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200408 return (1);
409 }
410
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800411 if (dev->state & DEV_DISCONNECTED) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800412 em28xx_videodbg("device not present\n");
Sascha Sommer5a804152007-11-03 21:22:38 -0300413 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800414 return -ENODEV;
415 }
416
417 if (dev->state & DEV_MISCONFIGURED) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800418 em28xx_videodbg("device misconfigured; close and open it again\n");
Sascha Sommer5a804152007-11-03 21:22:38 -0300419 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800420 return -EIO;
421 }
422
423 if (dev->io == IO_MMAP) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800424 em28xx_videodbg ("IO method is set to mmap; close and open"
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800425 " the device again to choose the read method\n");
Sascha Sommer5a804152007-11-03 21:22:38 -0300426 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800427 return -EINVAL;
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300428 } else {
429 dev->reader = 1;
430 fh->reader = 1;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800431 }
432
433 if (dev->io == IO_NONE) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800434 if (!em28xx_request_buffers(dev, EM28XX_NUM_READ_FRAMES)) {
435 em28xx_errdev("read failed, not enough memory\n");
Sascha Sommer5a804152007-11-03 21:22:38 -0300436 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800437 return -ENOMEM;
438 }
439 dev->io = IO_READ;
440 dev->stream = STREAM_ON;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800441 em28xx_queue_unusedframes(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800442 }
443
444 if (!count) {
Sascha Sommer5a804152007-11-03 21:22:38 -0300445 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800446 return 0;
447 }
448
449 if (list_empty(&dev->outqueue)) {
450 if (filp->f_flags & O_NONBLOCK) {
Sascha Sommer5a804152007-11-03 21:22:38 -0300451 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800452 return -EAGAIN;
453 }
454 ret = wait_event_interruptible
455 (dev->wait_frame,
456 (!list_empty(&dev->outqueue)) ||
457 (dev->state & DEV_DISCONNECTED));
458 if (ret) {
Sascha Sommer5a804152007-11-03 21:22:38 -0300459 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800460 return ret;
461 }
462 if (dev->state & DEV_DISCONNECTED) {
Sascha Sommer5a804152007-11-03 21:22:38 -0300463 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800464 return -ENODEV;
465 }
466 }
467
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800468 f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800469
470 spin_lock_irqsave(&dev->queue_lock, lock_flags);
471 list_for_each_entry(i, &dev->outqueue, frame)
472 i->state = F_UNUSED;
473 INIT_LIST_HEAD(&dev->outqueue);
474 spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
475
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800476 em28xx_queue_unusedframes(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800477
478 if (count > f->buf.length)
479 count = f->buf.length;
480
481 if (copy_to_user(buf, f->bufmem, count)) {
Sascha Sommer5a804152007-11-03 21:22:38 -0300482 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800483 return -EFAULT;
484 }
485 *f_pos += count;
486
Sascha Sommer5a804152007-11-03 21:22:38 -0300487 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800488
489 return count;
490}
491
492/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800493 * em28xx_v4l2_poll()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800494 * will allocate buffers when called for the first time
495 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800496static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800497{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800498 unsigned int mask = 0;
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300499 struct em28xx_fh *fh = filp->private_data;
500 struct em28xx *dev = fh->dev;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800501
Sascha Sommer5a804152007-11-03 21:22:38 -0300502 mutex_lock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800503
504 if (dev->state & DEV_DISCONNECTED) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800505 em28xx_videodbg("device not present\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800506 } else if (dev->state & DEV_MISCONFIGURED) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800507 em28xx_videodbg("device is misconfigured; close and open it again\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800508 } else {
509 if (dev->io == IO_NONE) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800510 if (!em28xx_request_buffers
511 (dev, EM28XX_NUM_READ_FRAMES)) {
512 em28xx_warn
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800513 ("poll() failed, not enough memory\n");
514 } else {
515 dev->io = IO_READ;
516 dev->stream = STREAM_ON;
517 }
518 }
519
520 if (dev->io == IO_READ) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800521 em28xx_queue_unusedframes(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800522 poll_wait(filp, &dev->wait_frame, wait);
523
524 if (!list_empty(&dev->outqueue))
525 mask |= POLLIN | POLLRDNORM;
526
Sascha Sommer5a804152007-11-03 21:22:38 -0300527 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800528
529 return mask;
530 }
531 }
532
Sascha Sommer5a804152007-11-03 21:22:38 -0300533 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800534 return POLLERR;
535}
536
537/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800538 * em28xx_vm_open()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800539 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800540static void em28xx_vm_open(struct vm_area_struct *vma)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800541{
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800542 struct em28xx_frame_t *f = vma->vm_private_data;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800543 f->vma_use_count++;
544}
545
546/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800547 * em28xx_vm_close()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800548 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800549static void em28xx_vm_close(struct vm_area_struct *vma)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800550{
551 /* NOTE: buffers are not freed here */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800552 struct em28xx_frame_t *f = vma->vm_private_data;
Mauro Carvalho Chehab63337dd2007-11-10 10:26:20 -0300553
554 if (f->vma_use_count)
555 f->vma_use_count--;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800556}
557
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800558static struct vm_operations_struct em28xx_vm_ops = {
559 .open = em28xx_vm_open,
560 .close = em28xx_vm_close,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800561};
562
563/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800564 * em28xx_v4l2_mmap()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800565 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800566static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800567{
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300568 struct em28xx_fh *fh = filp->private_data;
569 struct em28xx *dev = fh->dev;
570 unsigned long size = vma->vm_end - vma->vm_start;
571 unsigned long start = vma->vm_start;
572 void *pos;
573 u32 i;
Markus Rechberger9c755412005-11-08 21:37:52 -0800574
Sascha Sommer5a804152007-11-03 21:22:38 -0300575 mutex_lock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800576
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300577 if (dev->reader > 0 && fh->reader == 0) {
578 mutex_unlock(&dev->lock);
579 return -EBUSY;
580 } else {
581 dev->reader = 1;
582 fh->reader = 1;
583 }
584
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800585 if (dev->state & DEV_DISCONNECTED) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800586 em28xx_videodbg("mmap: device not present\n");
Sascha Sommer5a804152007-11-03 21:22:38 -0300587 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800588 return -ENODEV;
589 }
590
591 if (dev->state & DEV_MISCONFIGURED) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800592 em28xx_videodbg ("mmap: Device is misconfigured; close and "
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800593 "open it again\n");
Sascha Sommer5a804152007-11-03 21:22:38 -0300594 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800595 return -EIO;
596 }
597
598 if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
599 size != PAGE_ALIGN(dev->frame[0].buf.length)) {
Sascha Sommer5a804152007-11-03 21:22:38 -0300600 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800601 return -EINVAL;
602 }
603
604 for (i = 0; i < dev->num_frames; i++) {
605 if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
606 break;
607 }
608 if (i == dev->num_frames) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800609 em28xx_videodbg("mmap: user supplied mapping address is out of range\n");
Sascha Sommer5a804152007-11-03 21:22:38 -0300610 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800611 return -EINVAL;
612 }
613
614 /* VM_IO is eventually going to replace PageReserved altogether */
615 vma->vm_flags |= VM_IO;
616 vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
617
Sascha Sommer3639c862005-12-12 00:37:30 -0800618 pos = dev->frame[i].bufmem;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800619 while (size > 0) { /* size is page-aligned */
Sascha Sommer3639c862005-12-12 00:37:30 -0800620 if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
621 em28xx_videodbg("mmap: vm_insert_page failed\n");
Sascha Sommer5a804152007-11-03 21:22:38 -0300622 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800623 return -EAGAIN;
624 }
625 start += PAGE_SIZE;
626 pos += PAGE_SIZE;
627 size -= PAGE_SIZE;
628 }
629
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800630 vma->vm_ops = &em28xx_vm_ops;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800631 vma->vm_private_data = &dev->frame[i];
632
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800633 em28xx_vm_open(vma);
Sascha Sommer5a804152007-11-03 21:22:38 -0300634 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800635 return 0;
636}
637
638/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800639 * em28xx_get_ctrl()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800640 * return the current saturation, brightness or contrast, mute state
641 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800642static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800643{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800644 switch (ctrl->id) {
645 case V4L2_CID_AUDIO_MUTE:
646 ctrl->value = dev->mute;
647 return 0;
648 case V4L2_CID_AUDIO_VOLUME:
649 ctrl->value = dev->volume;
650 return 0;
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -0200651 default:
652 return -EINVAL;
653 }
654}
655
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800656/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800657 * em28xx_set_ctrl()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800658 * mute or set new saturation, brightness or contrast
659 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800660static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800661{
662 switch (ctrl->id) {
663 case V4L2_CID_AUDIO_MUTE:
664 if (ctrl->value != dev->mute) {
665 dev->mute = ctrl->value;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800666 em28xx_audio_usb_mute(dev, ctrl->value);
667 return em28xx_audio_analog_set(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800668 }
669 return 0;
670 case V4L2_CID_AUDIO_VOLUME:
671 dev->volume = ctrl->value;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800672 return em28xx_audio_analog_set(dev);
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -0200673 default:
674 return -EINVAL;
675 }
676}
677
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800678/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800679 * em28xx_stream_interrupt()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800680 * stops streaming
681 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800682static int em28xx_stream_interrupt(struct em28xx *dev)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800683{
684 int ret = 0;
685
686 /* stop reading from the device */
687
688 dev->stream = STREAM_INTERRUPT;
689 ret = wait_event_timeout(dev->wait_stream,
690 (dev->stream == STREAM_OFF) ||
691 (dev->state & DEV_DISCONNECTED),
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800692 EM28XX_URB_TIMEOUT);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800693 if (dev->state & DEV_DISCONNECTED)
694 return -ENODEV;
695 else if (ret) {
696 dev->state |= DEV_MISCONFIGURED;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800697 em28xx_videodbg("device is misconfigured; close and "
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200698 "open /dev/video%d again\n",
699 dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800700 return ret;
701 }
702
703 return 0;
704}
705
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800706static int em28xx_set_norm(struct em28xx *dev, int width, int height)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800707{
708 unsigned int hscale, vscale;
709 unsigned int maxh, maxw;
710
711 maxw = norm_maxw(dev);
712 maxh = norm_maxh(dev);
713
714 /* width must even because of the YUYV format */
715 /* height must be even because of interlacing */
716 height &= 0xfffe;
717 width &= 0xfffe;
718
719 if (height < 32)
720 height = 32;
721 if (height > maxh)
722 height = maxh;
723 if (width < 48)
724 width = 48;
725 if (width > maxw)
726 width = maxw;
727
728 if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000)
729 hscale = 0x3fff;
730 width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
731
732 if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000)
733 vscale = 0x3fff;
734 height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
735
736 /* set new image size */
737 dev->width = width;
738 dev->height = height;
739 dev->frame_size = dev->width * dev->height * 2;
740 dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
741 dev->bytesperline = dev->width * 2;
742 dev->hscale = hscale;
743 dev->vscale = vscale;
744
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800745 em28xx_resolution_set(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800746
747 return 0;
748}
749
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200750static int em28xx_get_fmt(struct em28xx *dev, struct v4l2_format *format)
751{
752 em28xx_videodbg("VIDIOC_G_FMT: type=%s\n",
753 (format->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
754 "V4L2_BUF_TYPE_VIDEO_CAPTURE" :
755 (format->type ==V4L2_BUF_TYPE_VBI_CAPTURE) ?
756 "V4L2_BUF_TYPE_VBI_CAPTURE" :
757 (format->type ==V4L2_CAP_SLICED_VBI_CAPTURE) ?
758 "V4L2_BUF_TYPE_SLICED_VBI_CAPTURE " :
759 "not supported");
760
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200761 switch (format->type) {
762 case V4L2_BUF_TYPE_VIDEO_CAPTURE:
763 {
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200764 format->fmt.pix.width = dev->width;
765 format->fmt.pix.height = dev->height;
766 format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
767 format->fmt.pix.bytesperline = dev->bytesperline;
768 format->fmt.pix.sizeimage = dev->frame_size;
769 format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
770 format->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
771
772 em28xx_videodbg("VIDIOC_G_FMT: %dx%d\n", dev->width,
773 dev->height);
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200774 break;
775 }
776
777 case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
778 {
779 format->fmt.sliced.service_set=0;
780
781 em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format);
782
783 if (format->fmt.sliced.service_set==0)
784 return -EINVAL;
785
786 break;
787 }
788
789 default:
790 return -EINVAL;
791 }
792 return (0);
793}
794
795static int em28xx_set_fmt(struct em28xx *dev, unsigned int cmd, struct v4l2_format *format)
796{
797 u32 i;
798 int ret = 0;
799 int width = format->fmt.pix.width;
800 int height = format->fmt.pix.height;
801 unsigned int hscale, vscale;
802 unsigned int maxh, maxw;
803
804 maxw = norm_maxw(dev);
805 maxh = norm_maxh(dev);
806
807 em28xx_videodbg("%s: type=%s\n",
808 cmd == VIDIOC_TRY_FMT ?
809 "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
810 format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
811 "V4L2_BUF_TYPE_VIDEO_CAPTURE" :
812 format->type == V4L2_BUF_TYPE_VBI_CAPTURE ?
813 "V4L2_BUF_TYPE_VBI_CAPTURE " :
814 "not supported");
815
816 if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
817 em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format);
818
819 if (format->fmt.sliced.service_set==0)
820 return -EINVAL;
821
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200822 return 0;
823 }
824
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200825
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200826 if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
827 return -EINVAL;
828
829 em28xx_videodbg("%s: requested %dx%d\n",
830 cmd == VIDIOC_TRY_FMT ?
831 "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
832 format->fmt.pix.width, format->fmt.pix.height);
833
834 /* FIXME: Move some code away from here */
835 /* width must even because of the YUYV format */
836 /* height must be even because of interlacing */
837 height &= 0xfffe;
838 width &= 0xfffe;
839
840 if (height < 32)
841 height = 32;
842 if (height > maxh)
843 height = maxh;
844 if (width < 48)
845 width = 48;
846 if (width > maxw)
847 width = maxw;
848
849 if(dev->is_em2800){
850 /* the em2800 can only scale down to 50% */
851 if(height % (maxh / 2))
852 height=maxh;
853 if(width % (maxw / 2))
854 width=maxw;
855 /* according to empiatech support */
856 /* the MaxPacketSize is to small to support */
857 /* framesizes larger than 640x480 @ 30 fps */
858 /* or 640x576 @ 25 fps. As this would cut */
859 /* of a part of the image we prefer */
860 /* 360x576 or 360x480 for now */
861 if(width == maxw && height == maxh)
862 width /= 2;
863 }
864
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200865 if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000)
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200866 hscale = 0x3fff;
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200867
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200868 width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
869
870 if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000)
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200871 vscale = 0x3fff;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200872
873 height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200874
875 format->fmt.pix.width = width;
876 format->fmt.pix.height = height;
877 format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
878 format->fmt.pix.bytesperline = width * 2;
879 format->fmt.pix.sizeimage = width * 2 * height;
880 format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
881 format->fmt.pix.field = V4L2_FIELD_INTERLACED;
882
883 em28xx_videodbg("%s: returned %dx%d (%d, %d)\n",
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200884 cmd == VIDIOC_TRY_FMT ?
885 "VIDIOC_TRY_FMT" :"VIDIOC_S_FMT",
886 format->fmt.pix.width, format->fmt.pix.height, hscale, vscale);
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200887
888 if (cmd == VIDIOC_TRY_FMT)
889 return 0;
890
891 for (i = 0; i < dev->num_frames; i++)
892 if (dev->frame[i].vma_use_count) {
893 em28xx_videodbg("VIDIOC_S_FMT failed. "
894 "Unmap the buffers first.\n");
895 return -EINVAL;
896 }
897
898 /* stop io in case it is already in progress */
899 if (dev->stream == STREAM_ON) {
Robert P. J. Day3a4fa0a2007-10-19 23:10:43 +0200900 em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n");
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200901 if ((ret = em28xx_stream_interrupt(dev)))
902 return ret;
903 }
904
905 em28xx_release_buffers(dev);
906 dev->io = IO_NONE;
907
908 /* set new image size */
909 dev->width = width;
910 dev->height = height;
911 dev->frame_size = dev->width * dev->height * 2;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200912 dev->field_size = dev->frame_size >> 1;
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200913 dev->bytesperline = dev->width * 2;
914 dev->hscale = hscale;
915 dev->vscale = vscale;
916 em28xx_uninit_isoc(dev);
917 em28xx_set_alternate(dev);
918 em28xx_capture_start(dev, 1);
919 em28xx_resolution_set(dev);
920 em28xx_init_isoc(dev);
921
922 return 0;
923}
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200924
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800925/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800926 * em28xx_v4l2_do_ioctl()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800927 * This function is _not_ called directly, but from
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800928 * em28xx_v4l2_ioctl. Userspace
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800929 * copying is done already, arg is a kernel pointer.
930 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800931static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
932 struct em28xx *dev, unsigned int cmd, void *arg,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800933 v4l2_kioctl driver_ioctl)
934{
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -0300935 struct em28xx_fh *fh = filp->private_data;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800936 int ret;
937
938 switch (cmd) {
939 /* ---------- tv norms ---------- */
940 case VIDIOC_ENUMSTD:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200941 {
942 struct v4l2_standard *e = arg;
943 unsigned int i;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800944
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200945 i = e->index;
946 if (i >= TVNORMS)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800947 return -EINVAL;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200948 ret = v4l2_video_std_construct(e, tvnorms[e->index].id,
949 tvnorms[e->index].name);
950 e->index = i;
951 if (ret < 0)
952 return ret;
953 return 0;
954 }
955 case VIDIOC_G_STD:
956 {
957 v4l2_std_id *id = arg;
958
959 *id = dev->tvnorm->id;
960 return 0;
961 }
962 case VIDIOC_S_STD:
963 {
964 v4l2_std_id *id = arg;
965 unsigned int i;
966
967 for (i = 0; i < TVNORMS; i++)
968 if (*id == tvnorms[i].id)
969 break;
970 if (i == TVNORMS)
971 for (i = 0; i < TVNORMS; i++)
972 if (*id & tvnorms[i].id)
973 break;
974 if (i == TVNORMS)
975 return -EINVAL;
976
Ingo Molnar3593cab2006-02-07 06:49:14 -0200977 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200978 dev->tvnorm = &tvnorms[i];
979
980 em28xx_set_norm(dev, dev->width, dev->height);
981
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200982 em28xx_i2c_call_clients(dev, VIDIOC_S_STD,
983 &dev->tvnorm->id);
984
Ingo Molnar3593cab2006-02-07 06:49:14 -0200985 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200986
987 return 0;
988 }
989
990 /* ------ input switching ---------- */
991 case VIDIOC_ENUMINPUT:
992 {
993 struct v4l2_input *i = arg;
994 unsigned int n;
995 static const char *iname[] = {
996 [EM28XX_VMUX_COMPOSITE1] = "Composite1",
997 [EM28XX_VMUX_COMPOSITE2] = "Composite2",
998 [EM28XX_VMUX_COMPOSITE3] = "Composite3",
999 [EM28XX_VMUX_COMPOSITE4] = "Composite4",
1000 [EM28XX_VMUX_SVIDEO] = "S-Video",
1001 [EM28XX_VMUX_TELEVISION] = "Television",
1002 [EM28XX_VMUX_CABLE] = "Cable TV",
1003 [EM28XX_VMUX_DVB] = "DVB",
1004 [EM28XX_VMUX_DEBUG] = "for debug only",
1005 };
1006
1007 n = i->index;
1008 if (n >= MAX_EM28XX_INPUT)
1009 return -EINVAL;
1010 if (0 == INPUT(n)->type)
1011 return -EINVAL;
1012 memset(i, 0, sizeof(*i));
1013 i->index = n;
1014 i->type = V4L2_INPUT_TYPE_CAMERA;
1015 strcpy(i->name, iname[INPUT(n)->type]);
1016 if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
1017 (EM28XX_VMUX_CABLE == INPUT(n)->type))
1018 i->type = V4L2_INPUT_TYPE_TUNER;
1019 for (n = 0; n < ARRAY_SIZE(tvnorms); n++)
1020 i->std |= tvnorms[n].id;
1021 return 0;
1022 }
1023 case VIDIOC_G_INPUT:
1024 {
1025 int *i = arg;
1026 *i = dev->ctl_input;
1027
1028 return 0;
1029 }
1030 case VIDIOC_S_INPUT:
1031 {
1032 int *index = arg;
1033
1034 if (*index >= MAX_EM28XX_INPUT)
1035 return -EINVAL;
1036 if (0 == INPUT(*index)->type)
1037 return -EINVAL;
1038
Ingo Molnar3593cab2006-02-07 06:49:14 -02001039 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001040 video_mux(dev, *index);
Ingo Molnar3593cab2006-02-07 06:49:14 -02001041 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001042
1043 return 0;
1044 }
1045 case VIDIOC_G_AUDIO:
1046 {
1047 struct v4l2_audio *a = arg;
1048 unsigned int index = a->index;
1049
1050 if (a->index > 1)
1051 return -EINVAL;
1052 memset(a, 0, sizeof(*a));
1053 index = dev->ctl_ainput;
1054
1055 if (index == 0) {
1056 strcpy(a->name, "Television");
1057 } else {
1058 strcpy(a->name, "Line In");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001059 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001060 a->capability = V4L2_AUDCAP_STEREO;
1061 a->index = index;
1062 return 0;
1063 }
1064 case VIDIOC_S_AUDIO:
1065 {
1066 struct v4l2_audio *a = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001067
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001068 if (a->index != dev->ctl_ainput)
1069 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001070
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001071 return 0;
1072 }
1073
1074 /* --- controls ---------------------------------------------- */
1075 case VIDIOC_QUERYCTRL:
1076 {
1077 struct v4l2_queryctrl *qc = arg;
1078 int i, id=qc->id;
1079
1080 memset(qc,0,sizeof(*qc));
1081 qc->id=id;
1082
1083 if (!dev->has_msp34xx) {
1084 for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
1085 if (qc->id && qc->id == em28xx_qctrl[i].id) {
1086 memcpy(qc, &(em28xx_qctrl[i]),
1087 sizeof(*qc));
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -02001088 return 0;
1089 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001090 }
1091 }
Sascha Sommer5a804152007-11-03 21:22:38 -03001092 mutex_lock(&dev->lock);
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -03001093 em28xx_i2c_call_clients(dev,cmd,qc);
Sascha Sommer5a804152007-11-03 21:22:38 -03001094 mutex_unlock(&dev->lock);
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -03001095 if (qc->type)
1096 return 0;
1097 else
1098 return -EINVAL;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001099 }
1100 case VIDIOC_G_CTRL:
1101 {
1102 struct v4l2_control *ctrl = arg;
1103 int retval=-EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001104
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001105 if (!dev->has_msp34xx)
1106 retval=em28xx_get_ctrl(dev, ctrl);
1107 if (retval==-EINVAL) {
Sascha Sommer5a804152007-11-03 21:22:38 -03001108 mutex_lock(&dev->lock);
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -03001109 em28xx_i2c_call_clients(dev,cmd,arg);
Sascha Sommer5a804152007-11-03 21:22:38 -03001110 mutex_unlock(&dev->lock);
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -03001111 return 0;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001112 } else return retval;
1113 }
1114 case VIDIOC_S_CTRL:
1115 {
1116 struct v4l2_control *ctrl = arg;
1117 u8 i;
Sascha Sommer5a804152007-11-03 21:22:38 -03001118 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001119
1120 if (!dev->has_msp34xx){
1121 for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
1122 if (ctrl->id == em28xx_qctrl[i].id) {
Sascha Sommer5a804152007-11-03 21:22:38 -03001123 int retval=-EINVAL;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001124 if (ctrl->value <
1125 em28xx_qctrl[i].minimum
1126 || ctrl->value >
1127 em28xx_qctrl[i].maximum)
1128 return -ERANGE;
Sascha Sommer5a804152007-11-03 21:22:38 -03001129 retval = em28xx_set_ctrl(dev, ctrl);
1130 mutex_unlock(&dev->lock);
1131 return retval;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001132 }
1133 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001134 }
1135
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -03001136 em28xx_i2c_call_clients(dev,cmd,arg);
Sascha Sommer5a804152007-11-03 21:22:38 -03001137 mutex_unlock(&dev->lock);
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -03001138 return 0;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001139 }
1140 /* --- tuner ioctls ------------------------------------------ */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001141 case VIDIOC_G_TUNER:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001142 {
1143 struct v4l2_tuner *t = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001144
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001145 if (0 != t->index)
1146 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001147
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001148 memset(t, 0, sizeof(*t));
1149 strcpy(t->name, "Tuner");
Ingo Molnar3593cab2006-02-07 06:49:14 -02001150 mutex_lock(&dev->lock);
Hans Verkuilab4cecf2006-04-01 16:40:21 -03001151 /* let clients fill in the remainder of this struct */
1152 em28xx_i2c_call_clients(dev, cmd, t);
Ingo Molnar3593cab2006-02-07 06:49:14 -02001153 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001154 em28xx_videodbg("VIDIO_G_TUNER: signal=%x, afc=%x\n", t->signal,
1155 t->afc);
1156 return 0;
1157 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001158 case VIDIOC_S_TUNER:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001159 {
1160 struct v4l2_tuner *t = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001161
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001162 if (0 != t->index)
1163 return -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -02001164 mutex_lock(&dev->lock);
Hans Verkuilab4cecf2006-04-01 16:40:21 -03001165 /* let clients handle this */
1166 em28xx_i2c_call_clients(dev, cmd, t);
Ingo Molnar3593cab2006-02-07 06:49:14 -02001167 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001168 return 0;
1169 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001170 case VIDIOC_G_FREQUENCY:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001171 {
1172 struct v4l2_frequency *f = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001173
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001174 memset(f, 0, sizeof(*f));
1175 f->type = V4L2_TUNER_ANALOG_TV;
1176 f->frequency = dev->ctl_freq;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001177
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001178 return 0;
1179 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001180 case VIDIOC_S_FREQUENCY:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001181 {
1182 struct v4l2_frequency *f = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001183
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001184 if (0 != f->tuner)
1185 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001186
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001187 if (V4L2_TUNER_ANALOG_TV != f->type)
1188 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001189
Ingo Molnar3593cab2006-02-07 06:49:14 -02001190 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001191 dev->ctl_freq = f->frequency;
1192 em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
Ingo Molnar3593cab2006-02-07 06:49:14 -02001193 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001194 return 0;
1195 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001196 case VIDIOC_CROPCAP:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001197 {
1198 struct v4l2_cropcap *cc = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001199
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001200 if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1201 return -EINVAL;
1202 cc->bounds.left = 0;
1203 cc->bounds.top = 0;
1204 cc->bounds.width = dev->width;
1205 cc->bounds.height = dev->height;
1206 cc->defrect = cc->bounds;
1207 cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
1208 cc->pixelaspect.denominator = 59;
1209 return 0;
1210 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001211 case VIDIOC_STREAMON:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001212 {
1213 int *type = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001214
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001215 if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE
1216 || dev->io != IO_MMAP)
1217 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001218
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001219 if (list_empty(&dev->inqueue))
1220 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001221
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001222 dev->stream = STREAM_ON; /* FIXME: Start video capture here? */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001223
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001224 em28xx_videodbg("VIDIOC_STREAMON: starting stream\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001225
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001226 return 0;
1227 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001228 case VIDIOC_STREAMOFF:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001229 {
1230 int *type = arg;
1231 int ret;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001232
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001233 if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE
1234 || dev->io != IO_MMAP)
1235 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001236
Sascha Sommer5a804152007-11-03 21:22:38 -03001237 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001238 if (dev->stream == STREAM_ON) {
1239 em28xx_videodbg ("VIDIOC_STREAMOFF: interrupting stream\n");
Sascha Sommer5a804152007-11-03 21:22:38 -03001240 if ((ret = em28xx_stream_interrupt(dev))){
1241 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001242 return ret;
Sascha Sommer5a804152007-11-03 21:22:38 -03001243 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001244 }
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -03001245
1246 fh->reader = 0;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001247 em28xx_empty_framequeues(dev);
Sascha Sommer5a804152007-11-03 21:22:38 -03001248 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001249
1250 return 0;
1251 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001252 default:
1253 return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
1254 driver_ioctl);
1255 }
1256 return 0;
1257}
1258
1259/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001260 * em28xx_v4l2_do_ioctl()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001261 * This function is _not_ called directly, but from
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001262 * em28xx_v4l2_ioctl. Userspace
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001263 * copying is done already, arg is a kernel pointer.
1264 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001265static int em28xx_video_do_ioctl(struct inode *inode, struct file *filp,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001266 unsigned int cmd, void *arg)
1267{
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -03001268 struct em28xx_fh *fh = filp->private_data;
1269 struct em28xx *dev = fh->dev;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001270
1271 if (!dev)
1272 return -ENODEV;
1273
1274 if (video_debug > 1)
Michael Krufky5e453dc2006-01-09 15:32:31 -02001275 v4l_print_ioctl(dev->name,cmd);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001276
1277 switch (cmd) {
1278
1279 /* --- capabilities ------------------------------------------ */
1280 case VIDIOC_QUERYCAP:
1281 {
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001282 struct v4l2_capability *cap = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001283
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001284 memset(cap, 0, sizeof(*cap));
1285 strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
1286 strlcpy(cap->card, em28xx_boards[dev->model].name,
1287 sizeof(cap->card));
1288 strlcpy(cap->bus_info, dev->udev->dev.bus_id,
1289 sizeof(cap->bus_info));
1290 cap->version = EM28XX_VERSION_CODE;
1291 cap->capabilities =
1292 V4L2_CAP_SLICED_VBI_CAPTURE |
1293 V4L2_CAP_VIDEO_CAPTURE |
1294 V4L2_CAP_AUDIO |
1295 V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
1296 if (dev->has_tuner)
1297 cap->capabilities |= V4L2_CAP_TUNER;
1298 return 0;
1299 }
1300 /* --- capture ioctls ---------------------------------------- */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001301 case VIDIOC_ENUM_FMT:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001302 {
1303 struct v4l2_fmtdesc *fmtd = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001304
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001305 if (fmtd->index != 0)
1306 return -EINVAL;
1307 memset(fmtd, 0, sizeof(*fmtd));
1308 fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1309 strcpy(fmtd->description, "Packed YUY2");
1310 fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
1311 memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
1312 return 0;
1313 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001314 case VIDIOC_G_FMT:
Sascha Sommer5a804152007-11-03 21:22:38 -03001315 {
1316 int retval;
1317 mutex_lock(&dev->lock);
1318 retval = em28xx_get_fmt(dev, (struct v4l2_format *) arg);
1319 mutex_unlock(&dev->lock);
1320 return retval;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001321
Sascha Sommer5a804152007-11-03 21:22:38 -03001322 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001323 case VIDIOC_TRY_FMT:
1324 case VIDIOC_S_FMT:
Sascha Sommer5a804152007-11-03 21:22:38 -03001325 {
1326 int retval;
1327 mutex_lock(&dev->lock);
1328 retval = em28xx_set_fmt(dev, cmd, (struct v4l2_format *)arg);
1329 mutex_unlock(&dev->lock);
1330 return retval;
1331 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001332
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001333 case VIDIOC_REQBUFS:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001334 {
1335 struct v4l2_requestbuffers *rb = arg;
1336 u32 i;
1337 int ret;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001338
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001339 if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
1340 rb->memory != V4L2_MEMORY_MMAP)
1341 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001342
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001343 if (dev->io == IO_READ) {
1344 em28xx_videodbg ("method is set to read;"
1345 " close and open the device again to"
1346 " choose the mmap I/O method\n");
1347 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001348 }
1349
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001350 for (i = 0; i < dev->num_frames; i++)
1351 if (dev->frame[i].vma_use_count) {
1352 em28xx_videodbg ("VIDIOC_REQBUFS failed; previous buffers are still mapped\n");
1353 return -EINVAL;
1354 }
1355
Sascha Sommer5a804152007-11-03 21:22:38 -03001356 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001357 if (dev->stream == STREAM_ON) {
1358 em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n");
Sascha Sommer5a804152007-11-03 21:22:38 -03001359 if ((ret = em28xx_stream_interrupt(dev))){
1360 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001361 return ret;
Sascha Sommer5a804152007-11-03 21:22:38 -03001362 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001363 }
1364
1365 em28xx_empty_framequeues(dev);
1366
1367 em28xx_release_buffers(dev);
1368 if (rb->count)
1369 rb->count =
1370 em28xx_request_buffers(dev, rb->count);
1371
1372 dev->frame_current = NULL;
1373
1374 em28xx_videodbg ("VIDIOC_REQBUFS: setting io method to mmap: num bufs %i\n",
1375 rb->count);
1376 dev->io = rb->count ? IO_MMAP : IO_NONE;
Sascha Sommer5a804152007-11-03 21:22:38 -03001377 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001378 return 0;
1379 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001380 case VIDIOC_QUERYBUF:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001381 {
1382 struct v4l2_buffer *b = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001383
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001384 if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
1385 b->index >= dev->num_frames || dev->io != IO_MMAP)
1386 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001387
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001388 memcpy(b, &dev->frame[b->index].buf, sizeof(*b));
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001389
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001390 if (dev->frame[b->index].vma_use_count) {
1391 b->flags |= V4L2_BUF_FLAG_MAPPED;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001392 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001393 if (dev->frame[b->index].state == F_DONE)
1394 b->flags |= V4L2_BUF_FLAG_DONE;
1395 else if (dev->frame[b->index].state != F_UNUSED)
1396 b->flags |= V4L2_BUF_FLAG_QUEUED;
1397 return 0;
1398 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001399 case VIDIOC_QBUF:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001400 {
1401 struct v4l2_buffer *b = arg;
1402 unsigned long lock_flags;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001403
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001404 if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
1405 b->index >= dev->num_frames || dev->io != IO_MMAP) {
1406 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001407 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001408
1409 if (dev->frame[b->index].state != F_UNUSED) {
1410 return -EAGAIN;
1411 }
1412 dev->frame[b->index].state = F_QUEUED;
1413
1414 /* add frame to fifo */
1415 spin_lock_irqsave(&dev->queue_lock, lock_flags);
1416 list_add_tail(&dev->frame[b->index].frame,
1417 &dev->inqueue);
1418 spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
1419
1420 return 0;
1421 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001422 case VIDIOC_DQBUF:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001423 {
1424 struct v4l2_buffer *b = arg;
1425 struct em28xx_frame_t *f;
1426 unsigned long lock_flags;
1427 int ret = 0;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001428
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001429 if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
1430 || dev->io != IO_MMAP)
1431 return -EINVAL;
1432
1433 if (list_empty(&dev->outqueue)) {
1434 if (dev->stream == STREAM_OFF)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001435 return -EINVAL;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001436 if (filp->f_flags & O_NONBLOCK)
1437 return -EAGAIN;
1438 ret = wait_event_interruptible
1439 (dev->wait_frame,
1440 (!list_empty(&dev->outqueue)) ||
1441 (dev->state & DEV_DISCONNECTED));
1442 if (ret)
1443 return ret;
1444 if (dev->state & DEV_DISCONNECTED)
1445 return -ENODEV;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001446 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001447
1448 spin_lock_irqsave(&dev->queue_lock, lock_flags);
1449 f = list_entry(dev->outqueue.next,
1450 struct em28xx_frame_t, frame);
1451 list_del(dev->outqueue.next);
1452 spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
1453
1454 f->state = F_UNUSED;
1455 memcpy(b, &f->buf, sizeof(*b));
1456
1457 if (f->vma_use_count)
1458 b->flags |= V4L2_BUF_FLAG_MAPPED;
1459
1460 return 0;
1461 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001462 default:
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001463 return em28xx_do_ioctl(inode, filp, dev, cmd, arg,
1464 em28xx_video_do_ioctl);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001465 }
1466 return 0;
1467}
1468
1469/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001470 * em28xx_v4l2_ioctl()
1471 * handle v4l2 ioctl the main action happens in em28xx_v4l2_do_ioctl()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001472 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001473static int em28xx_v4l2_ioctl(struct inode *inode, struct file *filp,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001474 unsigned int cmd, unsigned long arg)
1475{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001476 int ret = 0;
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -03001477 struct em28xx_fh *fh = filp->private_data;
1478 struct em28xx *dev = fh->dev;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001479
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001480 if (dev->state & DEV_DISCONNECTED) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001481 em28xx_errdev("v4l2 ioctl: device not present\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001482 return -ENODEV;
1483 }
1484
1485 if (dev->state & DEV_MISCONFIGURED) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001486 em28xx_errdev
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001487 ("v4l2 ioctl: device is misconfigured; close and open it again\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001488 return -EIO;
1489 }
1490
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001491 ret = video_usercopy(inode, filp, cmd, arg, em28xx_video_do_ioctl);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001492
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001493 return ret;
1494}
1495
Arjan van de Venfa027c22007-02-12 00:55:33 -08001496static const struct file_operations em28xx_v4l_fops = {
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001497 .owner = THIS_MODULE,
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001498 .open = em28xx_v4l2_open,
1499 .release = em28xx_v4l2_close,
1500 .ioctl = em28xx_v4l2_ioctl,
1501 .read = em28xx_v4l2_read,
1502 .poll = em28xx_v4l2_poll,
1503 .mmap = em28xx_v4l2_mmap,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001504 .llseek = no_llseek,
Mauro Carvalho Chehab17cbe2e2006-01-09 15:24:58 -02001505 .compat_ioctl = v4l_compat_ioctl32,
1506
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001507};
1508
1509/******************************** usb interface *****************************************/
1510
1511/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001512 * em28xx_init_dev()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001513 * allocates and inits the device structs, registers i2c bus and v4l device
1514 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001515static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001516 int minor)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001517{
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001518 struct em28xx *dev = *devhandle;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001519 int retval = -ENOMEM;
1520 int errCode, i;
1521 unsigned int maxh, maxw;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001522
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001523 dev->udev = udev;
Ingo Molnar3593cab2006-02-07 06:49:14 -02001524 mutex_init(&dev->lock);
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -03001525 spin_lock_init(&dev->queue_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001526 init_waitqueue_head(&dev->open);
Mauro Carvalho Chehaba3a048c2007-11-10 22:21:01 -03001527 init_waitqueue_head(&dev->wait_frame);
1528 init_waitqueue_head(&dev->wait_stream);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001529
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001530 dev->em28xx_write_regs = em28xx_write_regs;
1531 dev->em28xx_read_reg = em28xx_read_reg;
1532 dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
1533 dev->em28xx_write_regs_req = em28xx_write_regs_req;
1534 dev->em28xx_read_reg_req = em28xx_read_reg_req;
Sascha Sommerfad7b952007-11-04 08:06:48 -03001535 dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001536
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001537 /* setup video picture settings for saa7113h */
1538 memset(&dev->vpic, 0, sizeof(dev->vpic));
1539 dev->vpic.colour = 128 << 8;
1540 dev->vpic.hue = 128 << 8;
1541 dev->vpic.brightness = 128 << 8;
1542 dev->vpic.contrast = 192 << 8;
1543 dev->vpic.whiteness = 128 << 8; /* This one isn't used */
1544 dev->vpic.depth = 16;
1545 dev->vpic.palette = VIDEO_PALETTE_YUV422;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001546
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001547 em28xx_pre_card_setup(dev);
1548
1549 errCode = em28xx_config(dev);
1550 if (errCode) {
1551 em28xx_errdev("error configuring device\n");
1552 em28xx_devused &= ~(1<<dev->devno);
1553 kfree(dev);
1554 return -ENOMEM;
1555 }
1556
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001557 /* register i2c bus */
1558 em28xx_i2c_register(dev);
1559
1560 /* Do board specific init and eeprom reading */
1561 em28xx_card_setup(dev);
1562
1563 /* configure the device */
1564 em28xx_config_i2c(dev);
1565
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001566 for (i = 0; i < TVNORMS; i++)
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001567 if (em28xx_boards[dev->model].norm == tvnorms[i].mode)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001568 break;
1569 if (i == TVNORMS)
1570 i = 0;
1571
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001572 dev->tvnorm = &tvnorms[i]; /* set default norm */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001573
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001574 em28xx_videodbg("tvnorm=%s\n", dev->tvnorm->name);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001575
1576 maxw = norm_maxw(dev);
1577 maxh = norm_maxh(dev);
1578
1579 /* set default image size */
1580 dev->width = maxw;
1581 dev->height = maxh;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001582 dev->interlaced = EM28XX_INTERLACED_DEFAULT;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001583 dev->field_size = dev->width * dev->height;
1584 dev->frame_size =
1585 dev->interlaced ? dev->field_size << 1 : dev->field_size;
1586 dev->bytesperline = dev->width * 2;
1587 dev->hscale = 0;
1588 dev->vscale = 0;
1589 dev->ctl_input = 2;
1590
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001591 errCode = em28xx_config(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001592
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001593 /* allocate and fill v4l2 device struct */
1594 dev->vdev = video_device_alloc();
1595 if (NULL == dev->vdev) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001596 em28xx_errdev("cannot allocate video_device.\n");
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001597 em28xx_devused&=~(1<<dev->devno);
Eric Sesterhenn7c908fb2006-04-11 18:19:33 -03001598 kfree(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001599 return -ENOMEM;
1600 }
1601
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001602 dev->vbi_dev = video_device_alloc();
1603 if (NULL == dev->vbi_dev) {
1604 em28xx_errdev("cannot allocate video_device.\n");
1605 kfree(dev->vdev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001606 em28xx_devused&=~(1<<dev->devno);
Eric Sesterhenn7c908fb2006-04-11 18:19:33 -03001607 kfree(dev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001608 return -ENOMEM;
1609 }
1610
1611 /* Fills VBI device info */
1612 dev->vbi_dev->type = VFL_TYPE_VBI;
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001613 dev->vbi_dev->fops = &em28xx_v4l_fops;
1614 dev->vbi_dev->minor = -1;
1615 dev->vbi_dev->dev = &dev->udev->dev;
1616 dev->vbi_dev->release = video_device_release;
1617 snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name), "%s#%d %s",
1618 "em28xx",dev->devno,"vbi");
1619
1620 /* Fills CAPTURE device info */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001621 dev->vdev->type = VID_TYPE_CAPTURE;
1622 if (dev->has_tuner)
1623 dev->vdev->type |= VID_TYPE_TUNER;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001624 dev->vdev->fops = &em28xx_v4l_fops;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001625 dev->vdev->minor = -1;
1626 dev->vdev->dev = &dev->udev->dev;
1627 dev->vdev->release = video_device_release;
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001628 snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name), "%s#%d %s",
1629 "em28xx",dev->devno,"video");
1630
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001631 list_add_tail(&dev->devlist,&em28xx_devlist);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001632
Sascha Sommer5a804152007-11-03 21:22:38 -03001633
1634 if (dev->has_msp34xx) {
1635 /* Send a reset to other chips via gpio */
1636 em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1);
1637 msleep(3);
1638 em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1);
1639 msleep(3);
1640
1641 }
1642 video_mux(dev, 0);
1643
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001644 /* register v4l2 device */
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001645 if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
1646 video_nr[dev->devno]))) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001647 em28xx_errdev("unable to register video device (error=%i).\n",
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001648 retval);
Ingo Molnar3593cab2006-02-07 06:49:14 -02001649 mutex_unlock(&dev->lock);
Markus Rechberger9c755412005-11-08 21:37:52 -08001650 list_del(&dev->devlist);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001651 video_device_release(dev->vdev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001652 em28xx_devused&=~(1<<dev->devno);
Eric Sesterhenn7c908fb2006-04-11 18:19:33 -03001653 kfree(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001654 return -ENODEV;
1655 }
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001656
1657 if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
1658 vbi_nr[dev->devno]) < 0) {
1659 printk("unable to register vbi device\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02001660 mutex_unlock(&dev->lock);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001661 list_del(&dev->devlist);
1662 video_device_release(dev->vbi_dev);
1663 video_device_release(dev->vdev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001664 em28xx_devused&=~(1<<dev->devno);
Eric Sesterhenn7c908fb2006-04-11 18:19:33 -03001665 kfree(dev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001666 return -ENODEV;
1667 } else {
1668 printk("registered VBI\n");
1669 }
1670
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001671 em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
1672 dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
1673 dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001674
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001675 return 0;
1676}
1677
1678/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001679 * em28xx_usb_probe()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001680 * checks for supported devices
1681 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001682static int em28xx_usb_probe(struct usb_interface *interface,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001683 const struct usb_device_id *id)
1684{
1685 const struct usb_endpoint_descriptor *endpoint;
1686 struct usb_device *udev;
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001687 struct usb_interface *uif;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001688 struct em28xx *dev = NULL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001689 int retval = -ENODEV;
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001690 int i, nr, ifnum;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001691
1692 udev = usb_get_dev(interface_to_usbdev(interface));
Mauro Carvalho Chehabd5e52652005-11-08 21:37:32 -08001693 ifnum = interface->altsetting[0].desc.bInterfaceNumber;
1694
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001695 /* Check to see next free device and mark as used */
1696 nr=find_first_zero_bit(&em28xx_devused,EM28XX_MAXBOARDS);
1697 em28xx_devused|=1<<nr;
Mauro Carvalho Chehab91cad0f2005-11-08 21:38:13 -08001698
1699 /* Don't register audio interfaces */
1700 if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001701 em28xx_err(DRIVER_NAME " audio device (%04x:%04x): interface %i, class %i\n",
Mauro Carvalho Chehab91cad0f2005-11-08 21:38:13 -08001702 udev->descriptor.idVendor,udev->descriptor.idProduct,
1703 ifnum,
1704 interface->altsetting[0].desc.bInterfaceClass);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001705
1706 em28xx_devused&=~(1<<nr);
Mauro Carvalho Chehab91cad0f2005-11-08 21:38:13 -08001707 return -ENODEV;
1708 }
1709
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001710 em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n",
Mauro Carvalho Chehabd5e52652005-11-08 21:37:32 -08001711 udev->descriptor.idVendor,udev->descriptor.idProduct,
1712 ifnum,
1713 interface->altsetting[0].desc.bInterfaceClass);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001714
Mauro Carvalho Chehabd5e52652005-11-08 21:37:32 -08001715 endpoint = &interface->cur_altsetting->endpoint[1].desc;
1716
Michael Opdenacker59c51592007-05-09 08:57:56 +02001717 /* check if the device has the iso in endpoint at the correct place */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001718 if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
1719 USB_ENDPOINT_XFER_ISOC) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001720 em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n");
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001721 em28xx_devused&=~(1<<nr);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001722 return -ENODEV;
1723 }
1724 if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001725 em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n");
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001726 em28xx_devused&=~(1<<nr);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001727 return -ENODEV;
1728 }
1729
Mauro Carvalho Chehab19478842006-03-14 17:24:57 -03001730 if (nr >= EM28XX_MAXBOARDS) {
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001731 printk (DRIVER_NAME ": Supports only %i em28xx boards.\n",EM28XX_MAXBOARDS);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001732 em28xx_devused&=~(1<<nr);
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001733 return -ENOMEM;
1734 }
1735
1736 /* allocate memory for our device state and initialize it */
Panagiotis Issaris74081872006-01-11 19:40:56 -02001737 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001738 if (dev == NULL) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001739 em28xx_err(DRIVER_NAME ": out of memory!\n");
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001740 em28xx_devused&=~(1<<nr);
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001741 return -ENOMEM;
1742 }
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001743
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001744 snprintf(dev->name, 29, "em28xx #%d", nr);
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001745 dev->devno = nr;
1746 dev->model = id->driver_info;
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001747
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001748 /* compute alternate max packet sizes */
1749 uif = udev->actconfig->interface[0];
1750
1751 dev->num_alt=uif->num_altsetting;
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001752 em28xx_info("Alternate settings: %i\n",dev->num_alt);
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001753// dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)*
1754 dev->alt_max_pkt_size = kmalloc(32*
1755 dev->num_alt,GFP_KERNEL);
1756 if (dev->alt_max_pkt_size == NULL) {
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001757 em28xx_errdev("out of memory!\n");
1758 em28xx_devused&=~(1<<nr);
Jesper Juhl1207cf842007-08-09 23:02:36 +02001759 kfree(dev);
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001760 return -ENOMEM;
1761 }
1762
1763 for (i = 0; i < dev->num_alt ; i++) {
1764 u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
1765 wMaxPacketSize);
1766 dev->alt_max_pkt_size[i] =
1767 (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001768 em28xx_info("Alternate setting %i, max size= %i\n",i,
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001769 dev->alt_max_pkt_size[i]);
1770 }
1771
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001772 if ((card[nr]>=0)&&(card[nr]<em28xx_bcount))
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001773 dev->model = card[nr];
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001774
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001775 /* allocate device struct */
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001776 retval = em28xx_init_dev(&dev, udev, nr);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001777 if (retval)
1778 return retval;
1779
Mauro Carvalho Chehab03910cc2007-11-03 21:20:59 -03001780 em28xx_info("Found %s\n", em28xx_boards[dev->model].name);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001781
1782 /* save our data pointer in this interface device */
1783 usb_set_intfdata(interface, dev);
1784 return 0;
1785}
1786
1787/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001788 * em28xx_usb_disconnect()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001789 * called when the device gets diconencted
1790 * video device will be unregistered on v4l2_close in case it is still open
1791 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001792static void em28xx_usb_disconnect(struct usb_interface *interface)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001793{
Sascha Sommer5a804152007-11-03 21:22:38 -03001794 struct em28xx *dev;
1795
1796 dev = usb_get_intfdata(interface);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001797 usb_set_intfdata(interface, NULL);
1798
1799 if (!dev)
1800 return;
1801
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001802 em28xx_info("disconnecting %s\n", dev->vdev->name);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001803
Sascha Sommer5a804152007-11-03 21:22:38 -03001804 /* wait until all current v4l2 io is finished then deallocate resources */
1805 mutex_lock(&dev->lock);
1806
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001807 wake_up_interruptible_all(&dev->open);
1808
1809 if (dev->users) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001810 em28xx_warn
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001811 ("device /dev/video%d is open! Deregistration and memory "
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001812 "deallocation are deferred on close.\n",
1813 dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
1814
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001815 dev->state |= DEV_MISCONFIGURED;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001816 em28xx_uninit_isoc(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001817 dev->state |= DEV_DISCONNECTED;
1818 wake_up_interruptible(&dev->wait_frame);
1819 wake_up_interruptible(&dev->wait_stream);
1820 } else {
1821 dev->state |= DEV_DISCONNECTED;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001822 em28xx_release_resources(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001823 }
1824
Sascha Sommer5a804152007-11-03 21:22:38 -03001825
Ingo Molnar3593cab2006-02-07 06:49:14 -02001826 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001827
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001828 if (!dev->users) {
1829 kfree(dev->alt_max_pkt_size);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001830 kfree(dev);
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001831 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001832
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001833}
1834
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001835static struct usb_driver em28xx_usb_driver = {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001836 .name = "em28xx",
1837 .probe = em28xx_usb_probe,
1838 .disconnect = em28xx_usb_disconnect,
1839 .id_table = em28xx_id_table,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001840};
1841
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001842static int __init em28xx_module_init(void)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001843{
1844 int result;
1845
1846 printk(KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded\n",
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001847 (EM28XX_VERSION_CODE >> 16) & 0xff,
1848 (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001849#ifdef SNAPSHOT
1850 printk(KERN_INFO DRIVER_NAME " snapshot date %04d-%02d-%02d\n",
1851 SNAPSHOT / 10000, (SNAPSHOT / 100) % 100, SNAPSHOT % 100);
1852#endif
1853
1854 /* register this driver with the USB subsystem */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001855 result = usb_register(&em28xx_usb_driver);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001856 if (result)
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001857 em28xx_err(DRIVER_NAME
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001858 " usb_register failed. Error number %d.\n", result);
1859
1860 return result;
1861}
1862
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001863static void __exit em28xx_module_exit(void)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001864{
1865 /* deregister this driver with the USB subsystem */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001866 usb_deregister(&em28xx_usb_driver);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001867}
1868
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001869module_init(em28xx_module_init);
1870module_exit(em28xx_module_exit);