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