]>
Commit | Line | Data |
---|---|---|
44d0b80e JP |
1 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
2 | ||
1da177e4 | 3 | #include <media/saa7146_vv.h> |
1b8dac15 | 4 | #include <media/v4l2-chip-ident.h> |
7a707b89 | 5 | #include <linux/module.h> |
1da177e4 LT |
6 | |
7 | static int max_memory = 32; | |
8 | ||
9 | module_param(max_memory, int, 0644); | |
10 | MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)"); | |
11 | ||
12 | #define IS_CAPTURE_ACTIVE(fh) \ | |
13 | (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh)) | |
14 | ||
15 | #define IS_OVERLAY_ACTIVE(fh) \ | |
16 | (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh)) | |
17 | ||
18 | /* format descriptions for capture and preview */ | |
19 | static struct saa7146_format formats[] = { | |
20 | { | |
21 | .name = "RGB-8 (3-3-2)", | |
22 | .pixelformat = V4L2_PIX_FMT_RGB332, | |
23 | .trans = RGB08_COMPOSED, | |
24 | .depth = 8, | |
25 | .flags = 0, | |
26 | }, { | |
27 | .name = "RGB-16 (5/B-6/G-5/R)", | |
28 | .pixelformat = V4L2_PIX_FMT_RGB565, | |
29 | .trans = RGB16_COMPOSED, | |
30 | .depth = 16, | |
31 | .flags = 0, | |
32 | }, { | |
33 | .name = "RGB-24 (B-G-R)", | |
34 | .pixelformat = V4L2_PIX_FMT_BGR24, | |
35 | .trans = RGB24_COMPOSED, | |
36 | .depth = 24, | |
37 | .flags = 0, | |
38 | }, { | |
39 | .name = "RGB-32 (B-G-R)", | |
40 | .pixelformat = V4L2_PIX_FMT_BGR32, | |
41 | .trans = RGB32_COMPOSED, | |
42 | .depth = 32, | |
43 | .flags = 0, | |
44 | }, { | |
45 | .name = "RGB-32 (R-G-B)", | |
46 | .pixelformat = V4L2_PIX_FMT_RGB32, | |
47 | .trans = RGB32_COMPOSED, | |
48 | .depth = 32, | |
49 | .flags = 0, | |
50 | .swap = 0x2, | |
51 | }, { | |
52 | .name = "Greyscale-8", | |
53 | .pixelformat = V4L2_PIX_FMT_GREY, | |
54 | .trans = Y8, | |
55 | .depth = 8, | |
56 | .flags = 0, | |
57 | }, { | |
58 | .name = "YUV 4:2:2 planar (Y-Cb-Cr)", | |
59 | .pixelformat = V4L2_PIX_FMT_YUV422P, | |
60 | .trans = YUV422_DECOMPOSED, | |
61 | .depth = 16, | |
62 | .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, | |
63 | }, { | |
64 | .name = "YVU 4:2:0 planar (Y-Cb-Cr)", | |
65 | .pixelformat = V4L2_PIX_FMT_YVU420, | |
66 | .trans = YUV420_DECOMPOSED, | |
67 | .depth = 12, | |
68 | .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, | |
69 | }, { | |
70 | .name = "YUV 4:2:0 planar (Y-Cb-Cr)", | |
71 | .pixelformat = V4L2_PIX_FMT_YUV420, | |
72 | .trans = YUV420_DECOMPOSED, | |
73 | .depth = 12, | |
74 | .flags = FORMAT_IS_PLANAR, | |
75 | }, { | |
76 | .name = "YUV 4:2:2 (U-Y-V-Y)", | |
77 | .pixelformat = V4L2_PIX_FMT_UYVY, | |
78 | .trans = YUV422_COMPOSED, | |
79 | .depth = 16, | |
80 | .flags = 0, | |
81 | } | |
82 | }; | |
83 | ||
84 | /* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps. | |
85 | due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped | |
86 | (like V4L2_PIX_FMT_YUYV) ... 8-( */ | |
87 | ||
88 | static int NUM_FORMATS = sizeof(formats)/sizeof(struct saa7146_format); | |
89 | ||
a757ee22 | 90 | struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc) |
1da177e4 LT |
91 | { |
92 | int i, j = NUM_FORMATS; | |
93 | ||
94 | for (i = 0; i < j; i++) { | |
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; | |
392 | struct saa7146_format *fmt = NULL; | |
393 | unsigned long flags; | |
394 | unsigned int resource; | |
395 | u32 dmas = 0; | |
44d0b80e | 396 | DEB_EE("dev:%p, fh:%p\n", dev, fh); |
1da177e4 LT |
397 | |
398 | if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { | |
44d0b80e | 399 | DEB_S("not capturing\n"); |
1da177e4 LT |
400 | return 0; |
401 | } | |
402 | ||
403 | if (vv->video_fh != fh) { | |
44d0b80e | 404 | DEB_S("capturing, but in another open\n"); |
1da177e4 LT |
405 | return -EBUSY; |
406 | } | |
407 | ||
fd74d6eb | 408 | fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); |
1da177e4 LT |
409 | /* we need to have a valid format set here */ |
410 | BUG_ON(NULL == fmt); | |
411 | ||
412 | if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { | |
413 | resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; | |
414 | dmas = MASK_22 | MASK_21 | MASK_20; | |
415 | } else { | |
416 | resource = RESOURCE_DMA1_HPS; | |
417 | dmas = MASK_22; | |
418 | } | |
419 | spin_lock_irqsave(&dev->slock,flags); | |
420 | ||
421 | /* disable rps0 */ | |
422 | saa7146_write(dev, MC1, MASK_28); | |
423 | ||
424 | /* disable rps0 irqs */ | |
425 | SAA7146_IER_DISABLE(dev, MASK_27); | |
426 | ||
427 | /* shut down all used video dma transfers */ | |
428 | saa7146_write(dev, MC1, dmas); | |
429 | ||
430 | spin_unlock_irqrestore(&dev->slock, flags); | |
431 | ||
432 | vv->video_fh = NULL; | |
433 | vv->video_status = 0; | |
434 | ||
435 | saa7146_res_free(fh, resource); | |
436 | ||
437 | if (vv->ov_suspend != NULL) { | |
438 | saa7146_start_preview(vv->ov_suspend); | |
439 | vv->ov_suspend = NULL; | |
440 | } | |
441 | ||
442 | return 0; | |
443 | } | |
444 | ||
b960074f HV |
445 | static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) |
446 | { | |
447 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
448 | ||
449 | strcpy((char *)cap->driver, "saa7146 v4l2"); | |
450 | strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); | |
451 | sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci)); | |
452 | cap->version = SAA7146_VERSION_CODE; | |
6e65ca94 | 453 | cap->device_caps = |
b960074f HV |
454 | V4L2_CAP_VIDEO_CAPTURE | |
455 | V4L2_CAP_VIDEO_OVERLAY | | |
456 | V4L2_CAP_READWRITE | | |
457 | V4L2_CAP_STREAMING; | |
6e65ca94 HV |
458 | cap->device_caps |= dev->ext_vv_data->capabilities; |
459 | cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; | |
b960074f HV |
460 | return 0; |
461 | } | |
1da177e4 | 462 | |
b960074f | 463 | static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) |
1da177e4 | 464 | { |
b960074f | 465 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
1da177e4 LT |
466 | struct saa7146_vv *vv = dev->vv_data; |
467 | ||
b960074f HV |
468 | *fb = vv->ov_fb; |
469 | fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; | |
5da545ad | 470 | fb->flags = V4L2_FBUF_FLAG_PRIMARY; |
b960074f HV |
471 | return 0; |
472 | } | |
1da177e4 | 473 | |
b960074f HV |
474 | static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) |
475 | { | |
476 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
477 | struct saa7146_vv *vv = dev->vv_data; | |
478 | struct saa7146_format *fmt; | |
1da177e4 | 479 | |
44d0b80e | 480 | DEB_EE("VIDIOC_S_FBUF\n"); |
1da177e4 | 481 | |
b960074f HV |
482 | if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) |
483 | return -EPERM; | |
1da177e4 | 484 | |
b960074f | 485 | /* check args */ |
a757ee22 | 486 | fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat); |
b960074f HV |
487 | if (NULL == fmt) |
488 | return -EINVAL; | |
1da177e4 | 489 | |
b960074f HV |
490 | /* planar formats are not allowed for overlay video, clipping and video dma would clash */ |
491 | if (fmt->flags & FORMAT_IS_PLANAR) | |
44d0b80e JP |
492 | DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n", |
493 | (char *)&fmt->pixelformat); | |
b960074f HV |
494 | |
495 | /* check if overlay is running */ | |
496 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | |
497 | if (vv->video_fh != fh) { | |
44d0b80e | 498 | DEB_D("refusing to change framebuffer informations while overlay is active in another open\n"); |
b960074f HV |
499 | return -EBUSY; |
500 | } | |
1da177e4 LT |
501 | } |
502 | ||
b960074f HV |
503 | /* ok, accept it */ |
504 | vv->ov_fb = *fb; | |
505 | vv->ov_fmt = fmt; | |
84a1d9c8 MH |
506 | |
507 | if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) { | |
508 | vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8; | |
44d0b80e | 509 | DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline); |
84a1d9c8 | 510 | } |
b960074f HV |
511 | return 0; |
512 | } | |
513 | ||
514 | static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) | |
515 | { | |
516 | if (f->index >= NUM_FORMATS) | |
517 | return -EINVAL; | |
518 | strlcpy((char *)f->description, formats[f->index].name, | |
519 | sizeof(f->description)); | |
520 | f->pixelformat = formats[f->index].pixelformat; | |
521 | return 0; | |
522 | } | |
523 | ||
6e65ca94 | 524 | int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) |
b960074f | 525 | { |
6e65ca94 HV |
526 | struct saa7146_dev *dev = container_of(ctrl->handler, |
527 | struct saa7146_dev, ctrl_handler); | |
b960074f | 528 | struct saa7146_vv *vv = dev->vv_data; |
6e65ca94 | 529 | u32 val; |
b960074f | 530 | |
6e65ca94 | 531 | switch (ctrl->id) { |
b960074f | 532 | case V4L2_CID_BRIGHTNESS: |
6e65ca94 HV |
533 | val = saa7146_read(dev, BCS_CTRL); |
534 | val &= 0x00ffffff; | |
535 | val |= (ctrl->val << 24); | |
536 | saa7146_write(dev, BCS_CTRL, val); | |
b960074f HV |
537 | saa7146_write(dev, MC2, MASK_22 | MASK_06); |
538 | break; | |
6e65ca94 HV |
539 | |
540 | case V4L2_CID_CONTRAST: | |
541 | val = saa7146_read(dev, BCS_CTRL); | |
542 | val &= 0xff00ffff; | |
543 | val |= (ctrl->val << 16); | |
544 | saa7146_write(dev, BCS_CTRL, val); | |
b960074f HV |
545 | saa7146_write(dev, MC2, MASK_22 | MASK_06); |
546 | break; | |
6e65ca94 HV |
547 | |
548 | case V4L2_CID_SATURATION: | |
549 | val = saa7146_read(dev, BCS_CTRL); | |
550 | val &= 0xffffff00; | |
551 | val |= (ctrl->val << 0); | |
552 | saa7146_write(dev, BCS_CTRL, val); | |
b960074f HV |
553 | saa7146_write(dev, MC2, MASK_22 | MASK_06); |
554 | break; | |
6e65ca94 | 555 | |
b960074f HV |
556 | case V4L2_CID_HFLIP: |
557 | /* fixme: we can support changing VFLIP and HFLIP here... */ | |
6e65ca94 | 558 | if ((vv->video_status & STATUS_CAPTURE)) |
5a5b9647 | 559 | return -EBUSY; |
6e65ca94 | 560 | vv->hflip = ctrl->val; |
b960074f | 561 | break; |
6e65ca94 | 562 | |
b960074f | 563 | case V4L2_CID_VFLIP: |
6e65ca94 | 564 | if ((vv->video_status & STATUS_CAPTURE)) |
5a5b9647 | 565 | return -EBUSY; |
6e65ca94 | 566 | vv->vflip = ctrl->val; |
b960074f | 567 | break; |
6e65ca94 | 568 | |
b960074f | 569 | default: |
b960074f HV |
570 | return -EINVAL; |
571 | } | |
1da177e4 | 572 | |
6e65ca94 HV |
573 | if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */ |
574 | struct saa7146_fh *fh = vv->video_fh; | |
575 | ||
b960074f HV |
576 | saa7146_stop_preview(fh); |
577 | saa7146_start_preview(fh); | |
578 | } | |
579 | return 0; | |
580 | } | |
1da177e4 | 581 | |
b960074f HV |
582 | static int vidioc_g_parm(struct file *file, void *fh, |
583 | struct v4l2_streamparm *parm) | |
584 | { | |
717167e8 TP |
585 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
586 | struct saa7146_vv *vv = dev->vv_data; | |
587 | ||
6e65ca94 HV |
588 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
589 | return -EINVAL; | |
b960074f | 590 | parm->parm.capture.readbuffers = 1; |
717167e8 TP |
591 | v4l2_video_std_frame_period(vv->standard->id, |
592 | &parm->parm.capture.timeperframe); | |
b960074f HV |
593 | return 0; |
594 | } | |
1da177e4 | 595 | |
b960074f HV |
596 | static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) |
597 | { | |
fd74d6eb HV |
598 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
599 | struct saa7146_vv *vv = dev->vv_data; | |
600 | ||
601 | f->fmt.pix = vv->video_fmt; | |
b960074f HV |
602 | return 0; |
603 | } | |
1da177e4 | 604 | |
b960074f HV |
605 | static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) |
606 | { | |
5da545ad HV |
607 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
608 | struct saa7146_vv *vv = dev->vv_data; | |
609 | ||
610 | f->fmt.win = vv->ov.win; | |
b960074f HV |
611 | return 0; |
612 | } | |
1da177e4 | 613 | |
b960074f HV |
614 | static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) |
615 | { | |
fca3469a HV |
616 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; |
617 | struct saa7146_vv *vv = dev->vv_data; | |
618 | ||
619 | f->fmt.vbi = vv->vbi_fmt; | |
b960074f HV |
620 | return 0; |
621 | } | |
1da177e4 | 622 | |
b960074f HV |
623 | static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) |
624 | { | |
625 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
626 | struct saa7146_vv *vv = dev->vv_data; | |
627 | struct saa7146_format *fmt; | |
628 | enum v4l2_field field; | |
629 | int maxw, maxh; | |
630 | int calc_bpl; | |
631 | ||
44d0b80e | 632 | DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); |
b960074f | 633 | |
a757ee22 | 634 | fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat); |
b960074f HV |
635 | if (NULL == fmt) |
636 | return -EINVAL; | |
637 | ||
638 | field = f->fmt.pix.field; | |
639 | maxw = vv->standard->h_max_out; | |
640 | maxh = vv->standard->v_max_out; | |
641 | ||
642 | if (V4L2_FIELD_ANY == field) { | |
643 | field = (f->fmt.pix.height > maxh / 2) | |
644 | ? V4L2_FIELD_INTERLACED | |
645 | : V4L2_FIELD_BOTTOM; | |
646 | } | |
647 | switch (field) { | |
648 | case V4L2_FIELD_ALTERNATE: | |
649 | vv->last_field = V4L2_FIELD_TOP; | |
650 | maxh = maxh / 2; | |
651 | break; | |
652 | case V4L2_FIELD_TOP: | |
653 | case V4L2_FIELD_BOTTOM: | |
654 | vv->last_field = V4L2_FIELD_INTERLACED; | |
655 | maxh = maxh / 2; | |
656 | break; | |
657 | case V4L2_FIELD_INTERLACED: | |
658 | vv->last_field = V4L2_FIELD_INTERLACED; | |
659 | break; | |
660 | default: | |
44d0b80e | 661 | DEB_D("no known field mode '%d'\n", field); |
b960074f | 662 | return -EINVAL; |
1da177e4 | 663 | } |
1da177e4 | 664 | |
b960074f | 665 | f->fmt.pix.field = field; |
6e65ca94 | 666 | f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; |
b960074f HV |
667 | if (f->fmt.pix.width > maxw) |
668 | f->fmt.pix.width = maxw; | |
669 | if (f->fmt.pix.height > maxh) | |
670 | f->fmt.pix.height = maxh; | |
1da177e4 | 671 | |
b960074f | 672 | calc_bpl = (f->fmt.pix.width * fmt->depth) / 8; |
1da177e4 | 673 | |
b960074f HV |
674 | if (f->fmt.pix.bytesperline < calc_bpl) |
675 | f->fmt.pix.bytesperline = calc_bpl; | |
676 | ||
677 | if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */ | |
678 | f->fmt.pix.bytesperline = calc_bpl; | |
679 | ||
680 | f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; | |
44d0b80e JP |
681 | DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n", |
682 | f->fmt.pix.width, f->fmt.pix.height, | |
683 | f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); | |
b960074f HV |
684 | |
685 | return 0; | |
686 | } | |
687 | ||
688 | ||
689 | static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) | |
690 | { | |
691 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
692 | struct saa7146_vv *vv = dev->vv_data; | |
693 | struct v4l2_window *win = &f->fmt.win; | |
694 | enum v4l2_field field; | |
695 | int maxw, maxh; | |
696 | ||
44d0b80e | 697 | DEB_EE("dev:%p\n", dev); |
b960074f HV |
698 | |
699 | if (NULL == vv->ov_fb.base) { | |
44d0b80e | 700 | DEB_D("no fb base set\n"); |
b960074f | 701 | return -EINVAL; |
1da177e4 | 702 | } |
b960074f | 703 | if (NULL == vv->ov_fmt) { |
44d0b80e | 704 | DEB_D("no fb fmt set\n"); |
b960074f | 705 | return -EINVAL; |
1da177e4 | 706 | } |
b960074f | 707 | if (win->w.width < 48 || win->w.height < 32) { |
44d0b80e JP |
708 | DEB_D("min width/height. (%d,%d)\n", |
709 | win->w.width, win->w.height); | |
b960074f | 710 | return -EINVAL; |
1da177e4 | 711 | } |
b960074f | 712 | if (win->clipcount > 16) { |
44d0b80e | 713 | DEB_D("clipcount too big\n"); |
b960074f | 714 | return -EINVAL; |
afd1a0c9 | 715 | } |
b960074f HV |
716 | |
717 | field = win->field; | |
718 | maxw = vv->standard->h_max_out; | |
719 | maxh = vv->standard->v_max_out; | |
720 | ||
721 | if (V4L2_FIELD_ANY == field) { | |
722 | field = (win->w.height > maxh / 2) | |
723 | ? V4L2_FIELD_INTERLACED | |
724 | : V4L2_FIELD_TOP; | |
725 | } | |
726 | switch (field) { | |
727 | case V4L2_FIELD_TOP: | |
728 | case V4L2_FIELD_BOTTOM: | |
729 | case V4L2_FIELD_ALTERNATE: | |
730 | maxh = maxh / 2; | |
731 | break; | |
732 | case V4L2_FIELD_INTERLACED: | |
733 | break; | |
734 | default: | |
44d0b80e | 735 | DEB_D("no known field mode '%d'\n", field); |
b960074f | 736 | return -EINVAL; |
1da177e4 | 737 | } |
b960074f HV |
738 | |
739 | win->field = field; | |
740 | if (win->w.width > maxw) | |
741 | win->w.width = maxw; | |
742 | if (win->w.height > maxh) | |
743 | win->w.height = maxh; | |
744 | ||
745 | return 0; | |
746 | } | |
747 | ||
748 | static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) | |
749 | { | |
750 | struct saa7146_fh *fh = __fh; | |
751 | struct saa7146_dev *dev = fh->dev; | |
752 | struct saa7146_vv *vv = dev->vv_data; | |
753 | int err; | |
754 | ||
44d0b80e | 755 | DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); |
b960074f | 756 | if (IS_CAPTURE_ACTIVE(fh) != 0) { |
44d0b80e | 757 | DEB_EE("streaming capture is active\n"); |
b960074f | 758 | return -EBUSY; |
1da177e4 | 759 | } |
b960074f HV |
760 | err = vidioc_try_fmt_vid_cap(file, fh, f); |
761 | if (0 != err) | |
762 | return err; | |
fd74d6eb | 763 | vv->video_fmt = f->fmt.pix; |
44d0b80e | 764 | DEB_EE("set to pixelformat '%4.4s'\n", |
fd74d6eb | 765 | (char *)&vv->video_fmt.pixelformat); |
b960074f HV |
766 | return 0; |
767 | } | |
768 | ||
769 | static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f) | |
770 | { | |
771 | struct saa7146_fh *fh = __fh; | |
772 | struct saa7146_dev *dev = fh->dev; | |
773 | struct saa7146_vv *vv = dev->vv_data; | |
774 | int err; | |
775 | ||
44d0b80e | 776 | DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh); |
b960074f HV |
777 | err = vidioc_try_fmt_vid_overlay(file, fh, f); |
778 | if (0 != err) | |
779 | return err; | |
5da545ad HV |
780 | vv->ov.win = f->fmt.win; |
781 | vv->ov.nclips = f->fmt.win.clipcount; | |
782 | if (vv->ov.nclips > 16) | |
783 | vv->ov.nclips = 16; | |
784 | if (copy_from_user(vv->ov.clips, f->fmt.win.clips, | |
785 | sizeof(struct v4l2_clip) * vv->ov.nclips)) { | |
b960074f | 786 | return -EFAULT; |
1da177e4 | 787 | } |
b960074f | 788 | |
5da545ad HV |
789 | /* vv->ov.fh is used to indicate that we have valid overlay informations, too */ |
790 | vv->ov.fh = fh; | |
b960074f | 791 | |
b960074f HV |
792 | /* check if our current overlay is active */ |
793 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | |
794 | saa7146_stop_preview(fh); | |
795 | saa7146_start_preview(fh); | |
1da177e4 | 796 | } |
b960074f HV |
797 | return 0; |
798 | } | |
799 | ||
800 | static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) | |
801 | { | |
802 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
803 | struct saa7146_vv *vv = dev->vv_data; | |
804 | ||
805 | *norm = vv->standard->id; | |
806 | return 0; | |
807 | } | |
808 | ||
1da177e4 LT |
809 | /* the saa7146 supfhrts (used in conjunction with the saa7111a for example) |
810 | PAL / NTSC / SECAM. if your hardware does not (or does more) | |
811 | -- override this function in your extension */ | |
b960074f | 812 | /* |
1da177e4 LT |
813 | case VIDIOC_ENUMSTD: |
814 | { | |
815 | struct v4l2_standard *e = arg; | |
816 | if (e->index < 0 ) | |
817 | return -EINVAL; | |
818 | if( e->index < dev->ext_vv_data->num_stds ) { | |
44d0b80e | 819 | DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index); |
1da177e4 LT |
820 | v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name); |
821 | return 0; | |
822 | } | |
823 | return -EINVAL; | |
824 | } | |
b960074f | 825 | */ |
1da177e4 | 826 | |
b960074f HV |
827 | static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id) |
828 | { | |
829 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | |
830 | struct saa7146_vv *vv = dev->vv_data; | |
831 | int found = 0; | |
832 | int err, i; | |
1da177e4 | 833 | |
44d0b80e | 834 | DEB_EE("VIDIOC_S_STD\n"); |
1da177e4 | 835 | |
b960074f | 836 | if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) { |
44d0b80e | 837 | DEB_D("cannot change video standard while streaming capture is active\n"); |
b960074f HV |
838 | return -EBUSY; |
839 | } | |
1da177e4 | 840 | |
b960074f HV |
841 | if ((vv->video_status & STATUS_OVERLAY) != 0) { |
842 | vv->ov_suspend = vv->video_fh; | |
843 | err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ | |
844 | if (0 != err) { | |
44d0b80e | 845 | DEB_D("suspending video failed. aborting\n"); |
b960074f | 846 | return err; |
1da177e4 | 847 | } |
b960074f | 848 | } |
1da177e4 | 849 | |
b960074f HV |
850 | for (i = 0; i < dev->ext_vv_data->num_stds; i++) |
851 | if (*id & dev->ext_vv_data->stds[i].id) | |
852 | break; | |
853 | if (i != dev->ext_vv_data->num_stds) { | |
854 | vv->standard = &dev->ext_vv_data->stds[i]; | |
855 | if (NULL != dev->ext_vv_data->std_callback) | |
856 | dev->ext_vv_data->std_callback(dev, vv->standard); | |
857 | found = 1; | |
858 | } | |
1da177e4 | 859 | |
b960074f HV |
860 | if (vv->ov_suspend != NULL) { |
861 | saa7146_start_preview(vv->ov_suspend); | |
862 | vv->ov_suspend = NULL; | |
1da177e4 | 863 | } |
1da177e4 | 864 | |
b960074f | 865 | if (!found) { |
44d0b80e | 866 | DEB_EE("VIDIOC_S_STD: standard not found\n"); |
b960074f | 867 | return -EINVAL; |
1da177e4 | 868 | } |
1da177e4 | 869 | |
44d0b80e | 870 | DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name); |
b960074f HV |
871 | return 0; |
872 | } | |
1da177e4 | 873 | |
b960074f HV |
874 | static int vidioc_overlay(struct file *file, void *fh, unsigned int on) |
875 | { | |
876 | int err; | |
1da177e4 | 877 | |
44d0b80e | 878 | DEB_D("VIDIOC_OVERLAY on:%d\n", on); |
b960074f HV |
879 | if (on) |
880 | err = saa7146_start_preview(fh); | |
881 | else | |
882 | err = saa7146_stop_preview(fh); | |
883 | return err; | |
884 | } | |
1da177e4 | 885 | |
b960074f HV |
886 | static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b) |
887 | { | |
888 | struct saa7146_fh *fh = __fh; | |
1da177e4 | 889 | |
b960074f HV |
890 | if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) |
891 | return videobuf_reqbufs(&fh->video_q, b); | |
892 | if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
893 | return videobuf_reqbufs(&fh->vbi_q, b); | |
894 | return -EINVAL; | |
895 | } | |
1da177e4 | 896 | |
b960074f HV |
897 | static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) |
898 | { | |
899 | struct saa7146_fh *fh = __fh; | |
1da177e4 | 900 | |
b960074f HV |
901 | if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) |
902 | return videobuf_querybuf(&fh->video_q, buf); | |
903 | if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
904 | return videobuf_querybuf(&fh->vbi_q, buf); | |
905 | return -EINVAL; | |
906 | } | |
1da177e4 | 907 | |
b960074f HV |
908 | static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) |
909 | { | |
910 | struct saa7146_fh *fh = __fh; | |
911 | ||
912 | if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
913 | return videobuf_qbuf(&fh->video_q, buf); | |
914 | if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
915 | return videobuf_qbuf(&fh->vbi_q, buf); | |
916 | return -EINVAL; | |
917 | } | |
49ee718e | 918 | |
b960074f HV |
919 | static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) |
920 | { | |
921 | struct saa7146_fh *fh = __fh; | |
922 | ||
923 | if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
924 | return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK); | |
925 | if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
926 | return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK); | |
927 | return -EINVAL; | |
928 | } | |
929 | ||
930 | static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type) | |
931 | { | |
932 | struct saa7146_fh *fh = __fh; | |
933 | int err; | |
934 | ||
44d0b80e | 935 | DEB_D("VIDIOC_STREAMON, type:%d\n", type); |
b960074f HV |
936 | |
937 | err = video_begin(fh); | |
938 | if (err) | |
939 | return err; | |
940 | if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
941 | return videobuf_streamon(&fh->video_q); | |
942 | if (type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
943 | return videobuf_streamon(&fh->vbi_q); | |
944 | return -EINVAL; | |
945 | } | |
946 | ||
947 | static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type) | |
948 | { | |
949 | struct saa7146_fh *fh = __fh; | |
950 | struct saa7146_dev *dev = fh->dev; | |
951 | struct saa7146_vv *vv = dev->vv_data; | |
952 | int err; | |
953 | ||
44d0b80e | 954 | DEB_D("VIDIOC_STREAMOFF, type:%d\n", type); |
b960074f HV |
955 | |
956 | /* ugly: we need to copy some checks from video_end(), | |
957 | because videobuf_streamoff() relies on the capture running. | |
958 | check and fix this */ | |
959 | if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { | |
44d0b80e | 960 | DEB_S("not capturing\n"); |
1da177e4 LT |
961 | return 0; |
962 | } | |
b960074f HV |
963 | |
964 | if (vv->video_fh != fh) { | |
44d0b80e | 965 | DEB_S("capturing, but in another open\n"); |
b960074f | 966 | return -EBUSY; |
1da177e4 | 967 | } |
b960074f HV |
968 | |
969 | err = -EINVAL; | |
970 | if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
971 | err = videobuf_streamoff(&fh->video_q); | |
972 | else if (type == V4L2_BUF_TYPE_VBI_CAPTURE) | |
973 | err = videobuf_streamoff(&fh->vbi_q); | |
974 | if (0 != err) { | |
44d0b80e | 975 | DEB_D("warning: videobuf_streamoff() failed\n"); |
b960074f HV |
976 | video_end(fh, file); |
977 | } else { | |
978 | err = video_end(fh, file); | |
979 | } | |
980 | return err; | |
981 | } | |
982 | ||
1b8dac15 HV |
983 | static int vidioc_g_chip_ident(struct file *file, void *__fh, |
984 | struct v4l2_dbg_chip_ident *chip) | |
985 | { | |
986 | struct saa7146_fh *fh = __fh; | |
987 | struct saa7146_dev *dev = fh->dev; | |
988 | ||
989 | chip->ident = V4L2_IDENT_NONE; | |
990 | chip->revision = 0; | |
eae4d69b | 991 | if (chip->match.type == V4L2_CHIP_MATCH_HOST && !chip->match.addr) { |
1b8dac15 HV |
992 | chip->ident = V4L2_IDENT_SAA7146; |
993 | return 0; | |
994 | } | |
995 | return v4l2_device_call_until_err(&dev->v4l2_dev, 0, | |
996 | core, g_chip_ident, chip); | |
997 | } | |
998 | ||
b960074f HV |
999 | const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { |
1000 | .vidioc_querycap = vidioc_querycap, | |
1001 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, | |
717167e8 | 1002 | .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap, |
b960074f HV |
1003 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, |
1004 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, | |
1005 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, | |
1006 | .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, | |
1007 | .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, | |
1008 | .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, | |
1009 | .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, | |
1b8dac15 | 1010 | .vidioc_g_chip_ident = vidioc_g_chip_ident, |
b960074f HV |
1011 | |
1012 | .vidioc_overlay = vidioc_overlay, | |
1013 | .vidioc_g_fbuf = vidioc_g_fbuf, | |
1014 | .vidioc_s_fbuf = vidioc_s_fbuf, | |
1015 | .vidioc_reqbufs = vidioc_reqbufs, | |
1016 | .vidioc_querybuf = vidioc_querybuf, | |
1017 | .vidioc_qbuf = vidioc_qbuf, | |
1018 | .vidioc_dqbuf = vidioc_dqbuf, | |
1019 | .vidioc_g_std = vidioc_g_std, | |
1020 | .vidioc_s_std = vidioc_s_std, | |
b960074f HV |
1021 | .vidioc_streamon = vidioc_streamon, |
1022 | .vidioc_streamoff = vidioc_streamoff, | |
1023 | .vidioc_g_parm = vidioc_g_parm, | |
b960074f | 1024 | }; |
1da177e4 LT |
1025 | |
1026 | /*********************************************************************************/ | |
1027 | /* buffer handling functions */ | |
1028 | ||
1029 | static int buffer_activate (struct saa7146_dev *dev, | |
1030 | struct saa7146_buf *buf, | |
1031 | struct saa7146_buf *next) | |
1032 | { | |
1033 | struct saa7146_vv *vv = dev->vv_data; | |
1034 | ||
0fc0686e | 1035 | buf->vb.state = VIDEOBUF_ACTIVE; |
1da177e4 LT |
1036 | saa7146_set_capture(dev,buf,next); |
1037 | ||
9bb60193 | 1038 | mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT); |
1da177e4 LT |
1039 | return 0; |
1040 | } | |
1041 | ||
311c70e1 JF |
1042 | static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf) |
1043 | { | |
1044 | saa7146_pgtable_free(dev->pci, &buf->pt[0]); | |
1045 | saa7146_pgtable_free(dev->pci, &buf->pt[1]); | |
1046 | saa7146_pgtable_free(dev->pci, &buf->pt[2]); | |
1047 | } | |
1048 | ||
1da177e4 LT |
1049 | static int buffer_prepare(struct videobuf_queue *q, |
1050 | struct videobuf_buffer *vb, enum v4l2_field field) | |
1051 | { | |
1052 | struct file *file = q->priv_data; | |
1053 | struct saa7146_fh *fh = file->private_data; | |
1054 | struct saa7146_dev *dev = fh->dev; | |
1055 | struct saa7146_vv *vv = dev->vv_data; | |
1056 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | |
1057 | int size,err = 0; | |
1058 | ||
44d0b80e | 1059 | DEB_CAP("vbuf:%p\n", vb); |
1da177e4 LT |
1060 | |
1061 | /* sanity checks */ | |
fd74d6eb HV |
1062 | if (vv->video_fmt.width < 48 || |
1063 | vv->video_fmt.height < 32 || | |
1064 | vv->video_fmt.width > vv->standard->h_max_out || | |
1065 | vv->video_fmt.height > vv->standard->v_max_out) { | |
44d0b80e | 1066 | DEB_D("w (%d) / h (%d) out of bounds\n", |
fd74d6eb | 1067 | vv->video_fmt.width, vv->video_fmt.height); |
1da177e4 LT |
1068 | return -EINVAL; |
1069 | } | |
1070 | ||
fd74d6eb | 1071 | size = vv->video_fmt.sizeimage; |
1da177e4 | 1072 | if (0 != buf->vb.baddr && buf->vb.bsize < size) { |
44d0b80e | 1073 | DEB_D("size mismatch\n"); |
1da177e4 LT |
1074 | return -EINVAL; |
1075 | } | |
1076 | ||
44d0b80e | 1077 | DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", |
fd74d6eb HV |
1078 | vv->video_fmt.width, vv->video_fmt.height, |
1079 | size, v4l2_field_names[vv->video_fmt.field]); | |
1080 | if (buf->vb.width != vv->video_fmt.width || | |
1081 | buf->vb.bytesperline != vv->video_fmt.bytesperline || | |
1082 | buf->vb.height != vv->video_fmt.height || | |
1da177e4 LT |
1083 | buf->vb.size != size || |
1084 | buf->vb.field != field || | |
fd74d6eb HV |
1085 | buf->vb.field != vv->video_fmt.field || |
1086 | buf->fmt != &vv->video_fmt) { | |
c7b0ac05 | 1087 | saa7146_dma_free(dev,q,buf); |
1da177e4 LT |
1088 | } |
1089 | ||
0fc0686e | 1090 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { |
1da177e4 LT |
1091 | struct saa7146_format *sfmt; |
1092 | ||
fd74d6eb HV |
1093 | buf->vb.bytesperline = vv->video_fmt.bytesperline; |
1094 | buf->vb.width = vv->video_fmt.width; | |
1095 | buf->vb.height = vv->video_fmt.height; | |
1da177e4 LT |
1096 | buf->vb.size = size; |
1097 | buf->vb.field = field; | |
fd74d6eb HV |
1098 | buf->fmt = &vv->video_fmt; |
1099 | buf->vb.field = vv->video_fmt.field; | |
1da177e4 | 1100 | |
a757ee22 | 1101 | sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); |
1da177e4 | 1102 | |
311c70e1 | 1103 | release_all_pagetables(dev, buf); |
1da177e4 | 1104 | if( 0 != IS_PLANAR(sfmt->trans)) { |
1da177e4 LT |
1105 | saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); |
1106 | saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); | |
1107 | saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); | |
1108 | } else { | |
1da177e4 LT |
1109 | saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); |
1110 | } | |
1111 | ||
c7b0ac05 | 1112 | err = videobuf_iolock(q,&buf->vb, &vv->ov_fb); |
1da177e4 LT |
1113 | if (err) |
1114 | goto oops; | |
1115 | err = saa7146_pgtable_build(dev,buf); | |
1116 | if (err) | |
1117 | goto oops; | |
1118 | } | |
0fc0686e | 1119 | buf->vb.state = VIDEOBUF_PREPARED; |
1da177e4 LT |
1120 | buf->activate = buffer_activate; |
1121 | ||
1122 | return 0; | |
1123 | ||
1124 | oops: | |
44d0b80e | 1125 | DEB_D("error out\n"); |
c7b0ac05 | 1126 | saa7146_dma_free(dev,q,buf); |
1da177e4 LT |
1127 | |
1128 | return err; | |
1129 | } | |
1130 | ||
1131 | static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) | |
1132 | { | |
1133 | struct file *file = q->priv_data; | |
1134 | struct saa7146_fh *fh = file->private_data; | |
fd74d6eb | 1135 | struct saa7146_vv *vv = fh->dev->vv_data; |
1da177e4 LT |
1136 | |
1137 | if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) | |
1138 | *count = MAX_SAA7146_CAPTURE_BUFFERS; | |
1139 | ||
fd74d6eb | 1140 | *size = vv->video_fmt.sizeimage; |
1da177e4 LT |
1141 | |
1142 | /* check if we exceed the "max_memory" parameter */ | |
1143 | if( (*count * *size) > (max_memory*1048576) ) { | |
1144 | *count = (max_memory*1048576) / *size; | |
1145 | } | |
1146 | ||
44d0b80e | 1147 | DEB_CAP("%d buffers, %d bytes each\n", *count, *size); |
1da177e4 LT |
1148 | |
1149 | return 0; | |
1150 | } | |
1151 | ||
1152 | static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | |
1153 | { | |
1154 | struct file *file = q->priv_data; | |
1155 | struct saa7146_fh *fh = file->private_data; | |
1156 | struct saa7146_dev *dev = fh->dev; | |
1157 | struct saa7146_vv *vv = dev->vv_data; | |
1158 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | |
1159 | ||
44d0b80e | 1160 | DEB_CAP("vbuf:%p\n", vb); |
9bb60193 | 1161 | saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf); |
1da177e4 LT |
1162 | } |
1163 | ||
1da177e4 LT |
1164 | static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) |
1165 | { | |
1166 | struct file *file = q->priv_data; | |
1167 | struct saa7146_fh *fh = file->private_data; | |
1168 | struct saa7146_dev *dev = fh->dev; | |
1169 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | |
1170 | ||
44d0b80e | 1171 | DEB_CAP("vbuf:%p\n", vb); |
311c70e1 | 1172 | |
c7b0ac05 | 1173 | saa7146_dma_free(dev,q,buf); |
ba9e9f3c MCC |
1174 | |
1175 | release_all_pagetables(dev, buf); | |
1da177e4 LT |
1176 | } |
1177 | ||
1178 | static struct videobuf_queue_ops video_qops = { | |
1179 | .buf_setup = buffer_setup, | |
1180 | .buf_prepare = buffer_prepare, | |
1181 | .buf_queue = buffer_queue, | |
1182 | .buf_release = buffer_release, | |
1183 | }; | |
1184 | ||
1185 | /********************************************************************************/ | |
1186 | /* file operations */ | |
1187 | ||
1188 | static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) | |
1189 | { | |
9bb60193 | 1190 | INIT_LIST_HEAD(&vv->video_dmaq.queue); |
1da177e4 | 1191 | |
9bb60193 HV |
1192 | init_timer(&vv->video_dmaq.timeout); |
1193 | vv->video_dmaq.timeout.function = saa7146_buffer_timeout; | |
1194 | vv->video_dmaq.timeout.data = (unsigned long)(&vv->video_dmaq); | |
1195 | vv->video_dmaq.dev = dev; | |
1da177e4 LT |
1196 | |
1197 | /* set some default values */ | |
1198 | vv->standard = &dev->ext_vv_data->stds[0]; | |
1199 | ||
1200 | /* FIXME: what's this? */ | |
1201 | vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A; | |
1202 | vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A; | |
1203 | } | |
1204 | ||
1205 | ||
1206 | static int video_open(struct saa7146_dev *dev, struct file *file) | |
1207 | { | |
abf84383 | 1208 | struct saa7146_fh *fh = file->private_data; |
1da177e4 | 1209 | |
0705135e GL |
1210 | videobuf_queue_sg_init(&fh->video_q, &video_qops, |
1211 | &dev->pci->dev, &dev->slock, | |
1da177e4 LT |
1212 | V4L2_BUF_TYPE_VIDEO_CAPTURE, |
1213 | V4L2_FIELD_INTERLACED, | |
1214 | sizeof(struct saa7146_buf), | |
9af39713 | 1215 | file, &dev->v4l2_lock); |
1da177e4 | 1216 | |
1da177e4 LT |
1217 | return 0; |
1218 | } | |
1219 | ||
1220 | ||
1221 | static void video_close(struct saa7146_dev *dev, struct file *file) | |
1222 | { | |
abf84383 | 1223 | struct saa7146_fh *fh = file->private_data; |
1da177e4 | 1224 | struct saa7146_vv *vv = dev->vv_data; |
2970c492 | 1225 | struct videobuf_queue *q = &fh->video_q; |
1da177e4 | 1226 | |
7b4668ef HV |
1227 | if (IS_CAPTURE_ACTIVE(fh) != 0) |
1228 | video_end(fh, file); | |
1229 | else if (IS_OVERLAY_ACTIVE(fh) != 0) | |
1230 | saa7146_stop_preview(fh); | |
1da177e4 | 1231 | |
19bc5133 | 1232 | videobuf_stop(q); |
1da177e4 | 1233 | /* hmm, why is this function declared void? */ |
1da177e4 LT |
1234 | } |
1235 | ||
1236 | ||
1237 | static void video_irq_done(struct saa7146_dev *dev, unsigned long st) | |
1238 | { | |
1239 | struct saa7146_vv *vv = dev->vv_data; | |
9bb60193 | 1240 | struct saa7146_dmaqueue *q = &vv->video_dmaq; |
1da177e4 LT |
1241 | |
1242 | spin_lock(&dev->slock); | |
44d0b80e | 1243 | DEB_CAP("called\n"); |
1da177e4 LT |
1244 | |
1245 | /* only finish the buffer if we have one... */ | |
1246 | if( NULL != q->curr ) { | |
0fc0686e | 1247 | saa7146_buffer_finish(dev,q,VIDEOBUF_DONE); |
1da177e4 LT |
1248 | } |
1249 | saa7146_buffer_next(dev,q,0); | |
1250 | ||
1251 | spin_unlock(&dev->slock); | |
1252 | } | |
1253 | ||
1254 | static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | |
1255 | { | |
1256 | struct saa7146_fh *fh = file->private_data; | |
1257 | struct saa7146_dev *dev = fh->dev; | |
1258 | struct saa7146_vv *vv = dev->vv_data; | |
1259 | ssize_t ret = 0; | |
1260 | ||
44d0b80e | 1261 | DEB_EE("called\n"); |
1da177e4 LT |
1262 | |
1263 | if ((vv->video_status & STATUS_CAPTURE) != 0) { | |
1264 | /* fixme: should we allow read() captures while streaming capture? */ | |
1265 | if (vv->video_fh == fh) { | |
44d0b80e | 1266 | DEB_S("already capturing\n"); |
1da177e4 LT |
1267 | return -EBUSY; |
1268 | } | |
44d0b80e | 1269 | DEB_S("already capturing in another open\n"); |
1da177e4 LT |
1270 | return -EBUSY; |
1271 | } | |
1272 | ||
1273 | ret = video_begin(fh); | |
1274 | if( 0 != ret) { | |
1275 | goto out; | |
1276 | } | |
1277 | ||
1278 | ret = videobuf_read_one(&fh->video_q , data, count, ppos, | |
1279 | file->f_flags & O_NONBLOCK); | |
1280 | if (ret != 0) { | |
1281 | video_end(fh, file); | |
1282 | } else { | |
1283 | ret = video_end(fh, file); | |
1284 | } | |
1285 | out: | |
1286 | /* restart overlay if it was active before */ | |
1287 | if (vv->ov_suspend != NULL) { | |
1288 | saa7146_start_preview(vv->ov_suspend); | |
1289 | vv->ov_suspend = NULL; | |
1290 | } | |
1291 | ||
1292 | return ret; | |
1293 | } | |
1294 | ||
1295 | struct saa7146_use_ops saa7146_video_uops = { | |
1296 | .init = video_init, | |
1297 | .open = video_open, | |
1298 | .release = video_close, | |
1299 | .irq_done = video_irq_done, | |
1300 | .read = video_read, | |
1301 | }; |