]>
Commit | Line | Data |
---|---|---|
44d0b80e JP |
1 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
2 | ||
d647f0b7 | 3 | #include <media/drv-intf/saa7146_vv.h> |
537fa492 HV |
4 | #include <media/v4l2-event.h> |
5 | #include <media/v4l2-ctrls.h> | |
7a707b89 | 6 | #include <linux/module.h> |
e40d14a8 | 7 | #include <linux/kernel.h> |
1da177e4 LT |
8 | |
9 | static int max_memory = 32; | |
10 | ||
11 | module_param(max_memory, int, 0644); | |
12 | MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)"); | |
13 | ||
14 | #define IS_CAPTURE_ACTIVE(fh) \ | |
15 | (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh)) | |
16 | ||
17 | #define IS_OVERLAY_ACTIVE(fh) \ | |
18 | (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh)) | |
19 | ||
20 | /* format descriptions for capture and preview */ | |
21 | static struct saa7146_format formats[] = { | |
22 | { | |
23 | .name = "RGB-8 (3-3-2)", | |
24 | .pixelformat = V4L2_PIX_FMT_RGB332, | |
25 | .trans = RGB08_COMPOSED, | |
26 | .depth = 8, | |
27 | .flags = 0, | |
28 | }, { | |
29 | .name = "RGB-16 (5/B-6/G-5/R)", | |
30 | .pixelformat = V4L2_PIX_FMT_RGB565, | |
31 | .trans = RGB16_COMPOSED, | |
32 | .depth = 16, | |
33 | .flags = 0, | |
34 | }, { | |
35 | .name = "RGB-24 (B-G-R)", | |
36 | .pixelformat = V4L2_PIX_FMT_BGR24, | |
37 | .trans = RGB24_COMPOSED, | |
38 | .depth = 24, | |
39 | .flags = 0, | |
40 | }, { | |
41 | .name = "RGB-32 (B-G-R)", | |
42 | .pixelformat = V4L2_PIX_FMT_BGR32, | |
43 | .trans = RGB32_COMPOSED, | |
44 | .depth = 32, | |
45 | .flags = 0, | |
46 | }, { | |
47 | .name = "RGB-32 (R-G-B)", | |
48 | .pixelformat = V4L2_PIX_FMT_RGB32, | |
49 | .trans = RGB32_COMPOSED, | |
50 | .depth = 32, | |
51 | .flags = 0, | |
52 | .swap = 0x2, | |
53 | }, { | |
54 | .name = "Greyscale-8", | |
55 | .pixelformat = V4L2_PIX_FMT_GREY, | |
56 | .trans = Y8, | |
57 | .depth = 8, | |
58 | .flags = 0, | |
59 | }, { | |
60 | .name = "YUV 4:2:2 planar (Y-Cb-Cr)", | |
61 | .pixelformat = V4L2_PIX_FMT_YUV422P, | |
62 | .trans = YUV422_DECOMPOSED, | |
63 | .depth = 16, | |
64 | .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, | |
65 | }, { | |
66 | .name = "YVU 4:2:0 planar (Y-Cb-Cr)", | |
67 | .pixelformat = V4L2_PIX_FMT_YVU420, | |
68 | .trans = YUV420_DECOMPOSED, | |
69 | .depth = 12, | |
70 | .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, | |
71 | }, { | |
72 | .name = "YUV 4:2:0 planar (Y-Cb-Cr)", | |
73 | .pixelformat = V4L2_PIX_FMT_YUV420, | |
74 | .trans = YUV420_DECOMPOSED, | |
75 | .depth = 12, | |
76 | .flags = FORMAT_IS_PLANAR, | |
77 | }, { | |
78 | .name = "YUV 4:2:2 (U-Y-V-Y)", | |
79 | .pixelformat = V4L2_PIX_FMT_UYVY, | |
80 | .trans = YUV422_COMPOSED, | |
81 | .depth = 16, | |
82 | .flags = 0, | |
83 | } | |
84 | }; | |
85 | ||
86 | /* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps. | |
87 | due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped | |
88 | (like V4L2_PIX_FMT_YUYV) ... 8-( */ | |
89 | ||
a757ee22 | 90 | struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc) |
1da177e4 | 91 | { |
e40d14a8 | 92 | int i; |
1da177e4 | 93 | |
e40d14a8 | 94 | for (i = 0; i < ARRAY_SIZE(formats); i++) { |
1da177e4 LT |
95 | if (formats[i].pixelformat == fourcc) { |
96 | return formats+i; | |
97 | } | |
98 | } | |
99 | ||
44d0b80e | 100 | DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc); |
1da177e4 LT |
101 | return NULL; |
102 | } | |
103 | ||
b960074f | 104 | static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f); |
1da177e4 LT |
105 | |
106 | int saa7146_start_preview(struct saa7146_fh *fh) | |
107 | { | |
108 | struct saa7146_dev *dev = fh->dev; | |
109 | struct saa7146_vv *vv = dev->vv_data; | |
b960074f | 110 | struct v4l2_format fmt; |
1da177e4 LT |
111 | int ret = 0, err = 0; |
112 | ||
44d0b80e | 113 | DEB_EE("dev:%p, fh:%p\n", dev, fh); |
1da177e4 | 114 | |
5da545ad HV |
115 | /* check if we have overlay information */ |
116 | if (vv->ov.fh == NULL) { | |
44d0b80e | 117 | DEB_D("no overlay data available. try S_FMT first.\n"); |
1da177e4 LT |
118 | return -EAGAIN; |
119 | } | |
120 | ||
121 | /* check if streaming capture is running */ | |
122 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | |
44d0b80e | 123 | DEB_D("streaming capture is active\n"); |
1da177e4 LT |
124 | return -EBUSY; |
125 | } | |
126 | ||
127 | /* check if overlay is running */ | |
128 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | |
129 | if (vv->video_fh == fh) { | |
44d0b80e | 130 | DEB_D("overlay is already active\n"); |
1da177e4 LT |
131 | return 0; |
132 | } | |
44d0b80e | 133 | DEB_D("overlay is already active in another open\n"); |
1da177e4 LT |
134 | return -EBUSY; |
135 | } | |
136 | ||
137 | if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) { | |
44d0b80e | 138 | DEB_D("cannot get necessary overlay resources\n"); |
1da177e4 LT |
139 | return -EBUSY; |
140 | } | |
141 | ||
5da545ad | 142 | fmt.fmt.win = vv->ov.win; |
b960074f | 143 | err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt); |
1da177e4 LT |
144 | if (0 != err) { |
145 | saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); | |
146 | return -EBUSY; | |
147 | } | |
5da545ad | 148 | vv->ov.win = fmt.fmt.win; |
1da177e4 | 149 | |
44d0b80e | 150 | DEB_D("%dx%d+%d+%d %s field=%s\n", |
5da545ad HV |
151 | vv->ov.win.w.width, vv->ov.win.w.height, |
152 | vv->ov.win.w.left, vv->ov.win.w.top, | |
153 | vv->ov_fmt->name, v4l2_field_names[vv->ov.win.field]); | |
1da177e4 LT |
154 | |
155 | if (0 != (ret = saa7146_enable_overlay(fh))) { | |
44d0b80e | 156 | DEB_D("enabling overlay failed: %d\n", ret); |
1da177e4 LT |
157 | saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); |
158 | return ret; | |
159 | } | |
160 | ||
161 | vv->video_status = STATUS_OVERLAY; | |
162 | vv->video_fh = fh; | |
163 | ||
164 | return 0; | |
165 | } | |
5d7dc8c4 | 166 | EXPORT_SYMBOL_GPL(saa7146_start_preview); |
1da177e4 LT |
167 | |
168 | int saa7146_stop_preview(struct saa7146_fh *fh) | |
169 | { | |
170 | struct saa7146_dev *dev = fh->dev; | |
171 | struct saa7146_vv *vv = dev->vv_data; | |
172 | ||
44d0b80e | 173 | DEB_EE("dev:%p, fh:%p\n", dev, fh); |
1da177e4 LT |
174 | |
175 | /* check if streaming capture is running */ | |
176 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | |
44d0b80e | 177 | DEB_D("streaming capture is active\n"); |
1da177e4 LT |
178 | return -EBUSY; |
179 | } | |
180 | ||
181 | /* check if overlay is running at all */ | |
182 | if ((vv->video_status & STATUS_OVERLAY) == 0) { | |
44d0b80e | 183 | DEB_D("no active overlay\n"); |
1da177e4 LT |
184 | return 0; |
185 | } | |
186 | ||
187 | if (vv->video_fh != fh) { | |
44d0b80e | 188 | DEB_D("overlay is active, but in another open\n"); |
1da177e4 LT |
189 | return -EBUSY; |
190 | } | |
191 | ||
192 | vv->video_status = 0; | |
193 | vv->video_fh = NULL; | |
194 | ||
195 | saa7146_disable_overlay(fh); | |
196 | ||
197 | saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); | |
198 | ||
199 | return 0; | |
200 | } | |
5d7dc8c4 | 201 | EXPORT_SYMBOL_GPL(saa7146_stop_preview); |
1da177e4 | 202 | |
1da177e4 LT |
203 | /********************************************************************************/ |
204 | /* common pagetable functions */ | |
205 | ||
206 | static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf) | |
207 | { | |
208 | struct pci_dev *pci = dev->pci; | |
c1accaa2 MCC |
209 | struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); |
210 | struct scatterlist *list = dma->sglist; | |
211 | int length = dma->sglen; | |
a757ee22 | 212 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); |
1da177e4 | 213 | |
44d0b80e | 214 | DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length); |
1da177e4 LT |
215 | |
216 | if( 0 != IS_PLANAR(sfmt->trans)) { | |
217 | struct saa7146_pgtable *pt1 = &buf->pt[0]; | |
218 | struct saa7146_pgtable *pt2 = &buf->pt[1]; | |
219 | struct saa7146_pgtable *pt3 = &buf->pt[2]; | |
a36ef6b1 AV |
220 | __le32 *ptr1, *ptr2, *ptr3; |
221 | __le32 fill; | |
1da177e4 LT |
222 | |
223 | int size = buf->fmt->width*buf->fmt->height; | |
224 | int i,p,m1,m2,m3,o1,o2; | |
225 | ||
226 | switch( sfmt->depth ) { | |
227 | case 12: { | |
228 | /* create some offsets inside the page table */ | |
229 | m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; | |
230 | m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1; | |
231 | m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; | |
232 | o1 = size%PAGE_SIZE; | |
233 | o2 = (size+(size/4))%PAGE_SIZE; | |
44d0b80e JP |
234 | DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", |
235 | size, m1, m2, m3, o1, o2); | |
1da177e4 LT |
236 | break; |
237 | } | |
238 | case 16: { | |
239 | /* create some offsets inside the page table */ | |
240 | m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; | |
241 | m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; | |
242 | m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1; | |
243 | o1 = size%PAGE_SIZE; | |
244 | o2 = (size+(size/2))%PAGE_SIZE; | |
44d0b80e JP |
245 | DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", |
246 | size, m1, m2, m3, o1, o2); | |
1da177e4 LT |
247 | break; |
248 | } | |
249 | default: { | |
250 | return -1; | |
251 | } | |
252 | } | |
253 | ||
254 | ptr1 = pt1->cpu; | |
255 | ptr2 = pt2->cpu; | |
256 | ptr3 = pt3->cpu; | |
257 | ||
258 | /* walk all pages, copy all page addresses to ptr1 */ | |
259 | for (i = 0; i < length; i++, list++) { | |
260 | for (p = 0; p * 4096 < list->length; p++, ptr1++) { | |
261 | *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset); | |
262 | } | |
263 | } | |
264 | /* | |
265 | ptr1 = pt1->cpu; | |
266 | for(j=0;j<40;j++) { | |
267 | printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); | |
268 | } | |
269 | */ | |
270 | ||
271 | /* if we have a user buffer, the first page may not be | |
272 | aligned to a page boundary. */ | |
429e9089 | 273 | pt1->offset = dma->sglist->offset; |
1da177e4 LT |
274 | pt2->offset = pt1->offset+o1; |
275 | pt3->offset = pt1->offset+o2; | |
276 | ||
277 | /* create video-dma2 page table */ | |
278 | ptr1 = pt1->cpu; | |
279 | for(i = m1; i <= m2 ; i++, ptr2++) { | |
280 | *ptr2 = ptr1[i]; | |
281 | } | |
282 | fill = *(ptr2-1); | |
283 | for(;i<1024;i++,ptr2++) { | |
284 | *ptr2 = fill; | |
285 | } | |
286 | /* create video-dma3 page table */ | |
287 | ptr1 = pt1->cpu; | |
288 | for(i = m2; i <= m3; i++,ptr3++) { | |
289 | *ptr3 = ptr1[i]; | |
290 | } | |
291 | fill = *(ptr3-1); | |
292 | for(;i<1024;i++,ptr3++) { | |
293 | *ptr3 = fill; | |
294 | } | |
295 | /* finally: finish up video-dma1 page table */ | |
296 | ptr1 = pt1->cpu+m1; | |
297 | fill = pt1->cpu[m1]; | |
298 | for(i=m1;i<1024;i++,ptr1++) { | |
299 | *ptr1 = fill; | |
300 | } | |
301 | /* | |
302 | ptr1 = pt1->cpu; | |
303 | ptr2 = pt2->cpu; | |
304 | ptr3 = pt3->cpu; | |
305 | for(j=0;j<40;j++) { | |
306 | printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); | |
307 | } | |
308 | for(j=0;j<40;j++) { | |
309 | printk("ptr2 %d: 0x%08x\n",j,ptr2[j]); | |
310 | } | |
311 | for(j=0;j<40;j++) { | |
312 | printk("ptr3 %d: 0x%08x\n",j,ptr3[j]); | |
313 | } | |
314 | */ | |
315 | } else { | |
316 | struct saa7146_pgtable *pt = &buf->pt[0]; | |
317 | return saa7146_pgtable_build_single(pci, pt, list, length); | |
318 | } | |
319 | ||
320 | return 0; | |
321 | } | |
322 | ||
323 | ||
324 | /********************************************************************************/ | |
325 | /* file operations */ | |
326 | ||
327 | static int video_begin(struct saa7146_fh *fh) | |
328 | { | |
329 | struct saa7146_dev *dev = fh->dev; | |
330 | struct saa7146_vv *vv = dev->vv_data; | |
331 | struct saa7146_format *fmt = NULL; | |
332 | unsigned int resource; | |
333 | int ret = 0, err = 0; | |
334 | ||
44d0b80e | 335 | DEB_EE("dev:%p, fh:%p\n", dev, fh); |
1da177e4 LT |
336 | |
337 | if ((vv->video_status & STATUS_CAPTURE) != 0) { | |
338 | if (vv->video_fh == fh) { | |
44d0b80e | 339 | DEB_S("already capturing\n"); |
1da177e4 LT |
340 | return 0; |
341 | } | |
44d0b80e | 342 | DEB_S("already capturing in another open\n"); |
1da177e4 LT |
343 | return -EBUSY; |
344 | } | |
345 | ||
346 | if ((vv->video_status & STATUS_OVERLAY) != 0) { | |
44d0b80e | 347 | DEB_S("warning: suspending overlay video for streaming capture\n"); |
1da177e4 LT |
348 | vv->ov_suspend = vv->video_fh; |
349 | err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ | |
350 | if (0 != err) { | |
44d0b80e | 351 | DEB_D("suspending video failed. aborting\n"); |
1da177e4 LT |
352 | return err; |
353 | } | |
354 | } | |
355 | ||
fd74d6eb | 356 | fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); |
1da177e4 LT |
357 | /* we need to have a valid format set here */ |
358 | BUG_ON(NULL == fmt); | |
359 | ||
360 | if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { | |
361 | resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; | |
362 | } else { | |
363 | resource = RESOURCE_DMA1_HPS; | |
364 | } | |
365 | ||
366 | ret = saa7146_res_get(fh, resource); | |
367 | if (0 == ret) { | |
44d0b80e | 368 | DEB_S("cannot get capture resource %d\n", resource); |
1da177e4 LT |
369 | if (vv->ov_suspend != NULL) { |
370 | saa7146_start_preview(vv->ov_suspend); | |
371 | vv->ov_suspend = NULL; | |
372 | } | |
373 | return -EBUSY; | |
374 | } | |
375 | ||
376 | /* clear out beginning of streaming bit (rps register 0)*/ | |
377 | saa7146_write(dev, MC2, MASK_27 ); | |
378 | ||
379 | /* enable rps0 irqs */ | |
380 | SAA7146_IER_ENABLE(dev, MASK_27); | |
381 | ||
382 | vv->video_fh = fh; | |
383 | vv->video_status = STATUS_CAPTURE; | |
384 | ||
385 | return 0; | |
386 | } | |
387 | ||
388 | static int video_end(struct saa7146_fh *fh, struct file *file) | |
389 | { | |
390 | struct saa7146_dev *dev = fh->dev; | |
391 | struct saa7146_vv *vv = dev->vv_data; | |
59eba2d1 | 392 | struct saa7146_dmaqueue *q = &vv->video_dmaq; |
1da177e4 LT |
393 | struct saa7146_format *fmt = NULL; |
394 | unsigned long flags; | |
395 | unsigned int resource; | |
396 | u32 dmas = 0; | |
44d0b80e | 397 | DEB_EE("dev:%p, fh:%p\n", dev, fh); |
1da177e4 LT |
398 | |
399 | if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { | |
44d0b80e | 400 | DEB_S("not capturing\n"); |
1da177e4 LT |
401 | return 0; |
402 | } | |
403 | ||
404 | if (vv->video_fh != fh) { | |
44d0b80e | 405 | DEB_S("capturing, but in another open\n"); |
1da177e4 LT |
406 | return -EBUSY; |
407 | } | |
408 | ||
fd74d6eb | 409 | fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); |
1da177e4 LT |
410 | /* we need to have a valid format set here */ |
411 | BUG_ON(NULL == fmt); | |
412 | ||
413 | if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { | |
414 | resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; | |
415 | dmas = MASK_22 | MASK_21 | MASK_20; | |
416 | } else { | |
417 | resource = RESOURCE_DMA1_HPS; | |
418 | dmas = MASK_22; | |
419 | } | |
420 | spin_lock_irqsave(&dev->slock,flags); | |
421 | ||
422 | /* disable rps0 */ | |
423 | saa7146_write(dev, MC1, MASK_28); | |
424 | ||
425 | /* disable rps0 irqs */ | |
426 | SAA7146_IER_DISABLE(dev, MASK_27); | |
427 | ||
428 | /* shut down all used video dma transfers */ | |
429 | saa7146_write(dev, MC1, dmas); | |
430 | ||
59eba2d1 AU |
431 | if (q->curr) |
432 | saa7146_buffer_finish(dev, q, VIDEOBUF_DONE); | |
433 | ||
1da177e4 LT |
434 | spin_unlock_irqrestore(&dev->slock, flags); |
435 | ||
436 | vv->video_fh = NULL; | |
437 | vv->video_status = 0; | |
438 | ||
439 | saa7146_res_free(fh, resource); | |
440 | ||
441 | if (vv->ov_suspend != NULL) { | |
442 | saa7146_start_preview(vv->ov_suspend); | |
443 | vv->ov_suspend = NULL; | |
444 | } | |
445 | ||
446 | return 0; | |
447 | } | |
448 | ||
b960074f HV |
449 | static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) |
450 | { | |
ab49ae0f | 451 | struct video_device *vdev = video_devdata(file); |
b960074f HV |
452 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
453 | ||
454 | strcpy((char *)cap->driver, "saa7146 v4l2"); | |
455 | strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); | |
456 | sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci)); | |
6e65ca94 | 457 | cap->device_caps = |
b960074f HV |
458 | V4L2_CAP_VIDEO_CAPTURE | |
459 | V4L2_CAP_VIDEO_OVERLAY | | |
460 | V4L2_CAP_READWRITE | | |
461 | V4L2_CAP_STREAMING; | |
6e65ca94 | 462 | cap->device_caps |= dev->ext_vv_data->capabilities; |
a299e407 | 463 | cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; |
ab49ae0f HV |
464 | if (vdev->vfl_type == VFL_TYPE_GRABBER) |
465 | cap->device_caps &= | |
466 | ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); | |
467 | else | |
468 | cap->device_caps &= | |
a299e407 | 469 | ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO); |
b960074f HV |
470 | return 0; |
471 | } | |
1da177e4 | 472 | |
b960074f | 473 | static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) |
1da177e4 | 474 | { |
b960074f | 475 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
1da177e4 LT |
476 | struct saa7146_vv *vv = dev->vv_data; |
477 | ||
b960074f HV |
478 | *fb = vv->ov_fb; |
479 | fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; | |
5da545ad | 480 | fb->flags = V4L2_FBUF_FLAG_PRIMARY; |
b960074f HV |
481 | return 0; |
482 | } | |
1da177e4 | 483 | |
e6eb28c2 | 484 | static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb) |
b960074f HV |
485 | { |
486 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
487 | struct saa7146_vv *vv = dev->vv_data; | |
488 | struct saa7146_format *fmt; | |
1da177e4 | 489 | |
44d0b80e | 490 | DEB_EE("VIDIOC_S_FBUF\n"); |
1da177e4 | 491 | |
b960074f HV |
492 | if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) |
493 | return -EPERM; | |
1da177e4 | 494 | |
b960074f | 495 | /* check args */ |
a757ee22 | 496 | fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat); |
b960074f HV |
497 | if (NULL == fmt) |
498 | return -EINVAL; | |
1da177e4 | 499 | |
b960074f HV |
500 | /* planar formats are not allowed for overlay video, clipping and video dma would clash */ |
501 | if (fmt->flags & FORMAT_IS_PLANAR) | |
44d0b80e JP |
502 | DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n", |
503 | (char *)&fmt->pixelformat); | |
b960074f HV |
504 | |
505 | /* check if overlay is running */ | |
506 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | |
507 | if (vv->video_fh != fh) { | |
e3d132d1 | 508 | DEB_D("refusing to change framebuffer information while overlay is active in another open\n"); |
b960074f HV |
509 | return -EBUSY; |
510 | } | |
1da177e4 LT |
511 | } |
512 | ||
b960074f HV |
513 | /* ok, accept it */ |
514 | vv->ov_fb = *fb; | |
515 | vv->ov_fmt = fmt; | |
84a1d9c8 MH |
516 | |
517 | if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) { | |
518 | vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8; | |
44d0b80e | 519 | DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline); |
84a1d9c8 | 520 | } |
b960074f HV |
521 | return 0; |
522 | } | |
523 | ||
524 | static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) | |
525 | { | |
e40d14a8 | 526 | if (f->index >= ARRAY_SIZE(formats)) |
b960074f HV |
527 | return -EINVAL; |
528 | strlcpy((char *)f->description, formats[f->index].name, | |
529 | sizeof(f->description)); | |
530 | f->pixelformat = formats[f->index].pixelformat; | |
531 | return 0; | |
532 | } | |
533 | ||
6e65ca94 | 534 | int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) |
b960074f | 535 | { |
6e65ca94 HV |
536 | struct saa7146_dev *dev = container_of(ctrl->handler, |
537 | struct saa7146_dev, ctrl_handler); | |
b960074f | 538 | struct saa7146_vv *vv = dev->vv_data; |
6e65ca94 | 539 | u32 val; |
b960074f | 540 | |
6e65ca94 | 541 | switch (ctrl->id) { |
b960074f | 542 | case V4L2_CID_BRIGHTNESS: |
6e65ca94 HV |
543 | val = saa7146_read(dev, BCS_CTRL); |
544 | val &= 0x00ffffff; | |
545 | val |= (ctrl->val << 24); | |
546 | saa7146_write(dev, BCS_CTRL, val); | |
b960074f HV |
547 | saa7146_write(dev, MC2, MASK_22 | MASK_06); |
548 | break; | |
6e65ca94 HV |
549 | |
550 | case V4L2_CID_CONTRAST: | |
551 | val = saa7146_read(dev, BCS_CTRL); | |
552 | val &= 0xff00ffff; | |
553 | val |= (ctrl->val << 16); | |
554 | saa7146_write(dev, BCS_CTRL, val); | |
b960074f HV |
555 | saa7146_write(dev, MC2, MASK_22 | MASK_06); |
556 | break; | |
6e65ca94 HV |
557 | |
558 | case V4L2_CID_SATURATION: | |
559 | val = saa7146_read(dev, BCS_CTRL); | |
560 | val &= 0xffffff00; | |
561 | val |= (ctrl->val << 0); | |
562 | saa7146_write(dev, BCS_CTRL, val); | |
b960074f HV |
563 | saa7146_write(dev, MC2, MASK_22 | MASK_06); |
564 | break; | |
6e65ca94 | 565 | |
b960074f HV |
566 | case V4L2_CID_HFLIP: |
567 | /* fixme: we can support changing VFLIP and HFLIP here... */ | |
6e65ca94 | 568 | if ((vv->video_status & STATUS_CAPTURE)) |
5a5b9647 | 569 | return -EBUSY; |
6e65ca94 | 570 | vv->hflip = ctrl->val; |
b960074f | 571 | break; |
6e65ca94 | 572 | |
b960074f | 573 | case V4L2_CID_VFLIP: |
6e65ca94 | 574 | if ((vv->video_status & STATUS_CAPTURE)) |
5a5b9647 | 575 | return -EBUSY; |
6e65ca94 | 576 | vv->vflip = ctrl->val; |
b960074f | 577 | break; |
6e65ca94 | 578 | |
b960074f | 579 | default: |
b960074f HV |
580 | return -EINVAL; |
581 | } | |
1da177e4 | 582 | |
6e65ca94 HV |
583 | if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */ |
584 | struct saa7146_fh *fh = vv->video_fh; | |
585 | ||
b960074f HV |
586 | saa7146_stop_preview(fh); |
587 | saa7146_start_preview(fh); | |
588 | } | |
589 | return 0; | |
590 | } | |
1da177e4 | 591 | |
b960074f HV |
592 | static int vidioc_g_parm(struct file *file, void *fh, |
593 | struct v4l2_streamparm *parm) | |
594 | { | |
717167e8 TP |
595 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
596 | struct saa7146_vv *vv = dev->vv_data; | |
597 | ||
6e65ca94 HV |
598 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
599 | return -EINVAL; | |
b960074f | 600 | parm->parm.capture.readbuffers = 1; |
717167e8 TP |
601 | v4l2_video_std_frame_period(vv->standard->id, |
602 | &parm->parm.capture.timeperframe); | |
b960074f HV |
603 | return 0; |
604 | } | |
1da177e4 | 605 | |
b960074f HV |
606 | static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) |
607 | { | |
fd74d6eb HV |
608 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
609 | struct saa7146_vv *vv = dev->vv_data; | |
610 | ||
611 | f->fmt.pix = vv->video_fmt; | |
b960074f HV |
612 | return 0; |
613 | } | |
1da177e4 | 614 | |
b960074f HV |
615 | static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) |
616 | { | |
5da545ad HV |
617 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
618 | struct saa7146_vv *vv = dev->vv_data; | |
619 | ||
620 | f->fmt.win = vv->ov.win; | |
b960074f HV |
621 | return 0; |
622 | } | |
1da177e4 | 623 | |
b960074f HV |
624 | static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) |
625 | { | |
fca3469a HV |
626 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
627 | struct saa7146_vv *vv = dev->vv_data; | |
628 | ||
629 | f->fmt.vbi = vv->vbi_fmt; | |
b960074f HV |
630 | return 0; |
631 | } | |
1da177e4 | 632 | |
b960074f HV |
633 | static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) |
634 | { | |
635 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
636 | struct saa7146_vv *vv = dev->vv_data; | |
637 | struct saa7146_format *fmt; | |
638 | enum v4l2_field field; | |
639 | int maxw, maxh; | |
640 | int calc_bpl; | |
641 | ||
44d0b80e | 642 | DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); |
b960074f | 643 | |
a757ee22 | 644 | fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat); |
b960074f HV |
645 | if (NULL == fmt) |
646 | return -EINVAL; | |
647 | ||
648 | field = f->fmt.pix.field; | |
649 | maxw = vv->standard->h_max_out; | |
650 | maxh = vv->standard->v_max_out; | |
651 | ||
652 | if (V4L2_FIELD_ANY == field) { | |
653 | field = (f->fmt.pix.height > maxh / 2) | |
654 | ? V4L2_FIELD_INTERLACED | |
655 | : V4L2_FIELD_BOTTOM; | |
656 | } | |
657 | switch (field) { | |
658 | case V4L2_FIELD_ALTERNATE: | |
659 | vv->last_field = V4L2_FIELD_TOP; | |
660 | maxh = maxh / 2; | |
661 | break; | |
662 | case V4L2_FIELD_TOP: | |
663 | case V4L2_FIELD_BOTTOM: | |
664 | vv->last_field = V4L2_FIELD_INTERLACED; | |
665 | maxh = maxh / 2; | |
666 | break; | |
667 | case V4L2_FIELD_INTERLACED: | |
668 | vv->last_field = V4L2_FIELD_INTERLACED; | |
669 | break; | |
670 | default: | |
44d0b80e | 671 | DEB_D("no known field mode '%d'\n", field); |
b960074f | 672 | return -EINVAL; |
1da177e4 | 673 | } |
1da177e4 | 674 | |
b960074f | 675 | f->fmt.pix.field = field; |
6e65ca94 | 676 | f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; |
b960074f HV |
677 | if (f->fmt.pix.width > maxw) |
678 | f->fmt.pix.width = maxw; | |
679 | if (f->fmt.pix.height > maxh) | |
680 | f->fmt.pix.height = maxh; | |
1da177e4 | 681 | |
b960074f | 682 | calc_bpl = (f->fmt.pix.width * fmt->depth) / 8; |
1da177e4 | 683 | |
b960074f HV |
684 | if (f->fmt.pix.bytesperline < calc_bpl) |
685 | f->fmt.pix.bytesperline = calc_bpl; | |
686 | ||
687 | if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */ | |
688 | f->fmt.pix.bytesperline = calc_bpl; | |
689 | ||
690 | f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; | |
44d0b80e JP |
691 | DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n", |
692 | f->fmt.pix.width, f->fmt.pix.height, | |
693 | f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); | |
b960074f HV |
694 | |
695 | return 0; | |
696 | } | |
697 | ||
698 | ||
699 | static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) | |
700 | { | |
701 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
702 | struct saa7146_vv *vv = dev->vv_data; | |
703 | struct v4l2_window *win = &f->fmt.win; | |
704 | enum v4l2_field field; | |
705 | int maxw, maxh; | |
706 | ||
44d0b80e | 707 | DEB_EE("dev:%p\n", dev); |
b960074f HV |
708 | |
709 | if (NULL == vv->ov_fb.base) { | |
44d0b80e | 710 | DEB_D("no fb base set\n"); |
b960074f | 711 | return -EINVAL; |
1da177e4 | 712 | } |
b960074f | 713 | if (NULL == vv->ov_fmt) { |
44d0b80e | 714 | DEB_D("no fb fmt set\n"); |
b960074f | 715 | return -EINVAL; |
1da177e4 | 716 | } |
b960074f | 717 | if (win->w.width < 48 || win->w.height < 32) { |
44d0b80e JP |
718 | DEB_D("min width/height. (%d,%d)\n", |
719 | win->w.width, win->w.height); | |
b960074f | 720 | return -EINVAL; |
1da177e4 | 721 | } |
b960074f | 722 | if (win->clipcount > 16) { |
44d0b80e | 723 | DEB_D("clipcount too big\n"); |
b960074f | 724 | return -EINVAL; |
afd1a0c9 | 725 | } |
b960074f HV |
726 | |
727 | field = win->field; | |
728 | maxw = vv->standard->h_max_out; | |
729 | maxh = vv->standard->v_max_out; | |
730 | ||
731 | if (V4L2_FIELD_ANY == field) { | |
732 | field = (win->w.height > maxh / 2) | |
733 | ? V4L2_FIELD_INTERLACED | |
734 | : V4L2_FIELD_TOP; | |
735 | } | |
736 | switch (field) { | |
737 | case V4L2_FIELD_TOP: | |
738 | case V4L2_FIELD_BOTTOM: | |
739 | case V4L2_FIELD_ALTERNATE: | |
740 | maxh = maxh / 2; | |
741 | break; | |
742 | case V4L2_FIELD_INTERLACED: | |
743 | break; | |
744 | default: | |
44d0b80e | 745 | DEB_D("no known field mode '%d'\n", field); |
b960074f | 746 | return -EINVAL; |
1da177e4 | 747 | } |
b960074f HV |
748 | |
749 | win->field = field; | |
750 | if (win->w.width > maxw) | |
751 | win->w.width = maxw; | |
752 | if (win->w.height > maxh) | |
753 | win->w.height = maxh; | |
754 | ||
755 | return 0; | |
756 | } | |
757 | ||
758 | static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) | |
759 | { | |
760 | struct saa7146_fh *fh = __fh; | |
761 | struct saa7146_dev *dev = fh->dev; | |
762 | struct saa7146_vv *vv = dev->vv_data; | |
763 | int err; | |
764 | ||
44d0b80e | 765 | DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); |
b960074f | 766 | if (IS_CAPTURE_ACTIVE(fh) != 0) { |
44d0b80e | 767 | DEB_EE("streaming capture is active\n"); |
b960074f | 768 | return -EBUSY; |
1da177e4 | 769 | } |
b960074f HV |
770 | err = vidioc_try_fmt_vid_cap(file, fh, f); |
771 | if (0 != err) | |
772 | return err; | |
fd74d6eb | 773 | vv->video_fmt = f->fmt.pix; |
44d0b80e | 774 | DEB_EE("set to pixelformat '%4.4s'\n", |
fd74d6eb | 775 | (char *)&vv->video_fmt.pixelformat); |
b960074f HV |
776 | return 0; |
777 | } | |
778 | ||
779 | static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f) | |
780 | { | |
781 | struct saa7146_fh *fh = __fh; | |
782 | struct saa7146_dev *dev = fh->dev; | |
783 | struct saa7146_vv *vv = dev->vv_data; | |
784 | int err; | |
785 | ||
44d0b80e | 786 | DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh); |
b960074f HV |
787 | err = vidioc_try_fmt_vid_overlay(file, fh, f); |
788 | if (0 != err) | |
789 | return err; | |
5da545ad HV |
790 | vv->ov.win = f->fmt.win; |
791 | vv->ov.nclips = f->fmt.win.clipcount; | |
792 | if (vv->ov.nclips > 16) | |
793 | vv->ov.nclips = 16; | |
794 | if (copy_from_user(vv->ov.clips, f->fmt.win.clips, | |
795 | sizeof(struct v4l2_clip) * vv->ov.nclips)) { | |
b960074f | 796 | return -EFAULT; |
1da177e4 | 797 | } |
b960074f | 798 | |
5da545ad HV |
799 | /* vv->ov.fh is used to indicate that we have valid overlay informations, too */ |
800 | vv->ov.fh = fh; | |
b960074f | 801 | |
b960074f HV |
802 | /* check if our current overlay is active */ |
803 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | |
804 | saa7146_stop_preview(fh); | |
805 | saa7146_start_preview(fh); | |
1da177e4 | 806 | } |
b960074f HV |
807 | return 0; |
808 | } | |
809 | ||
810 | static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) | |
811 | { | |
812 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
813 | struct saa7146_vv *vv = dev->vv_data; | |
814 | ||
815 | *norm = vv->standard->id; | |
816 | return 0; | |
817 | } | |
818 | ||
1da177e4 LT |
819 | /* the saa7146 supfhrts (used in conjunction with the saa7111a for example) |
820 | PAL / NTSC / SECAM. if your hardware does not (or does more) | |
821 | -- override this function in your extension */ | |
b960074f | 822 | /* |
1da177e4 LT |
823 | case VIDIOC_ENUMSTD: |
824 | { | |
825 | struct v4l2_standard *e = arg; | |
826 | if (e->index < 0 ) | |
827 | return -EINVAL; | |
828 | if( e->index < dev->ext_vv_data->num_stds ) { | |
44d0b80e | 829 | DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index); |
1da177e4 LT |
830 | v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name); |
831 | return 0; | |
832 | } | |
833 | return -EINVAL; | |
834 | } | |
b960074f | 835 | */ |
1da177e4 | 836 | |
314527ac | 837 | static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) |
b960074f HV |
838 | { |
839 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
840 | struct saa7146_vv *vv = dev->vv_data; | |
841 | int found = 0; | |
842 | int err, i; | |
1da177e4 | 843 | |
44d0b80e | 844 | DEB_EE("VIDIOC_S_STD\n"); |
1da177e4 | 845 | |
b960074f | 846 | if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) { |
44d0b80e | 847 | DEB_D("cannot change video standard while streaming capture is active\n"); |
b960074f HV |
848 | return -EBUSY; |
849 | } | |
1da177e4 | 850 | |
b960074f HV |
851 | if ((vv->video_status & STATUS_OVERLAY) != 0) { |
852 | vv->ov_suspend = vv->video_fh; | |
853 | err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ | |
854 | if (0 != err) { | |
44d0b80e | 855 | DEB_D("suspending video failed. aborting\n"); |
b960074f | 856 | return err; |
1da177e4 | 857 | } |
b960074f | 858 | } |
1da177e4 | 859 | |
b960074f | 860 | for (i = 0; i < dev->ext_vv_data->num_stds; i++) |
314527ac | 861 | if (id & dev->ext_vv_data->stds[i].id) |
b960074f HV |
862 | break; |
863 | if (i != dev->ext_vv_data->num_stds) { | |
864 | vv->standard = &dev->ext_vv_data->stds[i]; | |
865 | if (NULL != dev->ext_vv_data->std_callback) | |
866 | dev->ext_vv_data->std_callback(dev, vv->standard); | |
867 | found = 1; | |
868 | } | |
1da177e4 | 869 | |
b960074f HV |
870 | if (vv->ov_suspend != NULL) { |
871 | saa7146_start_preview(vv->ov_suspend); | |
872 | vv->ov_suspend = NULL; | |
1da177e4 | 873 | } |
1da177e4 | 874 | |
b960074f | 875 | if (!found) { |
44d0b80e | 876 | DEB_EE("VIDIOC_S_STD: standard not found\n"); |
b960074f | 877 | return -EINVAL; |
1da177e4 | 878 | } |
1da177e4 | 879 | |
44d0b80e | 880 | DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name); |
b960074f HV |
881 | return 0; |
882 | } | |
1da177e4 | 883 | |
b960074f HV |
884 | static int vidioc_overlay(struct file *file, void *fh, unsigned int on) |
885 | { | |
886 | int err; | |
1da177e4 | 887 | |
44d0b80e | 888 | DEB_D("VIDIOC_OVERLAY on:%d\n", on); |
b960074f HV |
889 | if (on) |
890 | err = saa7146_start_preview(fh); | |
891 | else | |
892 | err = saa7146_stop_preview(fh); | |
893 | return err; | |
894 | } | |
1da177e4 | 895 | |
b960074f HV |
896 | static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b) |
897 | { | |
898 | struct saa7146_fh *fh = __fh; | |
1da177e4 | 899 | |
b960074f HV |
900 | if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) |
901 | return videobuf_reqbufs(&fh->video_q, b); | |
902 | if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
903 | return videobuf_reqbufs(&fh->vbi_q, b); | |
904 | return -EINVAL; | |
905 | } | |
1da177e4 | 906 | |
b960074f HV |
907 | static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) |
908 | { | |
909 | struct saa7146_fh *fh = __fh; | |
1da177e4 | 910 | |
b960074f HV |
911 | if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) |
912 | return videobuf_querybuf(&fh->video_q, buf); | |
913 | if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
914 | return videobuf_querybuf(&fh->vbi_q, buf); | |
915 | return -EINVAL; | |
916 | } | |
1da177e4 | 917 | |
b960074f HV |
918 | static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) |
919 | { | |
920 | struct saa7146_fh *fh = __fh; | |
921 | ||
922 | if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
923 | return videobuf_qbuf(&fh->video_q, buf); | |
924 | if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
925 | return videobuf_qbuf(&fh->vbi_q, buf); | |
926 | return -EINVAL; | |
927 | } | |
49ee718e | 928 | |
b960074f HV |
929 | static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) |
930 | { | |
931 | struct saa7146_fh *fh = __fh; | |
932 | ||
933 | if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
934 | return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK); | |
935 | if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
936 | return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK); | |
937 | return -EINVAL; | |
938 | } | |
939 | ||
940 | static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type) | |
941 | { | |
942 | struct saa7146_fh *fh = __fh; | |
943 | int err; | |
944 | ||
44d0b80e | 945 | DEB_D("VIDIOC_STREAMON, type:%d\n", type); |
b960074f HV |
946 | |
947 | err = video_begin(fh); | |
948 | if (err) | |
949 | return err; | |
950 | if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
951 | return videobuf_streamon(&fh->video_q); | |
952 | if (type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
953 | return videobuf_streamon(&fh->vbi_q); | |
954 | return -EINVAL; | |
955 | } | |
956 | ||
957 | static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type) | |
958 | { | |
959 | struct saa7146_fh *fh = __fh; | |
960 | struct saa7146_dev *dev = fh->dev; | |
961 | struct saa7146_vv *vv = dev->vv_data; | |
962 | int err; | |
963 | ||
44d0b80e | 964 | DEB_D("VIDIOC_STREAMOFF, type:%d\n", type); |
b960074f HV |
965 | |
966 | /* ugly: we need to copy some checks from video_end(), | |
967 | because videobuf_streamoff() relies on the capture running. | |
968 | check and fix this */ | |
969 | if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { | |
44d0b80e | 970 | DEB_S("not capturing\n"); |
1da177e4 LT |
971 | return 0; |
972 | } | |
b960074f HV |
973 | |
974 | if (vv->video_fh != fh) { | |
44d0b80e | 975 | DEB_S("capturing, but in another open\n"); |
b960074f | 976 | return -EBUSY; |
1da177e4 | 977 | } |
b960074f HV |
978 | |
979 | err = -EINVAL; | |
980 | if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
981 | err = videobuf_streamoff(&fh->video_q); | |
982 | else if (type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
983 | err = videobuf_streamoff(&fh->vbi_q); | |
984 | if (0 != err) { | |
44d0b80e | 985 | DEB_D("warning: videobuf_streamoff() failed\n"); |
b960074f HV |
986 | video_end(fh, file); |
987 | } else { | |
988 | err = video_end(fh, file); | |
989 | } | |
990 | return err; | |
991 | } | |
992 | ||
b960074f HV |
993 | const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { |
994 | .vidioc_querycap = vidioc_querycap, | |
995 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, | |
717167e8 | 996 | .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap, |
b960074f HV |
997 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, |
998 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, | |
999 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, | |
1000 | .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, | |
1001 | .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, | |
1002 | .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, | |
b960074f HV |
1003 | |
1004 | .vidioc_overlay = vidioc_overlay, | |
1005 | .vidioc_g_fbuf = vidioc_g_fbuf, | |
1006 | .vidioc_s_fbuf = vidioc_s_fbuf, | |
1007 | .vidioc_reqbufs = vidioc_reqbufs, | |
1008 | .vidioc_querybuf = vidioc_querybuf, | |
1009 | .vidioc_qbuf = vidioc_qbuf, | |
1010 | .vidioc_dqbuf = vidioc_dqbuf, | |
1011 | .vidioc_g_std = vidioc_g_std, | |
1012 | .vidioc_s_std = vidioc_s_std, | |
b960074f HV |
1013 | .vidioc_streamon = vidioc_streamon, |
1014 | .vidioc_streamoff = vidioc_streamoff, | |
1015 | .vidioc_g_parm = vidioc_g_parm, | |
537fa492 HV |
1016 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, |
1017 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | |
b960074f | 1018 | }; |
1da177e4 | 1019 | |
ab49ae0f HV |
1020 | const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = { |
1021 | .vidioc_querycap = vidioc_querycap, | |
1022 | .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, | |
ab49ae0f HV |
1023 | |
1024 | .vidioc_reqbufs = vidioc_reqbufs, | |
1025 | .vidioc_querybuf = vidioc_querybuf, | |
1026 | .vidioc_qbuf = vidioc_qbuf, | |
1027 | .vidioc_dqbuf = vidioc_dqbuf, | |
1028 | .vidioc_g_std = vidioc_g_std, | |
1029 | .vidioc_s_std = vidioc_s_std, | |
1030 | .vidioc_streamon = vidioc_streamon, | |
1031 | .vidioc_streamoff = vidioc_streamoff, | |
1032 | .vidioc_g_parm = vidioc_g_parm, | |
1033 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, | |
1034 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | |
1035 | }; | |
1036 | ||
1da177e4 LT |
1037 | /*********************************************************************************/ |
1038 | /* buffer handling functions */ | |
1039 | ||
1040 | static int buffer_activate (struct saa7146_dev *dev, | |
1041 | struct saa7146_buf *buf, | |
1042 | struct saa7146_buf *next) | |
1043 | { | |
1044 | struct saa7146_vv *vv = dev->vv_data; | |
1045 | ||
0fc0686e | 1046 | buf->vb.state = VIDEOBUF_ACTIVE; |
1da177e4 LT |
1047 | saa7146_set_capture(dev,buf,next); |
1048 | ||
9bb60193 | 1049 | mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT); |
1da177e4 LT |
1050 | return 0; |
1051 | } | |
1052 | ||
311c70e1 JF |
1053 | static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf) |
1054 | { | |
1055 | saa7146_pgtable_free(dev->pci, &buf->pt[0]); | |
1056 | saa7146_pgtable_free(dev->pci, &buf->pt[1]); | |
1057 | saa7146_pgtable_free(dev->pci, &buf->pt[2]); | |
1058 | } | |
1059 | ||
1da177e4 LT |
1060 | static int buffer_prepare(struct videobuf_queue *q, |
1061 | struct videobuf_buffer *vb, enum v4l2_field field) | |
1062 | { | |
1063 | struct file *file = q->priv_data; | |
1064 | struct saa7146_fh *fh = file->private_data; | |
1065 | struct saa7146_dev *dev = fh->dev; | |
1066 | struct saa7146_vv *vv = dev->vv_data; | |
1067 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | |
1068 | int size,err = 0; | |
1069 | ||
44d0b80e | 1070 | DEB_CAP("vbuf:%p\n", vb); |
1da177e4 LT |
1071 | |
1072 | /* sanity checks */ | |
fd74d6eb HV |
1073 | if (vv->video_fmt.width < 48 || |
1074 | vv->video_fmt.height < 32 || | |
1075 | vv->video_fmt.width > vv->standard->h_max_out || | |
1076 | vv->video_fmt.height > vv->standard->v_max_out) { | |
44d0b80e | 1077 | DEB_D("w (%d) / h (%d) out of bounds\n", |
fd74d6eb | 1078 | vv->video_fmt.width, vv->video_fmt.height); |
1da177e4 LT |
1079 | return -EINVAL; |
1080 | } | |
1081 | ||
fd74d6eb | 1082 | size = vv->video_fmt.sizeimage; |
1da177e4 | 1083 | if (0 != buf->vb.baddr && buf->vb.bsize < size) { |
44d0b80e | 1084 | DEB_D("size mismatch\n"); |
1da177e4 LT |
1085 | return -EINVAL; |
1086 | } | |
1087 | ||
44d0b80e | 1088 | DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", |
fd74d6eb HV |
1089 | vv->video_fmt.width, vv->video_fmt.height, |
1090 | size, v4l2_field_names[vv->video_fmt.field]); | |
1091 | if (buf->vb.width != vv->video_fmt.width || | |
1092 | buf->vb.bytesperline != vv->video_fmt.bytesperline || | |
1093 | buf->vb.height != vv->video_fmt.height || | |
1da177e4 LT |
1094 | buf->vb.size != size || |
1095 | buf->vb.field != field || | |
fd74d6eb HV |
1096 | buf->vb.field != vv->video_fmt.field || |
1097 | buf->fmt != &vv->video_fmt) { | |
c7b0ac05 | 1098 | saa7146_dma_free(dev,q,buf); |
1da177e4 LT |
1099 | } |
1100 | ||
0fc0686e | 1101 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { |
1da177e4 LT |
1102 | struct saa7146_format *sfmt; |
1103 | ||
fd74d6eb HV |
1104 | buf->vb.bytesperline = vv->video_fmt.bytesperline; |
1105 | buf->vb.width = vv->video_fmt.width; | |
1106 | buf->vb.height = vv->video_fmt.height; | |
1da177e4 LT |
1107 | buf->vb.size = size; |
1108 | buf->vb.field = field; | |
fd74d6eb HV |
1109 | buf->fmt = &vv->video_fmt; |
1110 | buf->vb.field = vv->video_fmt.field; | |
1da177e4 | 1111 | |
a757ee22 | 1112 | sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); |
1da177e4 | 1113 | |
311c70e1 | 1114 | release_all_pagetables(dev, buf); |
1da177e4 | 1115 | if( 0 != IS_PLANAR(sfmt->trans)) { |
1da177e4 LT |
1116 | saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); |
1117 | saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); | |
1118 | saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); | |
1119 | } else { | |
1da177e4 LT |
1120 | saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); |
1121 | } | |
1122 | ||
c7b0ac05 | 1123 | err = videobuf_iolock(q,&buf->vb, &vv->ov_fb); |
1da177e4 LT |
1124 | if (err) |
1125 | goto oops; | |
1126 | err = saa7146_pgtable_build(dev,buf); | |
1127 | if (err) | |
1128 | goto oops; | |
1129 | } | |
0fc0686e | 1130 | buf->vb.state = VIDEOBUF_PREPARED; |
1da177e4 LT |
1131 | buf->activate = buffer_activate; |
1132 | ||
1133 | return 0; | |
1134 | ||
1135 | oops: | |
44d0b80e | 1136 | DEB_D("error out\n"); |
c7b0ac05 | 1137 | saa7146_dma_free(dev,q,buf); |
1da177e4 LT |
1138 | |
1139 | return err; | |
1140 | } | |
1141 | ||
1142 | static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) | |
1143 | { | |
1144 | struct file *file = q->priv_data; | |
1145 | struct saa7146_fh *fh = file->private_data; | |
fd74d6eb | 1146 | struct saa7146_vv *vv = fh->dev->vv_data; |
1da177e4 LT |
1147 | |
1148 | if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) | |
1149 | *count = MAX_SAA7146_CAPTURE_BUFFERS; | |
1150 | ||
fd74d6eb | 1151 | *size = vv->video_fmt.sizeimage; |
1da177e4 LT |
1152 | |
1153 | /* check if we exceed the "max_memory" parameter */ | |
1154 | if( (*count * *size) > (max_memory*1048576) ) { | |
1155 | *count = (max_memory*1048576) / *size; | |
1156 | } | |
1157 | ||
44d0b80e | 1158 | DEB_CAP("%d buffers, %d bytes each\n", *count, *size); |
1da177e4 LT |
1159 | |
1160 | return 0; | |
1161 | } | |
1162 | ||
1163 | static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | |
1164 | { | |
1165 | struct file *file = q->priv_data; | |
1166 | struct saa7146_fh *fh = file->private_data; | |
1167 | struct saa7146_dev *dev = fh->dev; | |
1168 | struct saa7146_vv *vv = dev->vv_data; | |
1169 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | |
1170 | ||
44d0b80e | 1171 | DEB_CAP("vbuf:%p\n", vb); |
9bb60193 | 1172 | saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf); |
1da177e4 LT |
1173 | } |
1174 | ||
1da177e4 LT |
1175 | static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) |
1176 | { | |
1177 | struct file *file = q->priv_data; | |
1178 | struct saa7146_fh *fh = file->private_data; | |
1179 | struct saa7146_dev *dev = fh->dev; | |
1180 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | |
1181 | ||
44d0b80e | 1182 | DEB_CAP("vbuf:%p\n", vb); |
311c70e1 | 1183 | |
c7b0ac05 | 1184 | saa7146_dma_free(dev,q,buf); |
ba9e9f3c MCC |
1185 | |
1186 | release_all_pagetables(dev, buf); | |
1da177e4 LT |
1187 | } |
1188 | ||
11c2078b | 1189 | static const struct videobuf_queue_ops video_qops = { |
1da177e4 LT |
1190 | .buf_setup = buffer_setup, |
1191 | .buf_prepare = buffer_prepare, | |
1192 | .buf_queue = buffer_queue, | |
1193 | .buf_release = buffer_release, | |
1194 | }; | |
1195 | ||
1196 | /********************************************************************************/ | |
1197 | /* file operations */ | |
1198 | ||
1199 | static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) | |
1200 | { | |
9bb60193 | 1201 | INIT_LIST_HEAD(&vv->video_dmaq.queue); |
1da177e4 | 1202 | |
12a83612 | 1203 | timer_setup(&vv->video_dmaq.timeout, saa7146_buffer_timeout, 0); |
9bb60193 | 1204 | vv->video_dmaq.dev = dev; |
1da177e4 LT |
1205 | |
1206 | /* set some default values */ | |
1207 | vv->standard = &dev->ext_vv_data->stds[0]; | |
1208 | ||
1209 | /* FIXME: what's this? */ | |
1210 | vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A; | |
1211 | vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A; | |
1212 | } | |
1213 | ||
1214 | ||
1215 | static int video_open(struct saa7146_dev *dev, struct file *file) | |
1216 | { | |
abf84383 | 1217 | struct saa7146_fh *fh = file->private_data; |
1da177e4 | 1218 | |
0705135e GL |
1219 | videobuf_queue_sg_init(&fh->video_q, &video_qops, |
1220 | &dev->pci->dev, &dev->slock, | |
1da177e4 LT |
1221 | V4L2_BUF_TYPE_VIDEO_CAPTURE, |
1222 | V4L2_FIELD_INTERLACED, | |
1223 | sizeof(struct saa7146_buf), | |
9af39713 | 1224 | file, &dev->v4l2_lock); |
1da177e4 | 1225 | |
1da177e4 LT |
1226 | return 0; |
1227 | } | |
1228 | ||
1229 | ||
1230 | static void video_close(struct saa7146_dev *dev, struct file *file) | |
1231 | { | |
abf84383 | 1232 | struct saa7146_fh *fh = file->private_data; |
1da177e4 | 1233 | struct saa7146_vv *vv = dev->vv_data; |
2970c492 | 1234 | struct videobuf_queue *q = &fh->video_q; |
1da177e4 | 1235 | |
7b4668ef HV |
1236 | if (IS_CAPTURE_ACTIVE(fh) != 0) |
1237 | video_end(fh, file); | |
1238 | else if (IS_OVERLAY_ACTIVE(fh) != 0) | |
1239 | saa7146_stop_preview(fh); | |
1da177e4 | 1240 | |
19bc5133 | 1241 | videobuf_stop(q); |
1da177e4 | 1242 | /* hmm, why is this function declared void? */ |
1da177e4 LT |
1243 | } |
1244 | ||
1245 | ||
1246 | static void video_irq_done(struct saa7146_dev *dev, unsigned long st) | |
1247 | { | |
1248 | struct saa7146_vv *vv = dev->vv_data; | |
9bb60193 | 1249 | struct saa7146_dmaqueue *q = &vv->video_dmaq; |
1da177e4 LT |
1250 | |
1251 | spin_lock(&dev->slock); | |
44d0b80e | 1252 | DEB_CAP("called\n"); |
1da177e4 LT |
1253 | |
1254 | /* only finish the buffer if we have one... */ | |
1255 | if( NULL != q->curr ) { | |
0fc0686e | 1256 | saa7146_buffer_finish(dev,q,VIDEOBUF_DONE); |
1da177e4 LT |
1257 | } |
1258 | saa7146_buffer_next(dev,q,0); | |
1259 | ||
1260 | spin_unlock(&dev->slock); | |
1261 | } | |
1262 | ||
1263 | static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | |
1264 | { | |
1265 | struct saa7146_fh *fh = file->private_data; | |
1266 | struct saa7146_dev *dev = fh->dev; | |
1267 | struct saa7146_vv *vv = dev->vv_data; | |
1268 | ssize_t ret = 0; | |
1269 | ||
44d0b80e | 1270 | DEB_EE("called\n"); |
1da177e4 LT |
1271 | |
1272 | if ((vv->video_status & STATUS_CAPTURE) != 0) { | |
1273 | /* fixme: should we allow read() captures while streaming capture? */ | |
1274 | if (vv->video_fh == fh) { | |
44d0b80e | 1275 | DEB_S("already capturing\n"); |
1da177e4 LT |
1276 | return -EBUSY; |
1277 | } | |
44d0b80e | 1278 | DEB_S("already capturing in another open\n"); |
1da177e4 LT |
1279 | return -EBUSY; |
1280 | } | |
1281 | ||
1282 | ret = video_begin(fh); | |
1283 | if( 0 != ret) { | |
1284 | goto out; | |
1285 | } | |
1286 | ||
1287 | ret = videobuf_read_one(&fh->video_q , data, count, ppos, | |
1288 | file->f_flags & O_NONBLOCK); | |
1289 | if (ret != 0) { | |
1290 | video_end(fh, file); | |
1291 | } else { | |
1292 | ret = video_end(fh, file); | |
1293 | } | |
1294 | out: | |
1295 | /* restart overlay if it was active before */ | |
1296 | if (vv->ov_suspend != NULL) { | |
1297 | saa7146_start_preview(vv->ov_suspend); | |
1298 | vv->ov_suspend = NULL; | |
1299 | } | |
1300 | ||
1301 | return ret; | |
1302 | } | |
1303 | ||
ad627017 | 1304 | const struct saa7146_use_ops saa7146_video_uops = { |
1da177e4 LT |
1305 | .init = video_init, |
1306 | .open = video_open, | |
1307 | .release = video_close, | |
1308 | .irq_done = video_irq_done, | |
1309 | .read = video_read, | |
1310 | }; |