1 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
2 | |
3 | #include <media/drv-intf/saa7146_vv.h> |
4 | #include <media/v4l2-event.h> |
5 | #include <media/v4l2-ctrls.h> |
6 | #include <linux/module.h> |
7 | #include <linux/kernel.h> |
8 | |
9 | /* format descriptions for capture and preview */ |
10 | static struct saa7146_format formats[] = { |
11 | { |
12 | .pixelformat = V4L2_PIX_FMT_RGB332, |
13 | .trans = RGB08_COMPOSED, |
14 | .depth = 8, |
15 | .flags = 0, |
16 | }, { |
17 | .pixelformat = V4L2_PIX_FMT_RGB565, |
18 | .trans = RGB16_COMPOSED, |
19 | .depth = 16, |
20 | .flags = 0, |
21 | }, { |
22 | .pixelformat = V4L2_PIX_FMT_BGR24, |
23 | .trans = RGB24_COMPOSED, |
24 | .depth = 24, |
25 | .flags = 0, |
26 | }, { |
27 | .pixelformat = V4L2_PIX_FMT_BGR32, |
28 | .trans = RGB32_COMPOSED, |
29 | .depth = 32, |
30 | .flags = 0, |
31 | }, { |
32 | .pixelformat = V4L2_PIX_FMT_RGB32, |
33 | .trans = RGB32_COMPOSED, |
34 | .depth = 32, |
35 | .flags = 0, |
36 | .swap = 0x2, |
37 | }, { |
38 | .pixelformat = V4L2_PIX_FMT_GREY, |
39 | .trans = Y8, |
40 | .depth = 8, |
41 | .flags = 0, |
42 | }, { |
43 | .pixelformat = V4L2_PIX_FMT_YUV422P, |
44 | .trans = YUV422_DECOMPOSED, |
45 | .depth = 16, |
46 | .flags = FORMAT_IS_PLANAR, |
47 | }, { |
48 | .pixelformat = V4L2_PIX_FMT_YVU420, |
49 | .trans = YUV420_DECOMPOSED, |
50 | .depth = 12, |
51 | .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, |
52 | }, { |
53 | .pixelformat = V4L2_PIX_FMT_YUV420, |
54 | .trans = YUV420_DECOMPOSED, |
55 | .depth = 12, |
56 | .flags = FORMAT_IS_PLANAR, |
57 | }, { |
58 | .pixelformat = V4L2_PIX_FMT_UYVY, |
59 | .trans = YUV422_COMPOSED, |
60 | .depth = 16, |
61 | .flags = 0, |
62 | } |
63 | }; |
64 | |
65 | /* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps. |
66 | due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped |
67 | (like V4L2_PIX_FMT_YUYV) ... 8-( */ |
68 | |
69 | struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc) |
70 | { |
71 | int i; |
72 | |
73 | for (i = 0; i < ARRAY_SIZE(formats); i++) { |
74 | if (formats[i].pixelformat == fourcc) { |
75 | return formats+i; |
76 | } |
77 | } |
78 | |
79 | DEB_D("unknown pixelformat:'%4.4s'\n" , (char *)&fourcc); |
80 | return NULL; |
81 | } |
82 | |
83 | /********************************************************************************/ |
84 | /* common pagetable functions */ |
85 | |
86 | static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf) |
87 | { |
88 | struct saa7146_vv *vv = dev->vv_data; |
89 | struct pci_dev *pci = dev->pci; |
90 | struct sg_table *sgt = vb2_dma_sg_plane_desc(vb: &buf->vb.vb2_buf, plane_no: 0); |
91 | struct scatterlist *list = sgt->sgl; |
92 | int length = sgt->nents; |
93 | struct v4l2_pix_format *pix = &vv->video_fmt; |
94 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, fourcc: pix->pixelformat); |
95 | |
96 | DEB_EE("dev:%p, buf:%p, sg_len:%d\n" , dev, buf, length); |
97 | |
98 | if( 0 != IS_PLANAR(sfmt->trans)) { |
99 | struct saa7146_pgtable *pt1 = &buf->pt[0]; |
100 | struct saa7146_pgtable *pt2 = &buf->pt[1]; |
101 | struct saa7146_pgtable *pt3 = &buf->pt[2]; |
102 | struct sg_dma_page_iter dma_iter; |
103 | __le32 *ptr1, *ptr2, *ptr3; |
104 | __le32 fill; |
105 | |
106 | int size = pix->width * pix->height; |
107 | int i, m1, m2, m3, o1, o2; |
108 | |
109 | switch( sfmt->depth ) { |
110 | case 12: { |
111 | /* create some offsets inside the page table */ |
112 | m1 = ((size + PAGE_SIZE) / PAGE_SIZE) - 1; |
113 | m2 = ((size + (size / 4) + PAGE_SIZE) / PAGE_SIZE) - 1; |
114 | m3 = ((size + (size / 2) + PAGE_SIZE) / PAGE_SIZE) - 1; |
115 | o1 = size % PAGE_SIZE; |
116 | o2 = (size + (size / 4)) % PAGE_SIZE; |
117 | DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n" , |
118 | size, m1, m2, m3, o1, o2); |
119 | break; |
120 | } |
121 | case 16: { |
122 | /* create some offsets inside the page table */ |
123 | m1 = ((size + PAGE_SIZE) / PAGE_SIZE) - 1; |
124 | m2 = ((size + (size / 2) + PAGE_SIZE) / PAGE_SIZE) - 1; |
125 | m3 = ((2 * size + PAGE_SIZE) / PAGE_SIZE) - 1; |
126 | o1 = size % PAGE_SIZE; |
127 | o2 = (size + (size / 2)) % PAGE_SIZE; |
128 | DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n" , |
129 | size, m1, m2, m3, o1, o2); |
130 | break; |
131 | } |
132 | default: { |
133 | return -1; |
134 | } |
135 | } |
136 | |
137 | ptr1 = pt1->cpu; |
138 | ptr2 = pt2->cpu; |
139 | ptr3 = pt3->cpu; |
140 | |
141 | for_each_sg_dma_page(list, &dma_iter, length, 0) |
142 | *ptr1++ = cpu_to_le32(sg_page_iter_dma_address(&dma_iter) - list->offset); |
143 | |
144 | /* if we have a user buffer, the first page may not be |
145 | aligned to a page boundary. */ |
146 | pt1->offset = sgt->sgl->offset; |
147 | pt2->offset = pt1->offset + o1; |
148 | pt3->offset = pt1->offset + o2; |
149 | |
150 | /* create video-dma2 page table */ |
151 | ptr1 = pt1->cpu; |
152 | for (i = m1; i <= m2; i++, ptr2++) |
153 | *ptr2 = ptr1[i]; |
154 | fill = *(ptr2 - 1); |
155 | for (; i < 1024; i++, ptr2++) |
156 | *ptr2 = fill; |
157 | /* create video-dma3 page table */ |
158 | ptr1 = pt1->cpu; |
159 | for (i = m2; i <= m3; i++, ptr3++) |
160 | *ptr3 = ptr1[i]; |
161 | fill = *(ptr3 - 1); |
162 | for (; i < 1024; i++, ptr3++) |
163 | *ptr3 = fill; |
164 | /* finally: finish up video-dma1 page table */ |
165 | ptr1 = pt1->cpu + m1; |
166 | fill = pt1->cpu[m1]; |
167 | for (i = m1; i < 1024; i++, ptr1++) |
168 | *ptr1 = fill; |
169 | } else { |
170 | struct saa7146_pgtable *pt = &buf->pt[0]; |
171 | |
172 | return saa7146_pgtable_build_single(pci, pt, list, length); |
173 | } |
174 | |
175 | return 0; |
176 | } |
177 | |
178 | |
179 | /********************************************************************************/ |
180 | /* file operations */ |
181 | |
182 | static int video_begin(struct saa7146_dev *dev) |
183 | { |
184 | struct saa7146_vv *vv = dev->vv_data; |
185 | struct saa7146_format *fmt = NULL; |
186 | unsigned int resource; |
187 | int ret = 0; |
188 | |
189 | DEB_EE("dev:%p\n" , dev); |
190 | |
191 | fmt = saa7146_format_by_fourcc(dev, fourcc: vv->video_fmt.pixelformat); |
192 | /* we need to have a valid format set here */ |
193 | if (!fmt) |
194 | return -EINVAL; |
195 | |
196 | if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { |
197 | resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; |
198 | } else { |
199 | resource = RESOURCE_DMA1_HPS; |
200 | } |
201 | |
202 | ret = saa7146_res_get(dev, bit: resource); |
203 | if (0 == ret) { |
204 | DEB_S("cannot get capture resource %d\n" , resource); |
205 | return -EBUSY; |
206 | } |
207 | |
208 | /* clear out beginning of streaming bit (rps register 0)*/ |
209 | saa7146_write(dev, MC2, MASK_27 ); |
210 | |
211 | /* enable rps0 irqs */ |
212 | SAA7146_IER_ENABLE(x: dev, MASK_27); |
213 | |
214 | return 0; |
215 | } |
216 | |
217 | static void video_end(struct saa7146_dev *dev) |
218 | { |
219 | struct saa7146_vv *vv = dev->vv_data; |
220 | struct saa7146_format *fmt = NULL; |
221 | unsigned long flags; |
222 | unsigned int resource; |
223 | u32 dmas = 0; |
224 | DEB_EE("dev:%p\n" , dev); |
225 | |
226 | fmt = saa7146_format_by_fourcc(dev, fourcc: vv->video_fmt.pixelformat); |
227 | /* we need to have a valid format set here */ |
228 | if (!fmt) |
229 | return; |
230 | |
231 | if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { |
232 | resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; |
233 | dmas = MASK_22 | MASK_21 | MASK_20; |
234 | } else { |
235 | resource = RESOURCE_DMA1_HPS; |
236 | dmas = MASK_22; |
237 | } |
238 | spin_lock_irqsave(&dev->slock,flags); |
239 | |
240 | /* disable rps0 */ |
241 | saa7146_write(dev, MC1, MASK_28); |
242 | |
243 | /* disable rps0 irqs */ |
244 | SAA7146_IER_DISABLE(x: dev, MASK_27); |
245 | |
246 | /* shut down all used video dma transfers */ |
247 | saa7146_write(dev, MC1, dmas); |
248 | |
249 | spin_unlock_irqrestore(lock: &dev->slock, flags); |
250 | |
251 | saa7146_res_free(dev, bits: resource); |
252 | } |
253 | |
254 | static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) |
255 | { |
256 | struct saa7146_dev *dev = video_drvdata(file); |
257 | |
258 | strscpy((char *)cap->driver, "saa7146 v4l2" , sizeof(cap->driver)); |
259 | strscpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); |
260 | cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | |
261 | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | |
262 | V4L2_CAP_DEVICE_CAPS; |
263 | cap->capabilities |= dev->ext_vv_data->capabilities; |
264 | return 0; |
265 | } |
266 | |
267 | static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) |
268 | { |
269 | if (f->index >= ARRAY_SIZE(formats)) |
270 | return -EINVAL; |
271 | f->pixelformat = formats[f->index].pixelformat; |
272 | return 0; |
273 | } |
274 | |
275 | int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) |
276 | { |
277 | struct saa7146_dev *dev = container_of(ctrl->handler, |
278 | struct saa7146_dev, ctrl_handler); |
279 | struct saa7146_vv *vv = dev->vv_data; |
280 | u32 val; |
281 | |
282 | switch (ctrl->id) { |
283 | case V4L2_CID_BRIGHTNESS: |
284 | val = saa7146_read(dev, BCS_CTRL); |
285 | val &= 0x00ffffff; |
286 | val |= (ctrl->val << 24); |
287 | saa7146_write(dev, BCS_CTRL, val); |
288 | saa7146_write(dev, MC2, MASK_22 | MASK_06); |
289 | break; |
290 | |
291 | case V4L2_CID_CONTRAST: |
292 | val = saa7146_read(dev, BCS_CTRL); |
293 | val &= 0xff00ffff; |
294 | val |= (ctrl->val << 16); |
295 | saa7146_write(dev, BCS_CTRL, val); |
296 | saa7146_write(dev, MC2, MASK_22 | MASK_06); |
297 | break; |
298 | |
299 | case V4L2_CID_SATURATION: |
300 | val = saa7146_read(dev, BCS_CTRL); |
301 | val &= 0xffffff00; |
302 | val |= (ctrl->val << 0); |
303 | saa7146_write(dev, BCS_CTRL, val); |
304 | saa7146_write(dev, MC2, MASK_22 | MASK_06); |
305 | break; |
306 | |
307 | case V4L2_CID_HFLIP: |
308 | /* fixme: we can support changing VFLIP and HFLIP here... */ |
309 | if (vb2_is_busy(q: &vv->video_dmaq.q)) |
310 | return -EBUSY; |
311 | vv->hflip = ctrl->val; |
312 | break; |
313 | |
314 | case V4L2_CID_VFLIP: |
315 | if (vb2_is_busy(q: &vv->video_dmaq.q)) |
316 | return -EBUSY; |
317 | vv->vflip = ctrl->val; |
318 | break; |
319 | |
320 | default: |
321 | return -EINVAL; |
322 | } |
323 | return 0; |
324 | } |
325 | |
326 | static int vidioc_g_parm(struct file *file, void *fh, |
327 | struct v4l2_streamparm *parm) |
328 | { |
329 | struct saa7146_dev *dev = video_drvdata(file); |
330 | struct saa7146_vv *vv = dev->vv_data; |
331 | |
332 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
333 | return -EINVAL; |
334 | parm->parm.capture.readbuffers = 1; |
335 | v4l2_video_std_frame_period(id: vv->standard->id, |
336 | frameperiod: &parm->parm.capture.timeperframe); |
337 | return 0; |
338 | } |
339 | |
340 | static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) |
341 | { |
342 | struct saa7146_dev *dev = video_drvdata(file); |
343 | struct saa7146_vv *vv = dev->vv_data; |
344 | |
345 | f->fmt.pix = vv->video_fmt; |
346 | return 0; |
347 | } |
348 | |
349 | static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) |
350 | { |
351 | struct saa7146_dev *dev = video_drvdata(file); |
352 | struct saa7146_vv *vv = dev->vv_data; |
353 | |
354 | f->fmt.vbi = vv->vbi_fmt; |
355 | return 0; |
356 | } |
357 | |
358 | static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) |
359 | { |
360 | struct saa7146_dev *dev = video_drvdata(file); |
361 | struct saa7146_vv *vv = dev->vv_data; |
362 | struct saa7146_format *fmt; |
363 | enum v4l2_field field; |
364 | int maxw, maxh; |
365 | int calc_bpl; |
366 | |
367 | DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n" , dev, fh); |
368 | |
369 | fmt = saa7146_format_by_fourcc(dev, fourcc: f->fmt.pix.pixelformat); |
370 | if (NULL == fmt) |
371 | return -EINVAL; |
372 | |
373 | field = f->fmt.pix.field; |
374 | maxw = vv->standard->h_max_out; |
375 | maxh = vv->standard->v_max_out; |
376 | |
377 | if (V4L2_FIELD_ANY == field) { |
378 | field = (f->fmt.pix.height > maxh / 2) |
379 | ? V4L2_FIELD_INTERLACED |
380 | : V4L2_FIELD_BOTTOM; |
381 | } |
382 | switch (field) { |
383 | case V4L2_FIELD_ALTERNATE: |
384 | case V4L2_FIELD_TOP: |
385 | case V4L2_FIELD_BOTTOM: |
386 | maxh = maxh / 2; |
387 | break; |
388 | default: |
389 | field = V4L2_FIELD_INTERLACED; |
390 | break; |
391 | } |
392 | |
393 | f->fmt.pix.field = field; |
394 | f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; |
395 | if (f->fmt.pix.width < 48) |
396 | f->fmt.pix.width = 48; |
397 | if (f->fmt.pix.height < 32) |
398 | f->fmt.pix.height = 32; |
399 | if (f->fmt.pix.width > maxw) |
400 | f->fmt.pix.width = maxw; |
401 | if (f->fmt.pix.height > maxh) |
402 | f->fmt.pix.height = maxh; |
403 | |
404 | calc_bpl = (f->fmt.pix.width * fmt->depth) / 8; |
405 | |
406 | if (f->fmt.pix.bytesperline < calc_bpl) |
407 | f->fmt.pix.bytesperline = calc_bpl; |
408 | |
409 | if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */ |
410 | f->fmt.pix.bytesperline = calc_bpl; |
411 | |
412 | f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; |
413 | DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n" , |
414 | f->fmt.pix.width, f->fmt.pix.height, |
415 | f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); |
416 | |
417 | return 0; |
418 | } |
419 | |
420 | static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) |
421 | { |
422 | struct saa7146_dev *dev = video_drvdata(file); |
423 | struct saa7146_vv *vv = dev->vv_data; |
424 | int err; |
425 | |
426 | DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p\n" , dev); |
427 | if (vb2_is_busy(q: &vv->video_dmaq.q)) { |
428 | DEB_EE("streaming capture is active\n" ); |
429 | return -EBUSY; |
430 | } |
431 | err = vidioc_try_fmt_vid_cap(file, fh, f); |
432 | if (0 != err) |
433 | return err; |
434 | switch (f->fmt.pix.field) { |
435 | case V4L2_FIELD_ALTERNATE: |
436 | vv->last_field = V4L2_FIELD_TOP; |
437 | break; |
438 | default: |
439 | vv->last_field = V4L2_FIELD_INTERLACED; |
440 | break; |
441 | } |
442 | vv->video_fmt = f->fmt.pix; |
443 | DEB_EE("set to pixelformat '%4.4s'\n" , |
444 | (char *)&vv->video_fmt.pixelformat); |
445 | return 0; |
446 | } |
447 | |
448 | static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) |
449 | { |
450 | struct saa7146_dev *dev = video_drvdata(file); |
451 | struct saa7146_vv *vv = dev->vv_data; |
452 | |
453 | *norm = vv->standard->id; |
454 | return 0; |
455 | } |
456 | |
457 | static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) |
458 | { |
459 | struct saa7146_dev *dev = video_drvdata(file); |
460 | struct saa7146_vv *vv = dev->vv_data; |
461 | int found = 0; |
462 | int i; |
463 | |
464 | DEB_EE("VIDIOC_S_STD\n" ); |
465 | |
466 | for (i = 0; i < dev->ext_vv_data->num_stds; i++) |
467 | if (id & dev->ext_vv_data->stds[i].id) |
468 | break; |
469 | |
470 | if (i != dev->ext_vv_data->num_stds && |
471 | vv->standard == &dev->ext_vv_data->stds[i]) |
472 | return 0; |
473 | |
474 | if (vb2_is_busy(q: &vv->video_dmaq.q) || vb2_is_busy(q: &vv->vbi_dmaq.q)) { |
475 | DEB_D("cannot change video standard while streaming capture is active\n" ); |
476 | return -EBUSY; |
477 | } |
478 | |
479 | if (i != dev->ext_vv_data->num_stds) { |
480 | vv->standard = &dev->ext_vv_data->stds[i]; |
481 | if (NULL != dev->ext_vv_data->std_callback) |
482 | dev->ext_vv_data->std_callback(dev, vv->standard); |
483 | found = 1; |
484 | } |
485 | |
486 | if (!found) { |
487 | DEB_EE("VIDIOC_S_STD: standard not found\n" ); |
488 | return -EINVAL; |
489 | } |
490 | |
491 | DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n" , vv->standard->name); |
492 | return 0; |
493 | } |
494 | |
495 | const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { |
496 | .vidioc_querycap = vidioc_querycap, |
497 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, |
498 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, |
499 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, |
500 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, |
501 | .vidioc_g_std = vidioc_g_std, |
502 | .vidioc_s_std = vidioc_s_std, |
503 | .vidioc_g_parm = vidioc_g_parm, |
504 | .vidioc_reqbufs = vb2_ioctl_reqbufs, |
505 | .vidioc_create_bufs = vb2_ioctl_create_bufs, |
506 | .vidioc_querybuf = vb2_ioctl_querybuf, |
507 | .vidioc_qbuf = vb2_ioctl_qbuf, |
508 | .vidioc_dqbuf = vb2_ioctl_dqbuf, |
509 | .vidioc_streamon = vb2_ioctl_streamon, |
510 | .vidioc_streamoff = vb2_ioctl_streamoff, |
511 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, |
512 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
513 | }; |
514 | |
515 | const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = { |
516 | .vidioc_querycap = vidioc_querycap, |
517 | .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, |
518 | .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, |
519 | .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, |
520 | .vidioc_g_std = vidioc_g_std, |
521 | .vidioc_s_std = vidioc_s_std, |
522 | .vidioc_g_parm = vidioc_g_parm, |
523 | .vidioc_reqbufs = vb2_ioctl_reqbufs, |
524 | .vidioc_create_bufs = vb2_ioctl_create_bufs, |
525 | .vidioc_querybuf = vb2_ioctl_querybuf, |
526 | .vidioc_qbuf = vb2_ioctl_qbuf, |
527 | .vidioc_dqbuf = vb2_ioctl_dqbuf, |
528 | .vidioc_streamon = vb2_ioctl_streamon, |
529 | .vidioc_streamoff = vb2_ioctl_streamoff, |
530 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, |
531 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
532 | }; |
533 | |
534 | /*********************************************************************************/ |
535 | /* buffer handling functions */ |
536 | |
537 | static int buffer_activate (struct saa7146_dev *dev, |
538 | struct saa7146_buf *buf, |
539 | struct saa7146_buf *next) |
540 | { |
541 | struct saa7146_vv *vv = dev->vv_data; |
542 | |
543 | saa7146_set_capture(dev,buf,next); |
544 | |
545 | mod_timer(timer: &vv->video_dmaq.timeout, expires: jiffies+BUFFER_TIMEOUT); |
546 | return 0; |
547 | } |
548 | |
549 | static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf) |
550 | { |
551 | saa7146_pgtable_free(pci: dev->pci, pt: &buf->pt[0]); |
552 | saa7146_pgtable_free(pci: dev->pci, pt: &buf->pt[1]); |
553 | saa7146_pgtable_free(pci: dev->pci, pt: &buf->pt[2]); |
554 | } |
555 | |
556 | static int queue_setup(struct vb2_queue *q, |
557 | unsigned int *num_buffers, unsigned int *num_planes, |
558 | unsigned int sizes[], struct device *alloc_devs[]) |
559 | { |
560 | struct saa7146_dev *dev = vb2_get_drv_priv(q); |
561 | unsigned int size = dev->vv_data->video_fmt.sizeimage; |
562 | |
563 | if (*num_planes) |
564 | return sizes[0] < size ? -EINVAL : 0; |
565 | *num_planes = 1; |
566 | sizes[0] = size; |
567 | |
568 | return 0; |
569 | } |
570 | |
571 | static void buf_queue(struct vb2_buffer *vb) |
572 | { |
573 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
574 | struct vb2_queue *vq = vb->vb2_queue; |
575 | struct saa7146_dev *dev = vb2_get_drv_priv(q: vq); |
576 | struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb); |
577 | unsigned long flags; |
578 | |
579 | spin_lock_irqsave(&dev->slock, flags); |
580 | |
581 | saa7146_buffer_queue(dev, q: &dev->vv_data->video_dmaq, buf); |
582 | spin_unlock_irqrestore(lock: &dev->slock, flags); |
583 | } |
584 | |
585 | static int buf_init(struct vb2_buffer *vb) |
586 | { |
587 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
588 | struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb); |
589 | struct vb2_queue *vq = vb->vb2_queue; |
590 | struct saa7146_dev *dev = vb2_get_drv_priv(q: vq); |
591 | struct saa7146_vv *vv = dev->vv_data; |
592 | struct saa7146_format *sfmt; |
593 | int ret; |
594 | |
595 | buf->activate = buffer_activate; |
596 | sfmt = saa7146_format_by_fourcc(dev, fourcc: vv->video_fmt.pixelformat); |
597 | |
598 | if (IS_PLANAR(sfmt->trans)) { |
599 | saa7146_pgtable_alloc(pci: dev->pci, pt: &buf->pt[0]); |
600 | saa7146_pgtable_alloc(pci: dev->pci, pt: &buf->pt[1]); |
601 | saa7146_pgtable_alloc(pci: dev->pci, pt: &buf->pt[2]); |
602 | } else { |
603 | saa7146_pgtable_alloc(pci: dev->pci, pt: &buf->pt[0]); |
604 | } |
605 | |
606 | ret = saa7146_pgtable_build(dev, buf); |
607 | if (ret) |
608 | release_all_pagetables(dev, buf); |
609 | return ret; |
610 | } |
611 | |
612 | static int buf_prepare(struct vb2_buffer *vb) |
613 | { |
614 | struct vb2_queue *vq = vb->vb2_queue; |
615 | struct saa7146_dev *dev = vb2_get_drv_priv(q: vq); |
616 | struct saa7146_vv *vv = dev->vv_data; |
617 | unsigned int size = vv->video_fmt.sizeimage; |
618 | |
619 | if (vb2_plane_size(vb, plane_no: 0) < size) |
620 | return -EINVAL; |
621 | vb2_set_plane_payload(vb, plane_no: 0, size); |
622 | return 0; |
623 | } |
624 | |
625 | static void buf_cleanup(struct vb2_buffer *vb) |
626 | { |
627 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
628 | struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb); |
629 | struct vb2_queue *vq = vb->vb2_queue; |
630 | struct saa7146_dev *dev = vb2_get_drv_priv(q: vq); |
631 | |
632 | release_all_pagetables(dev, buf); |
633 | } |
634 | |
635 | static void return_buffers(struct vb2_queue *q, int state) |
636 | { |
637 | struct saa7146_dev *dev = vb2_get_drv_priv(q); |
638 | struct saa7146_dmaqueue *dq = &dev->vv_data->video_dmaq; |
639 | struct saa7146_buf *buf; |
640 | |
641 | if (dq->curr) { |
642 | buf = dq->curr; |
643 | dq->curr = NULL; |
644 | vb2_buffer_done(vb: &buf->vb.vb2_buf, state); |
645 | } |
646 | while (!list_empty(head: &dq->queue)) { |
647 | buf = list_entry(dq->queue.next, struct saa7146_buf, list); |
648 | list_del(entry: &buf->list); |
649 | vb2_buffer_done(vb: &buf->vb.vb2_buf, state); |
650 | } |
651 | } |
652 | |
653 | static int start_streaming(struct vb2_queue *q, unsigned int count) |
654 | { |
655 | struct saa7146_dev *dev = vb2_get_drv_priv(q); |
656 | int ret; |
657 | |
658 | if (!vb2_is_streaming(q: &dev->vv_data->video_dmaq.q)) |
659 | dev->vv_data->seqnr = 0; |
660 | ret = video_begin(dev); |
661 | if (ret) |
662 | return_buffers(q, state: VB2_BUF_STATE_QUEUED); |
663 | return ret; |
664 | } |
665 | |
666 | static void stop_streaming(struct vb2_queue *q) |
667 | { |
668 | struct saa7146_dev *dev = vb2_get_drv_priv(q); |
669 | struct saa7146_dmaqueue *dq = &dev->vv_data->video_dmaq; |
670 | |
671 | del_timer(timer: &dq->timeout); |
672 | video_end(dev); |
673 | return_buffers(q, state: VB2_BUF_STATE_ERROR); |
674 | } |
675 | |
676 | const struct vb2_ops video_qops = { |
677 | .queue_setup = queue_setup, |
678 | .buf_queue = buf_queue, |
679 | .buf_init = buf_init, |
680 | .buf_prepare = buf_prepare, |
681 | .buf_cleanup = buf_cleanup, |
682 | .start_streaming = start_streaming, |
683 | .stop_streaming = stop_streaming, |
684 | .wait_prepare = vb2_ops_wait_prepare, |
685 | .wait_finish = vb2_ops_wait_finish, |
686 | }; |
687 | |
688 | /********************************************************************************/ |
689 | /* file operations */ |
690 | |
691 | static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) |
692 | { |
693 | INIT_LIST_HEAD(list: &vv->video_dmaq.queue); |
694 | |
695 | timer_setup(&vv->video_dmaq.timeout, saa7146_buffer_timeout, 0); |
696 | vv->video_dmaq.dev = dev; |
697 | |
698 | /* set some default values */ |
699 | vv->standard = &dev->ext_vv_data->stds[0]; |
700 | |
701 | /* FIXME: what's this? */ |
702 | vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A; |
703 | vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A; |
704 | } |
705 | |
706 | static void video_irq_done(struct saa7146_dev *dev, unsigned long st) |
707 | { |
708 | struct saa7146_vv *vv = dev->vv_data; |
709 | struct saa7146_dmaqueue *q = &vv->video_dmaq; |
710 | |
711 | spin_lock(lock: &dev->slock); |
712 | DEB_CAP("called\n" ); |
713 | |
714 | /* only finish the buffer if we have one... */ |
715 | if (q->curr) |
716 | saa7146_buffer_finish(dev, q, state: VB2_BUF_STATE_DONE); |
717 | saa7146_buffer_next(dev,q,vbi: 0); |
718 | |
719 | spin_unlock(lock: &dev->slock); |
720 | } |
721 | |
722 | const struct saa7146_use_ops saa7146_video_uops = { |
723 | .init = video_init, |
724 | .irq_done = video_irq_done, |
725 | }; |
726 | |