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