• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/staging/solo6x10/
1/*
2 * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
3 * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/kthread.h>
23#include <linux/freezer.h>
24
25#include <media/v4l2-ioctl.h>
26#include <media/v4l2-common.h>
27#include <media/videobuf-dma-contig.h>
28
29#include "solo6010.h"
30#include "solo6010-tw28.h"
31
32#define SOLO_HW_BPL		2048
33#define SOLO_DISP_PIX_FIELD	V4L2_FIELD_INTERLACED
34#define SOLO_DISP_BUF_SIZE	(64 * 1024) // 64k
35
36/* Image size is two fields, SOLO_HW_BPL is one horizontal line */
37#define solo_vlines(__solo)	(__solo->video_vsize * 2)
38#define solo_image_size(__solo) (solo_bytesperline(__solo) * \
39				 solo_vlines(__solo))
40#define solo_bytesperline(__solo) (__solo->video_hsize * 2)
41
42#define MIN_VID_BUFFERS		4
43
44/* Simple file handle */
45struct solo_filehandle {
46	struct solo6010_dev	*solo_dev;
47	struct videobuf_queue	vidq;
48	struct task_struct      *kthread;
49	spinlock_t		slock;
50	int			old_write;
51	struct list_head	vidq_active;
52};
53
54unsigned video_nr = -1;
55module_param(video_nr, uint, 0644);
56MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)");
57
58static void erase_on(struct solo6010_dev *solo_dev)
59{
60	solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
61	solo_dev->erasing = 1;
62	solo_dev->frame_blank = 0;
63}
64
65static int erase_off(struct solo6010_dev *solo_dev)
66{
67	if (!solo_dev->erasing)
68		return 0;
69
70	/* First time around, assert erase off */
71	if (!solo_dev->frame_blank)
72		solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0);
73	/* Keep the erasing flag on for 8 frames minimum */
74	if (solo_dev->frame_blank++ >= 8)
75		solo_dev->erasing = 0;
76
77	return 1;
78}
79
80void solo_video_in_isr(struct solo6010_dev *solo_dev)
81{
82	solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_VIDEO_IN);
83	wake_up_interruptible(&solo_dev->disp_thread_wait);
84}
85
86static void solo_win_setup(struct solo6010_dev *solo_dev, u8 ch,
87			   int sx, int sy, int ex, int ey, int scale)
88{
89	if (ch >= solo_dev->nr_chans)
90		return;
91
92	/* Here, we just keep window/channel the same */
93	solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch),
94		       SOLO_VI_WIN_CHANNEL(ch) |
95		       SOLO_VI_WIN_SX(sx) |
96		       SOLO_VI_WIN_EX(ex) |
97		       SOLO_VI_WIN_SCALE(scale));
98
99        solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch),
100		       SOLO_VI_WIN_SY(sy) |
101		       SOLO_VI_WIN_EY(ey));
102}
103
104static int solo_v4l2_ch_ext_4up(struct solo6010_dev *solo_dev, u8 idx, int on)
105{
106	u8 ch = idx * 4;
107
108	if (ch >= solo_dev->nr_chans)
109		return -EINVAL;
110
111	if (!on) {
112		u8 i;
113		for (i = ch; i < ch + 4; i++)
114			solo_win_setup(solo_dev, i, solo_dev->video_hsize,
115				       solo_vlines(solo_dev),
116				       solo_dev->video_hsize,
117				       solo_vlines(solo_dev), 0);
118		return 0;
119	}
120
121	/* Row 1 */
122	solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2,
123		       solo_vlines(solo_dev) / 2, 3);
124	solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0,
125		       solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3);
126	/* Row 2 */
127	solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2,
128		       solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3);
129	solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2,
130		       solo_vlines(solo_dev) / 2, solo_dev->video_hsize,
131		       solo_vlines(solo_dev), 3);
132
133	return 0;
134}
135
136static int solo_v4l2_ch_ext_16up(struct solo6010_dev *solo_dev, int on)
137{
138	int sy, ysize, hsize, i;
139
140	if (!on) {
141		for (i = 0; i < 16; i++)
142			solo_win_setup(solo_dev, i, solo_dev->video_hsize,
143				       solo_vlines(solo_dev),
144				       solo_dev->video_hsize,
145				       solo_vlines(solo_dev), 0);
146		return 0;
147	}
148
149	ysize = solo_vlines(solo_dev) / 4;
150	hsize = solo_dev->video_hsize / 4;
151
152	for (sy = 0, i = 0; i < 4; i++, sy += ysize) {
153		solo_win_setup(solo_dev, i * 4, 0, sy, hsize,
154			       sy + ysize, 5);
155		solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy,
156			       hsize * 2, sy + ysize, 5);
157		solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy,
158			       hsize * 3, sy + ysize, 5);
159		solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy,
160			       solo_dev->video_hsize, sy + ysize, 5);
161	}
162
163	return 0;
164}
165
166static int solo_v4l2_ch(struct solo6010_dev *solo_dev, u8 ch, int on)
167{
168	u8 ext_ch;
169
170	if (ch < solo_dev->nr_chans) {
171		solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize,
172			       on ? 0 : solo_vlines(solo_dev),
173			       solo_dev->video_hsize, solo_vlines(solo_dev),
174			       on ? 1 : 0);
175		return 0;
176	}
177
178	if (ch >= solo_dev->nr_chans + solo_dev->nr_ext)
179		return -EINVAL;
180
181	ext_ch = ch - solo_dev->nr_chans;
182
183	/* 4up's first */
184	if (ext_ch < 4)
185		return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on);
186
187	/* Remaining case is 16up for 16-port */
188	return solo_v4l2_ch_ext_16up(solo_dev, on);
189}
190
191static int solo_v4l2_set_ch(struct solo6010_dev *solo_dev, u8 ch)
192{
193	if (ch >= solo_dev->nr_chans + solo_dev->nr_ext)
194		return -EINVAL;
195
196	erase_on(solo_dev);
197
198	solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0);
199	solo_v4l2_ch(solo_dev, ch, 1);
200
201	solo_dev->cur_disp_ch = ch;
202
203	return 0;
204}
205
206static void solo_fillbuf(struct solo_filehandle *fh,
207			 struct videobuf_buffer *vb)
208{
209	struct solo6010_dev *solo_dev = fh->solo_dev;
210	dma_addr_t vbuf;
211	unsigned int fdma_addr;
212	int frame_size;
213	int error = 1;
214	int i;
215
216	if (!(vbuf = videobuf_to_dma_contig(vb)))
217		goto finish_buf;
218
219	if (erase_off(solo_dev)) {
220		void *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
221		int image_size = solo_image_size(solo_dev);
222		for (i = 0; i < image_size; i += 2) {
223			((u8 *)p)[i] = 0x80;
224			((u8 *)p)[i + 1] = 0x00;
225		}
226		error = 0;
227		goto finish_buf;
228	}
229
230	frame_size = SOLO_HW_BPL * solo_vlines(solo_dev);
231	fdma_addr = SOLO_DISP_EXT_ADDR(solo_dev) + (fh->old_write * frame_size);
232
233	for (i = 0; i < frame_size / SOLO_DISP_BUF_SIZE; i++) {
234		int j;
235		for (j = 0; j < (SOLO_DISP_BUF_SIZE / SOLO_HW_BPL); j++) {
236			if (solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_DISP, 0,
237					   vbuf, fdma_addr + (j * SOLO_HW_BPL),
238					   solo_bytesperline(solo_dev)))
239				goto finish_buf;
240			vbuf += solo_bytesperline(solo_dev);
241		}
242		fdma_addr += SOLO_DISP_BUF_SIZE;
243	}
244	error = 0;
245
246finish_buf:
247	if (error) {
248		vb->state = VIDEOBUF_ERROR;
249	} else {
250		vb->state = VIDEOBUF_DONE;
251		vb->field_count++;
252		do_gettimeofday(&vb->ts);
253	}
254
255	wake_up(&vb->done);
256
257	return;
258}
259
260static void solo_thread_try(struct solo_filehandle *fh)
261{
262	struct videobuf_buffer *vb;
263	unsigned int cur_write;
264
265	for (;;) {
266		spin_lock(&fh->slock);
267
268		if (list_empty(&fh->vidq_active))
269			break;
270
271		vb = list_first_entry(&fh->vidq_active, struct videobuf_buffer,
272				      queue);
273
274		if (!waitqueue_active(&vb->done))
275			break;
276
277		cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev,
278							SOLO_VI_STATUS0));
279		if (cur_write == fh->old_write)
280			break;
281
282		fh->old_write = cur_write;
283		list_del(&vb->queue);
284
285		spin_unlock(&fh->slock);
286
287		solo_fillbuf(fh, vb);
288	}
289
290	assert_spin_locked(&fh->slock);
291	spin_unlock(&fh->slock);
292}
293
294static int solo_thread(void *data)
295{
296	struct solo_filehandle *fh = data;
297	struct solo6010_dev *solo_dev = fh->solo_dev;
298	DECLARE_WAITQUEUE(wait, current);
299
300	set_freezable();
301	add_wait_queue(&solo_dev->disp_thread_wait, &wait);
302
303	for (;;) {
304		long timeout = schedule_timeout_interruptible(HZ);
305		if (timeout == -ERESTARTSYS || kthread_should_stop())
306			break;
307		solo_thread_try(fh);
308		try_to_freeze();
309	}
310
311	remove_wait_queue(&solo_dev->disp_thread_wait, &wait);
312
313        return 0;
314}
315
316static int solo_start_thread(struct solo_filehandle *fh)
317{
318	fh->kthread = kthread_run(solo_thread, fh, SOLO6010_NAME "_disp");
319
320	if (IS_ERR(fh->kthread))
321		return PTR_ERR(fh->kthread);
322
323	return 0;
324}
325
326static void solo_stop_thread(struct solo_filehandle *fh)
327{
328	if (fh->kthread) {
329		kthread_stop(fh->kthread);
330		fh->kthread = NULL;
331	}
332}
333
334static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count,
335			  unsigned int *size)
336{
337	struct solo_filehandle *fh = vq->priv_data;
338	struct solo6010_dev *solo_dev  = fh->solo_dev;
339
340        *size = solo_image_size(solo_dev);
341
342        if (*count < MIN_VID_BUFFERS)
343		*count = MIN_VID_BUFFERS;
344
345        return 0;
346}
347
348static int solo_buf_prepare(struct videobuf_queue *vq,
349			    struct videobuf_buffer *vb, enum v4l2_field field)
350{
351	struct solo_filehandle *fh  = vq->priv_data;
352	struct solo6010_dev *solo_dev = fh->solo_dev;
353
354	vb->size = solo_image_size(solo_dev);
355	if (vb->baddr != 0 && vb->bsize < vb->size)
356		return -EINVAL;
357
358	vb->width  = solo_dev->video_hsize;
359	vb->height = solo_vlines(solo_dev);
360	vb->bytesperline = solo_bytesperline(solo_dev);
361	vb->field  = field;
362
363	if (vb->state == VIDEOBUF_NEEDS_INIT) {
364		int rc = videobuf_iolock(vq, vb, NULL);
365		if (rc < 0) {
366			videobuf_dma_contig_free(vq, vb);
367			vb->state = VIDEOBUF_NEEDS_INIT;
368			return rc;
369		}
370	}
371	vb->state = VIDEOBUF_PREPARED;
372
373	return 0;
374}
375
376static void solo_buf_queue(struct videobuf_queue *vq,
377			   struct videobuf_buffer *vb)
378{
379	struct solo_filehandle *fh = vq->priv_data;
380	struct solo6010_dev *solo_dev = fh->solo_dev;
381
382	vb->state = VIDEOBUF_QUEUED;
383	list_add_tail(&vb->queue, &fh->vidq_active);
384	wake_up_interruptible(&solo_dev->disp_thread_wait);
385}
386
387static void solo_buf_release(struct videobuf_queue *vq,
388			     struct videobuf_buffer *vb)
389{
390	videobuf_dma_contig_free(vq, vb);
391	vb->state = VIDEOBUF_NEEDS_INIT;
392}
393
394static struct videobuf_queue_ops solo_video_qops = {
395	.buf_setup	= solo_buf_setup,
396	.buf_prepare	= solo_buf_prepare,
397	.buf_queue	= solo_buf_queue,
398	.buf_release	= solo_buf_release,
399};
400
401static unsigned int solo_v4l2_poll(struct file *file,
402				   struct poll_table_struct *wait)
403{
404	struct solo_filehandle *fh = file->private_data;
405
406        return videobuf_poll_stream(file, &fh->vidq, wait);
407}
408
409static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
410{
411	struct solo_filehandle *fh = file->private_data;
412
413	return videobuf_mmap_mapper(&fh->vidq, vma);
414}
415
416static int solo_v4l2_open(struct file *file)
417{
418	struct solo6010_dev *solo_dev = video_drvdata(file);
419	struct solo_filehandle *fh;
420	int ret;
421
422	if ((fh = kzalloc(sizeof(*fh), GFP_KERNEL)) == NULL)
423		return -ENOMEM;
424
425	spin_lock_init(&fh->slock);
426	INIT_LIST_HEAD(&fh->vidq_active);
427	fh->solo_dev = solo_dev;
428	file->private_data = fh;
429
430	if ((ret = solo_start_thread(fh))) {
431		kfree(fh);
432		return ret;
433	}
434
435	videobuf_queue_dma_contig_init(&fh->vidq, &solo_video_qops,
436				    &solo_dev->pdev->dev, &fh->slock,
437				    V4L2_BUF_TYPE_VIDEO_CAPTURE,
438				    SOLO_DISP_PIX_FIELD,
439				    sizeof(struct videobuf_buffer), fh);
440
441	return 0;
442}
443
444static ssize_t solo_v4l2_read(struct file *file, char __user *data,
445			      size_t count, loff_t *ppos)
446{
447	struct solo_filehandle *fh = file->private_data;
448
449	return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
450				    file->f_flags & O_NONBLOCK);
451}
452
453static int solo_v4l2_release(struct file *file)
454{
455	struct solo_filehandle *fh = file->private_data;
456
457	videobuf_stop(&fh->vidq);
458	videobuf_mmap_free(&fh->vidq);
459	solo_stop_thread(fh);
460	kfree(fh);
461
462	return 0;
463}
464
465static int solo_querycap(struct file *file, void  *priv,
466			 struct v4l2_capability *cap)
467{
468	struct solo_filehandle  *fh  = priv;
469	struct solo6010_dev *solo_dev = fh->solo_dev;
470
471	strcpy(cap->driver, SOLO6010_NAME);
472	strcpy(cap->card, "Softlogic 6010");
473	snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s",
474		 pci_name(solo_dev->pdev));
475	cap->version = SOLO6010_VER_NUM;
476	cap->capabilities =     V4L2_CAP_VIDEO_CAPTURE |
477				V4L2_CAP_READWRITE |
478				V4L2_CAP_STREAMING;
479	return 0;
480}
481
482static int solo_enum_ext_input(struct solo6010_dev *solo_dev,
483			       struct v4l2_input *input)
484{
485	static const char *dispnames_1[] = { "4UP" };
486	static const char *dispnames_2[] = { "4UP-1", "4UP-2" };
487	static const char *dispnames_5[] = {
488		"4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP"
489	};
490	const char **dispnames;
491
492	if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext))
493		return -EINVAL;
494
495	if (solo_dev->nr_ext == 5)
496		dispnames = dispnames_5;
497	else if (solo_dev->nr_ext == 2)
498		dispnames = dispnames_2;
499	else
500		dispnames = dispnames_1;
501
502	snprintf(input->name, sizeof(input->name), "Multi %s",
503		 dispnames[input->index - solo_dev->nr_chans]);
504
505	return 0;
506}
507
508static int solo_enum_input(struct file *file, void *priv,
509			   struct v4l2_input *input)
510{
511	struct solo_filehandle *fh  = priv;
512	struct solo6010_dev *solo_dev = fh->solo_dev;
513
514	if (input->index >= solo_dev->nr_chans) {
515		int ret = solo_enum_ext_input(solo_dev, input);
516		if (ret < 0)
517			return ret;
518	} else {
519		snprintf(input->name, sizeof(input->name), "Camera %d",
520			 input->index + 1);
521
522		/* We can only check this for normal inputs */
523		if (!tw28_get_video_status(solo_dev, input->index))
524			input->status = V4L2_IN_ST_NO_SIGNAL;
525	}
526
527	input->type = V4L2_INPUT_TYPE_CAMERA;
528
529	if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC)
530		input->std = V4L2_STD_NTSC_M;
531	else
532		input->std = V4L2_STD_PAL_M;
533
534	return 0;
535}
536
537static int solo_set_input(struct file *file, void *priv, unsigned int index)
538{
539	struct solo_filehandle *fh = priv;
540
541	return solo_v4l2_set_ch(fh->solo_dev, index);
542}
543
544static int solo_get_input(struct file *file, void *priv, unsigned int *index)
545{
546	struct solo_filehandle *fh = priv;
547
548	*index = fh->solo_dev->cur_disp_ch;
549
550	return 0;
551}
552
553static int solo_enum_fmt_cap(struct file *file, void *priv,
554			     struct v4l2_fmtdesc *f)
555{
556	if (f->index)
557		return -EINVAL;
558
559	f->pixelformat = V4L2_PIX_FMT_UYVY;
560	strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description));
561
562	return 0;
563}
564
565static int solo_try_fmt_cap(struct file *file, void *priv,
566			    struct v4l2_format *f)
567{
568	struct solo_filehandle *fh = priv;
569	struct solo6010_dev *solo_dev = fh->solo_dev;
570	struct v4l2_pix_format *pix = &f->fmt.pix;
571	int image_size = solo_image_size(solo_dev);
572
573	/* Check supported sizes */
574	if (pix->width != solo_dev->video_hsize)
575		pix->width = solo_dev->video_hsize;
576	if (pix->height != solo_vlines(solo_dev))
577		pix->height = solo_vlines(solo_dev);
578	if (pix->sizeimage != image_size)
579		pix->sizeimage = image_size;
580
581	/* Check formats */
582	if (pix->field == V4L2_FIELD_ANY)
583		pix->field = SOLO_DISP_PIX_FIELD;
584
585	if (pix->pixelformat != V4L2_PIX_FMT_UYVY ||
586	    pix->field       != SOLO_DISP_PIX_FIELD ||
587	    pix->colorspace  != V4L2_COLORSPACE_SMPTE170M)
588		return -EINVAL;
589
590	return 0;
591}
592
593static int solo_set_fmt_cap(struct file *file, void *priv,
594			    struct v4l2_format *f)
595{
596	struct solo_filehandle *fh = priv;
597
598	if (videobuf_queue_is_busy(&fh->vidq))
599		return -EBUSY;
600
601	/* For right now, if it doesn't match our running config,
602	 * then fail */
603	return solo_try_fmt_cap(file, priv, f);
604}
605
606static int solo_get_fmt_cap(struct file *file, void *priv,
607			    struct v4l2_format *f)
608{
609	struct solo_filehandle *fh = priv;
610	struct solo6010_dev *solo_dev = fh->solo_dev;
611	struct v4l2_pix_format *pix = &f->fmt.pix;
612
613	pix->width = solo_dev->video_hsize;
614	pix->height = solo_vlines(solo_dev);
615	pix->pixelformat = V4L2_PIX_FMT_UYVY;
616	pix->field = SOLO_DISP_PIX_FIELD;
617	pix->sizeimage = solo_image_size(solo_dev);
618	pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
619	pix->bytesperline = solo_bytesperline(solo_dev);
620
621	return 0;
622}
623
624static int solo_reqbufs(struct file *file, void *priv,
625			struct v4l2_requestbuffers *req)
626{
627	struct solo_filehandle *fh = priv;
628
629	return videobuf_reqbufs(&fh->vidq, req);
630}
631
632static int solo_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf)
633{
634	struct solo_filehandle *fh = priv;
635
636	return videobuf_querybuf(&fh->vidq, buf);
637}
638
639static int solo_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
640{
641	struct solo_filehandle *fh = priv;
642
643	return videobuf_qbuf(&fh->vidq, buf);
644}
645
646static int solo_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
647{
648	struct solo_filehandle *fh = priv;
649
650	return videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
651}
652
653static int solo_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
654{
655	struct solo_filehandle *fh = priv;
656
657	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
658		return -EINVAL;
659
660	return videobuf_streamon(&fh->vidq);
661}
662
663static int solo_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
664{
665	struct solo_filehandle *fh = priv;
666
667	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
668		return -EINVAL;
669
670	return videobuf_streamoff(&fh->vidq);
671}
672
673static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i)
674{
675	return 0;
676}
677
678static const u32 solo_motion_ctrls[] = {
679	V4L2_CID_MOTION_TRACE,
680	0
681};
682
683static const u32 *solo_ctrl_classes[] = {
684	solo_motion_ctrls,
685	NULL
686};
687
688static int solo_disp_queryctrl(struct file *file, void *priv,
689			       struct v4l2_queryctrl *qc)
690{
691	qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id);
692	if (!qc->id)
693		return -EINVAL;
694
695	switch (qc->id) {
696#ifdef PRIVATE_CIDS
697	case V4L2_CID_MOTION_TRACE:
698		qc->type = V4L2_CTRL_TYPE_BOOLEAN;
699		qc->minimum = 0;
700		qc->maximum = qc->step = 1;
701		qc->default_value = 0;
702		strlcpy(qc->name, "Motion Detection Trace", sizeof(qc->name));
703		return 0;
704#else
705	case V4L2_CID_MOTION_TRACE:
706		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
707#endif
708	}
709	return -EINVAL;
710}
711
712static int solo_disp_g_ctrl(struct file *file, void *priv,
713			    struct v4l2_control *ctrl)
714{
715	struct solo_filehandle *fh = priv;
716	struct solo6010_dev *solo_dev = fh->solo_dev;
717
718	switch (ctrl->id) {
719	case V4L2_CID_MOTION_TRACE:
720		ctrl->value = solo_reg_read(solo_dev, SOLO_VI_MOTION_BAR)
721			? 1 : 0;
722		return 0;
723	}
724	return -EINVAL;
725}
726
727static int solo_disp_s_ctrl(struct file *file, void *priv,
728			    struct v4l2_control *ctrl)
729{
730	struct solo_filehandle *fh = priv;
731	struct solo6010_dev *solo_dev = fh->solo_dev;
732
733	switch (ctrl->id) {
734	case V4L2_CID_MOTION_TRACE:
735		if (ctrl->value) {
736			solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER,
737					SOLO_VI_MOTION_Y_ADD |
738					SOLO_VI_MOTION_Y_VALUE(0x20) |
739					SOLO_VI_MOTION_CB_VALUE(0x10) |
740					SOLO_VI_MOTION_CR_VALUE(0x10));
741			solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR,
742					SOLO_VI_MOTION_CR_ADD |
743					SOLO_VI_MOTION_Y_VALUE(0x10) |
744					SOLO_VI_MOTION_CB_VALUE(0x80) |
745					SOLO_VI_MOTION_CR_VALUE(0x10));
746		} else {
747			solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0);
748			solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0);
749		}
750		return 0;
751	}
752	return -EINVAL;
753}
754
755static const struct v4l2_file_operations solo_v4l2_fops = {
756	.owner			= THIS_MODULE,
757	.open			= solo_v4l2_open,
758	.release		= solo_v4l2_release,
759	.read			= solo_v4l2_read,
760	.poll			= solo_v4l2_poll,
761	.mmap			= solo_v4l2_mmap,
762	.ioctl			= video_ioctl2,
763};
764
765static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
766	.vidioc_querycap		= solo_querycap,
767	.vidioc_s_std			= solo_s_std,
768	/* Input callbacks */
769	.vidioc_enum_input		= solo_enum_input,
770	.vidioc_s_input			= solo_set_input,
771	.vidioc_g_input			= solo_get_input,
772	/* Video capture format callbacks */
773	.vidioc_enum_fmt_vid_cap	= solo_enum_fmt_cap,
774	.vidioc_try_fmt_vid_cap		= solo_try_fmt_cap,
775	.vidioc_s_fmt_vid_cap		= solo_set_fmt_cap,
776	.vidioc_g_fmt_vid_cap		= solo_get_fmt_cap,
777	/* Streaming I/O */
778	.vidioc_reqbufs			= solo_reqbufs,
779	.vidioc_querybuf		= solo_querybuf,
780	.vidioc_qbuf			= solo_qbuf,
781	.vidioc_dqbuf			= solo_dqbuf,
782	.vidioc_streamon		= solo_streamon,
783        .vidioc_streamoff		= solo_streamoff,
784	/* Controls */
785	.vidioc_queryctrl		= solo_disp_queryctrl,
786        .vidioc_g_ctrl			= solo_disp_g_ctrl,
787        .vidioc_s_ctrl			= solo_disp_s_ctrl,
788};
789
790static struct video_device solo_v4l2_template = {
791	.name			= SOLO6010_NAME,
792	.fops			= &solo_v4l2_fops,
793	.ioctl_ops		= &solo_v4l2_ioctl_ops,
794	.minor			= -1,
795	.release		= video_device_release,
796
797	.tvnorms		= V4L2_STD_NTSC_M | V4L2_STD_PAL_M,
798	.current_norm		= V4L2_STD_NTSC_M,
799};
800
801int solo_v4l2_init(struct solo6010_dev *solo_dev)
802{
803	int ret;
804	int i;
805
806	init_waitqueue_head(&solo_dev->disp_thread_wait);
807
808	solo_dev->vfd = video_device_alloc();
809	if (!solo_dev->vfd)
810		return -ENOMEM;
811
812	*solo_dev->vfd = solo_v4l2_template;
813	solo_dev->vfd->parent = &solo_dev->pdev->dev;
814
815	ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr);
816	if (ret < 0) {
817		video_device_release(solo_dev->vfd);
818		solo_dev->vfd = NULL;
819		return ret;
820	}
821
822	video_set_drvdata(solo_dev->vfd, solo_dev);
823
824	snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)",
825		 SOLO6010_NAME, solo_dev->vfd->num);
826
827	if (video_nr >= 0)
828		video_nr++;
829
830	dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with "
831		 "%d inputs (%d extended)\n", solo_dev->vfd->num,
832		 solo_dev->nr_chans, solo_dev->nr_ext);
833
834	/* Cycle all the channels and clear */
835	for (i = 0; i < solo_dev->nr_chans; i++) {
836		solo_v4l2_set_ch(solo_dev, i);
837		while (erase_off(solo_dev))
838			;// Do nothing
839	}
840
841	/* Set the default display channel */
842	solo_v4l2_set_ch(solo_dev, 0);
843	while (erase_off(solo_dev))
844		;// Do nothing
845
846	solo6010_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
847
848	return 0;
849}
850
851void solo_v4l2_exit(struct solo6010_dev *solo_dev)
852{
853	solo6010_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN);
854	if (solo_dev->vfd) {
855		video_unregister_device(solo_dev->vfd);
856		solo_dev->vfd = NULL;
857	}
858}
859