1 | /* |
2 | * omap_vout.c |
3 | * |
4 | * Copyright (C) 2005-2010 Texas Instruments. |
5 | * |
6 | * This file is licensed under the terms of the GNU General Public License |
7 | * version 2. This program is licensed "as is" without any warranty of any |
8 | * kind, whether express or implied. |
9 | * |
10 | * Leveraged code from the OMAP2 camera driver |
11 | * Video-for-Linux (Version 2) camera capture driver for |
12 | * the OMAP24xx camera controller. |
13 | * |
14 | * Author: Andy Lowe (source@mvista.com) |
15 | * |
16 | * Copyright (C) 2004 MontaVista Software, Inc. |
17 | * Copyright (C) 2010 Texas Instruments. |
18 | * |
19 | * History: |
20 | * 20-APR-2006 Khasim Modified VRFB based Rotation, |
21 | * The image data is always read from 0 degree |
22 | * view and written |
23 | * to the virtual space of desired rotation angle |
24 | * 4-DEC-2006 Jian Changed to support better memory management |
25 | * |
26 | * 17-Nov-2008 Hardik Changed driver to use video_ioctl2 |
27 | * |
28 | * 23-Feb-2010 Vaibhav H Modified to use new DSS2 interface |
29 | * |
30 | */ |
31 | |
32 | #include <linux/init.h> |
33 | #include <linux/module.h> |
34 | #include <linux/vmalloc.h> |
35 | #include <linux/sched.h> |
36 | #include <linux/types.h> |
37 | #include <linux/platform_device.h> |
38 | #include <linux/irq.h> |
39 | #include <linux/videodev2.h> |
40 | #include <linux/dma-mapping.h> |
41 | #include <linux/slab.h> |
42 | |
43 | #include <media/v4l2-device.h> |
44 | #include <media/v4l2-ioctl.h> |
45 | #include <media/v4l2-event.h> |
46 | |
47 | #include <video/omapvrfb.h> |
48 | #include <video/omapfb_dss.h> |
49 | |
50 | #include "omap_voutlib.h" |
51 | #include "omap_voutdef.h" |
52 | #include "omap_vout_vrfb.h" |
53 | |
54 | MODULE_AUTHOR("Texas Instruments" ); |
55 | MODULE_DESCRIPTION("OMAP Video for Linux Video out driver" ); |
56 | MODULE_LICENSE("GPL" ); |
57 | |
58 | /* Driver Configuration macros */ |
59 | #define VOUT_NAME "omap_vout" |
60 | |
61 | enum omap_vout_channels { |
62 | OMAP_VIDEO1, |
63 | OMAP_VIDEO2, |
64 | }; |
65 | |
66 | /* Variables configurable through module params*/ |
67 | static bool vid1_static_vrfb_alloc; |
68 | static bool vid2_static_vrfb_alloc; |
69 | static bool debug; |
70 | |
71 | /* Module parameters */ |
72 | module_param(vid1_static_vrfb_alloc, bool, S_IRUGO); |
73 | MODULE_PARM_DESC(vid1_static_vrfb_alloc, |
74 | "Static allocation of the VRFB buffer for video1 device" ); |
75 | |
76 | module_param(vid2_static_vrfb_alloc, bool, S_IRUGO); |
77 | MODULE_PARM_DESC(vid2_static_vrfb_alloc, |
78 | "Static allocation of the VRFB buffer for video2 device" ); |
79 | |
80 | module_param(debug, bool, S_IRUGO); |
81 | MODULE_PARM_DESC(debug, "Debug level (0-1)" ); |
82 | |
83 | /* list of image formats supported by OMAP2 video pipelines */ |
84 | static const struct v4l2_fmtdesc omap_formats[] = { |
85 | { |
86 | /* Note: V4L2 defines RGB565 as: |
87 | * |
88 | * Byte 0 Byte 1 |
89 | * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 |
90 | * |
91 | * We interpret RGB565 as: |
92 | * |
93 | * Byte 0 Byte 1 |
94 | * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 |
95 | */ |
96 | .pixelformat = V4L2_PIX_FMT_RGB565, |
97 | }, |
98 | { |
99 | /* Note: V4L2 defines RGB32 as: RGB-8-8-8-8 we use |
100 | * this for RGB24 unpack mode, the last 8 bits are ignored |
101 | * */ |
102 | .pixelformat = V4L2_PIX_FMT_RGB32, |
103 | }, |
104 | { |
105 | /* Note: V4L2 defines RGB24 as: RGB-8-8-8 we use |
106 | * this for RGB24 packed mode |
107 | * |
108 | */ |
109 | .pixelformat = V4L2_PIX_FMT_RGB24, |
110 | }, |
111 | { |
112 | .pixelformat = V4L2_PIX_FMT_YUYV, |
113 | }, |
114 | { |
115 | .pixelformat = V4L2_PIX_FMT_UYVY, |
116 | }, |
117 | }; |
118 | |
119 | #define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats)) |
120 | |
121 | /* |
122 | * Try format |
123 | */ |
124 | static int omap_vout_try_format(struct v4l2_pix_format *pix) |
125 | { |
126 | int ifmt, bpp = 0; |
127 | |
128 | pix->height = clamp(pix->height, (u32)VID_MIN_HEIGHT, |
129 | (u32)VID_MAX_HEIGHT); |
130 | pix->width = clamp(pix->width, (u32)VID_MIN_WIDTH, (u32)VID_MAX_WIDTH); |
131 | |
132 | for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++) { |
133 | if (pix->pixelformat == omap_formats[ifmt].pixelformat) |
134 | break; |
135 | } |
136 | |
137 | if (ifmt == NUM_OUTPUT_FORMATS) |
138 | ifmt = 0; |
139 | |
140 | pix->pixelformat = omap_formats[ifmt].pixelformat; |
141 | pix->field = V4L2_FIELD_NONE; |
142 | |
143 | switch (pix->pixelformat) { |
144 | case V4L2_PIX_FMT_YUYV: |
145 | case V4L2_PIX_FMT_UYVY: |
146 | default: |
147 | pix->colorspace = V4L2_COLORSPACE_SRGB; |
148 | bpp = YUYV_BPP; |
149 | break; |
150 | case V4L2_PIX_FMT_RGB565: |
151 | case V4L2_PIX_FMT_RGB565X: |
152 | pix->colorspace = V4L2_COLORSPACE_SRGB; |
153 | bpp = RGB565_BPP; |
154 | break; |
155 | case V4L2_PIX_FMT_RGB24: |
156 | pix->colorspace = V4L2_COLORSPACE_SRGB; |
157 | bpp = RGB24_BPP; |
158 | break; |
159 | case V4L2_PIX_FMT_RGB32: |
160 | case V4L2_PIX_FMT_BGR32: |
161 | pix->colorspace = V4L2_COLORSPACE_SRGB; |
162 | bpp = RGB32_BPP; |
163 | break; |
164 | } |
165 | pix->bytesperline = pix->width * bpp; |
166 | pix->sizeimage = pix->bytesperline * pix->height; |
167 | |
168 | return bpp; |
169 | } |
170 | |
171 | /* |
172 | * Convert V4L2 rotation to DSS rotation |
173 | * V4L2 understand 0, 90, 180, 270. |
174 | * Convert to 0, 1, 2 and 3 respectively for DSS |
175 | */ |
176 | static int v4l2_rot_to_dss_rot(int v4l2_rotation, |
177 | enum dss_rotation *rotation, bool mirror) |
178 | { |
179 | int ret = 0; |
180 | |
181 | switch (v4l2_rotation) { |
182 | case 90: |
183 | *rotation = dss_rotation_90_degree; |
184 | break; |
185 | case 180: |
186 | *rotation = dss_rotation_180_degree; |
187 | break; |
188 | case 270: |
189 | *rotation = dss_rotation_270_degree; |
190 | break; |
191 | case 0: |
192 | *rotation = dss_rotation_0_degree; |
193 | break; |
194 | default: |
195 | ret = -EINVAL; |
196 | } |
197 | return ret; |
198 | } |
199 | |
200 | static int omap_vout_calculate_offset(struct omap_vout_device *vout) |
201 | { |
202 | struct omapvideo_info *ovid; |
203 | struct v4l2_rect *crop = &vout->crop; |
204 | struct v4l2_pix_format *pix = &vout->pix; |
205 | int *cropped_offset = &vout->cropped_offset; |
206 | int ps = 2, line_length = 0; |
207 | |
208 | ovid = &vout->vid_info; |
209 | |
210 | if (ovid->rotation_type == VOUT_ROT_VRFB) { |
211 | omap_vout_calculate_vrfb_offset(vout); |
212 | } else { |
213 | vout->line_length = line_length = pix->width; |
214 | |
215 | if (V4L2_PIX_FMT_YUYV == pix->pixelformat || |
216 | V4L2_PIX_FMT_UYVY == pix->pixelformat) |
217 | ps = 2; |
218 | else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) |
219 | ps = 4; |
220 | else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) |
221 | ps = 3; |
222 | |
223 | vout->ps = ps; |
224 | |
225 | *cropped_offset = (line_length * ps) * |
226 | crop->top + crop->left * ps; |
227 | } |
228 | |
229 | v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "%s Offset:%x\n" , |
230 | __func__, vout->cropped_offset); |
231 | |
232 | return 0; |
233 | } |
234 | |
235 | /* |
236 | * Convert V4L2 pixel format to DSS pixel format |
237 | */ |
238 | static int video_mode_to_dss_mode(struct omap_vout_device *vout) |
239 | { |
240 | struct omap_overlay *ovl; |
241 | struct omapvideo_info *ovid; |
242 | struct v4l2_pix_format *pix = &vout->pix; |
243 | enum omap_color_mode mode; |
244 | |
245 | ovid = &vout->vid_info; |
246 | ovl = ovid->overlays[0]; |
247 | |
248 | switch (pix->pixelformat) { |
249 | case V4L2_PIX_FMT_YUYV: |
250 | mode = OMAP_DSS_COLOR_YUV2; |
251 | break; |
252 | case V4L2_PIX_FMT_UYVY: |
253 | mode = OMAP_DSS_COLOR_UYVY; |
254 | break; |
255 | case V4L2_PIX_FMT_RGB565: |
256 | mode = OMAP_DSS_COLOR_RGB16; |
257 | break; |
258 | case V4L2_PIX_FMT_RGB24: |
259 | mode = OMAP_DSS_COLOR_RGB24P; |
260 | break; |
261 | case V4L2_PIX_FMT_RGB32: |
262 | mode = (ovl->id == OMAP_DSS_VIDEO1) ? |
263 | OMAP_DSS_COLOR_RGB24U : OMAP_DSS_COLOR_ARGB32; |
264 | break; |
265 | case V4L2_PIX_FMT_BGR32: |
266 | mode = OMAP_DSS_COLOR_RGBX32; |
267 | break; |
268 | default: |
269 | mode = -EINVAL; |
270 | break; |
271 | } |
272 | return mode; |
273 | } |
274 | |
275 | /* |
276 | * Setup the overlay |
277 | */ |
278 | static int omapvid_setup_overlay(struct omap_vout_device *vout, |
279 | struct omap_overlay *ovl, int posx, int posy, int outw, |
280 | int outh, dma_addr_t addr) |
281 | { |
282 | int ret = 0; |
283 | struct omap_overlay_info info; |
284 | int cropheight, cropwidth, pixwidth; |
285 | |
286 | if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 && |
287 | (outw != vout->pix.width || outh != vout->pix.height)) { |
288 | ret = -EINVAL; |
289 | goto setup_ovl_err; |
290 | } |
291 | |
292 | vout->dss_mode = video_mode_to_dss_mode(vout); |
293 | if (vout->dss_mode == -EINVAL) { |
294 | ret = -EINVAL; |
295 | goto setup_ovl_err; |
296 | } |
297 | |
298 | /* Setup the input plane parameters according to |
299 | * rotation value selected. |
300 | */ |
301 | if (is_rotation_90_or_270(vout)) { |
302 | cropheight = vout->crop.width; |
303 | cropwidth = vout->crop.height; |
304 | pixwidth = vout->pix.height; |
305 | } else { |
306 | cropheight = vout->crop.height; |
307 | cropwidth = vout->crop.width; |
308 | pixwidth = vout->pix.width; |
309 | } |
310 | |
311 | ovl->get_overlay_info(ovl, &info); |
312 | info.paddr = addr; |
313 | info.width = cropwidth; |
314 | info.height = cropheight; |
315 | info.color_mode = vout->dss_mode; |
316 | info.mirror = vout->mirror; |
317 | info.pos_x = posx; |
318 | info.pos_y = posy; |
319 | info.out_width = outw; |
320 | info.out_height = outh; |
321 | info.global_alpha = vout->win.global_alpha; |
322 | if (!is_rotation_enabled(vout)) { |
323 | info.rotation = 0; |
324 | info.rotation_type = OMAP_DSS_ROT_DMA; |
325 | info.screen_width = pixwidth; |
326 | } else { |
327 | info.rotation = vout->rotation; |
328 | info.rotation_type = OMAP_DSS_ROT_VRFB; |
329 | info.screen_width = 2048; |
330 | } |
331 | |
332 | v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, |
333 | "%s enable=%d addr=%pad width=%d\n height=%d color_mode=%d\n" |
334 | "rotation=%d mirror=%d posx=%d posy=%d out_width = %d \n" |
335 | "out_height=%d rotation_type=%d screen_width=%d\n" , __func__, |
336 | ovl->is_enabled(ovl), &info.paddr, info.width, info.height, |
337 | info.color_mode, info.rotation, info.mirror, info.pos_x, |
338 | info.pos_y, info.out_width, info.out_height, info.rotation_type, |
339 | info.screen_width); |
340 | |
341 | ret = ovl->set_overlay_info(ovl, &info); |
342 | if (ret) |
343 | goto setup_ovl_err; |
344 | |
345 | return 0; |
346 | |
347 | setup_ovl_err: |
348 | v4l2_warn(&vout->vid_dev->v4l2_dev, "setup_overlay failed\n" ); |
349 | return ret; |
350 | } |
351 | |
352 | /* |
353 | * Initialize the overlay structure |
354 | */ |
355 | static int omapvid_init(struct omap_vout_device *vout, dma_addr_t addr) |
356 | { |
357 | int ret = 0, i; |
358 | struct v4l2_window *win; |
359 | struct omap_overlay *ovl; |
360 | int posx, posy, outw, outh; |
361 | struct omap_video_timings *timing; |
362 | struct omapvideo_info *ovid = &vout->vid_info; |
363 | |
364 | win = &vout->win; |
365 | for (i = 0; i < ovid->num_overlays; i++) { |
366 | struct omap_dss_device *dssdev; |
367 | |
368 | ovl = ovid->overlays[i]; |
369 | dssdev = ovl->get_device(ovl); |
370 | |
371 | if (!dssdev) |
372 | return -EINVAL; |
373 | |
374 | timing = &dssdev->panel.timings; |
375 | |
376 | outw = win->w.width; |
377 | outh = win->w.height; |
378 | switch (vout->rotation) { |
379 | case dss_rotation_90_degree: |
380 | /* Invert the height and width for 90 |
381 | * and 270 degree rotation |
382 | */ |
383 | swap(outw, outh); |
384 | posy = (timing->y_res - win->w.width) - win->w.left; |
385 | posx = win->w.top; |
386 | break; |
387 | |
388 | case dss_rotation_180_degree: |
389 | posx = (timing->x_res - win->w.width) - win->w.left; |
390 | posy = (timing->y_res - win->w.height) - win->w.top; |
391 | break; |
392 | |
393 | case dss_rotation_270_degree: |
394 | swap(outw, outh); |
395 | posy = win->w.left; |
396 | posx = (timing->x_res - win->w.height) - win->w.top; |
397 | break; |
398 | |
399 | default: |
400 | posx = win->w.left; |
401 | posy = win->w.top; |
402 | break; |
403 | } |
404 | |
405 | ret = omapvid_setup_overlay(vout, ovl, posx, posy, |
406 | outw, outh, addr); |
407 | if (ret) |
408 | goto omapvid_init_err; |
409 | } |
410 | return 0; |
411 | |
412 | omapvid_init_err: |
413 | v4l2_warn(&vout->vid_dev->v4l2_dev, "apply_changes failed\n" ); |
414 | return ret; |
415 | } |
416 | |
417 | /* |
418 | * Apply the changes set the go bit of DSS |
419 | */ |
420 | static int omapvid_apply_changes(struct omap_vout_device *vout) |
421 | { |
422 | int i; |
423 | struct omap_overlay *ovl; |
424 | struct omapvideo_info *ovid = &vout->vid_info; |
425 | |
426 | for (i = 0; i < ovid->num_overlays; i++) { |
427 | struct omap_dss_device *dssdev; |
428 | |
429 | ovl = ovid->overlays[i]; |
430 | dssdev = ovl->get_device(ovl); |
431 | if (!dssdev) |
432 | return -EINVAL; |
433 | ovl->manager->apply(ovl->manager); |
434 | } |
435 | |
436 | return 0; |
437 | } |
438 | |
439 | static int omapvid_handle_interlace_display(struct omap_vout_device *vout, |
440 | unsigned int irqstatus, u64 ts) |
441 | { |
442 | u32 fid; |
443 | |
444 | if (vout->first_int) { |
445 | vout->first_int = 0; |
446 | goto err; |
447 | } |
448 | |
449 | if (irqstatus & DISPC_IRQ_EVSYNC_ODD) |
450 | fid = 1; |
451 | else if (irqstatus & DISPC_IRQ_EVSYNC_EVEN) |
452 | fid = 0; |
453 | else |
454 | goto err; |
455 | |
456 | vout->field_id ^= 1; |
457 | if (fid != vout->field_id) { |
458 | if (fid == 0) |
459 | vout->field_id = fid; |
460 | } else if (0 == fid) { |
461 | if (vout->cur_frm == vout->next_frm) |
462 | goto err; |
463 | |
464 | vout->cur_frm->vbuf.vb2_buf.timestamp = ts; |
465 | vout->cur_frm->vbuf.sequence = vout->sequence++; |
466 | vb2_buffer_done(vb: &vout->cur_frm->vbuf.vb2_buf, state: VB2_BUF_STATE_DONE); |
467 | vout->cur_frm = vout->next_frm; |
468 | } else { |
469 | if (list_empty(head: &vout->dma_queue) || |
470 | (vout->cur_frm != vout->next_frm)) |
471 | goto err; |
472 | } |
473 | |
474 | return vout->field_id; |
475 | err: |
476 | return 0; |
477 | } |
478 | |
479 | static void omap_vout_isr(void *arg, unsigned int irqstatus) |
480 | { |
481 | int ret, fid, mgr_id; |
482 | dma_addr_t addr; |
483 | u32 irq; |
484 | struct omap_overlay *ovl; |
485 | u64 ts; |
486 | struct omapvideo_info *ovid; |
487 | struct omap_dss_device *cur_display; |
488 | struct omap_vout_device *vout = (struct omap_vout_device *)arg; |
489 | |
490 | ovid = &vout->vid_info; |
491 | ovl = ovid->overlays[0]; |
492 | |
493 | mgr_id = ovl->manager->id; |
494 | |
495 | /* get the display device attached to the overlay */ |
496 | cur_display = ovl->get_device(ovl); |
497 | |
498 | if (!cur_display) |
499 | return; |
500 | |
501 | spin_lock(lock: &vout->vbq_lock); |
502 | ts = ktime_get_ns(); |
503 | |
504 | switch (cur_display->type) { |
505 | case OMAP_DISPLAY_TYPE_DSI: |
506 | case OMAP_DISPLAY_TYPE_DPI: |
507 | case OMAP_DISPLAY_TYPE_DVI: |
508 | if (mgr_id == OMAP_DSS_CHANNEL_LCD) |
509 | irq = DISPC_IRQ_VSYNC; |
510 | else if (mgr_id == OMAP_DSS_CHANNEL_LCD2) |
511 | irq = DISPC_IRQ_VSYNC2; |
512 | else |
513 | goto vout_isr_err; |
514 | |
515 | if (!(irqstatus & irq)) |
516 | goto vout_isr_err; |
517 | break; |
518 | case OMAP_DISPLAY_TYPE_VENC: |
519 | fid = omapvid_handle_interlace_display(vout, irqstatus, |
520 | ts); |
521 | if (!fid) |
522 | goto vout_isr_err; |
523 | break; |
524 | case OMAP_DISPLAY_TYPE_HDMI: |
525 | if (!(irqstatus & DISPC_IRQ_EVSYNC_EVEN)) |
526 | goto vout_isr_err; |
527 | break; |
528 | default: |
529 | goto vout_isr_err; |
530 | } |
531 | |
532 | if (!vout->first_int && (vout->cur_frm != vout->next_frm)) { |
533 | vout->cur_frm->vbuf.vb2_buf.timestamp = ts; |
534 | vout->cur_frm->vbuf.sequence = vout->sequence++; |
535 | vb2_buffer_done(vb: &vout->cur_frm->vbuf.vb2_buf, state: VB2_BUF_STATE_DONE); |
536 | vout->cur_frm = vout->next_frm; |
537 | } |
538 | |
539 | vout->first_int = 0; |
540 | if (list_empty(head: &vout->dma_queue)) |
541 | goto vout_isr_err; |
542 | |
543 | vout->next_frm = list_entry(vout->dma_queue.next, |
544 | struct omap_vout_buffer, queue); |
545 | list_del(entry: &vout->next_frm->queue); |
546 | |
547 | addr = vout->queued_buf_addr[vout->next_frm->vbuf.vb2_buf.index] |
548 | + vout->cropped_offset; |
549 | |
550 | /* First save the configuration in ovelray structure */ |
551 | ret = omapvid_init(vout, addr); |
552 | if (ret) { |
553 | printk(KERN_ERR VOUT_NAME |
554 | "failed to set overlay info\n" ); |
555 | goto vout_isr_err; |
556 | } |
557 | |
558 | /* Enable the pipeline and set the Go bit */ |
559 | ret = omapvid_apply_changes(vout); |
560 | if (ret) |
561 | printk(KERN_ERR VOUT_NAME "failed to change mode\n" ); |
562 | |
563 | vout_isr_err: |
564 | spin_unlock(lock: &vout->vbq_lock); |
565 | } |
566 | |
567 | |
568 | /* |
569 | * V4L2 ioctls |
570 | */ |
571 | static int vidioc_querycap(struct file *file, void *fh, |
572 | struct v4l2_capability *cap) |
573 | { |
574 | struct omap_vout_device *vout = video_drvdata(file); |
575 | |
576 | strscpy(cap->driver, VOUT_NAME, sizeof(cap->driver)); |
577 | strscpy(cap->card, vout->vfd->name, sizeof(cap->card)); |
578 | snprintf(buf: cap->bus_info, size: sizeof(cap->bus_info), |
579 | fmt: "platform:%s.%d" , VOUT_NAME, vout->vid); |
580 | return 0; |
581 | } |
582 | |
583 | static int vidioc_enum_fmt_vid_out(struct file *file, void *fh, |
584 | struct v4l2_fmtdesc *fmt) |
585 | { |
586 | int index = fmt->index; |
587 | |
588 | if (index >= NUM_OUTPUT_FORMATS) |
589 | return -EINVAL; |
590 | |
591 | fmt->flags = omap_formats[index].flags; |
592 | fmt->pixelformat = omap_formats[index].pixelformat; |
593 | |
594 | return 0; |
595 | } |
596 | |
597 | static int vidioc_g_fmt_vid_out(struct file *file, void *fh, |
598 | struct v4l2_format *f) |
599 | { |
600 | struct omap_vout_device *vout = video_drvdata(file); |
601 | |
602 | f->fmt.pix = vout->pix; |
603 | return 0; |
604 | |
605 | } |
606 | |
607 | static int vidioc_try_fmt_vid_out(struct file *file, void *fh, |
608 | struct v4l2_format *f) |
609 | { |
610 | struct omap_overlay *ovl; |
611 | struct omapvideo_info *ovid; |
612 | struct omap_video_timings *timing; |
613 | struct omap_vout_device *vout = video_drvdata(file); |
614 | struct omap_dss_device *dssdev; |
615 | |
616 | ovid = &vout->vid_info; |
617 | ovl = ovid->overlays[0]; |
618 | /* get the display device attached to the overlay */ |
619 | dssdev = ovl->get_device(ovl); |
620 | |
621 | if (!dssdev) |
622 | return -EINVAL; |
623 | |
624 | timing = &dssdev->panel.timings; |
625 | |
626 | vout->fbuf.fmt.height = timing->y_res; |
627 | vout->fbuf.fmt.width = timing->x_res; |
628 | |
629 | omap_vout_try_format(pix: &f->fmt.pix); |
630 | return 0; |
631 | } |
632 | |
633 | static int vidioc_s_fmt_vid_out(struct file *file, void *fh, |
634 | struct v4l2_format *f) |
635 | { |
636 | int ret, bpp; |
637 | struct omap_overlay *ovl; |
638 | struct omapvideo_info *ovid; |
639 | struct omap_video_timings *timing; |
640 | struct omap_vout_device *vout = video_drvdata(file); |
641 | struct omap_dss_device *dssdev; |
642 | |
643 | if (vb2_is_busy(q: &vout->vq)) |
644 | return -EBUSY; |
645 | |
646 | ovid = &vout->vid_info; |
647 | ovl = ovid->overlays[0]; |
648 | dssdev = ovl->get_device(ovl); |
649 | |
650 | /* get the display device attached to the overlay */ |
651 | if (!dssdev) { |
652 | ret = -EINVAL; |
653 | goto s_fmt_vid_out_exit; |
654 | } |
655 | timing = &dssdev->panel.timings; |
656 | |
657 | /* We don't support RGB24-packed mode if vrfb rotation |
658 | * is enabled*/ |
659 | if ((is_rotation_enabled(vout)) && |
660 | f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) { |
661 | ret = -EINVAL; |
662 | goto s_fmt_vid_out_exit; |
663 | } |
664 | |
665 | /* get the framebuffer parameters */ |
666 | |
667 | if (is_rotation_90_or_270(vout)) { |
668 | vout->fbuf.fmt.height = timing->x_res; |
669 | vout->fbuf.fmt.width = timing->y_res; |
670 | } else { |
671 | vout->fbuf.fmt.height = timing->y_res; |
672 | vout->fbuf.fmt.width = timing->x_res; |
673 | } |
674 | |
675 | /* change to smaller size is OK */ |
676 | |
677 | bpp = omap_vout_try_format(pix: &f->fmt.pix); |
678 | f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height * bpp; |
679 | |
680 | /* try & set the new output format */ |
681 | vout->bpp = bpp; |
682 | vout->pix = f->fmt.pix; |
683 | vout->vrfb_bpp = 1; |
684 | |
685 | /* If YUYV then vrfb bpp is 2, for others its 1 */ |
686 | if (V4L2_PIX_FMT_YUYV == vout->pix.pixelformat || |
687 | V4L2_PIX_FMT_UYVY == vout->pix.pixelformat) |
688 | vout->vrfb_bpp = 2; |
689 | |
690 | /* set default crop and win */ |
691 | omap_vout_new_format(pix: &vout->pix, fbuf: &vout->fbuf, crop: &vout->crop, win: &vout->win); |
692 | |
693 | ret = 0; |
694 | |
695 | s_fmt_vid_out_exit: |
696 | return ret; |
697 | } |
698 | |
699 | static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, |
700 | struct v4l2_format *f) |
701 | { |
702 | int ret = 0; |
703 | struct omap_vout_device *vout = video_drvdata(file); |
704 | struct omap_overlay *ovl; |
705 | struct omapvideo_info *ovid; |
706 | struct v4l2_window *win = &f->fmt.win; |
707 | |
708 | ovid = &vout->vid_info; |
709 | ovl = ovid->overlays[0]; |
710 | |
711 | ret = omap_vout_try_window(fbuf: &vout->fbuf, new_win: win); |
712 | |
713 | if (!ret && !(ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA)) |
714 | win->global_alpha = 0; |
715 | |
716 | return ret; |
717 | } |
718 | |
719 | static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh, |
720 | struct v4l2_format *f) |
721 | { |
722 | int ret = 0; |
723 | struct omap_overlay *ovl; |
724 | struct omapvideo_info *ovid; |
725 | struct omap_vout_device *vout = video_drvdata(file); |
726 | struct v4l2_window *win = &f->fmt.win; |
727 | |
728 | ovid = &vout->vid_info; |
729 | ovl = ovid->overlays[0]; |
730 | |
731 | ret = omap_vout_new_window(crop: &vout->crop, win: &vout->win, fbuf: &vout->fbuf, new_win: win); |
732 | if (!ret) { |
733 | enum omap_dss_trans_key_type key_type = |
734 | OMAP_DSS_COLOR_KEY_GFX_DST; |
735 | int enable; |
736 | |
737 | /* Video1 plane does not support global alpha on OMAP3 */ |
738 | if (ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) |
739 | vout->win.global_alpha = win->global_alpha; |
740 | else |
741 | win->global_alpha = 0; |
742 | if (vout->fbuf.flags & (V4L2_FBUF_FLAG_CHROMAKEY | |
743 | V4L2_FBUF_FLAG_SRC_CHROMAKEY)) |
744 | enable = 1; |
745 | else |
746 | enable = 0; |
747 | if (vout->fbuf.flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) |
748 | key_type = OMAP_DSS_COLOR_KEY_VID_SRC; |
749 | |
750 | if (ovl->manager && ovl->manager->get_manager_info && |
751 | ovl->manager->set_manager_info) { |
752 | struct omap_overlay_manager_info info; |
753 | |
754 | ovl->manager->get_manager_info(ovl->manager, &info); |
755 | info.trans_enabled = enable; |
756 | info.trans_key_type = key_type; |
757 | info.trans_key = vout->win.chromakey; |
758 | |
759 | if (ovl->manager->set_manager_info(ovl->manager, &info)) |
760 | return -EINVAL; |
761 | } |
762 | } |
763 | return ret; |
764 | } |
765 | |
766 | static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, |
767 | struct v4l2_format *f) |
768 | { |
769 | struct omap_overlay *ovl; |
770 | struct omapvideo_info *ovid; |
771 | struct omap_vout_device *vout = video_drvdata(file); |
772 | struct v4l2_window *win = &f->fmt.win; |
773 | |
774 | ovid = &vout->vid_info; |
775 | ovl = ovid->overlays[0]; |
776 | |
777 | win->w = vout->win.w; |
778 | win->field = vout->win.field; |
779 | win->chromakey = vout->win.chromakey; |
780 | if (ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) |
781 | win->global_alpha = vout->win.global_alpha; |
782 | else |
783 | win->global_alpha = 0; |
784 | win->clips = NULL; |
785 | win->clipcount = 0; |
786 | win->bitmap = NULL; |
787 | return 0; |
788 | } |
789 | |
790 | static int vidioc_g_selection(struct file *file, void *fh, struct v4l2_selection *sel) |
791 | { |
792 | struct omap_vout_device *vout = video_drvdata(file); |
793 | struct v4l2_pix_format *pix = &vout->pix; |
794 | |
795 | if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) |
796 | return -EINVAL; |
797 | |
798 | switch (sel->target) { |
799 | case V4L2_SEL_TGT_CROP: |
800 | sel->r = vout->crop; |
801 | break; |
802 | case V4L2_SEL_TGT_CROP_DEFAULT: |
803 | omap_vout_default_crop(pix: &vout->pix, fbuf: &vout->fbuf, crop: &sel->r); |
804 | break; |
805 | case V4L2_SEL_TGT_CROP_BOUNDS: |
806 | /* Width and height are always even */ |
807 | sel->r.width = pix->width & ~1; |
808 | sel->r.height = pix->height & ~1; |
809 | break; |
810 | default: |
811 | return -EINVAL; |
812 | } |
813 | return 0; |
814 | } |
815 | |
816 | static int vidioc_s_selection(struct file *file, void *fh, struct v4l2_selection *sel) |
817 | { |
818 | int ret = -EINVAL; |
819 | struct omap_vout_device *vout = video_drvdata(file); |
820 | struct omapvideo_info *ovid; |
821 | struct omap_overlay *ovl; |
822 | struct omap_video_timings *timing; |
823 | struct omap_dss_device *dssdev; |
824 | |
825 | if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) |
826 | return -EINVAL; |
827 | |
828 | if (sel->target != V4L2_SEL_TGT_CROP) |
829 | return -EINVAL; |
830 | |
831 | if (vb2_is_busy(q: &vout->vq)) |
832 | return -EBUSY; |
833 | |
834 | ovid = &vout->vid_info; |
835 | ovl = ovid->overlays[0]; |
836 | /* get the display device attached to the overlay */ |
837 | dssdev = ovl->get_device(ovl); |
838 | |
839 | if (!dssdev) { |
840 | ret = -EINVAL; |
841 | goto s_crop_err; |
842 | } |
843 | |
844 | timing = &dssdev->panel.timings; |
845 | |
846 | if (is_rotation_90_or_270(vout)) { |
847 | vout->fbuf.fmt.height = timing->x_res; |
848 | vout->fbuf.fmt.width = timing->y_res; |
849 | } else { |
850 | vout->fbuf.fmt.height = timing->y_res; |
851 | vout->fbuf.fmt.width = timing->x_res; |
852 | } |
853 | |
854 | ret = omap_vout_new_crop(pix: &vout->pix, crop: &vout->crop, win: &vout->win, |
855 | fbuf: &vout->fbuf, new_crop: &sel->r); |
856 | |
857 | s_crop_err: |
858 | return ret; |
859 | } |
860 | |
861 | static int omap_vout_s_ctrl(struct v4l2_ctrl *ctrl) |
862 | { |
863 | struct omap_vout_device *vout = |
864 | container_of(ctrl->handler, struct omap_vout_device, ctrl_handler); |
865 | int ret = 0; |
866 | |
867 | switch (ctrl->id) { |
868 | case V4L2_CID_ROTATE: { |
869 | struct omapvideo_info *ovid; |
870 | int rotation = ctrl->val; |
871 | |
872 | ovid = &vout->vid_info; |
873 | |
874 | if (rotation && ovid->rotation_type == VOUT_ROT_NONE) { |
875 | ret = -ERANGE; |
876 | break; |
877 | } |
878 | |
879 | if (rotation && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) { |
880 | ret = -EINVAL; |
881 | break; |
882 | } |
883 | |
884 | if (v4l2_rot_to_dss_rot(v4l2_rotation: rotation, rotation: &vout->rotation, |
885 | mirror: vout->mirror)) { |
886 | ret = -EINVAL; |
887 | break; |
888 | } |
889 | break; |
890 | } |
891 | case V4L2_CID_BG_COLOR: |
892 | { |
893 | struct omap_overlay *ovl; |
894 | unsigned int color = ctrl->val; |
895 | struct omap_overlay_manager_info info; |
896 | |
897 | ovl = vout->vid_info.overlays[0]; |
898 | |
899 | if (!ovl->manager || !ovl->manager->get_manager_info) { |
900 | ret = -EINVAL; |
901 | break; |
902 | } |
903 | |
904 | ovl->manager->get_manager_info(ovl->manager, &info); |
905 | info.default_color = color; |
906 | if (ovl->manager->set_manager_info(ovl->manager, &info)) { |
907 | ret = -EINVAL; |
908 | break; |
909 | } |
910 | break; |
911 | } |
912 | case V4L2_CID_VFLIP: |
913 | { |
914 | struct omapvideo_info *ovid; |
915 | unsigned int mirror = ctrl->val; |
916 | |
917 | ovid = &vout->vid_info; |
918 | |
919 | if (mirror && ovid->rotation_type == VOUT_ROT_NONE) { |
920 | ret = -ERANGE; |
921 | break; |
922 | } |
923 | |
924 | if (mirror && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) { |
925 | ret = -EINVAL; |
926 | break; |
927 | } |
928 | vout->mirror = mirror; |
929 | break; |
930 | } |
931 | default: |
932 | return -EINVAL; |
933 | } |
934 | return ret; |
935 | } |
936 | |
937 | static const struct v4l2_ctrl_ops omap_vout_ctrl_ops = { |
938 | .s_ctrl = omap_vout_s_ctrl, |
939 | }; |
940 | |
941 | static int omap_vout_vb2_queue_setup(struct vb2_queue *vq, |
942 | unsigned int *nbufs, |
943 | unsigned int *num_planes, unsigned int sizes[], |
944 | struct device *alloc_devs[]) |
945 | { |
946 | struct omap_vout_device *vout = vb2_get_drv_priv(q: vq); |
947 | unsigned int q_num_bufs = vb2_get_num_buffers(q: vq); |
948 | int size = vout->pix.sizeimage; |
949 | |
950 | if (is_rotation_enabled(vout) && q_num_bufs + *nbufs > VRFB_NUM_BUFS) { |
951 | *nbufs = VRFB_NUM_BUFS - q_num_bufs; |
952 | if (*nbufs == 0) |
953 | return -EINVAL; |
954 | } |
955 | |
956 | if (*num_planes) |
957 | return sizes[0] < size ? -EINVAL : 0; |
958 | |
959 | *num_planes = 1; |
960 | sizes[0] = size; |
961 | return 0; |
962 | } |
963 | |
964 | static int omap_vout_vb2_prepare(struct vb2_buffer *vb) |
965 | { |
966 | struct omap_vout_device *vout = vb2_get_drv_priv(q: vb->vb2_queue); |
967 | struct omapvideo_info *ovid = &vout->vid_info; |
968 | struct omap_vout_buffer *voutbuf = vb2_to_omap_vout_buffer(vb); |
969 | dma_addr_t buf_phy_addr = vb2_dma_contig_plane_dma_addr(vb, plane_no: 0); |
970 | |
971 | if (vb2_plane_size(vb, plane_no: 0) < vout->pix.sizeimage) { |
972 | v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, |
973 | "%s data will not fit into plane (%lu < %u)\n" , |
974 | __func__, vb2_plane_size(vb, 0), vout->pix.sizeimage); |
975 | return -EINVAL; |
976 | } |
977 | |
978 | vb2_set_plane_payload(vb, plane_no: 0, size: vout->pix.sizeimage); |
979 | voutbuf->vbuf.field = V4L2_FIELD_NONE; |
980 | |
981 | vout->queued_buf_addr[vb->index] = buf_phy_addr; |
982 | if (ovid->rotation_type == VOUT_ROT_VRFB) |
983 | return omap_vout_prepare_vrfb(vout, vb); |
984 | return 0; |
985 | } |
986 | |
987 | static void omap_vout_vb2_queue(struct vb2_buffer *vb) |
988 | { |
989 | struct omap_vout_device *vout = vb2_get_drv_priv(q: vb->vb2_queue); |
990 | struct omap_vout_buffer *voutbuf = vb2_to_omap_vout_buffer(vb); |
991 | |
992 | list_add_tail(new: &voutbuf->queue, head: &vout->dma_queue); |
993 | } |
994 | |
995 | static int omap_vout_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) |
996 | { |
997 | struct omap_vout_device *vout = vb2_get_drv_priv(q: vq); |
998 | struct omapvideo_info *ovid = &vout->vid_info; |
999 | struct omap_vout_buffer *buf, *tmp; |
1000 | dma_addr_t addr = 0; |
1001 | u32 mask = 0; |
1002 | int ret, j; |
1003 | |
1004 | /* Get the next frame from the buffer queue */ |
1005 | vout->next_frm = vout->cur_frm = list_entry(vout->dma_queue.next, |
1006 | struct omap_vout_buffer, queue); |
1007 | /* Remove buffer from the buffer queue */ |
1008 | list_del(entry: &vout->cur_frm->queue); |
1009 | /* Initialize field_id and started member */ |
1010 | vout->field_id = 0; |
1011 | vout->first_int = 1; |
1012 | vout->sequence = 0; |
1013 | |
1014 | if (omap_vout_calculate_offset(vout)) { |
1015 | ret = -EINVAL; |
1016 | goto out; |
1017 | } |
1018 | if (ovid->rotation_type == VOUT_ROT_VRFB) |
1019 | if (omap_vout_vrfb_buffer_setup(vout, count: &count, startindex: 0)) { |
1020 | ret = -ENOMEM; |
1021 | goto out; |
1022 | } |
1023 | |
1024 | addr = vout->queued_buf_addr[vout->cur_frm->vbuf.vb2_buf.index] |
1025 | + vout->cropped_offset; |
1026 | |
1027 | mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD |
1028 | | DISPC_IRQ_VSYNC2; |
1029 | |
1030 | /* First save the configuration in overlay structure */ |
1031 | ret = omapvid_init(vout, addr); |
1032 | if (ret) { |
1033 | v4l2_err(&vout->vid_dev->v4l2_dev, |
1034 | "failed to set overlay info\n" ); |
1035 | goto streamon_err1; |
1036 | } |
1037 | |
1038 | omap_dispc_register_isr(isr: omap_vout_isr, arg: vout, mask); |
1039 | |
1040 | /* Enable the pipeline and set the Go bit */ |
1041 | ret = omapvid_apply_changes(vout); |
1042 | if (ret) |
1043 | v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n" ); |
1044 | |
1045 | for (j = 0; j < ovid->num_overlays; j++) { |
1046 | struct omap_overlay *ovl = ovid->overlays[j]; |
1047 | struct omap_dss_device *dssdev = ovl->get_device(ovl); |
1048 | |
1049 | if (dssdev) { |
1050 | ret = ovl->enable(ovl); |
1051 | if (ret) |
1052 | goto streamon_err1; |
1053 | } |
1054 | } |
1055 | return 0; |
1056 | |
1057 | streamon_err1: |
1058 | mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD |
1059 | | DISPC_IRQ_VSYNC2; |
1060 | |
1061 | omap_dispc_unregister_isr(isr: omap_vout_isr, arg: vout, mask); |
1062 | |
1063 | for (j = 0; j < ovid->num_overlays; j++) { |
1064 | struct omap_overlay *ovl = ovid->overlays[j]; |
1065 | struct omap_dss_device *dssdev = ovl->get_device(ovl); |
1066 | |
1067 | if (dssdev) |
1068 | ovl->disable(ovl); |
1069 | } |
1070 | /* Turn of the pipeline */ |
1071 | if (omapvid_apply_changes(vout)) |
1072 | v4l2_err(&vout->vid_dev->v4l2_dev, |
1073 | "failed to change mode in streamoff\n" ); |
1074 | |
1075 | out: |
1076 | vb2_buffer_done(vb: &vout->cur_frm->vbuf.vb2_buf, state: VB2_BUF_STATE_QUEUED); |
1077 | list_for_each_entry_safe(buf, tmp, &vout->dma_queue, queue) { |
1078 | list_del(entry: &buf->queue); |
1079 | vb2_buffer_done(vb: &buf->vbuf.vb2_buf, state: VB2_BUF_STATE_QUEUED); |
1080 | } |
1081 | return ret; |
1082 | } |
1083 | |
1084 | static void omap_vout_vb2_stop_streaming(struct vb2_queue *vq) |
1085 | { |
1086 | struct omap_vout_device *vout = vb2_get_drv_priv(q: vq); |
1087 | struct omapvideo_info *ovid = &vout->vid_info; |
1088 | struct omap_vout_buffer *buf, *tmp; |
1089 | u32 mask = 0; |
1090 | int j; |
1091 | |
1092 | mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD |
1093 | | DISPC_IRQ_VSYNC2; |
1094 | |
1095 | omap_dispc_unregister_isr(isr: omap_vout_isr, arg: vout, mask); |
1096 | |
1097 | for (j = 0; j < ovid->num_overlays; j++) { |
1098 | struct omap_overlay *ovl = ovid->overlays[j]; |
1099 | struct omap_dss_device *dssdev = ovl->get_device(ovl); |
1100 | |
1101 | if (dssdev) |
1102 | ovl->disable(ovl); |
1103 | } |
1104 | /* Turn of the pipeline */ |
1105 | if (omapvid_apply_changes(vout)) |
1106 | v4l2_err(&vout->vid_dev->v4l2_dev, |
1107 | "failed to change mode in streamoff\n" ); |
1108 | |
1109 | if (vout->next_frm != vout->cur_frm) |
1110 | vb2_buffer_done(vb: &vout->next_frm->vbuf.vb2_buf, state: VB2_BUF_STATE_ERROR); |
1111 | vb2_buffer_done(vb: &vout->cur_frm->vbuf.vb2_buf, state: VB2_BUF_STATE_ERROR); |
1112 | list_for_each_entry_safe(buf, tmp, &vout->dma_queue, queue) { |
1113 | list_del(entry: &buf->queue); |
1114 | vb2_buffer_done(vb: &buf->vbuf.vb2_buf, state: VB2_BUF_STATE_ERROR); |
1115 | } |
1116 | } |
1117 | |
1118 | static int vidioc_s_fbuf(struct file *file, void *fh, |
1119 | const struct v4l2_framebuffer *a) |
1120 | { |
1121 | int enable = 0; |
1122 | struct omap_overlay *ovl; |
1123 | struct omapvideo_info *ovid; |
1124 | struct omap_vout_device *vout = video_drvdata(file); |
1125 | struct omap_overlay_manager_info info; |
1126 | enum omap_dss_trans_key_type key_type = OMAP_DSS_COLOR_KEY_GFX_DST; |
1127 | |
1128 | ovid = &vout->vid_info; |
1129 | ovl = ovid->overlays[0]; |
1130 | |
1131 | /* OMAP DSS doesn't support Source and Destination color |
1132 | key together */ |
1133 | if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) && |
1134 | (a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) |
1135 | return -EINVAL; |
1136 | /* OMAP DSS Doesn't support the Destination color key |
1137 | and alpha blending together */ |
1138 | if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY) && |
1139 | (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA)) |
1140 | return -EINVAL; |
1141 | |
1142 | if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY)) { |
1143 | vout->fbuf.flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY; |
1144 | key_type = OMAP_DSS_COLOR_KEY_VID_SRC; |
1145 | } else |
1146 | vout->fbuf.flags &= ~V4L2_FBUF_FLAG_SRC_CHROMAKEY; |
1147 | |
1148 | if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) { |
1149 | vout->fbuf.flags |= V4L2_FBUF_FLAG_CHROMAKEY; |
1150 | key_type = OMAP_DSS_COLOR_KEY_GFX_DST; |
1151 | } else |
1152 | vout->fbuf.flags &= ~V4L2_FBUF_FLAG_CHROMAKEY; |
1153 | |
1154 | if (a->flags & (V4L2_FBUF_FLAG_CHROMAKEY | |
1155 | V4L2_FBUF_FLAG_SRC_CHROMAKEY)) |
1156 | enable = 1; |
1157 | else |
1158 | enable = 0; |
1159 | if (ovl->manager && ovl->manager->get_manager_info && |
1160 | ovl->manager->set_manager_info) { |
1161 | |
1162 | ovl->manager->get_manager_info(ovl->manager, &info); |
1163 | info.trans_enabled = enable; |
1164 | info.trans_key_type = key_type; |
1165 | info.trans_key = vout->win.chromakey; |
1166 | |
1167 | if (ovl->manager->set_manager_info(ovl->manager, &info)) |
1168 | return -EINVAL; |
1169 | } |
1170 | if (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) { |
1171 | vout->fbuf.flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; |
1172 | enable = 1; |
1173 | } else { |
1174 | vout->fbuf.flags &= ~V4L2_FBUF_FLAG_LOCAL_ALPHA; |
1175 | enable = 0; |
1176 | } |
1177 | if (ovl->manager && ovl->manager->get_manager_info && |
1178 | ovl->manager->set_manager_info) { |
1179 | ovl->manager->get_manager_info(ovl->manager, &info); |
1180 | /* enable this only if there is no zorder cap */ |
1181 | if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0) |
1182 | info.partial_alpha_enabled = enable; |
1183 | if (ovl->manager->set_manager_info(ovl->manager, &info)) |
1184 | return -EINVAL; |
1185 | } |
1186 | |
1187 | return 0; |
1188 | } |
1189 | |
1190 | static int vidioc_g_fbuf(struct file *file, void *fh, |
1191 | struct v4l2_framebuffer *a) |
1192 | { |
1193 | struct omap_overlay *ovl; |
1194 | struct omapvideo_info *ovid; |
1195 | struct omap_vout_device *vout = video_drvdata(file); |
1196 | struct omap_overlay_manager_info info; |
1197 | struct omap_video_timings *timing; |
1198 | struct omap_dss_device *dssdev; |
1199 | |
1200 | ovid = &vout->vid_info; |
1201 | ovl = ovid->overlays[0]; |
1202 | /* get the display device attached to the overlay */ |
1203 | dssdev = ovl->get_device(ovl); |
1204 | |
1205 | if (!dssdev) |
1206 | return -EINVAL; |
1207 | |
1208 | timing = &dssdev->panel.timings; |
1209 | |
1210 | vout->fbuf.fmt.height = timing->y_res; |
1211 | vout->fbuf.fmt.width = timing->x_res; |
1212 | a->fmt.field = V4L2_FIELD_NONE; |
1213 | a->fmt.colorspace = V4L2_COLORSPACE_SRGB; |
1214 | a->fmt.pixelformat = V4L2_PIX_FMT_RGBA32; |
1215 | a->fmt.height = vout->fbuf.fmt.height; |
1216 | a->fmt.width = vout->fbuf.fmt.width; |
1217 | a->fmt.bytesperline = vout->fbuf.fmt.width * 4; |
1218 | a->fmt.sizeimage = a->fmt.height * a->fmt.bytesperline; |
1219 | a->base = vout->fbuf.base; |
1220 | |
1221 | a->flags = vout->fbuf.flags; |
1222 | a->capability = vout->fbuf.capability; |
1223 | a->flags &= ~(V4L2_FBUF_FLAG_SRC_CHROMAKEY | V4L2_FBUF_FLAG_CHROMAKEY | |
1224 | V4L2_FBUF_FLAG_LOCAL_ALPHA); |
1225 | |
1226 | if (ovl->manager && ovl->manager->get_manager_info) { |
1227 | ovl->manager->get_manager_info(ovl->manager, &info); |
1228 | if (info.trans_key_type == OMAP_DSS_COLOR_KEY_VID_SRC) |
1229 | a->flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY; |
1230 | if (info.trans_key_type == OMAP_DSS_COLOR_KEY_GFX_DST) |
1231 | a->flags |= V4L2_FBUF_FLAG_CHROMAKEY; |
1232 | if (info.partial_alpha_enabled) |
1233 | a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; |
1234 | } |
1235 | |
1236 | return 0; |
1237 | } |
1238 | |
1239 | static int vidioc_enum_output(struct file *file, void *priv_fh, |
1240 | struct v4l2_output *out) |
1241 | { |
1242 | if (out->index) |
1243 | return -EINVAL; |
1244 | snprintf(buf: out->name, size: sizeof(out->name), fmt: "Overlay" ); |
1245 | out->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY; |
1246 | return 0; |
1247 | } |
1248 | |
1249 | static int vidioc_g_output(struct file *file, void *priv_fh, unsigned int *i) |
1250 | { |
1251 | *i = 0; |
1252 | return 0; |
1253 | } |
1254 | |
1255 | static int vidioc_s_output(struct file *file, void *priv_fh, unsigned int i) |
1256 | { |
1257 | return i ? -EINVAL : 0; |
1258 | } |
1259 | |
1260 | static const struct v4l2_ioctl_ops vout_ioctl_ops = { |
1261 | .vidioc_querycap = vidioc_querycap, |
1262 | .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, |
1263 | .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, |
1264 | .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, |
1265 | .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, |
1266 | .vidioc_s_fbuf = vidioc_s_fbuf, |
1267 | .vidioc_g_fbuf = vidioc_g_fbuf, |
1268 | .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_overlay, |
1269 | .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_overlay, |
1270 | .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_overlay, |
1271 | .vidioc_g_selection = vidioc_g_selection, |
1272 | .vidioc_s_selection = vidioc_s_selection, |
1273 | .vidioc_enum_output = vidioc_enum_output, |
1274 | .vidioc_g_output = vidioc_g_output, |
1275 | .vidioc_s_output = vidioc_s_output, |
1276 | .vidioc_reqbufs = vb2_ioctl_reqbufs, |
1277 | .vidioc_create_bufs = vb2_ioctl_create_bufs, |
1278 | .vidioc_querybuf = vb2_ioctl_querybuf, |
1279 | .vidioc_qbuf = vb2_ioctl_qbuf, |
1280 | .vidioc_dqbuf = vb2_ioctl_dqbuf, |
1281 | .vidioc_expbuf = vb2_ioctl_expbuf, |
1282 | .vidioc_streamon = vb2_ioctl_streamon, |
1283 | .vidioc_streamoff = vb2_ioctl_streamoff, |
1284 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, |
1285 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
1286 | }; |
1287 | |
1288 | static const struct v4l2_file_operations omap_vout_fops = { |
1289 | .owner = THIS_MODULE, |
1290 | .unlocked_ioctl = video_ioctl2, |
1291 | .poll = vb2_fop_poll, |
1292 | .mmap = vb2_fop_mmap, |
1293 | .open = v4l2_fh_open, |
1294 | .release = vb2_fop_release, |
1295 | }; |
1296 | |
1297 | static const struct vb2_ops omap_vout_vb2_ops = { |
1298 | .queue_setup = omap_vout_vb2_queue_setup, |
1299 | .buf_queue = omap_vout_vb2_queue, |
1300 | .buf_prepare = omap_vout_vb2_prepare, |
1301 | .start_streaming = omap_vout_vb2_start_streaming, |
1302 | .stop_streaming = omap_vout_vb2_stop_streaming, |
1303 | .wait_prepare = vb2_ops_wait_prepare, |
1304 | .wait_finish = vb2_ops_wait_finish, |
1305 | }; |
1306 | |
1307 | /* Init functions used during driver initialization */ |
1308 | /* Initial setup of video_data */ |
1309 | static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) |
1310 | { |
1311 | struct video_device *vfd; |
1312 | struct v4l2_pix_format *pix; |
1313 | struct omap_overlay *ovl = vout->vid_info.overlays[0]; |
1314 | struct omap_dss_device *display = ovl->get_device(ovl); |
1315 | struct v4l2_ctrl_handler *hdl; |
1316 | struct vb2_queue *vq; |
1317 | int ret; |
1318 | |
1319 | /* set the default pix */ |
1320 | pix = &vout->pix; |
1321 | |
1322 | /* Set the default picture of QVGA */ |
1323 | pix->width = QQVGA_WIDTH; |
1324 | pix->height = QQVGA_HEIGHT; |
1325 | |
1326 | /* Default pixel format is RGB 5-6-5 */ |
1327 | pix->pixelformat = V4L2_PIX_FMT_RGB565; |
1328 | pix->field = V4L2_FIELD_NONE; |
1329 | pix->bytesperline = pix->width * 2; |
1330 | pix->sizeimage = pix->bytesperline * pix->height; |
1331 | pix->colorspace = V4L2_COLORSPACE_SRGB; |
1332 | |
1333 | vout->bpp = RGB565_BPP; |
1334 | vout->fbuf.fmt.width = display->panel.timings.x_res; |
1335 | vout->fbuf.fmt.height = display->panel.timings.y_res; |
1336 | vout->cropped_offset = 0; |
1337 | |
1338 | /* Set the data structures for the overlay parameters*/ |
1339 | vout->fbuf.flags = V4L2_FBUF_FLAG_OVERLAY; |
1340 | vout->fbuf.capability = V4L2_FBUF_CAP_LOCAL_ALPHA | |
1341 | V4L2_FBUF_CAP_SRC_CHROMAKEY | V4L2_FBUF_CAP_CHROMAKEY | |
1342 | V4L2_FBUF_CAP_EXTERNOVERLAY; |
1343 | if (ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) { |
1344 | vout->win.global_alpha = 255; |
1345 | vout->fbuf.capability |= V4L2_FBUF_CAP_GLOBAL_ALPHA; |
1346 | vout->fbuf.flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA; |
1347 | } else { |
1348 | vout->win.global_alpha = 0; |
1349 | } |
1350 | vout->win.field = V4L2_FIELD_NONE; |
1351 | |
1352 | omap_vout_new_format(pix, fbuf: &vout->fbuf, crop: &vout->crop, win: &vout->win); |
1353 | |
1354 | hdl = &vout->ctrl_handler; |
1355 | v4l2_ctrl_handler_init(hdl, 3); |
1356 | if (vout->vid_info.rotation_type == VOUT_ROT_VRFB) { |
1357 | v4l2_ctrl_new_std(hdl, ops: &omap_vout_ctrl_ops, |
1358 | V4L2_CID_ROTATE, min: 0, max: 270, step: 90, def: 0); |
1359 | v4l2_ctrl_new_std(hdl, ops: &omap_vout_ctrl_ops, |
1360 | V4L2_CID_VFLIP, min: 0, max: 1, step: 1, def: 0); |
1361 | } |
1362 | v4l2_ctrl_new_std(hdl, ops: &omap_vout_ctrl_ops, |
1363 | V4L2_CID_BG_COLOR, min: 0, max: 0xffffff, step: 1, def: 0); |
1364 | if (hdl->error) |
1365 | return hdl->error; |
1366 | |
1367 | vout->rotation = 0; |
1368 | vout->mirror = false; |
1369 | INIT_LIST_HEAD(list: &vout->dma_queue); |
1370 | if (vout->vid_info.rotation_type == VOUT_ROT_VRFB) |
1371 | vout->vrfb_bpp = 2; |
1372 | |
1373 | /* initialize the video_device struct */ |
1374 | vfd = vout->vfd = video_device_alloc(); |
1375 | |
1376 | if (!vfd) { |
1377 | printk(KERN_ERR VOUT_NAME |
1378 | ": could not allocate video device struct\n" ); |
1379 | v4l2_ctrl_handler_free(hdl); |
1380 | return -ENOMEM; |
1381 | } |
1382 | vfd->ctrl_handler = hdl; |
1383 | vfd->release = video_device_release; |
1384 | vfd->ioctl_ops = &vout_ioctl_ops; |
1385 | |
1386 | strscpy(vfd->name, VOUT_NAME, sizeof(vfd->name)); |
1387 | |
1388 | vfd->fops = &omap_vout_fops; |
1389 | vfd->v4l2_dev = &vout->vid_dev->v4l2_dev; |
1390 | vfd->vfl_dir = VFL_DIR_TX; |
1391 | vfd->minor = -1; |
1392 | vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT | |
1393 | V4L2_CAP_VIDEO_OUTPUT_OVERLAY; |
1394 | mutex_init(&vout->lock); |
1395 | |
1396 | vq = &vout->vq; |
1397 | vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; |
1398 | vq->io_modes = VB2_MMAP | VB2_DMABUF; |
1399 | vq->drv_priv = vout; |
1400 | vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; |
1401 | vq->buf_struct_size = sizeof(struct omap_vout_buffer); |
1402 | vq->dev = vfd->v4l2_dev->dev; |
1403 | |
1404 | vq->ops = &omap_vout_vb2_ops; |
1405 | vq->mem_ops = &vb2_dma_contig_memops; |
1406 | vq->lock = &vout->lock; |
1407 | vq->min_queued_buffers = 1; |
1408 | vfd->queue = vq; |
1409 | |
1410 | ret = vb2_queue_init(q: vq); |
1411 | if (ret) { |
1412 | v4l2_ctrl_handler_free(hdl); |
1413 | video_device_release(vdev: vfd); |
1414 | } |
1415 | return ret; |
1416 | } |
1417 | |
1418 | /* Setup video buffers */ |
1419 | static int __init omap_vout_setup_video_bufs(struct platform_device *pdev, |
1420 | int vid_num) |
1421 | { |
1422 | struct omapvideo_info *ovid; |
1423 | struct omap_vout_device *vout; |
1424 | struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); |
1425 | struct omap2video_device *vid_dev = |
1426 | container_of(v4l2_dev, struct omap2video_device, v4l2_dev); |
1427 | int ret = 0; |
1428 | |
1429 | vout = vid_dev->vouts[vid_num]; |
1430 | ovid = &vout->vid_info; |
1431 | |
1432 | if (ovid->rotation_type == VOUT_ROT_VRFB) { |
1433 | bool static_vrfb_allocation = (vid_num == 0) ? |
1434 | vid1_static_vrfb_alloc : vid2_static_vrfb_alloc; |
1435 | ret = omap_vout_setup_vrfb_bufs(pdev, vid_num, |
1436 | static_vrfb_allocation); |
1437 | } |
1438 | return ret; |
1439 | } |
1440 | |
1441 | /* Create video out devices */ |
1442 | static int __init omap_vout_create_video_devices(struct platform_device *pdev) |
1443 | { |
1444 | int ret = 0, k; |
1445 | struct omap_vout_device *vout; |
1446 | struct video_device *vfd = NULL; |
1447 | struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); |
1448 | struct omap2video_device *vid_dev = container_of(v4l2_dev, |
1449 | struct omap2video_device, v4l2_dev); |
1450 | struct omap_overlay *ovl = vid_dev->overlays[0]; |
1451 | struct omap_overlay_info info; |
1452 | |
1453 | ovl->get_overlay_info(ovl, &info); |
1454 | |
1455 | for (k = 0; k < pdev->num_resources; k++) { |
1456 | |
1457 | vout = kzalloc(size: sizeof(struct omap_vout_device), GFP_KERNEL); |
1458 | if (!vout) { |
1459 | dev_err(&pdev->dev, ": could not allocate memory\n" ); |
1460 | return -ENOMEM; |
1461 | } |
1462 | |
1463 | vout->vid = k; |
1464 | vid_dev->vouts[k] = vout; |
1465 | vout->vid_dev = vid_dev; |
1466 | /* Select video2 if only 1 overlay is controlled by V4L2 */ |
1467 | if (pdev->num_resources == 1) |
1468 | vout->vid_info.overlays[0] = vid_dev->overlays[k + 2]; |
1469 | else |
1470 | /* Else select video1 and video2 one by one. */ |
1471 | vout->vid_info.overlays[0] = vid_dev->overlays[k + 1]; |
1472 | vout->vid_info.num_overlays = 1; |
1473 | vout->vid_info.id = k + 1; |
1474 | spin_lock_init(&vout->vbq_lock); |
1475 | /* |
1476 | * Set the framebuffer base, this allows applications to find |
1477 | * the fb corresponding to this overlay. |
1478 | * |
1479 | * To be precise: fbuf.base should match smem_start of |
1480 | * struct fb_fix_screeninfo. |
1481 | */ |
1482 | vout->fbuf.base = (void *)(uintptr_t)info.paddr; |
1483 | |
1484 | /* Set VRFB as rotation_type for omap2 and omap3 */ |
1485 | if (omap_vout_dss_omap24xx() || omap_vout_dss_omap34xx()) |
1486 | vout->vid_info.rotation_type = VOUT_ROT_VRFB; |
1487 | |
1488 | /* Setup the default configuration for the video devices |
1489 | */ |
1490 | if (omap_vout_setup_video_data(vout) != 0) { |
1491 | ret = -ENOMEM; |
1492 | goto error; |
1493 | } |
1494 | |
1495 | /* Allocate default number of buffers for the video streaming |
1496 | * and reserve the VRFB space for rotation |
1497 | */ |
1498 | if (omap_vout_setup_video_bufs(pdev, vid_num: k) != 0) { |
1499 | ret = -ENOMEM; |
1500 | goto error1; |
1501 | } |
1502 | |
1503 | /* Register the Video device with V4L2 |
1504 | */ |
1505 | vfd = vout->vfd; |
1506 | if (video_register_device(vdev: vfd, type: VFL_TYPE_VIDEO, nr: -1) < 0) { |
1507 | dev_err(&pdev->dev, |
1508 | ": Could not register Video for Linux device\n" ); |
1509 | vfd->minor = -1; |
1510 | ret = -ENODEV; |
1511 | goto error2; |
1512 | } |
1513 | video_set_drvdata(vdev: vfd, data: vout); |
1514 | |
1515 | dev_info(&pdev->dev, |
1516 | ": registered and initialized video device %d\n" , |
1517 | vfd->minor); |
1518 | if (k == (pdev->num_resources - 1)) |
1519 | return 0; |
1520 | |
1521 | continue; |
1522 | error2: |
1523 | if (vout->vid_info.rotation_type == VOUT_ROT_VRFB) |
1524 | omap_vout_release_vrfb(vout); |
1525 | error1: |
1526 | video_device_release(vdev: vfd); |
1527 | error: |
1528 | kfree(objp: vout); |
1529 | return ret; |
1530 | } |
1531 | |
1532 | return -ENODEV; |
1533 | } |
1534 | /* Driver functions */ |
1535 | static void omap_vout_cleanup_device(struct omap_vout_device *vout) |
1536 | { |
1537 | struct video_device *vfd; |
1538 | struct omapvideo_info *ovid; |
1539 | |
1540 | if (!vout) |
1541 | return; |
1542 | |
1543 | vfd = vout->vfd; |
1544 | ovid = &vout->vid_info; |
1545 | if (vfd) { |
1546 | if (!video_is_registered(vdev: vfd)) { |
1547 | /* |
1548 | * The device was never registered, so release the |
1549 | * video_device struct directly. |
1550 | */ |
1551 | video_device_release(vdev: vfd); |
1552 | } else { |
1553 | /* |
1554 | * The unregister function will release the video_device |
1555 | * struct as well as unregistering it. |
1556 | */ |
1557 | video_unregister_device(vdev: vfd); |
1558 | } |
1559 | } |
1560 | v4l2_ctrl_handler_free(hdl: &vout->ctrl_handler); |
1561 | if (ovid->rotation_type == VOUT_ROT_VRFB) { |
1562 | omap_vout_release_vrfb(vout); |
1563 | /* Free the VRFB buffer if allocated |
1564 | * init time |
1565 | */ |
1566 | if (vout->vrfb_static_allocation) |
1567 | omap_vout_free_vrfb_buffers(vout); |
1568 | } |
1569 | |
1570 | kfree(objp: vout); |
1571 | } |
1572 | |
1573 | static void omap_vout_remove(struct platform_device *pdev) |
1574 | { |
1575 | int k; |
1576 | struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); |
1577 | struct omap2video_device *vid_dev = container_of(v4l2_dev, struct |
1578 | omap2video_device, v4l2_dev); |
1579 | |
1580 | v4l2_device_unregister(v4l2_dev); |
1581 | for (k = 0; k < pdev->num_resources; k++) |
1582 | omap_vout_cleanup_device(vout: vid_dev->vouts[k]); |
1583 | |
1584 | for (k = 0; k < vid_dev->num_displays; k++) { |
1585 | if (vid_dev->displays[k]->state != OMAP_DSS_DISPLAY_DISABLED) |
1586 | vid_dev->displays[k]->driver->disable(vid_dev->displays[k]); |
1587 | |
1588 | omap_dss_put_device(dssdev: vid_dev->displays[k]); |
1589 | } |
1590 | kfree(objp: vid_dev); |
1591 | } |
1592 | |
1593 | static int __init omap_vout_probe(struct platform_device *pdev) |
1594 | { |
1595 | int ret = 0, i; |
1596 | struct omap_overlay *ovl; |
1597 | struct omap_dss_device *dssdev = NULL; |
1598 | struct omap_dss_device *def_display; |
1599 | struct omap2video_device *vid_dev = NULL; |
1600 | |
1601 | if (omapdss_is_initialized() == false) |
1602 | return -EPROBE_DEFER; |
1603 | |
1604 | ret = omapdss_compat_init(); |
1605 | if (ret) { |
1606 | dev_err(&pdev->dev, "failed to init dss\n" ); |
1607 | return ret; |
1608 | } |
1609 | |
1610 | if (pdev->num_resources == 0) { |
1611 | dev_err(&pdev->dev, "probed for an unknown device\n" ); |
1612 | ret = -ENODEV; |
1613 | goto err_dss_init; |
1614 | } |
1615 | |
1616 | vid_dev = kzalloc(size: sizeof(struct omap2video_device), GFP_KERNEL); |
1617 | if (vid_dev == NULL) { |
1618 | ret = -ENOMEM; |
1619 | goto err_dss_init; |
1620 | } |
1621 | |
1622 | vid_dev->num_displays = 0; |
1623 | for_each_dss_dev(dssdev) { |
1624 | omap_dss_get_device(dssdev); |
1625 | |
1626 | if (!dssdev->driver) { |
1627 | dev_warn(&pdev->dev, "no driver for display: %s\n" , |
1628 | dssdev->name); |
1629 | omap_dss_put_device(dssdev); |
1630 | continue; |
1631 | } |
1632 | |
1633 | vid_dev->displays[vid_dev->num_displays++] = dssdev; |
1634 | } |
1635 | |
1636 | if (vid_dev->num_displays == 0) { |
1637 | dev_err(&pdev->dev, "no displays\n" ); |
1638 | ret = -EINVAL; |
1639 | goto probe_err0; |
1640 | } |
1641 | |
1642 | vid_dev->num_overlays = omap_dss_get_num_overlays(); |
1643 | for (i = 0; i < vid_dev->num_overlays; i++) |
1644 | vid_dev->overlays[i] = omap_dss_get_overlay(num: i); |
1645 | |
1646 | vid_dev->num_managers = omap_dss_get_num_overlay_managers(); |
1647 | for (i = 0; i < vid_dev->num_managers; i++) |
1648 | vid_dev->managers[i] = omap_dss_get_overlay_manager(num: i); |
1649 | |
1650 | /* Get the Video1 overlay and video2 overlay. |
1651 | * Setup the Display attached to that overlays |
1652 | */ |
1653 | for (i = 1; i < vid_dev->num_overlays; i++) { |
1654 | ovl = omap_dss_get_overlay(num: i); |
1655 | dssdev = ovl->get_device(ovl); |
1656 | |
1657 | if (dssdev) { |
1658 | def_display = dssdev; |
1659 | } else { |
1660 | dev_warn(&pdev->dev, "cannot find display\n" ); |
1661 | def_display = NULL; |
1662 | } |
1663 | if (def_display) { |
1664 | struct omap_dss_driver *dssdrv = def_display->driver; |
1665 | |
1666 | ret = dssdrv->enable(def_display); |
1667 | if (ret) { |
1668 | /* Here we are not considering a error |
1669 | * as display may be enabled by frame |
1670 | * buffer driver |
1671 | */ |
1672 | dev_warn(&pdev->dev, |
1673 | "'%s' Display already enabled\n" , |
1674 | def_display->name); |
1675 | } |
1676 | } |
1677 | } |
1678 | |
1679 | if (v4l2_device_register(dev: &pdev->dev, v4l2_dev: &vid_dev->v4l2_dev) < 0) { |
1680 | dev_err(&pdev->dev, "v4l2_device_register failed\n" ); |
1681 | ret = -ENODEV; |
1682 | goto probe_err1; |
1683 | } |
1684 | |
1685 | ret = omap_vout_create_video_devices(pdev); |
1686 | if (ret) |
1687 | goto probe_err2; |
1688 | |
1689 | for (i = 0; i < vid_dev->num_displays; i++) { |
1690 | struct omap_dss_device *display = vid_dev->displays[i]; |
1691 | |
1692 | if (display->driver->update) |
1693 | display->driver->update(display, 0, 0, |
1694 | display->panel.timings.x_res, |
1695 | display->panel.timings.y_res); |
1696 | } |
1697 | return 0; |
1698 | |
1699 | probe_err2: |
1700 | v4l2_device_unregister(v4l2_dev: &vid_dev->v4l2_dev); |
1701 | probe_err1: |
1702 | for (i = 1; i < vid_dev->num_overlays; i++) { |
1703 | def_display = NULL; |
1704 | ovl = omap_dss_get_overlay(num: i); |
1705 | dssdev = ovl->get_device(ovl); |
1706 | |
1707 | if (dssdev) |
1708 | def_display = dssdev; |
1709 | |
1710 | if (def_display && def_display->driver) |
1711 | def_display->driver->disable(def_display); |
1712 | } |
1713 | probe_err0: |
1714 | kfree(objp: vid_dev); |
1715 | err_dss_init: |
1716 | omapdss_compat_uninit(); |
1717 | return ret; |
1718 | } |
1719 | |
1720 | static struct platform_driver omap_vout_driver = { |
1721 | .driver = { |
1722 | .name = VOUT_NAME, |
1723 | }, |
1724 | .remove_new = omap_vout_remove, |
1725 | }; |
1726 | |
1727 | static int __init omap_vout_init(void) |
1728 | { |
1729 | if (platform_driver_probe(&omap_vout_driver, omap_vout_probe) != 0) { |
1730 | printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n" ); |
1731 | return -EINVAL; |
1732 | } |
1733 | return 0; |
1734 | } |
1735 | |
1736 | static void omap_vout_cleanup(void) |
1737 | { |
1738 | platform_driver_unregister(&omap_vout_driver); |
1739 | } |
1740 | |
1741 | late_initcall(omap_vout_init); |
1742 | module_exit(omap_vout_cleanup); |
1743 | |