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