PageRenderTime 83ms CodeModel.GetById 12ms app.highlight 63ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/media/video/pwc/pwc-v4l.c

https://bitbucket.org/ndreys/linux-sunxi
C | 914 lines | 793 code | 84 blank | 37 comment | 126 complexity | a02b5ec3683e86ff99ad8db73a02fa1b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  1/* Linux driver for Philips webcam
  2   USB and Video4Linux interface part.
  3   (C) 1999-2004 Nemosoft Unv.
  4   (C) 2004-2006 Luc Saillard (luc@saillard.org)
  5
  6   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
  7   driver and thus may have bugs that are not present in the original version.
  8   Please send bug reports and support requests to <luc@saillard.org>.
  9   The decompression routines have been implemented by reverse-engineering the
 10   Nemosoft binary pwcx module. Caveat emptor.
 11
 12   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 25
 26*/
 27
 28#include <linux/errno.h>
 29#include <linux/init.h>
 30#include <linux/mm.h>
 31#include <linux/module.h>
 32#include <linux/poll.h>
 33#include <linux/vmalloc.h>
 34#include <asm/io.h>
 35
 36#include "pwc.h"
 37
 38static struct v4l2_queryctrl pwc_controls[] = {
 39	{
 40	    .id      = V4L2_CID_BRIGHTNESS,
 41	    .type    = V4L2_CTRL_TYPE_INTEGER,
 42	    .name    = "Brightness",
 43	    .minimum = 0,
 44	    .maximum = 128,
 45	    .step    = 1,
 46	    .default_value = 64,
 47	},
 48	{
 49	    .id      = V4L2_CID_CONTRAST,
 50	    .type    = V4L2_CTRL_TYPE_INTEGER,
 51	    .name    = "Contrast",
 52	    .minimum = 0,
 53	    .maximum = 64,
 54	    .step    = 1,
 55	    .default_value = 0,
 56	},
 57	{
 58	    .id      = V4L2_CID_SATURATION,
 59	    .type    = V4L2_CTRL_TYPE_INTEGER,
 60	    .name    = "Saturation",
 61	    .minimum = -100,
 62	    .maximum = 100,
 63	    .step    = 1,
 64	    .default_value = 0,
 65	},
 66	{
 67	    .id      = V4L2_CID_GAMMA,
 68	    .type    = V4L2_CTRL_TYPE_INTEGER,
 69	    .name    = "Gamma",
 70	    .minimum = 0,
 71	    .maximum = 32,
 72	    .step    = 1,
 73	    .default_value = 0,
 74	},
 75	{
 76	    .id      = V4L2_CID_RED_BALANCE,
 77	    .type    = V4L2_CTRL_TYPE_INTEGER,
 78	    .name    = "Red Gain",
 79	    .minimum = 0,
 80	    .maximum = 256,
 81	    .step    = 1,
 82	    .default_value = 0,
 83	},
 84	{
 85	    .id      = V4L2_CID_BLUE_BALANCE,
 86	    .type    = V4L2_CTRL_TYPE_INTEGER,
 87	    .name    = "Blue Gain",
 88	    .minimum = 0,
 89	    .maximum = 256,
 90	    .step    = 1,
 91	    .default_value = 0,
 92	},
 93	{
 94	    .id      = V4L2_CID_AUTO_WHITE_BALANCE,
 95	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
 96	    .name    = "Auto White Balance",
 97	    .minimum = 0,
 98	    .maximum = 1,
 99	    .step    = 1,
100	    .default_value = 0,
101	},
102	{
103	    .id      = V4L2_CID_EXPOSURE,
104	    .type    = V4L2_CTRL_TYPE_INTEGER,
105	    .name    = "Shutter Speed (Exposure)",
106	    .minimum = 0,
107	    .maximum = 256,
108	    .step    = 1,
109	    .default_value = 200,
110	},
111	{
112	    .id      = V4L2_CID_AUTOGAIN,
113	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
114	    .name    = "Auto Gain Enabled",
115	    .minimum = 0,
116	    .maximum = 1,
117	    .step    = 1,
118	    .default_value = 1,
119	},
120	{
121	    .id      = V4L2_CID_GAIN,
122	    .type    = V4L2_CTRL_TYPE_INTEGER,
123	    .name    = "Gain Level",
124	    .minimum = 0,
125	    .maximum = 256,
126	    .step    = 1,
127	    .default_value = 0,
128	},
129	{
130	    .id      = V4L2_CID_PRIVATE_SAVE_USER,
131	    .type    = V4L2_CTRL_TYPE_BUTTON,
132	    .name    = "Save User Settings",
133	    .minimum = 0,
134	    .maximum = 0,
135	    .step    = 0,
136	    .default_value = 0,
137	},
138	{
139	    .id      = V4L2_CID_PRIVATE_RESTORE_USER,
140	    .type    = V4L2_CTRL_TYPE_BUTTON,
141	    .name    = "Restore User Settings",
142	    .minimum = 0,
143	    .maximum = 0,
144	    .step    = 0,
145	    .default_value = 0,
146	},
147	{
148	    .id      = V4L2_CID_PRIVATE_RESTORE_FACTORY,
149	    .type    = V4L2_CTRL_TYPE_BUTTON,
150	    .name    = "Restore Factory Settings",
151	    .minimum = 0,
152	    .maximum = 0,
153	    .step    = 0,
154	    .default_value = 0,
155	},
156	{
157	    .id      = V4L2_CID_PRIVATE_COLOUR_MODE,
158	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
159	    .name    = "Colour mode",
160	    .minimum = 0,
161	    .maximum = 1,
162	    .step    = 1,
163	    .default_value = 0,
164	},
165	{
166	    .id      = V4L2_CID_PRIVATE_AUTOCONTOUR,
167	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
168	    .name    = "Auto contour",
169	    .minimum = 0,
170	    .maximum = 1,
171	    .step    = 1,
172	    .default_value = 0,
173	},
174	{
175	    .id      = V4L2_CID_PRIVATE_CONTOUR,
176	    .type    = V4L2_CTRL_TYPE_INTEGER,
177	    .name    = "Contour",
178	    .minimum = 0,
179	    .maximum = 63,
180	    .step    = 1,
181	    .default_value = 0,
182	},
183	{
184	    .id      = V4L2_CID_PRIVATE_BACKLIGHT,
185	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
186	    .name    = "Backlight compensation",
187	    .minimum = 0,
188	    .maximum = 1,
189	    .step    = 1,
190	    .default_value = 0,
191	},
192	{
193	  .id      = V4L2_CID_PRIVATE_FLICKERLESS,
194	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
195	    .name    = "Flickerless",
196	    .minimum = 0,
197	    .maximum = 1,
198	    .step    = 1,
199	    .default_value = 0,
200	},
201	{
202	    .id      = V4L2_CID_PRIVATE_NOISE_REDUCTION,
203	    .type    = V4L2_CTRL_TYPE_INTEGER,
204	    .name    = "Noise reduction",
205	    .minimum = 0,
206	    .maximum = 3,
207	    .step    = 1,
208	    .default_value = 0,
209	},
210};
211
212
213static void pwc_vidioc_fill_fmt(const struct pwc_device *pdev, struct v4l2_format *f)
214{
215	memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
216	f->fmt.pix.width        = pdev->view.x;
217	f->fmt.pix.height       = pdev->view.y;
218	f->fmt.pix.field        = V4L2_FIELD_NONE;
219	if (pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
220		f->fmt.pix.pixelformat  = V4L2_PIX_FMT_YUV420;
221		f->fmt.pix.bytesperline = (f->fmt.pix.width * 3)/2;
222		f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
223	} else {
224		/* vbandlength contains 4 lines ...  */
225		f->fmt.pix.bytesperline = pdev->vbandlength/4;
226		f->fmt.pix.sizeimage = pdev->frame_size + sizeof(struct pwc_raw_frame);
227		if (DEVICE_USE_CODEC1(pdev->type))
228			f->fmt.pix.pixelformat  = V4L2_PIX_FMT_PWC1;
229		else
230			f->fmt.pix.pixelformat  = V4L2_PIX_FMT_PWC2;
231	}
232	PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() "
233			"width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n",
234			f->fmt.pix.width,
235			f->fmt.pix.height,
236			f->fmt.pix.bytesperline,
237			f->fmt.pix.sizeimage,
238			(f->fmt.pix.pixelformat)&255,
239			(f->fmt.pix.pixelformat>>8)&255,
240			(f->fmt.pix.pixelformat>>16)&255,
241			(f->fmt.pix.pixelformat>>24)&255);
242}
243
244/* ioctl(VIDIOC_TRY_FMT) */
245static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f)
246{
247	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
248		PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
249		return -EINVAL;
250	}
251
252	switch (f->fmt.pix.pixelformat) {
253		case V4L2_PIX_FMT_YUV420:
254			break;
255		case V4L2_PIX_FMT_PWC1:
256			if (DEVICE_USE_CODEC23(pdev->type)) {
257				PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n");
258				return -EINVAL;
259			}
260			break;
261		case V4L2_PIX_FMT_PWC2:
262			if (DEVICE_USE_CODEC1(pdev->type)) {
263				PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n");
264				return -EINVAL;
265			}
266			break;
267		default:
268			PWC_DEBUG_IOCTL("Unsupported pixel format\n");
269			return -EINVAL;
270
271	}
272
273	if (f->fmt.pix.width > pdev->view_max.x)
274		f->fmt.pix.width = pdev->view_max.x;
275	else if (f->fmt.pix.width < pdev->view_min.x)
276		f->fmt.pix.width = pdev->view_min.x;
277
278	if (f->fmt.pix.height > pdev->view_max.y)
279		f->fmt.pix.height = pdev->view_max.y;
280	else if (f->fmt.pix.height < pdev->view_min.y)
281		f->fmt.pix.height = pdev->view_min.y;
282
283	return 0;
284}
285
286/* ioctl(VIDIOC_SET_FMT) */
287static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f)
288{
289	int ret, fps, snapshot, compression, pixelformat;
290
291	ret = pwc_vidioc_try_fmt(pdev, f);
292	if (ret<0)
293		return ret;
294
295	pixelformat = f->fmt.pix.pixelformat;
296	compression = pdev->vcompression;
297	snapshot = 0;
298	fps = pdev->vframes;
299	if (f->fmt.pix.priv) {
300		compression = (f->fmt.pix.priv & PWC_QLT_MASK) >> PWC_QLT_SHIFT;
301		snapshot = !!(f->fmt.pix.priv & PWC_FPS_SNAPSHOT);
302		fps = (f->fmt.pix.priv & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
303		if (fps == 0)
304			fps = pdev->vframes;
305	}
306
307	if (pixelformat != V4L2_PIX_FMT_YUV420 &&
308	    pixelformat != V4L2_PIX_FMT_PWC1 &&
309	    pixelformat != V4L2_PIX_FMT_PWC2)
310		return -EINVAL;
311
312	if (pdev->iso_init)
313		return -EBUSY;
314
315	PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d "
316			"compression=%d snapshot=%d format=%c%c%c%c\n",
317			f->fmt.pix.width, f->fmt.pix.height, fps,
318			compression, snapshot,
319			(pixelformat)&255,
320			(pixelformat>>8)&255,
321			(pixelformat>>16)&255,
322			(pixelformat>>24)&255);
323
324	ret = pwc_set_video_mode(pdev,
325				 f->fmt.pix.width,
326				 f->fmt.pix.height,
327				 fps,
328				 compression,
329				 snapshot);
330
331	PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret);
332
333	if (ret)
334		return ret;
335
336	pdev->pixfmt = pixelformat;
337
338	pwc_vidioc_fill_fmt(pdev, f);
339
340	return 0;
341
342}
343
344static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
345{
346	struct video_device *vdev = video_devdata(file);
347	struct pwc_device *pdev = video_drvdata(file);
348
349	strcpy(cap->driver, PWC_NAME);
350	strlcpy(cap->card, vdev->name, sizeof(cap->card));
351	usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
352	cap->version = PWC_VERSION_CODE;
353	cap->capabilities =
354		V4L2_CAP_VIDEO_CAPTURE	|
355		V4L2_CAP_STREAMING	|
356		V4L2_CAP_READWRITE;
357	return 0;
358}
359
360static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
361{
362	if (i->index)	/* Only one INPUT is supported */
363		return -EINVAL;
364
365	strcpy(i->name, "usb");
366	return 0;
367}
368
369static int pwc_g_input(struct file *file, void *fh, unsigned int *i)
370{
371	*i = 0;
372	return 0;
373}
374
375static int pwc_s_input(struct file *file, void *fh, unsigned int i)
376{
377	return i ? -EINVAL : 0;
378}
379
380static int pwc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
381{
382	int i, idx;
383	u32 id;
384
385	id = c->id;
386	if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
387		id &= V4L2_CTRL_ID_MASK;
388		id++;
389		idx = -1;
390		for (i = 0; i < ARRAY_SIZE(pwc_controls); i++) {
391			if (pwc_controls[i].id < id)
392				continue;
393			if (idx >= 0
394			 && pwc_controls[i].id > pwc_controls[idx].id)
395				continue;
396			idx = i;
397		}
398		if (idx < 0)
399			return -EINVAL;
400		memcpy(c, &pwc_controls[idx], sizeof pwc_controls[0]);
401		return 0;
402	}
403	for (i = 0; i < sizeof(pwc_controls) / sizeof(struct v4l2_queryctrl); i++) {
404		if (pwc_controls[i].id == c->id) {
405			PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n");
406			memcpy(c, &pwc_controls[i], sizeof(struct v4l2_queryctrl));
407			return 0;
408		}
409	}
410	return -EINVAL;
411}
412
413static int pwc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
414{
415	struct pwc_device *pdev = video_drvdata(file);
416	int ret;
417
418	switch (c->id) {
419	case V4L2_CID_BRIGHTNESS:
420		c->value = pwc_get_brightness(pdev);
421		if (c->value < 0)
422			return -EINVAL;
423		return 0;
424	case V4L2_CID_CONTRAST:
425		c->value = pwc_get_contrast(pdev);
426		if (c->value < 0)
427			return -EINVAL;
428		return 0;
429	case V4L2_CID_SATURATION:
430		ret = pwc_get_saturation(pdev, &c->value);
431		if (ret < 0)
432			return -EINVAL;
433		return 0;
434	case V4L2_CID_GAMMA:
435		c->value = pwc_get_gamma(pdev);
436		if (c->value < 0)
437			return -EINVAL;
438		return 0;
439	case V4L2_CID_RED_BALANCE:
440		ret = pwc_get_red_gain(pdev, &c->value);
441		if (ret < 0)
442			return -EINVAL;
443		c->value >>= 8;
444		return 0;
445	case V4L2_CID_BLUE_BALANCE:
446		ret = pwc_get_blue_gain(pdev, &c->value);
447		if (ret < 0)
448			return -EINVAL;
449		c->value >>= 8;
450		return 0;
451	case V4L2_CID_AUTO_WHITE_BALANCE:
452		ret = pwc_get_awb(pdev);
453		if (ret < 0)
454			return -EINVAL;
455		c->value = (ret == PWC_WB_MANUAL) ? 0 : 1;
456		return 0;
457	case V4L2_CID_GAIN:
458		ret = pwc_get_agc(pdev, &c->value);
459		if (ret < 0)
460			return -EINVAL;
461		c->value >>= 8;
462		return 0;
463	case V4L2_CID_AUTOGAIN:
464		ret = pwc_get_agc(pdev, &c->value);
465		if (ret < 0)
466			return -EINVAL;
467		c->value = (c->value < 0) ? 1 : 0;
468		return 0;
469	case V4L2_CID_EXPOSURE:
470		ret = pwc_get_shutter_speed(pdev, &c->value);
471		if (ret < 0)
472			return -EINVAL;
473		return 0;
474	case V4L2_CID_PRIVATE_COLOUR_MODE:
475		ret = pwc_get_colour_mode(pdev, &c->value);
476		if (ret < 0)
477			return -EINVAL;
478		return 0;
479	case V4L2_CID_PRIVATE_AUTOCONTOUR:
480		ret = pwc_get_contour(pdev, &c->value);
481		if (ret < 0)
482			return -EINVAL;
483		c->value = (c->value == -1 ? 1 : 0);
484		return 0;
485	case V4L2_CID_PRIVATE_CONTOUR:
486		ret = pwc_get_contour(pdev, &c->value);
487		if (ret < 0)
488			return -EINVAL;
489		c->value >>= 10;
490		return 0;
491	case V4L2_CID_PRIVATE_BACKLIGHT:
492		ret = pwc_get_backlight(pdev, &c->value);
493		if (ret < 0)
494			return -EINVAL;
495		return 0;
496	case V4L2_CID_PRIVATE_FLICKERLESS:
497		ret = pwc_get_flicker(pdev, &c->value);
498		if (ret < 0)
499			return -EINVAL;
500		c->value = (c->value ? 1 : 0);
501		return 0;
502	case V4L2_CID_PRIVATE_NOISE_REDUCTION:
503		ret = pwc_get_dynamic_noise(pdev, &c->value);
504		if (ret < 0)
505			return -EINVAL;
506		return 0;
507
508	case V4L2_CID_PRIVATE_SAVE_USER:
509	case V4L2_CID_PRIVATE_RESTORE_USER:
510	case V4L2_CID_PRIVATE_RESTORE_FACTORY:
511		return -EINVAL;
512	}
513	return -EINVAL;
514}
515
516static int pwc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
517{
518	struct pwc_device *pdev = video_drvdata(file);
519	int ret;
520
521	switch (c->id) {
522	case V4L2_CID_BRIGHTNESS:
523		c->value <<= 9;
524		ret = pwc_set_brightness(pdev, c->value);
525		if (ret < 0)
526			return -EINVAL;
527		return 0;
528	case V4L2_CID_CONTRAST:
529		c->value <<= 10;
530		ret = pwc_set_contrast(pdev, c->value);
531		if (ret < 0)
532			return -EINVAL;
533		return 0;
534	case V4L2_CID_SATURATION:
535		ret = pwc_set_saturation(pdev, c->value);
536		if (ret < 0)
537			return -EINVAL;
538		return 0;
539	case V4L2_CID_GAMMA:
540		c->value <<= 11;
541		ret = pwc_set_gamma(pdev, c->value);
542		if (ret < 0)
543			return -EINVAL;
544		return 0;
545	case V4L2_CID_RED_BALANCE:
546		c->value <<= 8;
547		ret = pwc_set_red_gain(pdev, c->value);
548		if (ret < 0)
549			return -EINVAL;
550		return 0;
551	case V4L2_CID_BLUE_BALANCE:
552		c->value <<= 8;
553		ret = pwc_set_blue_gain(pdev, c->value);
554		if (ret < 0)
555			return -EINVAL;
556		return 0;
557	case V4L2_CID_AUTO_WHITE_BALANCE:
558		c->value = (c->value == 0) ? PWC_WB_MANUAL : PWC_WB_AUTO;
559		ret = pwc_set_awb(pdev, c->value);
560		if (ret < 0)
561			return -EINVAL;
562		return 0;
563	case V4L2_CID_EXPOSURE:
564		c->value <<= 8;
565		ret = pwc_set_shutter_speed(pdev, c->value ? 0 : 1, c->value);
566		if (ret < 0)
567			return -EINVAL;
568		return 0;
569	case V4L2_CID_AUTOGAIN:
570		/* autogain off means nothing without a gain */
571		if (c->value == 0)
572			return 0;
573		ret = pwc_set_agc(pdev, c->value, 0);
574		if (ret < 0)
575			return -EINVAL;
576		return 0;
577	case V4L2_CID_GAIN:
578		c->value <<= 8;
579		ret = pwc_set_agc(pdev, 0, c->value);
580		if (ret < 0)
581			return -EINVAL;
582		return 0;
583	case V4L2_CID_PRIVATE_SAVE_USER:
584		if (pwc_save_user(pdev))
585			return -EINVAL;
586		return 0;
587	case V4L2_CID_PRIVATE_RESTORE_USER:
588		if (pwc_restore_user(pdev))
589			return -EINVAL;
590		return 0;
591	case V4L2_CID_PRIVATE_RESTORE_FACTORY:
592		if (pwc_restore_factory(pdev))
593			return -EINVAL;
594		return 0;
595	case V4L2_CID_PRIVATE_COLOUR_MODE:
596		ret = pwc_set_colour_mode(pdev, c->value);
597		if (ret < 0)
598			return -EINVAL;
599		return 0;
600	case V4L2_CID_PRIVATE_AUTOCONTOUR:
601		c->value = (c->value == 1) ? -1 : 0;
602		ret = pwc_set_contour(pdev, c->value);
603		if (ret < 0)
604			return -EINVAL;
605		return 0;
606	case V4L2_CID_PRIVATE_CONTOUR:
607		c->value <<= 10;
608		ret = pwc_set_contour(pdev, c->value);
609		if (ret < 0)
610			return -EINVAL;
611		return 0;
612	case V4L2_CID_PRIVATE_BACKLIGHT:
613		ret = pwc_set_backlight(pdev, c->value);
614		if (ret < 0)
615			return -EINVAL;
616		return 0;
617	case V4L2_CID_PRIVATE_FLICKERLESS:
618		ret = pwc_set_flicker(pdev, c->value);
619		if (ret < 0)
620			return -EINVAL;
621	case V4L2_CID_PRIVATE_NOISE_REDUCTION:
622		ret = pwc_set_dynamic_noise(pdev, c->value);
623		if (ret < 0)
624			return -EINVAL;
625		return 0;
626
627	}
628	return -EINVAL;
629}
630
631static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
632{
633	struct pwc_device *pdev = video_drvdata(file);
634
635	/* We only support two format: the raw format, and YUV */
636	switch (f->index) {
637	case 0:
638		/* RAW format */
639		f->pixelformat = pdev->type <= 646 ? V4L2_PIX_FMT_PWC1 : V4L2_PIX_FMT_PWC2;
640		f->flags = V4L2_FMT_FLAG_COMPRESSED;
641		strlcpy(f->description, "Raw Philips Webcam", sizeof(f->description));
642		break;
643	case 1:
644		f->pixelformat = V4L2_PIX_FMT_YUV420;
645		strlcpy(f->description, "4:2:0, planar, Y-Cb-Cr", sizeof(f->description));
646		break;
647	default:
648		return -EINVAL;
649	}
650	return 0;
651}
652
653static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
654{
655	struct pwc_device *pdev = video_drvdata(file);
656
657	PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",
658			pdev->image.x, pdev->image.y);
659	pwc_vidioc_fill_fmt(pdev, f);
660	return 0;
661}
662
663static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
664{
665	struct pwc_device *pdev = video_drvdata(file);
666
667	return pwc_vidioc_try_fmt(pdev, f);
668}
669
670static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
671{
672	struct pwc_device *pdev = video_drvdata(file);
673
674	return pwc_vidioc_set_fmt(pdev, f);
675}
676
677static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
678{
679	int nbuffers;
680
681	PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n", rb->count);
682	if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
683		return -EINVAL;
684	if (rb->memory != V4L2_MEMORY_MMAP)
685		return -EINVAL;
686
687	nbuffers = rb->count;
688	if (nbuffers < 2)
689		nbuffers = 2;
690	else if (nbuffers > pwc_mbufs)
691		nbuffers = pwc_mbufs;
692	/* Force to use our # of buffers */
693	rb->count = pwc_mbufs;
694	return 0;
695}
696
697static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
698{
699	struct pwc_device *pdev = video_drvdata(file);
700	int index;
701
702	PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n", buf->index);
703	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
704		PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n");
705		return -EINVAL;
706	}
707	index = buf->index;
708	if (index < 0 || index >= pwc_mbufs) {
709		PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index);
710		return -EINVAL;
711	}
712
713	buf->m.offset = index * pdev->len_per_image;
714	if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
715		buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
716	else
717		buf->bytesused = pdev->view.size;
718	buf->field = V4L2_FIELD_NONE;
719	buf->memory = V4L2_MEMORY_MMAP;
720	/*buf->flags = V4L2_BUF_FLAG_MAPPED;*/
721	buf->length = pdev->len_per_image;
722
723	PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n", buf->index);
724	PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n", buf->m.offset);
725	PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n", buf->bytesused);
726
727	return 0;
728}
729
730static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
731{
732	PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n", buf->index);
733	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
734		return -EINVAL;
735	if (buf->memory != V4L2_MEMORY_MMAP)
736		return -EINVAL;
737	if (buf->index >= pwc_mbufs)
738		return -EINVAL;
739
740	buf->flags |= V4L2_BUF_FLAG_QUEUED;
741	buf->flags &= ~V4L2_BUF_FLAG_DONE;
742
743	return 0;
744}
745
746static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
747{
748	DECLARE_WAITQUEUE(wait, current);
749	struct pwc_device *pdev = video_drvdata(file);
750	int ret;
751
752	PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n");
753
754	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
755		return -EINVAL;
756
757	add_wait_queue(&pdev->frameq, &wait);
758	while (pdev->full_frames == NULL) {
759		if (pdev->error_status) {
760			remove_wait_queue(&pdev->frameq, &wait);
761			set_current_state(TASK_RUNNING);
762			return -pdev->error_status;
763		}
764
765		if (signal_pending(current)) {
766			remove_wait_queue(&pdev->frameq, &wait);
767			set_current_state(TASK_RUNNING);
768			return -ERESTARTSYS;
769		}
770		mutex_unlock(&pdev->modlock);
771		schedule();
772		set_current_state(TASK_INTERRUPTIBLE);
773		mutex_lock(&pdev->modlock);
774	}
775	remove_wait_queue(&pdev->frameq, &wait);
776	set_current_state(TASK_RUNNING);
777
778	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n");
779	/* Decompress data in pdev->images[pdev->fill_image] */
780	ret = pwc_handle_frame(pdev);
781	if (ret)
782		return -EFAULT;
783	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
784
785	buf->index = pdev->fill_image;
786	if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
787		buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
788	else
789		buf->bytesused = pdev->view.size;
790	buf->flags = V4L2_BUF_FLAG_MAPPED;
791	buf->field = V4L2_FIELD_NONE;
792	do_gettimeofday(&buf->timestamp);
793	buf->sequence = 0;
794	buf->memory = V4L2_MEMORY_MMAP;
795	buf->m.offset = pdev->fill_image * pdev->len_per_image;
796	buf->length = pdev->len_per_image;
797	pwc_next_image(pdev);
798
799	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n", buf->index);
800	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n", buf->length);
801	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n", buf->m.offset);
802	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n", buf->bytesused);
803	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n");
804	return 0;
805
806}
807
808static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
809{
810	struct pwc_device *pdev = video_drvdata(file);
811
812	return pwc_isoc_init(pdev);
813}
814
815static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
816{
817	struct pwc_device *pdev = video_drvdata(file);
818
819	pwc_isoc_cleanup(pdev);
820	return 0;
821}
822
823static int pwc_enum_framesizes(struct file *file, void *fh,
824					 struct v4l2_frmsizeenum *fsize)
825{
826	struct pwc_device *pdev = video_drvdata(file);
827	unsigned int i = 0, index = fsize->index;
828
829	if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) {
830		for (i = 0; i < PSZ_MAX; i++) {
831			if (pdev->image_mask & (1UL << i)) {
832				if (!index--) {
833					fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
834					fsize->discrete.width = pwc_image_sizes[i].x;
835					fsize->discrete.height = pwc_image_sizes[i].y;
836					return 0;
837				}
838			}
839		}
840	} else if (fsize->index == 0 &&
841			((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) ||
842			 (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) {
843
844		fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
845		fsize->discrete.width = pdev->abs_max.x;
846		fsize->discrete.height = pdev->abs_max.y;
847		return 0;
848	}
849	return -EINVAL;
850}
851
852static int pwc_enum_frameintervals(struct file *file, void *fh,
853					   struct v4l2_frmivalenum *fival)
854{
855	struct pwc_device *pdev = video_drvdata(file);
856	int size = -1;
857	unsigned int i;
858
859	for (i = 0; i < PSZ_MAX; i++) {
860		if (pwc_image_sizes[i].x == fival->width &&
861				pwc_image_sizes[i].y == fival->height) {
862			size = i;
863			break;
864		}
865	}
866
867	/* TODO: Support raw format */
868	if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420)
869		return -EINVAL;
870
871	i = pwc_get_fps(pdev, fival->index, size);
872	if (!i)
873		return -EINVAL;
874
875	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
876	fival->discrete.numerator = 1;
877	fival->discrete.denominator = i;
878
879	return 0;
880}
881
882static long pwc_default(struct file *file, void *fh, bool valid_prio,
883			int cmd, void *arg)
884{
885	struct pwc_device *pdev = video_drvdata(file);
886
887	return pwc_ioctl(pdev, cmd, arg);
888}
889
890const struct v4l2_ioctl_ops pwc_ioctl_ops = {
891	.vidioc_querycap		    = pwc_querycap,
892	.vidioc_enum_input		    = pwc_enum_input,
893	.vidioc_g_input			    = pwc_g_input,
894	.vidioc_s_input			    = pwc_s_input,
895	.vidioc_enum_fmt_vid_cap	    = pwc_enum_fmt_vid_cap,
896	.vidioc_g_fmt_vid_cap		    = pwc_g_fmt_vid_cap,
897	.vidioc_s_fmt_vid_cap		    = pwc_s_fmt_vid_cap,
898	.vidioc_try_fmt_vid_cap		    = pwc_try_fmt_vid_cap,
899	.vidioc_queryctrl		    = pwc_queryctrl,
900	.vidioc_g_ctrl			    = pwc_g_ctrl,
901	.vidioc_s_ctrl			    = pwc_s_ctrl,
902	.vidioc_reqbufs			    = pwc_reqbufs,
903	.vidioc_querybuf		    = pwc_querybuf,
904	.vidioc_qbuf			    = pwc_qbuf,
905	.vidioc_dqbuf			    = pwc_dqbuf,
906	.vidioc_streamon		    = pwc_streamon,
907	.vidioc_streamoff		    = pwc_streamoff,
908	.vidioc_enum_framesizes		    = pwc_enum_framesizes,
909	.vidioc_enum_frameintervals	    = pwc_enum_frameintervals,
910	.vidioc_default		    = pwc_default,
911};
912
913
914/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */