diff options
author | Hemant Hariyani <hemanthariyani@ti.com> | 2011-08-25 02:57:15 -0400 |
---|---|---|
committer | Paolo Pisati <paolo.pisati@canonical.com> | 2012-08-17 04:18:33 -0400 |
commit | 6e90775f610ab87bd86a79f189aca993e44b3bdf (patch) | |
tree | 3b44756d6a8e69dea6c0dbeb2244482b31d32ffc /drivers/gpu/pvr/display/omap_display.c | |
parent | 1d63da95fb8e073f94c3c63d82f91c49bb2bd08a (diff) |
SGX-KM Initial SGX driver integration for 2.6.35 kernel.
This is the first version of SGX driver integration to ensure UI
boot-up. Power management and hwmod modifications will be added
as patches.
Change-Id: If71e6cd651a53f4809e7b978b693cb7d1a89178d
Signed-off-by: Hemant Hariyani <hemanthariyani@ti.com>
Diffstat (limited to 'drivers/gpu/pvr/display/omap_display.c')
-rw-r--r-- | drivers/gpu/pvr/display/omap_display.c | 1078 |
1 files changed, 1078 insertions, 0 deletions
diff --git a/drivers/gpu/pvr/display/omap_display.c b/drivers/gpu/pvr/display/omap_display.c new file mode 100644 index 00000000000..1fb5f353870 --- /dev/null +++ b/drivers/gpu/pvr/display/omap_display.c | |||
@@ -0,0 +1,1078 @@ | |||
1 | /* | ||
2 | * drivers/gpu/pvr/display/omap_display.c | ||
3 | * | ||
4 | * Copyright (C) 2010 Texas Instruments | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License version 2 as published by | ||
8 | * the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along with | ||
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/fb.h> | ||
22 | |||
23 | #include <plat/vrfb.h> | ||
24 | #include <plat/display.h> | ||
25 | |||
26 | /* Workaround for DEBUG macro clash in framebuffer */ | ||
27 | #ifdef RELEASE | ||
28 | #include <../drivers/video/omap2/omapfb/omapfb.h> | ||
29 | #undef DEBUG | ||
30 | #else | ||
31 | #undef DEBUG | ||
32 | #include <../drivers/video/omap2/omapfb/omapfb.h> | ||
33 | #endif | ||
34 | |||
35 | #define OMAP_DISP_DRV_NAME "omap_display" | ||
36 | #define OMAP_DISP_FRAMEBUFFER_COUNT num_registered_fb | ||
37 | |||
38 | #define OMAP_DISP_PAGE_MASK (PAGE_SIZE - 1) | ||
39 | #define OMAP_DISP_PAGE_TRUNCATE (~OMAP_DISP_PAGE_MASK) | ||
40 | #define OMAP_DISP_PAGE_ROUND_UP(x) \ | ||
41 | (((x)+OMAP_DISP_PAGE_MASK) & OMAP_DISP_PAGE_TRUNCATE) | ||
42 | |||
43 | #define OMAP_DISP_IRQ_TIMEOUT 500 | ||
44 | |||
45 | #ifdef DEBUG | ||
46 | #define DBG_PRINT(format, ...) printk(KERN_DEBUG OMAP_DISP_DRV_NAME \ | ||
47 | " (%s %i): " format "\n", __func__, __LINE__, ## __VA_ARGS__) | ||
48 | #define WRN_PRINT(format, ...) printk(KERN_WARNING OMAP_DISP_DRV_NAME \ | ||
49 | " (%s %i): " format "\n", __func__, __LINE__, ## __VA_ARGS__) | ||
50 | #define ERR_PRINT(format, ...) printk(KERN_ERR OMAP_DISP_DRV_NAME \ | ||
51 | " (%s %i): " format "\n", __func__, __LINE__, ## __VA_ARGS__) | ||
52 | #else | ||
53 | #define DBG_PRINT(format, ...) | ||
54 | #define WRN_PRINT(format, ...) | ||
55 | #define ERR_PRINT(format, ...) | ||
56 | #endif | ||
57 | |||
58 | #include "omap_display.h" | ||
59 | |||
60 | /* List for the available displays */ | ||
61 | static struct omap_display_device *omap_display_list; | ||
62 | static unsigned int omap_display_number; | ||
63 | |||
64 | /* Forward declarations */ | ||
65 | static struct omap_display_buffer *create_main_buffer( | ||
66 | struct omap_display_device *display); | ||
67 | static int display_destroy_buffer(struct omap_display_buffer *buffer); | ||
68 | |||
69 | static int open_display(struct omap_display_device *display, | ||
70 | enum omap_display_feature features) | ||
71 | { | ||
72 | int i; | ||
73 | |||
74 | DBG_PRINT("Opening display '%s'", display->name); | ||
75 | |||
76 | /* TODO: Support horizontal orientation */ | ||
77 | if (features & ORIENTATION_HORIZONTAL) { | ||
78 | DBG_PRINT("Horizontal orientation is not supported yet , " | ||
79 | "falling back to vertical orientation"); | ||
80 | features = ORIENTATION_VERTICAL; | ||
81 | } | ||
82 | |||
83 | display->features = features; | ||
84 | display->reference_count++; | ||
85 | for (i = 0; i < display->overlay_managers_count; i++) | ||
86 | omap_dss_get_device(display->overlay_managers[i]->device); | ||
87 | |||
88 | /* If the main buffer doesn't exist create it */ | ||
89 | if (!display->main_buffer) { | ||
90 | DBG_PRINT("Main buffer doesn't exist for display '%s', create" | ||
91 | " one", display->name); | ||
92 | display->main_buffer = create_main_buffer(display); | ||
93 | if (!display->main_buffer) { | ||
94 | ERR_PRINT("Failed to create main buffer for '%s'", | ||
95 | display->name); | ||
96 | return 1; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static int close_display(struct omap_display_device *display) | ||
104 | { | ||
105 | int err; | ||
106 | int i; | ||
107 | |||
108 | /* TODO: Is it the same thing to close a virtual and single display? */ | ||
109 | DBG_PRINT("Closing display '%s'", display->name); | ||
110 | |||
111 | display->reference_count--; | ||
112 | for (i = 0; i < display->overlay_managers_count; i++) | ||
113 | omap_dss_put_device(display->overlay_managers[i]->device); | ||
114 | |||
115 | if (display->flip_chain) { | ||
116 | err = display->destroy_flip_chain(display); | ||
117 | display->flip_chain = 0; | ||
118 | if (err) | ||
119 | WRN_PRINT("An error happened when destroying flip " | ||
120 | "chain for '%s'", display->name); | ||
121 | } | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int get_max_buffers(struct omap_display_device *display) | ||
127 | { | ||
128 | /* TODO: If TILER is wanted to be used how do you calculate this? */ | ||
129 | int fb_idx; | ||
130 | switch (display->id) { | ||
131 | case OMAP_DISPID_PRIMARY: | ||
132 | fb_idx = 0; | ||
133 | break; | ||
134 | case OMAP_DISPID_SECONDARY: | ||
135 | fb_idx = 1; | ||
136 | break; | ||
137 | case OMAP_DISPID_TERTIARY: | ||
138 | fb_idx = 2; | ||
139 | break; | ||
140 | case OMAP_DISPID_VIRTUAL: | ||
141 | fb_idx = 0; | ||
142 | break; | ||
143 | case OMAP_DISPID_BADSTATE: | ||
144 | default: | ||
145 | ERR_PRINT("Unknown display id %i", display->id); | ||
146 | BUG(); | ||
147 | } | ||
148 | |||
149 | /* Use the framebuffer memory */ | ||
150 | if (fb_idx >= 0 && fb_idx < num_registered_fb) { | ||
151 | struct fb_info *framebuffer = registered_fb[fb_idx]; | ||
152 | unsigned long buffer_size; | ||
153 | |||
154 | /* Single buffer size */ | ||
155 | buffer_size = display->width * display->height * | ||
156 | display->bytes_per_pixel; | ||
157 | /* Page align the buffer size, round up to the page size */ | ||
158 | buffer_size = OMAP_DISP_PAGE_ROUND_UP(buffer_size); | ||
159 | |||
160 | return (int) (framebuffer->fix.smem_len / buffer_size); | ||
161 | } else { | ||
162 | ERR_PRINT("Framebuffer %i doesn't exist for display '%s'", | ||
163 | fb_idx, display->name); | ||
164 | return 0; | ||
165 | } | ||
166 | } | ||
167 | |||
168 | static int create_flip_chain(struct omap_display_device *display, | ||
169 | unsigned int buffer_count) | ||
170 | { | ||
171 | int fb_idx; | ||
172 | |||
173 | /* TODO: What about TILER buffers */ | ||
174 | if (buffer_count <= 1) { | ||
175 | ERR_PRINT("Flip chains with %i buffers not supported", | ||
176 | buffer_count); | ||
177 | return 1; | ||
178 | } else if (buffer_count > display->buffers_available) { | ||
179 | ERR_PRINT("Requesting %i buffers when there is %i available" | ||
180 | " for '%s'", buffer_count, display->buffers_available, | ||
181 | display->name); | ||
182 | return 1; | ||
183 | } else if (display->flip_chain) { | ||
184 | ERR_PRINT("Flip chain already exists for '%s'", display->name); | ||
185 | return 1; | ||
186 | } | ||
187 | |||
188 | /* Create the flip chain with the framebuffer memory */ | ||
189 | switch (display->id) { | ||
190 | case OMAP_DISPID_PRIMARY: | ||
191 | fb_idx = 0; | ||
192 | break; | ||
193 | case OMAP_DISPID_SECONDARY: | ||
194 | fb_idx = 1; | ||
195 | break; | ||
196 | case OMAP_DISPID_TERTIARY: | ||
197 | fb_idx = 2; | ||
198 | break; | ||
199 | case OMAP_DISPID_VIRTUAL: | ||
200 | fb_idx = 0; | ||
201 | break; | ||
202 | case OMAP_DISPID_BADSTATE: | ||
203 | default: | ||
204 | ERR_PRINT("Unknown display id %i", display->id); | ||
205 | BUG(); | ||
206 | } | ||
207 | |||
208 | /* Use the framebuffer memory */ | ||
209 | if (fb_idx >= 0 && fb_idx < num_registered_fb) { | ||
210 | struct fb_info *framebuffer = registered_fb[fb_idx]; | ||
211 | unsigned long buffer_size; | ||
212 | struct omap_display_flip_chain *flip_chain; | ||
213 | int i; | ||
214 | |||
215 | if (!framebuffer || !framebuffer->fix.smem_start || | ||
216 | !framebuffer->screen_base) { | ||
217 | ERR_PRINT("Framebuffer %i doesn't seem to be " | ||
218 | "initialized", fb_idx); | ||
219 | return 1; | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | * Check if there is enough memory in the fb for the requested | ||
224 | * buffers | ||
225 | */ | ||
226 | buffer_size = display->width * display->height * | ||
227 | display->bytes_per_pixel; | ||
228 | /* Page align the buffer size, round up to the page size */ | ||
229 | buffer_size = OMAP_DISP_PAGE_ROUND_UP(buffer_size); | ||
230 | |||
231 | if (buffer_size * buffer_count > framebuffer->fix.smem_len) { | ||
232 | ERR_PRINT("Not enough memory to allocate %i buffers " | ||
233 | "(%lu bytes each), memory available %lu for " | ||
234 | "display '%s'", buffer_count, buffer_size, | ||
235 | (unsigned long)framebuffer->fix.smem_len, | ||
236 | display->name); | ||
237 | return 1; | ||
238 | } | ||
239 | |||
240 | flip_chain = kzalloc(sizeof(*flip_chain), GFP_KERNEL); | ||
241 | |||
242 | if (!flip_chain) { | ||
243 | ERR_PRINT("Out of memory"); | ||
244 | return 1; | ||
245 | } | ||
246 | |||
247 | for (i = 0; i < buffer_count; i++) { | ||
248 | struct omap_display_buffer *buffer; | ||
249 | |||
250 | /* | ||
251 | * Reuse the main buffer as the first buffer in the | ||
252 | * flip chain | ||
253 | */ | ||
254 | if (i == 0) { | ||
255 | buffer = display->main_buffer; | ||
256 | flip_chain->buffers[i] = buffer; | ||
257 | DBG_PRINT("Flip chain buffer %i has address " | ||
258 | "%lx for display '%s'", i, | ||
259 | buffer->physical_addr, display->name); | ||
260 | continue; | ||
261 | } | ||
262 | |||
263 | buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); | ||
264 | |||
265 | if (!buffer) { | ||
266 | /* | ||
267 | * FIXME: If one buffer allocation fails, | ||
268 | * deallocate flip chain and buffers | ||
269 | */ | ||
270 | ERR_PRINT("Out of memory"); | ||
271 | return 1; | ||
272 | } | ||
273 | |||
274 | buffer->physical_addr = framebuffer->fix.smem_start + | ||
275 | (buffer_size * i); | ||
276 | buffer->virtual_addr = | ||
277 | (unsigned long) framebuffer->screen_base + | ||
278 | (buffer_size * i); | ||
279 | buffer->size = buffer_size; | ||
280 | buffer->display = display; | ||
281 | flip_chain->buffers[i] = buffer; | ||
282 | |||
283 | DBG_PRINT("Flip chain buffer %i has address %lx for" | ||
284 | " display '%s'", i, buffer->physical_addr, | ||
285 | display->name); | ||
286 | } | ||
287 | |||
288 | display->flip_chain = flip_chain; | ||
289 | return 0; | ||
290 | } else { | ||
291 | ERR_PRINT("Framebuffer %i doesn't exist for display '%s'", | ||
292 | fb_idx, display->name); | ||
293 | return 1; | ||
294 | } | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static int destroy_flip_chain(struct omap_display_device *display) | ||
300 | { | ||
301 | int i; | ||
302 | int err; | ||
303 | |||
304 | if (!display->flip_chain) { | ||
305 | DBG_PRINT("No flip chain to destroy for '%s'", display->name); | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | for (i = 0; i < display->flip_chain->buffer_count; i++) { | ||
310 | struct omap_display_buffer *buffer = | ||
311 | display->flip_chain->buffers[i]; | ||
312 | /* If buffer is main buffer don't touch it */ | ||
313 | if (display->main_buffer == buffer) | ||
314 | continue; | ||
315 | |||
316 | err = display_destroy_buffer(buffer); | ||
317 | if (err) { | ||
318 | ERR_PRINT("Error destroying buffer in flip chain for" | ||
319 | " '%s'", display->name); | ||
320 | return 1; | ||
321 | } | ||
322 | } | ||
323 | |||
324 | DBG_PRINT("Destroying flip chain for '%s'", display->name); | ||
325 | kfree(display->flip_chain); | ||
326 | display->flip_chain = 0; | ||
327 | |||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | static int rotate_display(struct omap_display_device *display, | ||
332 | unsigned int rotation) | ||
333 | { | ||
334 | ERR_PRINT("Not supported yet"); | ||
335 | return 1; | ||
336 | } | ||
337 | |||
338 | static int display_destroy_buffer(struct omap_display_buffer *buffer) | ||
339 | { | ||
340 | kfree(buffer); | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static int present_buffer_virtual(struct omap_display_buffer *buffer) | ||
345 | { | ||
346 | /* | ||
347 | * TODO: Support for ORIENTATION_VERTICAL is in place, | ||
348 | * ORIENTATION_HORIZONTAL is missing | ||
349 | */ | ||
350 | struct omap_display_device *display_virtual = buffer->display; | ||
351 | struct omap_display_device *display_primary; | ||
352 | struct omap_display_device *display_secondary; | ||
353 | struct omap_display_buffer temp_buffer; | ||
354 | unsigned int buffer_offset; | ||
355 | |||
356 | if (display_virtual->id != OMAP_DISPID_VIRTUAL) { | ||
357 | ERR_PRINT("Not a virtual display"); | ||
358 | BUG(); | ||
359 | } | ||
360 | |||
361 | display_primary = omap_display_get(OMAP_DISPID_PRIMARY); | ||
362 | display_secondary = omap_display_get(OMAP_DISPID_SECONDARY); | ||
363 | /* | ||
364 | * Calculate offset without page alignment round up otherwise second | ||
365 | * display may see incorrect data | ||
366 | */ | ||
367 | buffer_offset = display_primary->height * display_virtual->byte_stride; | ||
368 | |||
369 | /* The first buffer will be the base */ | ||
370 | temp_buffer.physical_addr = buffer->physical_addr; | ||
371 | temp_buffer.virtual_addr = buffer->virtual_addr; | ||
372 | temp_buffer.size = buffer->size >> 1; | ||
373 | |||
374 | if (display_virtual->features & ORIENTATION_INVERT) { | ||
375 | /* Secondary display has the base */ | ||
376 | temp_buffer.display = display_secondary; | ||
377 | display_secondary->present_buffer(&temp_buffer); | ||
378 | } else { | ||
379 | /* Primary display has the base */ | ||
380 | temp_buffer.display = display_primary; | ||
381 | display_primary->present_buffer(&temp_buffer); | ||
382 | } | ||
383 | |||
384 | /* Remaining display will show the rest */ | ||
385 | temp_buffer.physical_addr = buffer->physical_addr + buffer_offset; | ||
386 | temp_buffer.virtual_addr = buffer->virtual_addr + buffer_offset; | ||
387 | |||
388 | if (display_virtual->features & ORIENTATION_INVERT) { | ||
389 | temp_buffer.display = display_primary; | ||
390 | display_primary->present_buffer(&temp_buffer); | ||
391 | } else { | ||
392 | temp_buffer.display = display_secondary; | ||
393 | display_secondary->present_buffer(&temp_buffer); | ||
394 | } | ||
395 | |||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static int present_buffer(struct omap_display_buffer *buffer) | ||
400 | { | ||
401 | struct omap_display_device *display = buffer->display; | ||
402 | int fb_idx; | ||
403 | |||
404 | switch (display->id) { | ||
405 | case OMAP_DISPID_PRIMARY: | ||
406 | fb_idx = 0; | ||
407 | break; | ||
408 | case OMAP_DISPID_SECONDARY: | ||
409 | fb_idx = 1; | ||
410 | break; | ||
411 | case OMAP_DISPID_TERTIARY: | ||
412 | fb_idx = 2; | ||
413 | break; | ||
414 | case OMAP_DISPID_VIRTUAL: | ||
415 | case OMAP_DISPID_BADSTATE: | ||
416 | default: | ||
417 | ERR_PRINT("Unable to handle display %i", display->id); | ||
418 | BUG(); | ||
419 | } | ||
420 | |||
421 | if (fb_idx >= 0 && fb_idx < num_registered_fb) { | ||
422 | struct fb_info *framebuffer = registered_fb[fb_idx]; | ||
423 | struct omapfb_info *ofbi = FB2OFB(framebuffer); | ||
424 | struct omapfb2_device *fbdev = ofbi->fbdev; | ||
425 | struct omap_overlay *overlay; | ||
426 | struct omap_overlay_info overlay_info; | ||
427 | int i; | ||
428 | |||
429 | omapfb_lock(fbdev); | ||
430 | |||
431 | /* Get the overlays attached to the framebuffer */ | ||
432 | for (i = 0; i < ofbi->num_overlays ; i++) { | ||
433 | overlay = ofbi->overlays[i]; | ||
434 | overlay->get_overlay_info(overlay, &overlay_info); | ||
435 | |||
436 | /* If the overlay is not enabled don't update it */ | ||
437 | if (!overlay_info.enabled) | ||
438 | continue; | ||
439 | |||
440 | overlay_info.paddr = buffer->physical_addr; | ||
441 | overlay_info.vaddr = (void *) buffer->virtual_addr; | ||
442 | overlay->set_overlay_info(overlay, &overlay_info); | ||
443 | |||
444 | if (overlay->manager) { | ||
445 | overlay->manager->apply(overlay->manager); | ||
446 | if (overlay->manager->device && | ||
447 | overlay->manager->device->update) { | ||
448 | overlay->manager->device->update( | ||
449 | overlay->manager->device, | ||
450 | 0, 0, | ||
451 | overlay_info.width, | ||
452 | overlay_info.height); | ||
453 | } | ||
454 | } | ||
455 | } | ||
456 | |||
457 | omapfb_unlock(fbdev); | ||
458 | } else { | ||
459 | ERR_PRINT("Framebuffer %i doesn't exist for display '%s'", | ||
460 | fb_idx, display->name); | ||
461 | return 1; | ||
462 | } | ||
463 | |||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | static u32 get_dss_irq(struct omap_dss_device *dss_device) | ||
468 | { | ||
469 | u32 irq; | ||
470 | |||
471 | if (dss_device->type == OMAP_DISPLAY_TYPE_VENC) | ||
472 | irq = DISPC_IRQ_EVSYNC_ODD; | ||
473 | else if (dss_device->type == OMAP_DISPLAY_TYPE_HDMI) | ||
474 | irq = DISPC_IRQ_EVSYNC_EVEN; | ||
475 | else if (dss_device->type == OMAP_DISPLAY_TYPE_DSI) | ||
476 | if (!strcmp(dss_device->name, "lcd")) | ||
477 | irq = DISPC_IRQ_FRAMEDONE; | ||
478 | else | ||
479 | irq = DISPC_IRQ_FRAMEDONE2; | ||
480 | else | ||
481 | if (!strcmp(dss_device->name, "lcd")) | ||
482 | irq = DISPC_IRQ_VSYNC; | ||
483 | else | ||
484 | irq = DISPC_IRQ_VSYNC2; | ||
485 | |||
486 | return irq; | ||
487 | } | ||
488 | |||
489 | static int present_buffer_sync(struct omap_display_buffer *buffer) | ||
490 | { | ||
491 | /* TODO: Cloning may tear with this implementation */ | ||
492 | int err = 0; | ||
493 | struct omap_display_device *display = buffer->display; | ||
494 | |||
495 | err = display->sync(display); | ||
496 | err |= display->present_buffer(buffer); | ||
497 | |||
498 | return err; | ||
499 | } | ||
500 | |||
501 | static int present_buffer_sync_virtual(struct omap_display_buffer *buffer) | ||
502 | { | ||
503 | void display_irq_wait_1(void *data, u32 mask) | ||
504 | { | ||
505 | struct omap_display_sync_item *sync_item = | ||
506 | (struct omap_display_sync_item *) data; | ||
507 | |||
508 | if (sync_item->invalidate) | ||
509 | return; | ||
510 | |||
511 | /* IRQ happened, present immediately */ | ||
512 | sync_item->invalidate = 1; | ||
513 | sync_item->buffer->display->present_buffer(sync_item->buffer); | ||
514 | complete(sync_item->task); | ||
515 | } | ||
516 | |||
517 | void display_irq_wait_2(void *data, u32 mask) | ||
518 | { | ||
519 | struct omap_display_sync_item *sync_item = | ||
520 | (struct omap_display_sync_item *) data; | ||
521 | |||
522 | if (sync_item->invalidate) | ||
523 | return; | ||
524 | |||
525 | /* IRQ happened, present immediately */ | ||
526 | sync_item->invalidate = 1; | ||
527 | sync_item->buffer->display->present_buffer(sync_item->buffer); | ||
528 | complete(sync_item->task); | ||
529 | } | ||
530 | |||
531 | /* | ||
532 | * TODO: Support for ORIENTATION_VERTICAL is in place, | ||
533 | * ORIENTATION_HORIZONTAL is missing. Some code can be reduced here, | ||
534 | * it will be simplified in the future. | ||
535 | */ | ||
536 | struct omap_display_device *display_virtual = buffer->display; | ||
537 | struct omap_display_device *display_primary; | ||
538 | struct omap_display_device *display_secondary; | ||
539 | struct omap_display_buffer temp_buffer_top; | ||
540 | struct omap_display_buffer temp_buffer_bottom; | ||
541 | struct omap_display_sync_item sync_item_primary; | ||
542 | struct omap_display_sync_item sync_item_secondary; | ||
543 | DECLARE_COMPLETION_ONSTACK(completion_primary); | ||
544 | DECLARE_COMPLETION_ONSTACK(completion_secondary); | ||
545 | unsigned int buffer_offset; | ||
546 | |||
547 | if (display_virtual->id != OMAP_DISPID_VIRTUAL) { | ||
548 | ERR_PRINT("Not a virtual display"); | ||
549 | BUG(); | ||
550 | } | ||
551 | |||
552 | display_primary = omap_display_get(OMAP_DISPID_PRIMARY); | ||
553 | display_secondary = omap_display_get(OMAP_DISPID_SECONDARY); | ||
554 | /* | ||
555 | * Calculate offset without page alignment round up otherwise second | ||
556 | * display may see incorrect data | ||
557 | */ | ||
558 | buffer_offset = display_primary->height * display_virtual->byte_stride; | ||
559 | |||
560 | /* The first buffer will be the top */ | ||
561 | temp_buffer_top.physical_addr = buffer->physical_addr; | ||
562 | temp_buffer_top.virtual_addr = buffer->virtual_addr; | ||
563 | temp_buffer_top.size = buffer->size >> 1; | ||
564 | /* Then the bottom */ | ||
565 | temp_buffer_bottom.physical_addr = buffer->physical_addr + | ||
566 | buffer_offset; | ||
567 | temp_buffer_bottom.virtual_addr = buffer->virtual_addr + buffer_offset; | ||
568 | temp_buffer_bottom.size = buffer->size >> 1; | ||
569 | |||
570 | if (display_virtual->features & ORIENTATION_INVERT) { | ||
571 | /* Secondary display has the base */ | ||
572 | temp_buffer_top.display = display_secondary; | ||
573 | temp_buffer_bottom.display = display_primary; | ||
574 | sync_item_primary.buffer = &temp_buffer_bottom; | ||
575 | sync_item_secondary.buffer = &temp_buffer_top; | ||
576 | |||
577 | } else { | ||
578 | /* Primary display has the base */ | ||
579 | temp_buffer_top.display = display_primary; | ||
580 | temp_buffer_bottom.display = display_secondary; | ||
581 | sync_item_primary.buffer = &temp_buffer_top; | ||
582 | sync_item_secondary.buffer = &temp_buffer_bottom; | ||
583 | } | ||
584 | |||
585 | sync_item_primary.task = &completion_primary; | ||
586 | sync_item_secondary.task = &completion_secondary; | ||
587 | sync_item_primary.invalidate = 0; | ||
588 | sync_item_secondary.invalidate = 0; | ||
589 | |||
590 | /* Register an ISR per display with its corresponding IRQ */ | ||
591 | omap_dispc_register_isr(display_irq_wait_1, &sync_item_primary, | ||
592 | get_dss_irq(display_primary->overlay_managers[0]->device)); | ||
593 | |||
594 | omap_dispc_register_isr(display_irq_wait_2, &sync_item_secondary, | ||
595 | get_dss_irq(display_secondary->overlay_managers[0]->device)); | ||
596 | |||
597 | /* Wait until each display sync and present */ | ||
598 | wait_for_completion_interruptible_timeout(&completion_primary, | ||
599 | OMAP_DISP_IRQ_TIMEOUT); | ||
600 | wait_for_completion_interruptible_timeout(&completion_secondary, | ||
601 | OMAP_DISP_IRQ_TIMEOUT); | ||
602 | |||
603 | /* Unregister ISRs */ | ||
604 | omap_dispc_unregister_isr(display_irq_wait_1, &sync_item_primary, | ||
605 | get_dss_irq(display_primary->overlay_managers[0]->device)); | ||
606 | |||
607 | omap_dispc_unregister_isr(display_irq_wait_2, &sync_item_secondary, | ||
608 | get_dss_irq(display_secondary->overlay_managers[0]->device)); | ||
609 | |||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | static int display_sync(struct omap_display_device *display) | ||
614 | { | ||
615 | /* TODO: Synchronize properly with multiple managers */ | ||
616 | struct omap_dss_device *dss_device = | ||
617 | display->overlay_managers[0]->device; | ||
618 | if (!dss_device || !dss_device->wait_vsync) { | ||
619 | ERR_PRINT("Unable to synchronize with '%s'", display->name); | ||
620 | return 1; | ||
621 | } | ||
622 | dss_device->wait_vsync(dss_device); | ||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | static int display_sync_virtual(struct omap_display_device *display_virtual) | ||
627 | { | ||
628 | void display_irq_wait(void *data, u32 mask) | ||
629 | { | ||
630 | complete((struct completion *)data); | ||
631 | } | ||
632 | |||
633 | /* | ||
634 | * Return as soon as one display generates an IRQ | ||
635 | */ | ||
636 | struct omap_display_device *display_primary; | ||
637 | struct omap_display_device *display_secondary; | ||
638 | u32 irq_primary; | ||
639 | u32 irq_secondary; | ||
640 | u32 irq_mask; | ||
641 | DECLARE_COMPLETION_ONSTACK(completion); | ||
642 | |||
643 | if (display_virtual->id != OMAP_DISPID_VIRTUAL) { | ||
644 | ERR_PRINT("Not a virtual display"); | ||
645 | BUG(); | ||
646 | } | ||
647 | |||
648 | display_primary = omap_display_get(OMAP_DISPID_PRIMARY); | ||
649 | display_secondary = omap_display_get(OMAP_DISPID_SECONDARY); | ||
650 | |||
651 | irq_primary = get_dss_irq( | ||
652 | display_primary->overlay_managers[0]->device); | ||
653 | |||
654 | irq_secondary = get_dss_irq( | ||
655 | display_secondary->overlay_managers[0]->device); | ||
656 | |||
657 | irq_mask = irq_primary | irq_secondary; | ||
658 | |||
659 | /* Register an ISR with both IRQs and wait, then unregister */ | ||
660 | omap_dispc_register_isr(display_irq_wait, &completion, irq_mask); | ||
661 | |||
662 | wait_for_completion_interruptible_timeout(&completion, | ||
663 | OMAP_DISP_IRQ_TIMEOUT); | ||
664 | |||
665 | omap_dispc_unregister_isr(display_irq_wait, &completion, irq_mask); | ||
666 | |||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | static struct omap_display_buffer *create_main_buffer( | ||
671 | struct omap_display_device *display) | ||
672 | { | ||
673 | int fb_idx; | ||
674 | switch (display->id) { | ||
675 | case OMAP_DISPID_PRIMARY: | ||
676 | fb_idx = 0; | ||
677 | break; | ||
678 | case OMAP_DISPID_SECONDARY: | ||
679 | fb_idx = 1; | ||
680 | break; | ||
681 | case OMAP_DISPID_TERTIARY: | ||
682 | fb_idx = 2; | ||
683 | break; | ||
684 | case OMAP_DISPID_VIRTUAL: | ||
685 | /* Use fb0 for virtual display */ | ||
686 | fb_idx = 0; | ||
687 | break; | ||
688 | case OMAP_DISPID_BADSTATE: | ||
689 | default: | ||
690 | ERR_PRINT("Unknown display id %i", display->id); | ||
691 | BUG(); | ||
692 | } | ||
693 | |||
694 | /* Use the framebuffer memory */ | ||
695 | if (fb_idx >= 0 && fb_idx < num_registered_fb) { | ||
696 | struct fb_info *framebuffer = registered_fb[fb_idx]; | ||
697 | unsigned long buffer_size; | ||
698 | struct omap_display_buffer *buffer; | ||
699 | |||
700 | if (!framebuffer || !framebuffer->fix.smem_start || | ||
701 | !framebuffer->screen_base) { | ||
702 | ERR_PRINT("Framebuffer %i doesn't seem to be " | ||
703 | "initialized", fb_idx); | ||
704 | return NULL; | ||
705 | } | ||
706 | |||
707 | /* | ||
708 | * Check if there is enough memory in the fb for the | ||
709 | * main buffer | ||
710 | */ | ||
711 | buffer_size = display->width * display->height * | ||
712 | display->bytes_per_pixel; | ||
713 | /* Page align the buffer size */ | ||
714 | buffer_size = OMAP_DISP_PAGE_ROUND_UP(buffer_size); | ||
715 | |||
716 | if (buffer_size > framebuffer->fix.smem_len) { | ||
717 | ERR_PRINT("Main buffer needs %lu bytes while the " | ||
718 | "framebuffer %i has only %lu bytes for display" | ||
719 | " '%s'", buffer_size, fb_idx, | ||
720 | (unsigned long)framebuffer->fix.smem_len, | ||
721 | display->name); | ||
722 | return NULL; | ||
723 | } | ||
724 | |||
725 | buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); | ||
726 | |||
727 | if (!buffer) { | ||
728 | ERR_PRINT("Out of memory"); | ||
729 | return NULL; | ||
730 | } | ||
731 | |||
732 | /* Use base addresses reported by the framebuffer */ | ||
733 | buffer->physical_addr = framebuffer->fix.smem_start; | ||
734 | buffer->virtual_addr = | ||
735 | (unsigned long) framebuffer->screen_base; | ||
736 | buffer->size = buffer_size; | ||
737 | buffer->display = display; | ||
738 | |||
739 | DBG_PRINT("Created main buffer %lx for display '%s'", | ||
740 | buffer->physical_addr, display->name); | ||
741 | |||
742 | return buffer; | ||
743 | } else { | ||
744 | ERR_PRINT("Framebuffer %i doesn't exist for display '%s'", | ||
745 | fb_idx, display->name); | ||
746 | return NULL; | ||
747 | } | ||
748 | } | ||
749 | |||
750 | static int populate_display_info(struct omap_display_device *display, | ||
751 | struct omap_overlay_manager *overlay_manager) | ||
752 | { | ||
753 | struct omap_dss_device *dss_device = overlay_manager->device; | ||
754 | u16 xres; | ||
755 | u16 yres; | ||
756 | int i; | ||
757 | |||
758 | if (!strcmp(dss_device->name, "lcd")) { | ||
759 | display->id = OMAP_DISPID_PRIMARY; | ||
760 | display->name = "primary"; | ||
761 | } else if (!strcmp(dss_device->name, "2lcd")) { | ||
762 | display->id = OMAP_DISPID_SECONDARY; | ||
763 | display->name = "secondary"; | ||
764 | } else if (!strcmp(dss_device->name, "hdmi")) { | ||
765 | display->id = OMAP_DISPID_TERTIARY; | ||
766 | display->name = "tertiary"; | ||
767 | } else { | ||
768 | ERR_PRINT("Display id '%s' not supported", dss_device->name); | ||
769 | return 1; | ||
770 | } | ||
771 | |||
772 | dss_device->get_resolution(dss_device, &xres, &yres); | ||
773 | if (xres == 0 || yres == 0) { | ||
774 | ERR_PRINT("Unable to handle display '%s' with width %i " | ||
775 | "and height %i", dss_device->name, xres, yres); | ||
776 | return 1; | ||
777 | } | ||
778 | |||
779 | display->width = xres; | ||
780 | display->height = yres; | ||
781 | |||
782 | display->bits_per_pixel = dss_device->get_recommended_bpp(dss_device); | ||
783 | switch (display->bits_per_pixel) { | ||
784 | case 16: | ||
785 | /* | ||
786 | * TODO: Asume RGB_565, maybe need to double check in | ||
787 | * the DSS if this is true | ||
788 | */ | ||
789 | display->pixel_format = RGB_565; | ||
790 | display->bytes_per_pixel = 2; | ||
791 | break; | ||
792 | case 24: /* 24 bits are encapsulated with 32 bits */ | ||
793 | case 32: | ||
794 | /* | ||
795 | * TODO: Asume ARGB_8888, maybe need to double check in | ||
796 | * the DSS if this is true | ||
797 | */ | ||
798 | display->pixel_format = ARGB_8888; | ||
799 | display->bytes_per_pixel = 4; | ||
800 | break; | ||
801 | default: | ||
802 | ERR_PRINT("Unable to handle %i bpp", display->bits_per_pixel); | ||
803 | return 1; | ||
804 | } | ||
805 | |||
806 | display->byte_stride = display->bytes_per_pixel * display->width; | ||
807 | display->rotation = OMAP_DSS_ROT_0; /* Asume rotation 0 degrees */ | ||
808 | display->main_buffer = 0; | ||
809 | display->flip_chain = 0; | ||
810 | |||
811 | /* Add the manager to the list */ | ||
812 | for (i = 0; i < OMAP_DISP_MAX_MANAGERS; i++) | ||
813 | display->overlay_managers[i] = 0; | ||
814 | |||
815 | display->overlay_managers[0] = overlay_manager; | ||
816 | display->overlay_managers_count = 1; | ||
817 | |||
818 | /* Assign function pointers for display operations */ | ||
819 | display->open = open_display; | ||
820 | display->close = close_display; | ||
821 | display->create_flip_chain = create_flip_chain; | ||
822 | display->destroy_flip_chain = destroy_flip_chain; | ||
823 | display->rotate = rotate_display; | ||
824 | display->present_buffer = present_buffer; | ||
825 | display->sync = display_sync; | ||
826 | display->present_buffer_sync = present_buffer_sync; | ||
827 | |||
828 | display->main_buffer = create_main_buffer(display); | ||
829 | if (!display->main_buffer) | ||
830 | WRN_PRINT("Failed to create main buffer for '%s'", | ||
831 | display->name); | ||
832 | |||
833 | display->buffers_available = get_max_buffers(display); | ||
834 | |||
835 | /* Just print some display info */ | ||
836 | DBG_PRINT("Found display '%s-%s' (%i,%i) %i bpp (%i bytes per pixel)" | ||
837 | " rotation %i", display->name, dss_device->name, | ||
838 | display->width, display->height, display->bits_per_pixel, | ||
839 | display->bytes_per_pixel, display->rotation); | ||
840 | |||
841 | return 0; | ||
842 | } | ||
843 | |||
844 | static int populate_virtual_display_info(struct omap_display_device *display) | ||
845 | { | ||
846 | struct omap_display_device *display_primary ; | ||
847 | struct omap_display_device *display_secondary; | ||
848 | int i; | ||
849 | |||
850 | display->id = OMAP_DISPID_VIRTUAL; | ||
851 | display->name = "virtual"; | ||
852 | |||
853 | display_primary = omap_display_get(OMAP_DISPID_PRIMARY); | ||
854 | display_secondary = omap_display_get(OMAP_DISPID_SECONDARY); | ||
855 | |||
856 | if (!display_primary) { | ||
857 | ERR_PRINT("Primary display doesn't exist"); | ||
858 | return 1; | ||
859 | } else if (!display_secondary) { | ||
860 | ERR_PRINT("Secondary display doesn't exist"); | ||
861 | return 1; | ||
862 | } | ||
863 | |||
864 | /* Combine primary and secondary display resolutions */ | ||
865 | if (display_primary->width != display_secondary->width || | ||
866 | display_primary->height != display_secondary->height) { | ||
867 | ERR_PRINT("Primary and seconday displays resolution are not" | ||
868 | " the same"); | ||
869 | return 1; | ||
870 | } | ||
871 | |||
872 | /* | ||
873 | * TODO: Here it is hardcoded the resolution asumming a vertical | ||
874 | * virtual config, what about horizontal? | ||
875 | */ | ||
876 | display->width = display_primary->width; | ||
877 | display->height = display_primary->height * 2; | ||
878 | |||
879 | if (display_primary->bits_per_pixel != | ||
880 | display_secondary->bits_per_pixel) { | ||
881 | ERR_PRINT("Primary and seconday displays format are" | ||
882 | " not the same"); | ||
883 | return 1; | ||
884 | } | ||
885 | |||
886 | display->bits_per_pixel = display_primary->bits_per_pixel; | ||
887 | switch (display->bits_per_pixel) { | ||
888 | case 16: | ||
889 | /* | ||
890 | * TODO: Asume RGB_565, maybe need to double check in | ||
891 | * the DSS if this is true | ||
892 | */ | ||
893 | display->pixel_format = RGB_565; | ||
894 | display->bytes_per_pixel = 2; | ||
895 | break; | ||
896 | case 24: /* 24 bits are encapsulated with 32 bits */ | ||
897 | case 32: | ||
898 | /* | ||
899 | * TODO: Asume ARGB_8888, maybe need to double check in | ||
900 | * the DSS if this is true | ||
901 | */ | ||
902 | display->pixel_format = ARGB_8888; | ||
903 | display->bytes_per_pixel = 4; | ||
904 | break; | ||
905 | default: | ||
906 | ERR_PRINT("Unable to handle %i bpp", | ||
907 | display->bits_per_pixel); | ||
908 | return 1; | ||
909 | } | ||
910 | |||
911 | /* TODO: Asumming a vertical virtual config too for stride */ | ||
912 | display->byte_stride = display->bytes_per_pixel * display->width; | ||
913 | display->rotation = OMAP_DSS_ROT_0; /* Asume rotation 0 degrees */ | ||
914 | display->main_buffer = 0; | ||
915 | display->flip_chain = 0; | ||
916 | |||
917 | /* Add the primary and secondary overlay managers */ | ||
918 | for (i = 0; i < OMAP_DISP_MAX_MANAGERS; i++) | ||
919 | display->overlay_managers[i] = 0; | ||
920 | |||
921 | display->overlay_managers[0] = display_primary->overlay_managers[0]; | ||
922 | display->overlay_managers[1] = display_secondary->overlay_managers[0]; | ||
923 | display->overlay_managers_count = 2; | ||
924 | |||
925 | /* Assign function pointers for display operations */ | ||
926 | display->open = open_display; | ||
927 | display->close = close_display; | ||
928 | display->create_flip_chain = create_flip_chain; | ||
929 | display->destroy_flip_chain = destroy_flip_chain; | ||
930 | display->rotate = rotate_display; | ||
931 | display->present_buffer = present_buffer_virtual; | ||
932 | display->sync = display_sync_virtual; | ||
933 | display->present_buffer_sync = present_buffer_sync_virtual; | ||
934 | |||
935 | display->main_buffer = create_main_buffer(display); | ||
936 | if (!display->main_buffer) | ||
937 | WRN_PRINT("Failed to create main buffer for '%s'", | ||
938 | display->name); | ||
939 | |||
940 | display->buffers_available = get_max_buffers(display); | ||
941 | |||
942 | /* Just print some display info */ | ||
943 | DBG_PRINT("Found display '%s' (%i,%i) %i bpp (%i bytes per pixel)" | ||
944 | " rotation %i", display->name, display->width, display->height, | ||
945 | display->bits_per_pixel, display->bytes_per_pixel, | ||
946 | display->rotation); | ||
947 | |||
948 | return 0; | ||
949 | } | ||
950 | |||
951 | static int create_display_list(void) | ||
952 | { | ||
953 | int i; | ||
954 | unsigned int bytes_to_alloc; | ||
955 | struct omap_display_device *display; | ||
956 | |||
957 | /* Query number of possible displays available first */ | ||
958 | omap_display_number = omap_dss_get_num_overlay_managers(); | ||
959 | /* For virtual display */ | ||
960 | omap_display_number++; | ||
961 | |||
962 | /* Allocate custom display list */ | ||
963 | omap_display_list = kzalloc( | ||
964 | sizeof(*display) * omap_display_number, GFP_KERNEL); | ||
965 | |||
966 | if (!omap_display_list) { | ||
967 | ERR_PRINT("Out of memory"); | ||
968 | return 1; | ||
969 | } | ||
970 | |||
971 | /* Populate each display info */ | ||
972 | for (i = 0; i < omap_display_number - 1; i++) { | ||
973 | struct omap_overlay_manager *overlay_manager = | ||
974 | omap_dss_get_overlay_manager(i); | ||
975 | display = &omap_display_list[i]; | ||
976 | if (!overlay_manager->device) { | ||
977 | WRN_PRINT("Display '%s' doesn't have a dss device " | ||
978 | "attached to it, ignoring", | ||
979 | overlay_manager->name); | ||
980 | display->id = OMAP_DISPID_BADSTATE; | ||
981 | continue; | ||
982 | } | ||
983 | if (populate_display_info(display, overlay_manager)) { | ||
984 | ERR_PRINT("Error populating display %i info with " | ||
985 | "manager '%s'", i, | ||
986 | overlay_manager->device->name); | ||
987 | display->id = OMAP_DISPID_BADSTATE; | ||
988 | continue; | ||
989 | } | ||
990 | } | ||
991 | |||
992 | /* Populate virtual display */ | ||
993 | display = &omap_display_list[omap_display_number - 1]; | ||
994 | if (populate_virtual_display_info(display)) { | ||
995 | ERR_PRINT("Error populating virtual display info"); | ||
996 | display->id = OMAP_DISPID_BADSTATE; | ||
997 | } | ||
998 | |||
999 | return 0; | ||
1000 | } | ||
1001 | |||
1002 | struct omap_display_device *omap_display_get(enum omap_display_id id) | ||
1003 | { | ||
1004 | int i; | ||
1005 | struct omap_display_device *display; | ||
1006 | |||
1007 | if (id == OMAP_DISPID_BADSTATE) { | ||
1008 | ERR_PRINT("Oops.. user must never request a bad display"); | ||
1009 | BUG(); | ||
1010 | } | ||
1011 | |||
1012 | for (i = 0; i < omap_display_number; i++) { | ||
1013 | display = &omap_display_list[i]; | ||
1014 | if (display->id == id) | ||
1015 | return display; | ||
1016 | } | ||
1017 | |||
1018 | ERR_PRINT("Unknown display %i requested", id); | ||
1019 | return 0; | ||
1020 | } | ||
1021 | EXPORT_SYMBOL(omap_display_get); | ||
1022 | |||
1023 | int omap_display_count(void) | ||
1024 | { | ||
1025 | return omap_display_number; | ||
1026 | } | ||
1027 | EXPORT_SYMBOL(omap_display_count); | ||
1028 | |||
1029 | int omap_display_init(void) | ||
1030 | { | ||
1031 | /* | ||
1032 | * TODO: Is there a better way to check if list is already created? | ||
1033 | */ | ||
1034 | if (!omap_display_list) { | ||
1035 | DBG_PRINT("Initializing driver"); | ||
1036 | if (create_display_list()) { | ||
1037 | ERR_PRINT("Error loading driver"); | ||
1038 | return 1; | ||
1039 | } | ||
1040 | } | ||
1041 | return 0; | ||
1042 | } | ||
1043 | EXPORT_SYMBOL(omap_display_init); | ||
1044 | |||
1045 | int omap_display_deinit(void) | ||
1046 | { | ||
1047 | int i; | ||
1048 | int err = 0; | ||
1049 | DBG_PRINT("Driver exiting"); | ||
1050 | |||
1051 | for (i = 0; i < omap_display_number; i++) { | ||
1052 | struct omap_display_device *display = &omap_display_list[i]; | ||
1053 | |||
1054 | if (!display) | ||
1055 | continue; | ||
1056 | |||
1057 | if (display->main_buffer) { | ||
1058 | err = display_destroy_buffer(display->main_buffer); | ||
1059 | display->main_buffer = 0; | ||
1060 | if (err) | ||
1061 | WRN_PRINT("An error happened when destroying " | ||
1062 | "main buffer for '%s'", display->name); | ||
1063 | } | ||
1064 | |||
1065 | err = display->close(display); | ||
1066 | |||
1067 | if (err) | ||
1068 | ERR_PRINT("Unable to close display '%s'", | ||
1069 | display->name); | ||
1070 | } | ||
1071 | |||
1072 | kfree(omap_display_list); | ||
1073 | omap_display_list = 0; | ||
1074 | |||
1075 | return err; | ||
1076 | } | ||
1077 | EXPORT_SYMBOL(omap_display_deinit); | ||
1078 | |||