diff options
Diffstat (limited to 'drivers/staging/cx25821/cx25821-video.c')
-rw-r--r-- | drivers/staging/cx25821/cx25821-video.c | 2008 |
1 files changed, 2008 insertions, 0 deletions
diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/staging/cx25821/cx25821-video.c new file mode 100644 index 00000000000..7a0304a8573 --- /dev/null +++ b/drivers/staging/cx25821/cx25821-video.c | |||
@@ -0,0 +1,2008 @@ | |||
1 | /* | ||
2 | * Driver for the Conexant CX25821 PCIe bridge | ||
3 | * | ||
4 | * Copyright (C) 2009 Conexant Systems Inc. | ||
5 | * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> | ||
6 | * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver | ||
7 | * Parts adapted/taken from Eduardo Moscoso Rubino | ||
8 | * Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.com> | ||
9 | * | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * | ||
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 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
28 | |||
29 | #include "cx25821-video.h" | ||
30 | |||
31 | MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); | ||
32 | MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); | ||
33 | MODULE_LICENSE("GPL"); | ||
34 | |||
35 | static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; | ||
36 | static unsigned int radio_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; | ||
37 | |||
38 | module_param_array(video_nr, int, NULL, 0444); | ||
39 | module_param_array(radio_nr, int, NULL, 0444); | ||
40 | |||
41 | MODULE_PARM_DESC(video_nr, "video device numbers"); | ||
42 | MODULE_PARM_DESC(radio_nr, "radio device numbers"); | ||
43 | |||
44 | static unsigned int video_debug = VIDEO_DEBUG; | ||
45 | module_param(video_debug, int, 0644); | ||
46 | MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); | ||
47 | |||
48 | static unsigned int irq_debug; | ||
49 | module_param(irq_debug, int, 0644); | ||
50 | MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]"); | ||
51 | |||
52 | unsigned int vid_limit = 16; | ||
53 | module_param(vid_limit, int, 0644); | ||
54 | MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); | ||
55 | |||
56 | static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num); | ||
57 | |||
58 | static const struct v4l2_file_operations video_fops; | ||
59 | static const struct v4l2_ioctl_ops video_ioctl_ops; | ||
60 | |||
61 | #define FORMAT_FLAGS_PACKED 0x01 | ||
62 | |||
63 | struct cx25821_fmt formats[] = { | ||
64 | { | ||
65 | .name = "8 bpp, gray", | ||
66 | .fourcc = V4L2_PIX_FMT_GREY, | ||
67 | .depth = 8, | ||
68 | .flags = FORMAT_FLAGS_PACKED, | ||
69 | }, { | ||
70 | .name = "4:1:1, packed, Y41P", | ||
71 | .fourcc = V4L2_PIX_FMT_Y41P, | ||
72 | .depth = 12, | ||
73 | .flags = FORMAT_FLAGS_PACKED, | ||
74 | }, { | ||
75 | .name = "4:2:2, packed, YUYV", | ||
76 | .fourcc = V4L2_PIX_FMT_YUYV, | ||
77 | .depth = 16, | ||
78 | .flags = FORMAT_FLAGS_PACKED, | ||
79 | }, { | ||
80 | .name = "4:2:2, packed, UYVY", | ||
81 | .fourcc = V4L2_PIX_FMT_UYVY, | ||
82 | .depth = 16, | ||
83 | .flags = FORMAT_FLAGS_PACKED, | ||
84 | }, { | ||
85 | .name = "4:2:0, YUV", | ||
86 | .fourcc = V4L2_PIX_FMT_YUV420, | ||
87 | .depth = 12, | ||
88 | .flags = FORMAT_FLAGS_PACKED, | ||
89 | }, | ||
90 | }; | ||
91 | |||
92 | int cx25821_get_format_size(void) | ||
93 | { | ||
94 | return ARRAY_SIZE(formats); | ||
95 | } | ||
96 | |||
97 | struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) | ||
98 | { | ||
99 | unsigned int i; | ||
100 | |||
101 | if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P) { | ||
102 | return formats + 1; | ||
103 | } | ||
104 | |||
105 | for (i = 0; i < ARRAY_SIZE(formats); i++) | ||
106 | if (formats[i].fourcc == fourcc) | ||
107 | return formats + i; | ||
108 | |||
109 | pr_err("%s(0x%08x) NOT FOUND\n", __func__, fourcc); | ||
110 | return NULL; | ||
111 | } | ||
112 | |||
113 | void cx25821_dump_video_queue(struct cx25821_dev *dev, struct cx25821_dmaqueue *q) | ||
114 | { | ||
115 | struct cx25821_buffer *buf; | ||
116 | struct list_head *item; | ||
117 | dprintk(1, "%s()\n", __func__); | ||
118 | |||
119 | if (!list_empty(&q->active)) { | ||
120 | list_for_each(item, &q->active) | ||
121 | buf = list_entry(item, struct cx25821_buffer, vb.queue); | ||
122 | } | ||
123 | |||
124 | if (!list_empty(&q->queued)) { | ||
125 | list_for_each(item, &q->queued) | ||
126 | buf = list_entry(item, struct cx25821_buffer, vb.queue); | ||
127 | } | ||
128 | |||
129 | } | ||
130 | |||
131 | void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, | ||
132 | u32 count) | ||
133 | { | ||
134 | struct cx25821_buffer *buf; | ||
135 | int bc; | ||
136 | |||
137 | for (bc = 0;; bc++) { | ||
138 | if (list_empty(&q->active)) { | ||
139 | dprintk(1, "bc=%d (=0: active empty)\n", bc); | ||
140 | break; | ||
141 | } | ||
142 | |||
143 | buf = | ||
144 | list_entry(q->active.next, struct cx25821_buffer, vb.queue); | ||
145 | |||
146 | /* count comes from the hw and it is 16bit wide -- | ||
147 | * this trick handles wrap-arounds correctly for | ||
148 | * up to 32767 buffers in flight... */ | ||
149 | if ((s16) (count - buf->count) < 0) { | ||
150 | break; | ||
151 | } | ||
152 | |||
153 | do_gettimeofday(&buf->vb.ts); | ||
154 | buf->vb.state = VIDEOBUF_DONE; | ||
155 | list_del(&buf->vb.queue); | ||
156 | wake_up(&buf->vb.done); | ||
157 | } | ||
158 | |||
159 | if (list_empty(&q->active)) | ||
160 | del_timer(&q->timeout); | ||
161 | else | ||
162 | mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); | ||
163 | if (bc != 1) | ||
164 | pr_err("%s: %d buffers handled (should be 1)\n", | ||
165 | __func__, bc); | ||
166 | } | ||
167 | |||
168 | #ifdef TUNER_FLAG | ||
169 | int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm) | ||
170 | { | ||
171 | dprintk(1, "%s(norm = 0x%08x) name: [%s]\n", | ||
172 | __func__, (unsigned int)norm, v4l2_norm_to_name(norm)); | ||
173 | |||
174 | dev->tvnorm = norm; | ||
175 | |||
176 | /* Tell the internal A/V decoder */ | ||
177 | cx25821_call_all(dev, core, s_std, norm); | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | #endif | ||
182 | |||
183 | struct video_device *cx25821_vdev_init(struct cx25821_dev *dev, | ||
184 | struct pci_dev *pci, | ||
185 | struct video_device *template, | ||
186 | char *type) | ||
187 | { | ||
188 | struct video_device *vfd; | ||
189 | dprintk(1, "%s()\n", __func__); | ||
190 | |||
191 | vfd = video_device_alloc(); | ||
192 | if (NULL == vfd) | ||
193 | return NULL; | ||
194 | *vfd = *template; | ||
195 | vfd->v4l2_dev = &dev->v4l2_dev; | ||
196 | vfd->release = video_device_release; | ||
197 | snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, | ||
198 | cx25821_boards[dev->board].name); | ||
199 | video_set_drvdata(vfd, dev); | ||
200 | return vfd; | ||
201 | } | ||
202 | |||
203 | /* | ||
204 | static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) | ||
205 | { | ||
206 | int i; | ||
207 | |||
208 | if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1) | ||
209 | return -EINVAL; | ||
210 | for (i = 0; i < CX25821_CTLS; i++) | ||
211 | if (cx25821_ctls[i].v.id == qctrl->id) | ||
212 | break; | ||
213 | if (i == CX25821_CTLS) { | ||
214 | *qctrl = no_ctl; | ||
215 | return 0; | ||
216 | } | ||
217 | *qctrl = cx25821_ctls[i].v; | ||
218 | return 0; | ||
219 | } | ||
220 | */ | ||
221 | |||
222 | /* resource management */ | ||
223 | int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bit) | ||
224 | { | ||
225 | dprintk(1, "%s()\n", __func__); | ||
226 | if (fh->resources & bit) | ||
227 | /* have it already allocated */ | ||
228 | return 1; | ||
229 | |||
230 | /* is it free? */ | ||
231 | mutex_lock(&dev->lock); | ||
232 | if (dev->channels[fh->channel_id].resources & bit) { | ||
233 | /* no, someone else uses it */ | ||
234 | mutex_unlock(&dev->lock); | ||
235 | return 0; | ||
236 | } | ||
237 | /* it's free, grab it */ | ||
238 | fh->resources |= bit; | ||
239 | dev->channels[fh->channel_id].resources |= bit; | ||
240 | dprintk(1, "res: get %d\n", bit); | ||
241 | mutex_unlock(&dev->lock); | ||
242 | return 1; | ||
243 | } | ||
244 | |||
245 | int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit) | ||
246 | { | ||
247 | return fh->resources & bit; | ||
248 | } | ||
249 | |||
250 | int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit) | ||
251 | { | ||
252 | return fh->dev->channels[fh->channel_id].resources & bit; | ||
253 | } | ||
254 | |||
255 | void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bits) | ||
256 | { | ||
257 | BUG_ON((fh->resources & bits) != bits); | ||
258 | dprintk(1, "%s()\n", __func__); | ||
259 | |||
260 | mutex_lock(&dev->lock); | ||
261 | fh->resources &= ~bits; | ||
262 | dev->channels[fh->channel_id].resources &= ~bits; | ||
263 | dprintk(1, "res: put %d\n", bits); | ||
264 | mutex_unlock(&dev->lock); | ||
265 | } | ||
266 | |||
267 | int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input) | ||
268 | { | ||
269 | struct v4l2_routing route; | ||
270 | memset(&route, 0, sizeof(route)); | ||
271 | |||
272 | dprintk(1, "%s(): video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n", | ||
273 | __func__, input, INPUT(input)->vmux, INPUT(input)->gpio0, | ||
274 | INPUT(input)->gpio1, INPUT(input)->gpio2, INPUT(input)->gpio3); | ||
275 | dev->input = input; | ||
276 | |||
277 | route.input = INPUT(input)->vmux; | ||
278 | |||
279 | /* Tell the internal A/V decoder */ | ||
280 | cx25821_call_all(dev, video, s_routing, INPUT(input)->vmux, 0, 0); | ||
281 | |||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | int cx25821_start_video_dma(struct cx25821_dev *dev, | ||
286 | struct cx25821_dmaqueue *q, | ||
287 | struct cx25821_buffer *buf, | ||
288 | struct sram_channel *channel) | ||
289 | { | ||
290 | int tmp = 0; | ||
291 | |||
292 | /* setup fifo + format */ | ||
293 | cx25821_sram_channel_setup(dev, channel, buf->bpl, buf->risc.dma); | ||
294 | |||
295 | /* reset counter */ | ||
296 | cx_write(channel->gpcnt_ctl, 3); | ||
297 | q->count = 1; | ||
298 | |||
299 | /* enable irq */ | ||
300 | cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i)); | ||
301 | cx_set(channel->int_msk, 0x11); | ||
302 | |||
303 | /* start dma */ | ||
304 | cx_write(channel->dma_ctl, 0x11); /* FIFO and RISC enable */ | ||
305 | |||
306 | /* make sure upstream setting if any is reversed */ | ||
307 | tmp = cx_read(VID_CH_MODE_SEL); | ||
308 | cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | int cx25821_restart_video_queue(struct cx25821_dev *dev, | ||
314 | struct cx25821_dmaqueue *q, | ||
315 | struct sram_channel *channel) | ||
316 | { | ||
317 | struct cx25821_buffer *buf, *prev; | ||
318 | struct list_head *item; | ||
319 | |||
320 | if (!list_empty(&q->active)) { | ||
321 | buf = | ||
322 | list_entry(q->active.next, struct cx25821_buffer, vb.queue); | ||
323 | |||
324 | cx25821_start_video_dma(dev, q, buf, channel); | ||
325 | |||
326 | list_for_each(item, &q->active) { | ||
327 | buf = list_entry(item, struct cx25821_buffer, vb.queue); | ||
328 | buf->count = q->count++; | ||
329 | } | ||
330 | |||
331 | mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | prev = NULL; | ||
336 | for (;;) { | ||
337 | if (list_empty(&q->queued)) | ||
338 | return 0; | ||
339 | |||
340 | buf = | ||
341 | list_entry(q->queued.next, struct cx25821_buffer, vb.queue); | ||
342 | |||
343 | if (NULL == prev) { | ||
344 | list_move_tail(&buf->vb.queue, &q->active); | ||
345 | cx25821_start_video_dma(dev, q, buf, channel); | ||
346 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
347 | buf->count = q->count++; | ||
348 | mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); | ||
349 | } else if (prev->vb.width == buf->vb.width && | ||
350 | prev->vb.height == buf->vb.height && | ||
351 | prev->fmt == buf->fmt) { | ||
352 | list_move_tail(&buf->vb.queue, &q->active); | ||
353 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
354 | buf->count = q->count++; | ||
355 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
356 | prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */ | ||
357 | } else { | ||
358 | return 0; | ||
359 | } | ||
360 | prev = buf; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | void cx25821_vid_timeout(unsigned long data) | ||
365 | { | ||
366 | struct cx25821_data *timeout_data = (struct cx25821_data *)data; | ||
367 | struct cx25821_dev *dev = timeout_data->dev; | ||
368 | struct sram_channel *channel = timeout_data->channel; | ||
369 | struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq; | ||
370 | struct cx25821_buffer *buf; | ||
371 | unsigned long flags; | ||
372 | |||
373 | /* cx25821_sram_channel_dump(dev, channel); */ | ||
374 | cx_clear(channel->dma_ctl, 0x11); | ||
375 | |||
376 | spin_lock_irqsave(&dev->slock, flags); | ||
377 | while (!list_empty(&q->active)) { | ||
378 | buf = | ||
379 | list_entry(q->active.next, struct cx25821_buffer, vb.queue); | ||
380 | list_del(&buf->vb.queue); | ||
381 | |||
382 | buf->vb.state = VIDEOBUF_ERROR; | ||
383 | wake_up(&buf->vb.done); | ||
384 | } | ||
385 | |||
386 | cx25821_restart_video_queue(dev, q, channel); | ||
387 | spin_unlock_irqrestore(&dev->slock, flags); | ||
388 | } | ||
389 | |||
390 | int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) | ||
391 | { | ||
392 | u32 count = 0; | ||
393 | int handled = 0; | ||
394 | u32 mask; | ||
395 | struct sram_channel *channel = dev->channels[chan_num].sram_channels; | ||
396 | |||
397 | mask = cx_read(channel->int_msk); | ||
398 | if (0 == (status & mask)) | ||
399 | return handled; | ||
400 | |||
401 | cx_write(channel->int_stat, status); | ||
402 | |||
403 | /* risc op code error */ | ||
404 | if (status & (1 << 16)) { | ||
405 | pr_warn("%s, %s: video risc op code error\n", | ||
406 | dev->name, channel->name); | ||
407 | cx_clear(channel->dma_ctl, 0x11); | ||
408 | cx25821_sram_channel_dump(dev, channel); | ||
409 | } | ||
410 | |||
411 | /* risc1 y */ | ||
412 | if (status & FLD_VID_DST_RISC1) { | ||
413 | spin_lock(&dev->slock); | ||
414 | count = cx_read(channel->gpcnt); | ||
415 | cx25821_video_wakeup(dev, | ||
416 | &dev->channels[channel->i].vidq, count); | ||
417 | spin_unlock(&dev->slock); | ||
418 | handled++; | ||
419 | } | ||
420 | |||
421 | /* risc2 y */ | ||
422 | if (status & 0x10) { | ||
423 | dprintk(2, "stopper video\n"); | ||
424 | spin_lock(&dev->slock); | ||
425 | cx25821_restart_video_queue(dev, | ||
426 | &dev->channels[channel->i].vidq, | ||
427 | channel); | ||
428 | spin_unlock(&dev->slock); | ||
429 | handled++; | ||
430 | } | ||
431 | return handled; | ||
432 | } | ||
433 | |||
434 | void cx25821_videoioctl_unregister(struct cx25821_dev *dev) | ||
435 | { | ||
436 | if (dev->ioctl_dev) { | ||
437 | if (video_is_registered(dev->ioctl_dev)) | ||
438 | video_unregister_device(dev->ioctl_dev); | ||
439 | else | ||
440 | video_device_release(dev->ioctl_dev); | ||
441 | |||
442 | dev->ioctl_dev = NULL; | ||
443 | } | ||
444 | } | ||
445 | |||
446 | void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) | ||
447 | { | ||
448 | cx_clear(PCI_INT_MSK, 1); | ||
449 | |||
450 | if (dev->channels[chan_num].video_dev) { | ||
451 | if (video_is_registered(dev->channels[chan_num].video_dev)) | ||
452 | video_unregister_device( | ||
453 | dev->channels[chan_num].video_dev); | ||
454 | else | ||
455 | video_device_release( | ||
456 | dev->channels[chan_num].video_dev); | ||
457 | |||
458 | dev->channels[chan_num].video_dev = NULL; | ||
459 | |||
460 | btcx_riscmem_free(dev->pci, | ||
461 | &dev->channels[chan_num].vidq.stopper); | ||
462 | |||
463 | pr_warn("device %d released!\n", chan_num); | ||
464 | } | ||
465 | |||
466 | } | ||
467 | |||
468 | int cx25821_video_register(struct cx25821_dev *dev) | ||
469 | { | ||
470 | int err; | ||
471 | int i; | ||
472 | |||
473 | struct video_device cx25821_video_device = { | ||
474 | .name = "cx25821-video", | ||
475 | .fops = &video_fops, | ||
476 | .minor = -1, | ||
477 | .ioctl_ops = &video_ioctl_ops, | ||
478 | .tvnorms = CX25821_NORMS, | ||
479 | .current_norm = V4L2_STD_NTSC_M, | ||
480 | }; | ||
481 | |||
482 | spin_lock_init(&dev->slock); | ||
483 | |||
484 | for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) { | ||
485 | cx25821_init_controls(dev, i); | ||
486 | |||
487 | cx25821_risc_stopper(dev->pci, | ||
488 | &dev->channels[i].vidq.stopper, | ||
489 | dev->channels[i].sram_channels->dma_ctl, | ||
490 | 0x11, 0); | ||
491 | |||
492 | dev->channels[i].sram_channels = &cx25821_sram_channels[i]; | ||
493 | dev->channels[i].video_dev = NULL; | ||
494 | dev->channels[i].resources = 0; | ||
495 | |||
496 | cx_write(dev->channels[i].sram_channels->int_stat, | ||
497 | 0xffffffff); | ||
498 | |||
499 | INIT_LIST_HEAD(&dev->channels[i].vidq.active); | ||
500 | INIT_LIST_HEAD(&dev->channels[i].vidq.queued); | ||
501 | |||
502 | dev->channels[i].timeout_data.dev = dev; | ||
503 | dev->channels[i].timeout_data.channel = | ||
504 | &cx25821_sram_channels[i]; | ||
505 | dev->channels[i].vidq.timeout.function = | ||
506 | cx25821_vid_timeout; | ||
507 | dev->channels[i].vidq.timeout.data = | ||
508 | (unsigned long)&dev->channels[i].timeout_data; | ||
509 | init_timer(&dev->channels[i].vidq.timeout); | ||
510 | |||
511 | /* register v4l devices */ | ||
512 | dev->channels[i].video_dev = cx25821_vdev_init(dev, | ||
513 | dev->pci, &cx25821_video_device, "video"); | ||
514 | |||
515 | err = video_register_device(dev->channels[i].video_dev, | ||
516 | VFL_TYPE_GRABBER, video_nr[dev->nr]); | ||
517 | |||
518 | if (err < 0) | ||
519 | goto fail_unreg; | ||
520 | |||
521 | } | ||
522 | |||
523 | /* set PCI interrupt */ | ||
524 | cx_set(PCI_INT_MSK, 0xff); | ||
525 | |||
526 | /* initial device configuration */ | ||
527 | mutex_lock(&dev->lock); | ||
528 | #ifdef TUNER_FLAG | ||
529 | dev->tvnorm = cx25821_video_device.current_norm; | ||
530 | cx25821_set_tvnorm(dev, dev->tvnorm); | ||
531 | #endif | ||
532 | mutex_unlock(&dev->lock); | ||
533 | |||
534 | |||
535 | return 0; | ||
536 | |||
537 | fail_unreg: | ||
538 | cx25821_video_unregister(dev, i); | ||
539 | return err; | ||
540 | } | ||
541 | |||
542 | int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, | ||
543 | unsigned int *size) | ||
544 | { | ||
545 | struct cx25821_fh *fh = q->priv_data; | ||
546 | |||
547 | *size = fh->fmt->depth * fh->width * fh->height >> 3; | ||
548 | |||
549 | if (0 == *count) | ||
550 | *count = 32; | ||
551 | |||
552 | if (*size * *count > vid_limit * 1024 * 1024) | ||
553 | *count = (vid_limit * 1024 * 1024) / *size; | ||
554 | |||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, | ||
559 | enum v4l2_field field) | ||
560 | { | ||
561 | struct cx25821_fh *fh = q->priv_data; | ||
562 | struct cx25821_dev *dev = fh->dev; | ||
563 | struct cx25821_buffer *buf = | ||
564 | container_of(vb, struct cx25821_buffer, vb); | ||
565 | int rc, init_buffer = 0; | ||
566 | u32 line0_offset, line1_offset; | ||
567 | struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); | ||
568 | int bpl_local = LINE_SIZE_D1; | ||
569 | int channel_opened = fh->channel_id; | ||
570 | |||
571 | BUG_ON(NULL == fh->fmt); | ||
572 | if (fh->width < 48 || fh->width > 720 || | ||
573 | fh->height < 32 || fh->height > 576) | ||
574 | return -EINVAL; | ||
575 | |||
576 | buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; | ||
577 | |||
578 | if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) | ||
579 | return -EINVAL; | ||
580 | |||
581 | if (buf->fmt != fh->fmt || | ||
582 | buf->vb.width != fh->width || | ||
583 | buf->vb.height != fh->height || buf->vb.field != field) { | ||
584 | buf->fmt = fh->fmt; | ||
585 | buf->vb.width = fh->width; | ||
586 | buf->vb.height = fh->height; | ||
587 | buf->vb.field = field; | ||
588 | init_buffer = 1; | ||
589 | } | ||
590 | |||
591 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { | ||
592 | init_buffer = 1; | ||
593 | rc = videobuf_iolock(q, &buf->vb, NULL); | ||
594 | if (0 != rc) { | ||
595 | printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n")); | ||
596 | goto fail; | ||
597 | } | ||
598 | } | ||
599 | |||
600 | dprintk(1, "init_buffer=%d\n", init_buffer); | ||
601 | |||
602 | if (init_buffer) { | ||
603 | |||
604 | channel_opened = dev->channel_opened; | ||
605 | channel_opened = (channel_opened < 0 | ||
606 | || channel_opened > 7) ? 7 : channel_opened; | ||
607 | |||
608 | if (dev->channels[channel_opened] | ||
609 | .pixel_formats == PIXEL_FRMT_411) | ||
610 | buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3; | ||
611 | else | ||
612 | buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width); | ||
613 | |||
614 | if (dev->channels[channel_opened] | ||
615 | .pixel_formats == PIXEL_FRMT_411) { | ||
616 | bpl_local = buf->bpl; | ||
617 | } else { | ||
618 | bpl_local = buf->bpl; /* Default */ | ||
619 | |||
620 | if (channel_opened >= 0 && channel_opened <= 7) { | ||
621 | if (dev->channels[channel_opened] | ||
622 | .use_cif_resolution) { | ||
623 | if (dev->tvnorm & V4L2_STD_PAL_BG | ||
624 | || dev->tvnorm & V4L2_STD_PAL_DK) | ||
625 | bpl_local = 352 << 1; | ||
626 | else | ||
627 | bpl_local = | ||
628 | dev->channels[channel_opened]. | ||
629 | cif_width << | ||
630 | 1; | ||
631 | } | ||
632 | } | ||
633 | } | ||
634 | |||
635 | switch (buf->vb.field) { | ||
636 | case V4L2_FIELD_TOP: | ||
637 | cx25821_risc_buffer(dev->pci, &buf->risc, | ||
638 | dma->sglist, 0, UNSET, | ||
639 | buf->bpl, 0, buf->vb.height); | ||
640 | break; | ||
641 | case V4L2_FIELD_BOTTOM: | ||
642 | cx25821_risc_buffer(dev->pci, &buf->risc, | ||
643 | dma->sglist, UNSET, 0, | ||
644 | buf->bpl, 0, buf->vb.height); | ||
645 | break; | ||
646 | case V4L2_FIELD_INTERLACED: | ||
647 | /* All other formats are top field first */ | ||
648 | line0_offset = 0; | ||
649 | line1_offset = buf->bpl; | ||
650 | dprintk(1, "top field first\n"); | ||
651 | |||
652 | cx25821_risc_buffer(dev->pci, &buf->risc, | ||
653 | dma->sglist, line0_offset, | ||
654 | bpl_local, bpl_local, bpl_local, | ||
655 | buf->vb.height >> 1); | ||
656 | break; | ||
657 | case V4L2_FIELD_SEQ_TB: | ||
658 | cx25821_risc_buffer(dev->pci, &buf->risc, | ||
659 | dma->sglist, | ||
660 | 0, buf->bpl * (buf->vb.height >> 1), | ||
661 | buf->bpl, 0, buf->vb.height >> 1); | ||
662 | break; | ||
663 | case V4L2_FIELD_SEQ_BT: | ||
664 | cx25821_risc_buffer(dev->pci, &buf->risc, | ||
665 | dma->sglist, | ||
666 | buf->bpl * (buf->vb.height >> 1), 0, | ||
667 | buf->bpl, 0, buf->vb.height >> 1); | ||
668 | break; | ||
669 | default: | ||
670 | BUG(); | ||
671 | } | ||
672 | } | ||
673 | |||
674 | dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", | ||
675 | buf, buf->vb.i, fh->width, fh->height, fh->fmt->depth, | ||
676 | fh->fmt->name, (unsigned long)buf->risc.dma); | ||
677 | |||
678 | buf->vb.state = VIDEOBUF_PREPARED; | ||
679 | |||
680 | return 0; | ||
681 | |||
682 | fail: | ||
683 | cx25821_free_buffer(q, buf); | ||
684 | return rc; | ||
685 | } | ||
686 | |||
687 | void cx25821_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
688 | { | ||
689 | struct cx25821_buffer *buf = | ||
690 | container_of(vb, struct cx25821_buffer, vb); | ||
691 | |||
692 | cx25821_free_buffer(q, buf); | ||
693 | } | ||
694 | |||
695 | struct videobuf_queue *get_queue(struct cx25821_fh *fh) | ||
696 | { | ||
697 | switch (fh->type) { | ||
698 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
699 | return &fh->vidq; | ||
700 | default: | ||
701 | BUG(); | ||
702 | return NULL; | ||
703 | } | ||
704 | } | ||
705 | |||
706 | int cx25821_get_resource(struct cx25821_fh *fh, int resource) | ||
707 | { | ||
708 | switch (fh->type) { | ||
709 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
710 | return resource; | ||
711 | default: | ||
712 | BUG(); | ||
713 | return 0; | ||
714 | } | ||
715 | } | ||
716 | |||
717 | int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma) | ||
718 | { | ||
719 | struct cx25821_fh *fh = file->private_data; | ||
720 | |||
721 | return videobuf_mmap_mapper(get_queue(fh), vma); | ||
722 | } | ||
723 | |||
724 | |||
725 | static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) | ||
726 | { | ||
727 | struct cx25821_buffer *buf = | ||
728 | container_of(vb, struct cx25821_buffer, vb); | ||
729 | struct cx25821_buffer *prev; | ||
730 | struct cx25821_fh *fh = vq->priv_data; | ||
731 | struct cx25821_dev *dev = fh->dev; | ||
732 | struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].vidq; | ||
733 | |||
734 | /* add jump to stopper */ | ||
735 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); | ||
736 | buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); | ||
737 | buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ | ||
738 | |||
739 | dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); | ||
740 | |||
741 | if (!list_empty(&q->queued)) { | ||
742 | list_add_tail(&buf->vb.queue, &q->queued); | ||
743 | buf->vb.state = VIDEOBUF_QUEUED; | ||
744 | dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, | ||
745 | buf->vb.i); | ||
746 | |||
747 | } else if (list_empty(&q->active)) { | ||
748 | list_add_tail(&buf->vb.queue, &q->active); | ||
749 | cx25821_start_video_dma(dev, q, buf, | ||
750 | dev->channels[fh->channel_id]. | ||
751 | sram_channels); | ||
752 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
753 | buf->count = q->count++; | ||
754 | mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); | ||
755 | dprintk(2, | ||
756 | "[%p/%d] buffer_queue - first active, buf cnt = %d, \ | ||
757 | q->count = %d\n", | ||
758 | buf, buf->vb.i, buf->count, q->count); | ||
759 | } else { | ||
760 | prev = | ||
761 | list_entry(q->active.prev, struct cx25821_buffer, vb.queue); | ||
762 | if (prev->vb.width == buf->vb.width | ||
763 | && prev->vb.height == buf->vb.height | ||
764 | && prev->fmt == buf->fmt) { | ||
765 | list_add_tail(&buf->vb.queue, &q->active); | ||
766 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
767 | buf->count = q->count++; | ||
768 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
769 | |||
770 | /* 64 bit bits 63-32 */ | ||
771 | prev->risc.jmp[2] = cpu_to_le32(0); | ||
772 | dprintk(2, | ||
773 | "[%p/%d] buffer_queue - append to active, \ | ||
774 | buf->count=%d\n", | ||
775 | buf, buf->vb.i, buf->count); | ||
776 | |||
777 | } else { | ||
778 | list_add_tail(&buf->vb.queue, &q->queued); | ||
779 | buf->vb.state = VIDEOBUF_QUEUED; | ||
780 | dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, | ||
781 | buf->vb.i); | ||
782 | } | ||
783 | } | ||
784 | |||
785 | if (list_empty(&q->active)) | ||
786 | dprintk(2, "active queue empty!\n"); | ||
787 | } | ||
788 | |||
789 | static struct videobuf_queue_ops cx25821_video_qops = { | ||
790 | .buf_setup = cx25821_buffer_setup, | ||
791 | .buf_prepare = cx25821_buffer_prepare, | ||
792 | .buf_queue = buffer_queue, | ||
793 | .buf_release = cx25821_buffer_release, | ||
794 | }; | ||
795 | |||
796 | static int video_open(struct file *file) | ||
797 | { | ||
798 | struct video_device *vdev = video_devdata(file); | ||
799 | struct cx25821_dev *h, *dev = video_drvdata(file); | ||
800 | struct cx25821_fh *fh; | ||
801 | struct list_head *list; | ||
802 | int minor = video_devdata(file)->minor; | ||
803 | enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
804 | u32 pix_format; | ||
805 | int ch_id = 0; | ||
806 | int i; | ||
807 | |||
808 | dprintk(1, "open dev=%s type=%s\n", | ||
809 | video_device_node_name(vdev), | ||
810 | v4l2_type_names[type]); | ||
811 | |||
812 | /* allocate + initialize per filehandle data */ | ||
813 | fh = kzalloc(sizeof(*fh), GFP_KERNEL); | ||
814 | if (NULL == fh) | ||
815 | return -ENOMEM; | ||
816 | |||
817 | mutex_lock(&cx25821_devlist_mutex); | ||
818 | |||
819 | list_for_each(list, &cx25821_devlist) | ||
820 | { | ||
821 | h = list_entry(list, struct cx25821_dev, devlist); | ||
822 | |||
823 | for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) { | ||
824 | if (h->channels[i].video_dev && | ||
825 | h->channels[i].video_dev->minor == minor) { | ||
826 | dev = h; | ||
827 | ch_id = i; | ||
828 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
829 | } | ||
830 | } | ||
831 | } | ||
832 | |||
833 | if (NULL == dev) { | ||
834 | mutex_unlock(&cx25821_devlist_mutex); | ||
835 | kfree(fh); | ||
836 | return -ENODEV; | ||
837 | } | ||
838 | |||
839 | file->private_data = fh; | ||
840 | fh->dev = dev; | ||
841 | fh->type = type; | ||
842 | fh->width = 720; | ||
843 | fh->channel_id = ch_id; | ||
844 | |||
845 | if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) | ||
846 | fh->height = 576; | ||
847 | else | ||
848 | fh->height = 480; | ||
849 | |||
850 | dev->channel_opened = fh->channel_id; | ||
851 | pix_format = | ||
852 | (dev->channels[ch_id].pixel_formats == | ||
853 | PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; | ||
854 | fh->fmt = cx25821_format_by_fourcc(pix_format); | ||
855 | |||
856 | v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio); | ||
857 | |||
858 | videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, | ||
859 | &dev->pci->dev, &dev->slock, | ||
860 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
861 | V4L2_FIELD_INTERLACED, | ||
862 | sizeof(struct cx25821_buffer), fh, NULL); | ||
863 | |||
864 | dprintk(1, "post videobuf_queue_init()\n"); | ||
865 | mutex_unlock(&cx25821_devlist_mutex); | ||
866 | |||
867 | return 0; | ||
868 | } | ||
869 | |||
870 | static ssize_t video_read(struct file *file, char __user * data, size_t count, | ||
871 | loff_t *ppos) | ||
872 | { | ||
873 | struct cx25821_fh *fh = file->private_data; | ||
874 | |||
875 | switch (fh->type) { | ||
876 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
877 | if (cx25821_res_locked(fh, RESOURCE_VIDEO0)) | ||
878 | return -EBUSY; | ||
879 | |||
880 | return videobuf_read_one(&fh->vidq, data, count, ppos, | ||
881 | file->f_flags & O_NONBLOCK); | ||
882 | |||
883 | default: | ||
884 | BUG(); | ||
885 | return 0; | ||
886 | } | ||
887 | } | ||
888 | |||
889 | static unsigned int video_poll(struct file *file, | ||
890 | struct poll_table_struct *wait) | ||
891 | { | ||
892 | struct cx25821_fh *fh = file->private_data; | ||
893 | struct cx25821_buffer *buf; | ||
894 | |||
895 | if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { | ||
896 | /* streaming capture */ | ||
897 | if (list_empty(&fh->vidq.stream)) | ||
898 | return POLLERR; | ||
899 | buf = list_entry(fh->vidq.stream.next, | ||
900 | struct cx25821_buffer, vb.stream); | ||
901 | } else { | ||
902 | /* read() capture */ | ||
903 | buf = (struct cx25821_buffer *)fh->vidq.read_buf; | ||
904 | if (NULL == buf) | ||
905 | return POLLERR; | ||
906 | } | ||
907 | |||
908 | poll_wait(file, &buf->vb.done, wait); | ||
909 | if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { | ||
910 | if (buf->vb.state == VIDEOBUF_DONE) { | ||
911 | struct cx25821_dev *dev = fh->dev; | ||
912 | |||
913 | if (dev && dev->channels[fh->channel_id] | ||
914 | .use_cif_resolution) { | ||
915 | u8 cam_id = *((char *)buf->vb.baddr + 3); | ||
916 | memcpy((char *)buf->vb.baddr, | ||
917 | (char *)buf->vb.baddr + (fh->width * 2), | ||
918 | (fh->width * 2)); | ||
919 | *((char *)buf->vb.baddr + 3) = cam_id; | ||
920 | } | ||
921 | } | ||
922 | |||
923 | return POLLIN | POLLRDNORM; | ||
924 | } | ||
925 | |||
926 | return 0; | ||
927 | } | ||
928 | |||
929 | static int video_release(struct file *file) | ||
930 | { | ||
931 | struct cx25821_fh *fh = file->private_data; | ||
932 | struct cx25821_dev *dev = fh->dev; | ||
933 | |||
934 | /* stop the risc engine and fifo */ | ||
935 | cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */ | ||
936 | |||
937 | /* stop video capture */ | ||
938 | if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { | ||
939 | videobuf_queue_cancel(&fh->vidq); | ||
940 | cx25821_res_free(dev, fh, RESOURCE_VIDEO0); | ||
941 | } | ||
942 | |||
943 | if (fh->vidq.read_buf) { | ||
944 | cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); | ||
945 | kfree(fh->vidq.read_buf); | ||
946 | } | ||
947 | |||
948 | videobuf_mmap_free(&fh->vidq); | ||
949 | |||
950 | v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio); | ||
951 | file->private_data = NULL; | ||
952 | kfree(fh); | ||
953 | |||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) | ||
958 | { | ||
959 | struct cx25821_fh *fh = priv; | ||
960 | struct cx25821_dev *dev = fh->dev; | ||
961 | |||
962 | if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) | ||
963 | return -EINVAL; | ||
964 | |||
965 | if (unlikely(i != fh->type)) | ||
966 | return -EINVAL; | ||
967 | |||
968 | if (unlikely(!cx25821_res_get(dev, fh, | ||
969 | cx25821_get_resource(fh, RESOURCE_VIDEO0)))) | ||
970 | return -EBUSY; | ||
971 | |||
972 | return videobuf_streamon(get_queue(fh)); | ||
973 | } | ||
974 | |||
975 | static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) | ||
976 | { | ||
977 | struct cx25821_fh *fh = priv; | ||
978 | struct cx25821_dev *dev = fh->dev; | ||
979 | int err, res; | ||
980 | |||
981 | if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
982 | return -EINVAL; | ||
983 | if (i != fh->type) | ||
984 | return -EINVAL; | ||
985 | |||
986 | res = cx25821_get_resource(fh, RESOURCE_VIDEO0); | ||
987 | err = videobuf_streamoff(get_queue(fh)); | ||
988 | if (err < 0) | ||
989 | return err; | ||
990 | cx25821_res_free(dev, fh, res); | ||
991 | return 0; | ||
992 | } | ||
993 | |||
994 | static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, | ||
995 | struct v4l2_format *f) | ||
996 | { | ||
997 | struct cx25821_fh *fh = priv; | ||
998 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
999 | struct v4l2_mbus_framefmt mbus_fmt; | ||
1000 | int err; | ||
1001 | int pix_format = PIXEL_FRMT_422; | ||
1002 | |||
1003 | if (fh) { | ||
1004 | err = v4l2_prio_check(&dev->channels[fh->channel_id] | ||
1005 | .prio, fh->prio); | ||
1006 | if (0 != err) | ||
1007 | return err; | ||
1008 | } | ||
1009 | |||
1010 | dprintk(2, "%s()\n", __func__); | ||
1011 | err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); | ||
1012 | |||
1013 | if (0 != err) | ||
1014 | return err; | ||
1015 | |||
1016 | fh->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); | ||
1017 | fh->vidq.field = f->fmt.pix.field; | ||
1018 | |||
1019 | /* check if width and height is valid based on set standard */ | ||
1020 | if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) | ||
1021 | fh->width = f->fmt.pix.width; | ||
1022 | |||
1023 | if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) | ||
1024 | fh->height = f->fmt.pix.height; | ||
1025 | |||
1026 | if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) | ||
1027 | pix_format = PIXEL_FRMT_411; | ||
1028 | else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) | ||
1029 | pix_format = PIXEL_FRMT_422; | ||
1030 | else | ||
1031 | return -EINVAL; | ||
1032 | |||
1033 | cx25821_set_pixel_format(dev, SRAM_CH00, pix_format); | ||
1034 | |||
1035 | /* check if cif resolution */ | ||
1036 | if (fh->width == 320 || fh->width == 352) | ||
1037 | dev->channels[fh->channel_id].use_cif_resolution = 1; | ||
1038 | else | ||
1039 | dev->channels[fh->channel_id].use_cif_resolution = 0; | ||
1040 | |||
1041 | dev->channels[fh->channel_id].cif_width = fh->width; | ||
1042 | medusa_set_resolution(dev, fh->width, SRAM_CH00); | ||
1043 | |||
1044 | dprintk(2, "%s(): width=%d height=%d field=%d\n", __func__, fh->width, | ||
1045 | fh->height, fh->vidq.field); | ||
1046 | v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); | ||
1047 | cx25821_call_all(dev, video, s_mbus_fmt, &mbus_fmt); | ||
1048 | |||
1049 | return 0; | ||
1050 | } | ||
1051 | |||
1052 | static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) | ||
1053 | { | ||
1054 | int ret_val = 0; | ||
1055 | struct cx25821_fh *fh = priv; | ||
1056 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1057 | |||
1058 | ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); | ||
1059 | |||
1060 | p->sequence = dev->channels[fh->channel_id].vidq.count; | ||
1061 | |||
1062 | return ret_val; | ||
1063 | } | ||
1064 | |||
1065 | static int vidioc_log_status(struct file *file, void *priv) | ||
1066 | { | ||
1067 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1068 | struct cx25821_fh *fh = priv; | ||
1069 | char name[32 + 2]; | ||
1070 | |||
1071 | struct sram_channel *sram_ch = dev->channels[fh->channel_id] | ||
1072 | .sram_channels; | ||
1073 | u32 tmp = 0; | ||
1074 | |||
1075 | snprintf(name, sizeof(name), "%s/2", dev->name); | ||
1076 | pr_info("%s/2: ============ START LOG STATUS ============\n", | ||
1077 | dev->name); | ||
1078 | cx25821_call_all(dev, core, log_status); | ||
1079 | tmp = cx_read(sram_ch->dma_ctl); | ||
1080 | pr_info("Video input 0 is %s\n", | ||
1081 | (tmp & 0x11) ? "streaming" : "stopped"); | ||
1082 | pr_info("%s/2: ============= END LOG STATUS =============\n", | ||
1083 | dev->name); | ||
1084 | return 0; | ||
1085 | } | ||
1086 | |||
1087 | static int vidioc_s_ctrl(struct file *file, void *priv, | ||
1088 | struct v4l2_control *ctl) | ||
1089 | { | ||
1090 | struct cx25821_fh *fh = priv; | ||
1091 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1092 | int err; | ||
1093 | |||
1094 | if (fh) { | ||
1095 | err = v4l2_prio_check(&dev->channels[fh->channel_id] | ||
1096 | .prio, fh->prio); | ||
1097 | if (0 != err) | ||
1098 | return err; | ||
1099 | } | ||
1100 | |||
1101 | return cx25821_set_control(dev, ctl, fh->channel_id); | ||
1102 | } | ||
1103 | |||
1104 | /* VIDEO IOCTLS */ | ||
1105 | int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) | ||
1106 | { | ||
1107 | struct cx25821_fh *fh = priv; | ||
1108 | |||
1109 | f->fmt.pix.width = fh->width; | ||
1110 | f->fmt.pix.height = fh->height; | ||
1111 | f->fmt.pix.field = fh->vidq.field; | ||
1112 | f->fmt.pix.pixelformat = fh->fmt->fourcc; | ||
1113 | f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; | ||
1114 | f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; | ||
1115 | |||
1116 | return 0; | ||
1117 | } | ||
1118 | |||
1119 | int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) | ||
1120 | { | ||
1121 | struct cx25821_fmt *fmt; | ||
1122 | enum v4l2_field field; | ||
1123 | unsigned int maxw, maxh; | ||
1124 | |||
1125 | fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); | ||
1126 | if (NULL == fmt) | ||
1127 | return -EINVAL; | ||
1128 | |||
1129 | field = f->fmt.pix.field; | ||
1130 | maxw = 720; | ||
1131 | maxh = 576; | ||
1132 | |||
1133 | if (V4L2_FIELD_ANY == field) { | ||
1134 | field = (f->fmt.pix.height > maxh / 2) | ||
1135 | ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; | ||
1136 | } | ||
1137 | |||
1138 | switch (field) { | ||
1139 | case V4L2_FIELD_TOP: | ||
1140 | case V4L2_FIELD_BOTTOM: | ||
1141 | maxh = maxh / 2; | ||
1142 | break; | ||
1143 | case V4L2_FIELD_INTERLACED: | ||
1144 | break; | ||
1145 | default: | ||
1146 | return -EINVAL; | ||
1147 | } | ||
1148 | |||
1149 | f->fmt.pix.field = field; | ||
1150 | if (f->fmt.pix.height < 32) | ||
1151 | f->fmt.pix.height = 32; | ||
1152 | if (f->fmt.pix.height > maxh) | ||
1153 | f->fmt.pix.height = maxh; | ||
1154 | if (f->fmt.pix.width < 48) | ||
1155 | f->fmt.pix.width = 48; | ||
1156 | if (f->fmt.pix.width > maxw) | ||
1157 | f->fmt.pix.width = maxw; | ||
1158 | f->fmt.pix.width &= ~0x03; | ||
1159 | f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; | ||
1160 | f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; | ||
1161 | |||
1162 | return 0; | ||
1163 | } | ||
1164 | |||
1165 | int cx25821_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) | ||
1166 | { | ||
1167 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1168 | |||
1169 | strcpy(cap->driver, "cx25821"); | ||
1170 | strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card)); | ||
1171 | sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); | ||
1172 | cap->version = CX25821_VERSION_CODE; | ||
1173 | cap->capabilities = | ||
1174 | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; | ||
1175 | if (UNSET != dev->tuner_type) | ||
1176 | cap->capabilities |= V4L2_CAP_TUNER; | ||
1177 | return 0; | ||
1178 | } | ||
1179 | |||
1180 | int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, | ||
1181 | struct v4l2_fmtdesc *f) | ||
1182 | { | ||
1183 | if (unlikely(f->index >= ARRAY_SIZE(formats))) | ||
1184 | return -EINVAL; | ||
1185 | |||
1186 | strlcpy(f->description, formats[f->index].name, sizeof(f->description)); | ||
1187 | f->pixelformat = formats[f->index].fourcc; | ||
1188 | |||
1189 | return 0; | ||
1190 | } | ||
1191 | |||
1192 | int cx25821_vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) | ||
1193 | { | ||
1194 | struct cx25821_fh *fh = priv; | ||
1195 | return videobuf_reqbufs(get_queue(fh), p); | ||
1196 | } | ||
1197 | |||
1198 | int cx25821_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) | ||
1199 | { | ||
1200 | struct cx25821_fh *fh = priv; | ||
1201 | return videobuf_querybuf(get_queue(fh), p); | ||
1202 | } | ||
1203 | |||
1204 | int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) | ||
1205 | { | ||
1206 | struct cx25821_fh *fh = priv; | ||
1207 | return videobuf_qbuf(get_queue(fh), p); | ||
1208 | } | ||
1209 | |||
1210 | int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) | ||
1211 | { | ||
1212 | struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; | ||
1213 | struct cx25821_fh *fh = f; | ||
1214 | |||
1215 | *p = v4l2_prio_max(&dev->channels[fh->channel_id].prio); | ||
1216 | |||
1217 | return 0; | ||
1218 | } | ||
1219 | |||
1220 | int cx25821_vidioc_s_priority(struct file *file, void *f, enum v4l2_priority prio) | ||
1221 | { | ||
1222 | struct cx25821_fh *fh = f; | ||
1223 | struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; | ||
1224 | |||
1225 | return v4l2_prio_change(&dev->channels[fh->channel_id] | ||
1226 | .prio, &fh->prio, prio); | ||
1227 | } | ||
1228 | |||
1229 | #ifdef TUNER_FLAG | ||
1230 | int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms) | ||
1231 | { | ||
1232 | struct cx25821_fh *fh = priv; | ||
1233 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1234 | int err; | ||
1235 | |||
1236 | dprintk(1, "%s()\n", __func__); | ||
1237 | |||
1238 | if (fh) { | ||
1239 | err = v4l2_prio_check(&dev->channels[fh->channel_id] | ||
1240 | .prio, fh->prio); | ||
1241 | if (0 != err) | ||
1242 | return err; | ||
1243 | } | ||
1244 | |||
1245 | if (dev->tvnorm == *tvnorms) { | ||
1246 | return 0; | ||
1247 | } | ||
1248 | |||
1249 | mutex_lock(&dev->lock); | ||
1250 | cx25821_set_tvnorm(dev, *tvnorms); | ||
1251 | mutex_unlock(&dev->lock); | ||
1252 | |||
1253 | medusa_set_videostandard(dev); | ||
1254 | |||
1255 | return 0; | ||
1256 | } | ||
1257 | #endif | ||
1258 | |||
1259 | int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i) | ||
1260 | { | ||
1261 | static const char *iname[] = { | ||
1262 | [CX25821_VMUX_COMPOSITE] = "Composite", | ||
1263 | [CX25821_VMUX_SVIDEO] = "S-Video", | ||
1264 | [CX25821_VMUX_DEBUG] = "for debug only", | ||
1265 | }; | ||
1266 | unsigned int n; | ||
1267 | dprintk(1, "%s()\n", __func__); | ||
1268 | |||
1269 | n = i->index; | ||
1270 | if (n >= 2) | ||
1271 | return -EINVAL; | ||
1272 | |||
1273 | if (0 == INPUT(n)->type) | ||
1274 | return -EINVAL; | ||
1275 | |||
1276 | i->type = V4L2_INPUT_TYPE_CAMERA; | ||
1277 | strcpy(i->name, iname[INPUT(n)->type]); | ||
1278 | |||
1279 | i->std = CX25821_NORMS; | ||
1280 | return 0; | ||
1281 | } | ||
1282 | |||
1283 | int cx25821_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) | ||
1284 | { | ||
1285 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1286 | dprintk(1, "%s()\n", __func__); | ||
1287 | return cx25821_enum_input(dev, i); | ||
1288 | } | ||
1289 | |||
1290 | int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i) | ||
1291 | { | ||
1292 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1293 | |||
1294 | *i = dev->input; | ||
1295 | dprintk(1, "%s(): returns %d\n", __func__, *i); | ||
1296 | return 0; | ||
1297 | } | ||
1298 | |||
1299 | int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i) | ||
1300 | { | ||
1301 | struct cx25821_fh *fh = priv; | ||
1302 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1303 | int err; | ||
1304 | |||
1305 | dprintk(1, "%s(%d)\n", __func__, i); | ||
1306 | |||
1307 | if (fh) { | ||
1308 | err = v4l2_prio_check(&dev->channels[fh->channel_id] | ||
1309 | .prio, fh->prio); | ||
1310 | if (0 != err) | ||
1311 | return err; | ||
1312 | } | ||
1313 | |||
1314 | if (i > 2) { | ||
1315 | dprintk(1, "%s(): -EINVAL\n", __func__); | ||
1316 | return -EINVAL; | ||
1317 | } | ||
1318 | |||
1319 | mutex_lock(&dev->lock); | ||
1320 | cx25821_video_mux(dev, i); | ||
1321 | mutex_unlock(&dev->lock); | ||
1322 | return 0; | ||
1323 | } | ||
1324 | |||
1325 | #ifdef TUNER_FLAG | ||
1326 | int cx25821_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) | ||
1327 | { | ||
1328 | struct cx25821_fh *fh = priv; | ||
1329 | struct cx25821_dev *dev = fh->dev; | ||
1330 | |||
1331 | f->frequency = dev->freq; | ||
1332 | |||
1333 | cx25821_call_all(dev, tuner, g_frequency, f); | ||
1334 | |||
1335 | return 0; | ||
1336 | } | ||
1337 | |||
1338 | int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f) | ||
1339 | { | ||
1340 | mutex_lock(&dev->lock); | ||
1341 | dev->freq = f->frequency; | ||
1342 | |||
1343 | cx25821_call_all(dev, tuner, s_frequency, f); | ||
1344 | |||
1345 | /* When changing channels it is required to reset TVAUDIO */ | ||
1346 | msleep(10); | ||
1347 | |||
1348 | mutex_unlock(&dev->lock); | ||
1349 | |||
1350 | return 0; | ||
1351 | } | ||
1352 | |||
1353 | int cx25821_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) | ||
1354 | { | ||
1355 | struct cx25821_fh *fh = priv; | ||
1356 | struct cx25821_dev *dev; | ||
1357 | int err; | ||
1358 | |||
1359 | if (fh) { | ||
1360 | dev = fh->dev; | ||
1361 | err = v4l2_prio_check(&dev->channels[fh->channel_id] | ||
1362 | .prio, fh->prio); | ||
1363 | if (0 != err) | ||
1364 | return err; | ||
1365 | } else { | ||
1366 | pr_err("Invalid fh pointer!\n"); | ||
1367 | return -EINVAL; | ||
1368 | } | ||
1369 | |||
1370 | return cx25821_set_freq(dev, f); | ||
1371 | } | ||
1372 | #endif | ||
1373 | |||
1374 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1375 | int cx25821_vidioc_g_register(struct file *file, void *fh, | ||
1376 | struct v4l2_dbg_register *reg) | ||
1377 | { | ||
1378 | struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; | ||
1379 | |||
1380 | if (!v4l2_chip_match_host(®->match)) | ||
1381 | return -EINVAL; | ||
1382 | |||
1383 | cx25821_call_all(dev, core, g_register, reg); | ||
1384 | |||
1385 | return 0; | ||
1386 | } | ||
1387 | |||
1388 | int cx25821_vidioc_s_register(struct file *file, void *fh, | ||
1389 | struct v4l2_dbg_register *reg) | ||
1390 | { | ||
1391 | struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; | ||
1392 | |||
1393 | if (!v4l2_chip_match_host(®->match)) | ||
1394 | return -EINVAL; | ||
1395 | |||
1396 | cx25821_call_all(dev, core, s_register, reg); | ||
1397 | |||
1398 | return 0; | ||
1399 | } | ||
1400 | |||
1401 | #endif | ||
1402 | |||
1403 | #ifdef TUNER_FLAG | ||
1404 | int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) | ||
1405 | { | ||
1406 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1407 | |||
1408 | if (unlikely(UNSET == dev->tuner_type)) | ||
1409 | return -EINVAL; | ||
1410 | if (0 != t->index) | ||
1411 | return -EINVAL; | ||
1412 | |||
1413 | strcpy(t->name, "Television"); | ||
1414 | t->type = V4L2_TUNER_ANALOG_TV; | ||
1415 | t->capability = V4L2_TUNER_CAP_NORM; | ||
1416 | t->rangehigh = 0xffffffffUL; | ||
1417 | |||
1418 | t->signal = 0xffff; /* LOCKED */ | ||
1419 | return 0; | ||
1420 | } | ||
1421 | |||
1422 | int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) | ||
1423 | { | ||
1424 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1425 | struct cx25821_fh *fh = priv; | ||
1426 | int err; | ||
1427 | |||
1428 | if (fh) { | ||
1429 | err = v4l2_prio_check(&dev->channels[fh->channel_id] | ||
1430 | .prio, fh->prio); | ||
1431 | if (0 != err) | ||
1432 | return err; | ||
1433 | } | ||
1434 | |||
1435 | dprintk(1, "%s()\n", __func__); | ||
1436 | if (UNSET == dev->tuner_type) | ||
1437 | return -EINVAL; | ||
1438 | if (0 != t->index) | ||
1439 | return -EINVAL; | ||
1440 | |||
1441 | return 0; | ||
1442 | } | ||
1443 | |||
1444 | #endif | ||
1445 | /*****************************************************************************/ | ||
1446 | static const struct v4l2_queryctrl no_ctl = { | ||
1447 | .name = "42", | ||
1448 | .flags = V4L2_CTRL_FLAG_DISABLED, | ||
1449 | }; | ||
1450 | |||
1451 | static struct v4l2_queryctrl cx25821_ctls[] = { | ||
1452 | /* --- video --- */ | ||
1453 | { | ||
1454 | .id = V4L2_CID_BRIGHTNESS, | ||
1455 | .name = "Brightness", | ||
1456 | .minimum = 0, | ||
1457 | .maximum = 10000, | ||
1458 | .step = 1, | ||
1459 | .default_value = 6200, | ||
1460 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
1461 | }, { | ||
1462 | .id = V4L2_CID_CONTRAST, | ||
1463 | .name = "Contrast", | ||
1464 | .minimum = 0, | ||
1465 | .maximum = 10000, | ||
1466 | .step = 1, | ||
1467 | .default_value = 5000, | ||
1468 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
1469 | }, { | ||
1470 | .id = V4L2_CID_SATURATION, | ||
1471 | .name = "Saturation", | ||
1472 | .minimum = 0, | ||
1473 | .maximum = 10000, | ||
1474 | .step = 1, | ||
1475 | .default_value = 5000, | ||
1476 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
1477 | }, { | ||
1478 | .id = V4L2_CID_HUE, | ||
1479 | .name = "Hue", | ||
1480 | .minimum = 0, | ||
1481 | .maximum = 10000, | ||
1482 | .step = 1, | ||
1483 | .default_value = 5000, | ||
1484 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
1485 | } | ||
1486 | }; | ||
1487 | static const int CX25821_CTLS = ARRAY_SIZE(cx25821_ctls); | ||
1488 | |||
1489 | static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) | ||
1490 | { | ||
1491 | int i; | ||
1492 | |||
1493 | if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1) | ||
1494 | return -EINVAL; | ||
1495 | for (i = 0; i < CX25821_CTLS; i++) | ||
1496 | if (cx25821_ctls[i].id == qctrl->id) | ||
1497 | break; | ||
1498 | if (i == CX25821_CTLS) { | ||
1499 | *qctrl = no_ctl; | ||
1500 | return 0; | ||
1501 | } | ||
1502 | *qctrl = cx25821_ctls[i]; | ||
1503 | return 0; | ||
1504 | } | ||
1505 | |||
1506 | int cx25821_vidioc_queryctrl(struct file *file, void *priv, | ||
1507 | struct v4l2_queryctrl *qctrl) | ||
1508 | { | ||
1509 | return cx25821_ctrl_query(qctrl); | ||
1510 | } | ||
1511 | |||
1512 | /* ------------------------------------------------------------------ */ | ||
1513 | /* VIDEO CTRL IOCTLS */ | ||
1514 | |||
1515 | static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) | ||
1516 | { | ||
1517 | unsigned int i; | ||
1518 | |||
1519 | for (i = 0; i < CX25821_CTLS; i++) | ||
1520 | if (cx25821_ctls[i].id == id) | ||
1521 | return cx25821_ctls + i; | ||
1522 | return NULL; | ||
1523 | } | ||
1524 | |||
1525 | int cx25821_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctl) | ||
1526 | { | ||
1527 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1528 | struct cx25821_fh *fh = priv; | ||
1529 | |||
1530 | const struct v4l2_queryctrl *ctrl; | ||
1531 | |||
1532 | ctrl = ctrl_by_id(ctl->id); | ||
1533 | |||
1534 | if (NULL == ctrl) | ||
1535 | return -EINVAL; | ||
1536 | switch (ctl->id) { | ||
1537 | case V4L2_CID_BRIGHTNESS: | ||
1538 | ctl->value = dev->channels[fh->channel_id].ctl_bright; | ||
1539 | break; | ||
1540 | case V4L2_CID_HUE: | ||
1541 | ctl->value = dev->channels[fh->channel_id].ctl_hue; | ||
1542 | break; | ||
1543 | case V4L2_CID_CONTRAST: | ||
1544 | ctl->value = dev->channels[fh->channel_id].ctl_contrast; | ||
1545 | break; | ||
1546 | case V4L2_CID_SATURATION: | ||
1547 | ctl->value = dev->channels[fh->channel_id].ctl_saturation; | ||
1548 | break; | ||
1549 | } | ||
1550 | return 0; | ||
1551 | } | ||
1552 | |||
1553 | int cx25821_set_control(struct cx25821_dev *dev, | ||
1554 | struct v4l2_control *ctl, int chan_num) | ||
1555 | { | ||
1556 | int err; | ||
1557 | const struct v4l2_queryctrl *ctrl; | ||
1558 | |||
1559 | err = -EINVAL; | ||
1560 | |||
1561 | ctrl = ctrl_by_id(ctl->id); | ||
1562 | |||
1563 | if (NULL == ctrl) | ||
1564 | return err; | ||
1565 | |||
1566 | switch (ctrl->type) { | ||
1567 | case V4L2_CTRL_TYPE_BOOLEAN: | ||
1568 | case V4L2_CTRL_TYPE_MENU: | ||
1569 | case V4L2_CTRL_TYPE_INTEGER: | ||
1570 | if (ctl->value < ctrl->minimum) | ||
1571 | ctl->value = ctrl->minimum; | ||
1572 | if (ctl->value > ctrl->maximum) | ||
1573 | ctl->value = ctrl->maximum; | ||
1574 | break; | ||
1575 | default: | ||
1576 | /* nothing */ ; | ||
1577 | } | ||
1578 | |||
1579 | switch (ctl->id) { | ||
1580 | case V4L2_CID_BRIGHTNESS: | ||
1581 | dev->channels[chan_num].ctl_bright = ctl->value; | ||
1582 | medusa_set_brightness(dev, ctl->value, chan_num); | ||
1583 | break; | ||
1584 | case V4L2_CID_HUE: | ||
1585 | dev->channels[chan_num].ctl_hue = ctl->value; | ||
1586 | medusa_set_hue(dev, ctl->value, chan_num); | ||
1587 | break; | ||
1588 | case V4L2_CID_CONTRAST: | ||
1589 | dev->channels[chan_num].ctl_contrast = ctl->value; | ||
1590 | medusa_set_contrast(dev, ctl->value, chan_num); | ||
1591 | break; | ||
1592 | case V4L2_CID_SATURATION: | ||
1593 | dev->channels[chan_num].ctl_saturation = ctl->value; | ||
1594 | medusa_set_saturation(dev, ctl->value, chan_num); | ||
1595 | break; | ||
1596 | } | ||
1597 | |||
1598 | err = 0; | ||
1599 | |||
1600 | return err; | ||
1601 | } | ||
1602 | |||
1603 | static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num) | ||
1604 | { | ||
1605 | struct v4l2_control ctrl; | ||
1606 | int i; | ||
1607 | for (i = 0; i < CX25821_CTLS; i++) { | ||
1608 | ctrl.id = cx25821_ctls[i].id; | ||
1609 | ctrl.value = cx25821_ctls[i].default_value; | ||
1610 | |||
1611 | cx25821_set_control(dev, &ctrl, chan_num); | ||
1612 | } | ||
1613 | } | ||
1614 | |||
1615 | int cx25821_vidioc_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cropcap) | ||
1616 | { | ||
1617 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1618 | |||
1619 | if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1620 | return -EINVAL; | ||
1621 | cropcap->bounds.top = cropcap->bounds.left = 0; | ||
1622 | cropcap->bounds.width = 720; | ||
1623 | cropcap->bounds.height = dev->tvnorm == V4L2_STD_PAL_BG ? 576 : 480; | ||
1624 | cropcap->pixelaspect.numerator = | ||
1625 | dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10; | ||
1626 | cropcap->pixelaspect.denominator = | ||
1627 | dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11; | ||
1628 | cropcap->defrect = cropcap->bounds; | ||
1629 | return 0; | ||
1630 | } | ||
1631 | |||
1632 | int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) | ||
1633 | { | ||
1634 | struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; | ||
1635 | struct cx25821_fh *fh = priv; | ||
1636 | int err; | ||
1637 | |||
1638 | if (fh) { | ||
1639 | err = v4l2_prio_check(&dev->channels[fh->channel_id]. | ||
1640 | prio, fh->prio); | ||
1641 | if (0 != err) | ||
1642 | return err; | ||
1643 | } | ||
1644 | /* cx25821_vidioc_s_crop not supported */ | ||
1645 | return -EINVAL; | ||
1646 | } | ||
1647 | |||
1648 | int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) | ||
1649 | { | ||
1650 | /* cx25821_vidioc_g_crop not supported */ | ||
1651 | return -EINVAL; | ||
1652 | } | ||
1653 | |||
1654 | int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm) | ||
1655 | { | ||
1656 | /* medusa does not support video standard sensing of current input */ | ||
1657 | *norm = CX25821_NORMS; | ||
1658 | |||
1659 | return 0; | ||
1660 | } | ||
1661 | |||
1662 | int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm) | ||
1663 | { | ||
1664 | if (tvnorm == V4L2_STD_PAL_BG) { | ||
1665 | if (width == 352 || width == 720) | ||
1666 | return 1; | ||
1667 | else | ||
1668 | return 0; | ||
1669 | } | ||
1670 | |||
1671 | if (tvnorm == V4L2_STD_NTSC_M) { | ||
1672 | if (width == 320 || width == 352 || width == 720) | ||
1673 | return 1; | ||
1674 | else | ||
1675 | return 0; | ||
1676 | } | ||
1677 | return 0; | ||
1678 | } | ||
1679 | |||
1680 | int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm) | ||
1681 | { | ||
1682 | if (tvnorm == V4L2_STD_PAL_BG) { | ||
1683 | if (height == 576 || height == 288) | ||
1684 | return 1; | ||
1685 | else | ||
1686 | return 0; | ||
1687 | } | ||
1688 | |||
1689 | if (tvnorm == V4L2_STD_NTSC_M) { | ||
1690 | if (height == 480 || height == 240) | ||
1691 | return 1; | ||
1692 | else | ||
1693 | return 0; | ||
1694 | } | ||
1695 | |||
1696 | return 0; | ||
1697 | } | ||
1698 | |||
1699 | static long video_ioctl_upstream9(struct file *file, unsigned int cmd, | ||
1700 | unsigned long arg) | ||
1701 | { | ||
1702 | struct cx25821_fh *fh = file->private_data; | ||
1703 | struct cx25821_dev *dev = fh->dev; | ||
1704 | int command = 0; | ||
1705 | struct upstream_user_struct *data_from_user; | ||
1706 | |||
1707 | data_from_user = (struct upstream_user_struct *)arg; | ||
1708 | |||
1709 | if (!data_from_user) { | ||
1710 | pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); | ||
1711 | return 0; | ||
1712 | } | ||
1713 | |||
1714 | command = data_from_user->command; | ||
1715 | |||
1716 | if (command != UPSTREAM_START_VIDEO && | ||
1717 | command != UPSTREAM_STOP_VIDEO) | ||
1718 | return 0; | ||
1719 | |||
1720 | dev->input_filename = data_from_user->input_filename; | ||
1721 | dev->input_audiofilename = data_from_user->input_filename; | ||
1722 | dev->vid_stdname = data_from_user->vid_stdname; | ||
1723 | dev->pixel_format = data_from_user->pixel_format; | ||
1724 | dev->channel_select = data_from_user->channel_select; | ||
1725 | dev->command = data_from_user->command; | ||
1726 | |||
1727 | switch (command) { | ||
1728 | case UPSTREAM_START_VIDEO: | ||
1729 | cx25821_start_upstream_video_ch1(dev, data_from_user); | ||
1730 | break; | ||
1731 | |||
1732 | case UPSTREAM_STOP_VIDEO: | ||
1733 | cx25821_stop_upstream_video_ch1(dev); | ||
1734 | break; | ||
1735 | } | ||
1736 | |||
1737 | return 0; | ||
1738 | } | ||
1739 | |||
1740 | static long video_ioctl_upstream10(struct file *file, unsigned int cmd, | ||
1741 | unsigned long arg) | ||
1742 | { | ||
1743 | struct cx25821_fh *fh = file->private_data; | ||
1744 | struct cx25821_dev *dev = fh->dev; | ||
1745 | int command = 0; | ||
1746 | struct upstream_user_struct *data_from_user; | ||
1747 | |||
1748 | data_from_user = (struct upstream_user_struct *)arg; | ||
1749 | |||
1750 | if (!data_from_user) { | ||
1751 | pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); | ||
1752 | return 0; | ||
1753 | } | ||
1754 | |||
1755 | command = data_from_user->command; | ||
1756 | |||
1757 | if (command != UPSTREAM_START_VIDEO && | ||
1758 | command != UPSTREAM_STOP_VIDEO) | ||
1759 | return 0; | ||
1760 | |||
1761 | dev->input_filename_ch2 = data_from_user->input_filename; | ||
1762 | dev->input_audiofilename = data_from_user->input_filename; | ||
1763 | dev->vid_stdname_ch2 = data_from_user->vid_stdname; | ||
1764 | dev->pixel_format_ch2 = data_from_user->pixel_format; | ||
1765 | dev->channel_select_ch2 = data_from_user->channel_select; | ||
1766 | dev->command_ch2 = data_from_user->command; | ||
1767 | |||
1768 | switch (command) { | ||
1769 | case UPSTREAM_START_VIDEO: | ||
1770 | cx25821_start_upstream_video_ch2(dev, data_from_user); | ||
1771 | break; | ||
1772 | |||
1773 | case UPSTREAM_STOP_VIDEO: | ||
1774 | cx25821_stop_upstream_video_ch2(dev); | ||
1775 | break; | ||
1776 | } | ||
1777 | |||
1778 | return 0; | ||
1779 | } | ||
1780 | |||
1781 | static long video_ioctl_upstream11(struct file *file, unsigned int cmd, | ||
1782 | unsigned long arg) | ||
1783 | { | ||
1784 | struct cx25821_fh *fh = file->private_data; | ||
1785 | struct cx25821_dev *dev = fh->dev; | ||
1786 | int command = 0; | ||
1787 | struct upstream_user_struct *data_from_user; | ||
1788 | |||
1789 | data_from_user = (struct upstream_user_struct *)arg; | ||
1790 | |||
1791 | if (!data_from_user) { | ||
1792 | pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); | ||
1793 | return 0; | ||
1794 | } | ||
1795 | |||
1796 | command = data_from_user->command; | ||
1797 | |||
1798 | if (command != UPSTREAM_START_AUDIO && | ||
1799 | command != UPSTREAM_STOP_AUDIO) | ||
1800 | return 0; | ||
1801 | |||
1802 | dev->input_filename = data_from_user->input_filename; | ||
1803 | dev->input_audiofilename = data_from_user->input_filename; | ||
1804 | dev->vid_stdname = data_from_user->vid_stdname; | ||
1805 | dev->pixel_format = data_from_user->pixel_format; | ||
1806 | dev->channel_select = data_from_user->channel_select; | ||
1807 | dev->command = data_from_user->command; | ||
1808 | |||
1809 | switch (command) { | ||
1810 | case UPSTREAM_START_AUDIO: | ||
1811 | cx25821_start_upstream_audio(dev, data_from_user); | ||
1812 | break; | ||
1813 | |||
1814 | case UPSTREAM_STOP_AUDIO: | ||
1815 | cx25821_stop_upstream_audio(dev); | ||
1816 | break; | ||
1817 | } | ||
1818 | |||
1819 | return 0; | ||
1820 | } | ||
1821 | |||
1822 | static long video_ioctl_set(struct file *file, unsigned int cmd, | ||
1823 | unsigned long arg) | ||
1824 | { | ||
1825 | struct cx25821_fh *fh = file->private_data; | ||
1826 | struct cx25821_dev *dev = fh->dev; | ||
1827 | struct downstream_user_struct *data_from_user; | ||
1828 | int command; | ||
1829 | int width = 720; | ||
1830 | int selected_channel = 0, pix_format = 0, i = 0; | ||
1831 | int cif_enable = 0, cif_width = 0; | ||
1832 | u32 value = 0; | ||
1833 | |||
1834 | data_from_user = (struct downstream_user_struct *)arg; | ||
1835 | |||
1836 | if (!data_from_user) { | ||
1837 | pr_err("%s(): User data is INVALID. Returning\n", __func__); | ||
1838 | return 0; | ||
1839 | } | ||
1840 | |||
1841 | command = data_from_user->command; | ||
1842 | |||
1843 | if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT | ||
1844 | && command != ENABLE_CIF_RESOLUTION && command != REG_READ | ||
1845 | && command != REG_WRITE && command != MEDUSA_READ | ||
1846 | && command != MEDUSA_WRITE) { | ||
1847 | return 0; | ||
1848 | } | ||
1849 | |||
1850 | switch (command) { | ||
1851 | case SET_VIDEO_STD: | ||
1852 | dev->tvnorm = | ||
1853 | !strcmp(data_from_user->vid_stdname, | ||
1854 | "PAL") ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; | ||
1855 | medusa_set_videostandard(dev); | ||
1856 | break; | ||
1857 | |||
1858 | case SET_PIXEL_FORMAT: | ||
1859 | selected_channel = data_from_user->decoder_select; | ||
1860 | pix_format = data_from_user->pixel_format; | ||
1861 | |||
1862 | if (!(selected_channel <= 7 && selected_channel >= 0)) { | ||
1863 | selected_channel -= 4; | ||
1864 | selected_channel = selected_channel % 8; | ||
1865 | } | ||
1866 | |||
1867 | if (selected_channel >= 0) | ||
1868 | cx25821_set_pixel_format(dev, selected_channel, | ||
1869 | pix_format); | ||
1870 | |||
1871 | break; | ||
1872 | |||
1873 | case ENABLE_CIF_RESOLUTION: | ||
1874 | selected_channel = data_from_user->decoder_select; | ||
1875 | cif_enable = data_from_user->cif_resolution_enable; | ||
1876 | cif_width = data_from_user->cif_width; | ||
1877 | |||
1878 | if (cif_enable) { | ||
1879 | if (dev->tvnorm & V4L2_STD_PAL_BG | ||
1880 | || dev->tvnorm & V4L2_STD_PAL_DK) | ||
1881 | width = 352; | ||
1882 | else | ||
1883 | width = (cif_width == 320 | ||
1884 | || cif_width == 352) ? cif_width : 320; | ||
1885 | } | ||
1886 | |||
1887 | if (!(selected_channel <= 7 && selected_channel >= 0)) { | ||
1888 | selected_channel -= 4; | ||
1889 | selected_channel = selected_channel % 8; | ||
1890 | } | ||
1891 | |||
1892 | if (selected_channel <= 7 && selected_channel >= 0) { | ||
1893 | dev->channels[selected_channel]. | ||
1894 | use_cif_resolution = cif_enable; | ||
1895 | dev->channels[selected_channel].cif_width = width; | ||
1896 | } else { | ||
1897 | for (i = 0; i < VID_CHANNEL_NUM; i++) { | ||
1898 | dev->channels[i].use_cif_resolution = | ||
1899 | cif_enable; | ||
1900 | dev->channels[i].cif_width = width; | ||
1901 | } | ||
1902 | } | ||
1903 | |||
1904 | medusa_set_resolution(dev, width, selected_channel); | ||
1905 | break; | ||
1906 | case REG_READ: | ||
1907 | data_from_user->reg_data = cx_read(data_from_user->reg_address); | ||
1908 | break; | ||
1909 | case REG_WRITE: | ||
1910 | cx_write(data_from_user->reg_address, data_from_user->reg_data); | ||
1911 | break; | ||
1912 | case MEDUSA_READ: | ||
1913 | value = | ||
1914 | cx25821_i2c_read(&dev->i2c_bus[0], | ||
1915 | (u16) data_from_user->reg_address, | ||
1916 | &data_from_user->reg_data); | ||
1917 | break; | ||
1918 | case MEDUSA_WRITE: | ||
1919 | cx25821_i2c_write(&dev->i2c_bus[0], | ||
1920 | (u16) data_from_user->reg_address, | ||
1921 | data_from_user->reg_data); | ||
1922 | break; | ||
1923 | } | ||
1924 | |||
1925 | return 0; | ||
1926 | } | ||
1927 | |||
1928 | static long cx25821_video_ioctl(struct file *file, | ||
1929 | unsigned int cmd, unsigned long arg) | ||
1930 | { | ||
1931 | int ret = 0; | ||
1932 | |||
1933 | struct cx25821_fh *fh = file->private_data; | ||
1934 | |||
1935 | /* check to see if it's the video upstream */ | ||
1936 | if (fh->channel_id == SRAM_CH09) { | ||
1937 | ret = video_ioctl_upstream9(file, cmd, arg); | ||
1938 | return ret; | ||
1939 | } else if (fh->channel_id == SRAM_CH10) { | ||
1940 | ret = video_ioctl_upstream10(file, cmd, arg); | ||
1941 | return ret; | ||
1942 | } else if (fh->channel_id == SRAM_CH11) { | ||
1943 | ret = video_ioctl_upstream11(file, cmd, arg); | ||
1944 | ret = video_ioctl_set(file, cmd, arg); | ||
1945 | return ret; | ||
1946 | } | ||
1947 | |||
1948 | return video_ioctl2(file, cmd, arg); | ||
1949 | } | ||
1950 | |||
1951 | /* exported stuff */ | ||
1952 | static const struct v4l2_file_operations video_fops = { | ||
1953 | .owner = THIS_MODULE, | ||
1954 | .open = video_open, | ||
1955 | .release = video_release, | ||
1956 | .read = video_read, | ||
1957 | .poll = video_poll, | ||
1958 | .mmap = cx25821_video_mmap, | ||
1959 | .ioctl = cx25821_video_ioctl, | ||
1960 | }; | ||
1961 | |||
1962 | static const struct v4l2_ioctl_ops video_ioctl_ops = { | ||
1963 | .vidioc_querycap = cx25821_vidioc_querycap, | ||
1964 | .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, | ||
1965 | .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, | ||
1966 | .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, | ||
1967 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, | ||
1968 | .vidioc_reqbufs = cx25821_vidioc_reqbufs, | ||
1969 | .vidioc_querybuf = cx25821_vidioc_querybuf, | ||
1970 | .vidioc_qbuf = cx25821_vidioc_qbuf, | ||
1971 | .vidioc_dqbuf = vidioc_dqbuf, | ||
1972 | #ifdef TUNER_FLAG | ||
1973 | .vidioc_s_std = cx25821_vidioc_s_std, | ||
1974 | .vidioc_querystd = cx25821_vidioc_querystd, | ||
1975 | #endif | ||
1976 | .vidioc_cropcap = cx25821_vidioc_cropcap, | ||
1977 | .vidioc_s_crop = cx25821_vidioc_s_crop, | ||
1978 | .vidioc_g_crop = cx25821_vidioc_g_crop, | ||
1979 | .vidioc_enum_input = cx25821_vidioc_enum_input, | ||
1980 | .vidioc_g_input = cx25821_vidioc_g_input, | ||
1981 | .vidioc_s_input = cx25821_vidioc_s_input, | ||
1982 | .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, | ||
1983 | .vidioc_s_ctrl = vidioc_s_ctrl, | ||
1984 | .vidioc_queryctrl = cx25821_vidioc_queryctrl, | ||
1985 | .vidioc_streamon = vidioc_streamon, | ||
1986 | .vidioc_streamoff = vidioc_streamoff, | ||
1987 | .vidioc_log_status = vidioc_log_status, | ||
1988 | .vidioc_g_priority = cx25821_vidioc_g_priority, | ||
1989 | .vidioc_s_priority = cx25821_vidioc_s_priority, | ||
1990 | #ifdef TUNER_FLAG | ||
1991 | .vidioc_g_tuner = cx25821_vidioc_g_tuner, | ||
1992 | .vidioc_s_tuner = cx25821_vidioc_s_tuner, | ||
1993 | .vidioc_g_frequency = cx25821_vidioc_g_frequency, | ||
1994 | .vidioc_s_frequency = cx25821_vidioc_s_frequency, | ||
1995 | #endif | ||
1996 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1997 | .vidioc_g_register = cx25821_vidioc_g_register, | ||
1998 | .vidioc_s_register = cx25821_vidioc_s_register, | ||
1999 | #endif | ||
2000 | }; | ||
2001 | |||
2002 | struct video_device cx25821_videoioctl_template = { | ||
2003 | .name = "cx25821-videoioctl", | ||
2004 | .fops = &video_fops, | ||
2005 | .ioctl_ops = &video_ioctl_ops, | ||
2006 | .tvnorms = CX25821_NORMS, | ||
2007 | .current_norm = V4L2_STD_NTSC_M, | ||
2008 | }; | ||