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