diff options
Diffstat (limited to 'drivers/video/omap2/dss/manager.c')
-rw-r--r-- | drivers/video/omap2/dss/manager.c | 1520 |
1 files changed, 1520 insertions, 0 deletions
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c new file mode 100644 index 000000000000..0820986d4a68 --- /dev/null +++ b/drivers/video/omap2/dss/manager.c | |||
@@ -0,0 +1,1520 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/omap2/dss/manager.c | ||
3 | * | ||
4 | * Copyright (C) 2009 Nokia Corporation | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||
6 | * | ||
7 | * Some code and ideas taken from drivers/video/omap/ driver | ||
8 | * by Imre Deak. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License version 2 as published by | ||
12 | * the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
17 | * more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along with | ||
20 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
21 | */ | ||
22 | |||
23 | #define DSS_SUBSYS_NAME "MANAGER" | ||
24 | |||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/spinlock.h> | ||
30 | #include <linux/jiffies.h> | ||
31 | |||
32 | #include <plat/display.h> | ||
33 | #include <plat/cpu.h> | ||
34 | |||
35 | #include "dss.h" | ||
36 | |||
37 | static int num_managers; | ||
38 | static struct list_head manager_list; | ||
39 | |||
40 | static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) | ||
41 | { | ||
42 | return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name); | ||
43 | } | ||
44 | |||
45 | static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) | ||
46 | { | ||
47 | return snprintf(buf, PAGE_SIZE, "%s\n", | ||
48 | mgr->device ? mgr->device->name : "<none>"); | ||
49 | } | ||
50 | |||
51 | static ssize_t manager_display_store(struct omap_overlay_manager *mgr, | ||
52 | const char *buf, size_t size) | ||
53 | { | ||
54 | int r = 0; | ||
55 | size_t len = size; | ||
56 | struct omap_dss_device *dssdev = NULL; | ||
57 | |||
58 | int match(struct omap_dss_device *dssdev, void *data) | ||
59 | { | ||
60 | const char *str = data; | ||
61 | return sysfs_streq(dssdev->name, str); | ||
62 | } | ||
63 | |||
64 | if (buf[size-1] == '\n') | ||
65 | --len; | ||
66 | |||
67 | if (len > 0) | ||
68 | dssdev = omap_dss_find_device((void *)buf, match); | ||
69 | |||
70 | if (len > 0 && dssdev == NULL) | ||
71 | return -EINVAL; | ||
72 | |||
73 | if (dssdev) | ||
74 | DSSDBG("display %s found\n", dssdev->name); | ||
75 | |||
76 | if (mgr->device) { | ||
77 | r = mgr->unset_device(mgr); | ||
78 | if (r) { | ||
79 | DSSERR("failed to unset display\n"); | ||
80 | goto put_device; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | if (dssdev) { | ||
85 | r = mgr->set_device(mgr, dssdev); | ||
86 | if (r) { | ||
87 | DSSERR("failed to set manager\n"); | ||
88 | goto put_device; | ||
89 | } | ||
90 | |||
91 | r = mgr->apply(mgr); | ||
92 | if (r) { | ||
93 | DSSERR("failed to apply dispc config\n"); | ||
94 | goto put_device; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | put_device: | ||
99 | if (dssdev) | ||
100 | omap_dss_put_device(dssdev); | ||
101 | |||
102 | return r ? r : size; | ||
103 | } | ||
104 | |||
105 | static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, | ||
106 | char *buf) | ||
107 | { | ||
108 | return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color); | ||
109 | } | ||
110 | |||
111 | static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, | ||
112 | const char *buf, size_t size) | ||
113 | { | ||
114 | struct omap_overlay_manager_info info; | ||
115 | u32 color; | ||
116 | int r; | ||
117 | |||
118 | if (sscanf(buf, "%d", &color) != 1) | ||
119 | return -EINVAL; | ||
120 | |||
121 | mgr->get_manager_info(mgr, &info); | ||
122 | |||
123 | info.default_color = color; | ||
124 | |||
125 | r = mgr->set_manager_info(mgr, &info); | ||
126 | if (r) | ||
127 | return r; | ||
128 | |||
129 | r = mgr->apply(mgr); | ||
130 | if (r) | ||
131 | return r; | ||
132 | |||
133 | return size; | ||
134 | } | ||
135 | |||
136 | static const char *trans_key_type_str[] = { | ||
137 | "gfx-destination", | ||
138 | "video-source", | ||
139 | }; | ||
140 | |||
141 | static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, | ||
142 | char *buf) | ||
143 | { | ||
144 | enum omap_dss_trans_key_type key_type; | ||
145 | |||
146 | key_type = mgr->info.trans_key_type; | ||
147 | BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); | ||
148 | |||
149 | return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]); | ||
150 | } | ||
151 | |||
152 | static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, | ||
153 | const char *buf, size_t size) | ||
154 | { | ||
155 | enum omap_dss_trans_key_type key_type; | ||
156 | struct omap_overlay_manager_info info; | ||
157 | int r; | ||
158 | |||
159 | for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST; | ||
160 | key_type < ARRAY_SIZE(trans_key_type_str); key_type++) { | ||
161 | if (sysfs_streq(buf, trans_key_type_str[key_type])) | ||
162 | break; | ||
163 | } | ||
164 | |||
165 | if (key_type == ARRAY_SIZE(trans_key_type_str)) | ||
166 | return -EINVAL; | ||
167 | |||
168 | mgr->get_manager_info(mgr, &info); | ||
169 | |||
170 | info.trans_key_type = key_type; | ||
171 | |||
172 | r = mgr->set_manager_info(mgr, &info); | ||
173 | if (r) | ||
174 | return r; | ||
175 | |||
176 | r = mgr->apply(mgr); | ||
177 | if (r) | ||
178 | return r; | ||
179 | |||
180 | return size; | ||
181 | } | ||
182 | |||
183 | static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, | ||
184 | char *buf) | ||
185 | { | ||
186 | return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key); | ||
187 | } | ||
188 | |||
189 | static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, | ||
190 | const char *buf, size_t size) | ||
191 | { | ||
192 | struct omap_overlay_manager_info info; | ||
193 | u32 key_value; | ||
194 | int r; | ||
195 | |||
196 | if (sscanf(buf, "%d", &key_value) != 1) | ||
197 | return -EINVAL; | ||
198 | |||
199 | mgr->get_manager_info(mgr, &info); | ||
200 | |||
201 | info.trans_key = key_value; | ||
202 | |||
203 | r = mgr->set_manager_info(mgr, &info); | ||
204 | if (r) | ||
205 | return r; | ||
206 | |||
207 | r = mgr->apply(mgr); | ||
208 | if (r) | ||
209 | return r; | ||
210 | |||
211 | return size; | ||
212 | } | ||
213 | |||
214 | static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, | ||
215 | char *buf) | ||
216 | { | ||
217 | return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_enabled); | ||
218 | } | ||
219 | |||
220 | static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, | ||
221 | const char *buf, size_t size) | ||
222 | { | ||
223 | struct omap_overlay_manager_info info; | ||
224 | int enable; | ||
225 | int r; | ||
226 | |||
227 | if (sscanf(buf, "%d", &enable) != 1) | ||
228 | return -EINVAL; | ||
229 | |||
230 | mgr->get_manager_info(mgr, &info); | ||
231 | |||
232 | info.trans_enabled = enable ? true : false; | ||
233 | |||
234 | r = mgr->set_manager_info(mgr, &info); | ||
235 | if (r) | ||
236 | return r; | ||
237 | |||
238 | r = mgr->apply(mgr); | ||
239 | if (r) | ||
240 | return r; | ||
241 | |||
242 | return size; | ||
243 | } | ||
244 | |||
245 | static ssize_t manager_alpha_blending_enabled_show( | ||
246 | struct omap_overlay_manager *mgr, char *buf) | ||
247 | { | ||
248 | return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled); | ||
249 | } | ||
250 | |||
251 | static ssize_t manager_alpha_blending_enabled_store( | ||
252 | struct omap_overlay_manager *mgr, | ||
253 | const char *buf, size_t size) | ||
254 | { | ||
255 | struct omap_overlay_manager_info info; | ||
256 | int enable; | ||
257 | int r; | ||
258 | |||
259 | if (sscanf(buf, "%d", &enable) != 1) | ||
260 | return -EINVAL; | ||
261 | |||
262 | mgr->get_manager_info(mgr, &info); | ||
263 | |||
264 | info.alpha_enabled = enable ? true : false; | ||
265 | |||
266 | r = mgr->set_manager_info(mgr, &info); | ||
267 | if (r) | ||
268 | return r; | ||
269 | |||
270 | r = mgr->apply(mgr); | ||
271 | if (r) | ||
272 | return r; | ||
273 | |||
274 | return size; | ||
275 | } | ||
276 | |||
277 | struct manager_attribute { | ||
278 | struct attribute attr; | ||
279 | ssize_t (*show)(struct omap_overlay_manager *, char *); | ||
280 | ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t); | ||
281 | }; | ||
282 | |||
283 | #define MANAGER_ATTR(_name, _mode, _show, _store) \ | ||
284 | struct manager_attribute manager_attr_##_name = \ | ||
285 | __ATTR(_name, _mode, _show, _store) | ||
286 | |||
287 | static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL); | ||
288 | static MANAGER_ATTR(display, S_IRUGO|S_IWUSR, | ||
289 | manager_display_show, manager_display_store); | ||
290 | static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR, | ||
291 | manager_default_color_show, manager_default_color_store); | ||
292 | static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR, | ||
293 | manager_trans_key_type_show, manager_trans_key_type_store); | ||
294 | static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR, | ||
295 | manager_trans_key_value_show, manager_trans_key_value_store); | ||
296 | static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, | ||
297 | manager_trans_key_enabled_show, | ||
298 | manager_trans_key_enabled_store); | ||
299 | static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, | ||
300 | manager_alpha_blending_enabled_show, | ||
301 | manager_alpha_blending_enabled_store); | ||
302 | |||
303 | |||
304 | static struct attribute *manager_sysfs_attrs[] = { | ||
305 | &manager_attr_name.attr, | ||
306 | &manager_attr_display.attr, | ||
307 | &manager_attr_default_color.attr, | ||
308 | &manager_attr_trans_key_type.attr, | ||
309 | &manager_attr_trans_key_value.attr, | ||
310 | &manager_attr_trans_key_enabled.attr, | ||
311 | &manager_attr_alpha_blending_enabled.attr, | ||
312 | NULL | ||
313 | }; | ||
314 | |||
315 | static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, | ||
316 | char *buf) | ||
317 | { | ||
318 | struct omap_overlay_manager *manager; | ||
319 | struct manager_attribute *manager_attr; | ||
320 | |||
321 | manager = container_of(kobj, struct omap_overlay_manager, kobj); | ||
322 | manager_attr = container_of(attr, struct manager_attribute, attr); | ||
323 | |||
324 | if (!manager_attr->show) | ||
325 | return -ENOENT; | ||
326 | |||
327 | return manager_attr->show(manager, buf); | ||
328 | } | ||
329 | |||
330 | static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr, | ||
331 | const char *buf, size_t size) | ||
332 | { | ||
333 | struct omap_overlay_manager *manager; | ||
334 | struct manager_attribute *manager_attr; | ||
335 | |||
336 | manager = container_of(kobj, struct omap_overlay_manager, kobj); | ||
337 | manager_attr = container_of(attr, struct manager_attribute, attr); | ||
338 | |||
339 | if (!manager_attr->store) | ||
340 | return -ENOENT; | ||
341 | |||
342 | return manager_attr->store(manager, buf, size); | ||
343 | } | ||
344 | |||
345 | static const struct sysfs_ops manager_sysfs_ops = { | ||
346 | .show = manager_attr_show, | ||
347 | .store = manager_attr_store, | ||
348 | }; | ||
349 | |||
350 | static struct kobj_type manager_ktype = { | ||
351 | .sysfs_ops = &manager_sysfs_ops, | ||
352 | .default_attrs = manager_sysfs_attrs, | ||
353 | }; | ||
354 | |||
355 | /* | ||
356 | * We have 4 levels of cache for the dispc settings. First two are in SW and | ||
357 | * the latter two in HW. | ||
358 | * | ||
359 | * +--------------------+ | ||
360 | * |overlay/manager_info| | ||
361 | * +--------------------+ | ||
362 | * v | ||
363 | * apply() | ||
364 | * v | ||
365 | * +--------------------+ | ||
366 | * | dss_cache | | ||
367 | * +--------------------+ | ||
368 | * v | ||
369 | * configure() | ||
370 | * v | ||
371 | * +--------------------+ | ||
372 | * | shadow registers | | ||
373 | * +--------------------+ | ||
374 | * v | ||
375 | * VFP or lcd/digit_enable | ||
376 | * v | ||
377 | * +--------------------+ | ||
378 | * | registers | | ||
379 | * +--------------------+ | ||
380 | */ | ||
381 | |||
382 | struct overlay_cache_data { | ||
383 | /* If true, cache changed, but not written to shadow registers. Set | ||
384 | * in apply(), cleared when registers written. */ | ||
385 | bool dirty; | ||
386 | /* If true, shadow registers contain changed values not yet in real | ||
387 | * registers. Set when writing to shadow registers, cleared at | ||
388 | * VSYNC/EVSYNC */ | ||
389 | bool shadow_dirty; | ||
390 | |||
391 | bool enabled; | ||
392 | |||
393 | u32 paddr; | ||
394 | void __iomem *vaddr; | ||
395 | u16 screen_width; | ||
396 | u16 width; | ||
397 | u16 height; | ||
398 | enum omap_color_mode color_mode; | ||
399 | u8 rotation; | ||
400 | enum omap_dss_rotation_type rotation_type; | ||
401 | bool mirror; | ||
402 | |||
403 | u16 pos_x; | ||
404 | u16 pos_y; | ||
405 | u16 out_width; /* if 0, out_width == width */ | ||
406 | u16 out_height; /* if 0, out_height == height */ | ||
407 | u8 global_alpha; | ||
408 | |||
409 | enum omap_channel channel; | ||
410 | bool replication; | ||
411 | bool ilace; | ||
412 | |||
413 | enum omap_burst_size burst_size; | ||
414 | u32 fifo_low; | ||
415 | u32 fifo_high; | ||
416 | |||
417 | bool manual_update; | ||
418 | }; | ||
419 | |||
420 | struct manager_cache_data { | ||
421 | /* If true, cache changed, but not written to shadow registers. Set | ||
422 | * in apply(), cleared when registers written. */ | ||
423 | bool dirty; | ||
424 | /* If true, shadow registers contain changed values not yet in real | ||
425 | * registers. Set when writing to shadow registers, cleared at | ||
426 | * VSYNC/EVSYNC */ | ||
427 | bool shadow_dirty; | ||
428 | |||
429 | u32 default_color; | ||
430 | |||
431 | enum omap_dss_trans_key_type trans_key_type; | ||
432 | u32 trans_key; | ||
433 | bool trans_enabled; | ||
434 | |||
435 | bool alpha_enabled; | ||
436 | |||
437 | bool manual_upd_display; | ||
438 | bool manual_update; | ||
439 | bool do_manual_update; | ||
440 | |||
441 | /* manual update region */ | ||
442 | u16 x, y, w, h; | ||
443 | }; | ||
444 | |||
445 | static struct { | ||
446 | spinlock_t lock; | ||
447 | struct overlay_cache_data overlay_cache[3]; | ||
448 | struct manager_cache_data manager_cache[2]; | ||
449 | |||
450 | bool irq_enabled; | ||
451 | } dss_cache; | ||
452 | |||
453 | |||
454 | |||
455 | static int omap_dss_set_device(struct omap_overlay_manager *mgr, | ||
456 | struct omap_dss_device *dssdev) | ||
457 | { | ||
458 | int i; | ||
459 | int r; | ||
460 | |||
461 | if (dssdev->manager) { | ||
462 | DSSERR("display '%s' already has a manager '%s'\n", | ||
463 | dssdev->name, dssdev->manager->name); | ||
464 | return -EINVAL; | ||
465 | } | ||
466 | |||
467 | if ((mgr->supported_displays & dssdev->type) == 0) { | ||
468 | DSSERR("display '%s' does not support manager '%s'\n", | ||
469 | dssdev->name, mgr->name); | ||
470 | return -EINVAL; | ||
471 | } | ||
472 | |||
473 | for (i = 0; i < mgr->num_overlays; i++) { | ||
474 | struct omap_overlay *ovl = mgr->overlays[i]; | ||
475 | |||
476 | if (ovl->manager != mgr || !ovl->info.enabled) | ||
477 | continue; | ||
478 | |||
479 | r = dss_check_overlay(ovl, dssdev); | ||
480 | if (r) | ||
481 | return r; | ||
482 | } | ||
483 | |||
484 | dssdev->manager = mgr; | ||
485 | mgr->device = dssdev; | ||
486 | mgr->device_changed = true; | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static int omap_dss_unset_device(struct omap_overlay_manager *mgr) | ||
492 | { | ||
493 | if (!mgr->device) { | ||
494 | DSSERR("failed to unset display, display not set.\n"); | ||
495 | return -EINVAL; | ||
496 | } | ||
497 | |||
498 | mgr->device->manager = NULL; | ||
499 | mgr->device = NULL; | ||
500 | mgr->device_changed = true; | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) | ||
506 | { | ||
507 | unsigned long timeout = msecs_to_jiffies(500); | ||
508 | u32 irq; | ||
509 | |||
510 | if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) | ||
511 | irq = DISPC_IRQ_EVSYNC_ODD; | ||
512 | else | ||
513 | irq = DISPC_IRQ_VSYNC; | ||
514 | |||
515 | return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); | ||
516 | } | ||
517 | |||
518 | static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) | ||
519 | { | ||
520 | unsigned long timeout = msecs_to_jiffies(500); | ||
521 | struct manager_cache_data *mc; | ||
522 | enum omap_channel channel; | ||
523 | u32 irq; | ||
524 | int r; | ||
525 | int i; | ||
526 | struct omap_dss_device *dssdev = mgr->device; | ||
527 | |||
528 | if (!dssdev) | ||
529 | return 0; | ||
530 | |||
531 | if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { | ||
532 | irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; | ||
533 | channel = OMAP_DSS_CHANNEL_DIGIT; | ||
534 | } else { | ||
535 | if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { | ||
536 | enum omap_dss_update_mode mode; | ||
537 | mode = dssdev->driver->get_update_mode(dssdev); | ||
538 | if (mode != OMAP_DSS_UPDATE_AUTO) | ||
539 | return 0; | ||
540 | |||
541 | irq = DISPC_IRQ_FRAMEDONE; | ||
542 | } else { | ||
543 | irq = DISPC_IRQ_VSYNC; | ||
544 | } | ||
545 | channel = OMAP_DSS_CHANNEL_LCD; | ||
546 | } | ||
547 | |||
548 | mc = &dss_cache.manager_cache[mgr->id]; | ||
549 | i = 0; | ||
550 | while (1) { | ||
551 | unsigned long flags; | ||
552 | bool shadow_dirty, dirty; | ||
553 | |||
554 | spin_lock_irqsave(&dss_cache.lock, flags); | ||
555 | dirty = mc->dirty; | ||
556 | shadow_dirty = mc->shadow_dirty; | ||
557 | spin_unlock_irqrestore(&dss_cache.lock, flags); | ||
558 | |||
559 | if (!dirty && !shadow_dirty) { | ||
560 | r = 0; | ||
561 | break; | ||
562 | } | ||
563 | |||
564 | /* 4 iterations is the worst case: | ||
565 | * 1 - initial iteration, dirty = true (between VFP and VSYNC) | ||
566 | * 2 - first VSYNC, dirty = true | ||
567 | * 3 - dirty = false, shadow_dirty = true | ||
568 | * 4 - shadow_dirty = false */ | ||
569 | if (i++ == 3) { | ||
570 | DSSERR("mgr(%d)->wait_for_go() not finishing\n", | ||
571 | mgr->id); | ||
572 | r = 0; | ||
573 | break; | ||
574 | } | ||
575 | |||
576 | r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); | ||
577 | if (r == -ERESTARTSYS) | ||
578 | break; | ||
579 | |||
580 | if (r) { | ||
581 | DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id); | ||
582 | break; | ||
583 | } | ||
584 | } | ||
585 | |||
586 | return r; | ||
587 | } | ||
588 | |||
589 | int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) | ||
590 | { | ||
591 | unsigned long timeout = msecs_to_jiffies(500); | ||
592 | enum omap_channel channel; | ||
593 | struct overlay_cache_data *oc; | ||
594 | struct omap_dss_device *dssdev; | ||
595 | u32 irq; | ||
596 | int r; | ||
597 | int i; | ||
598 | |||
599 | if (!ovl->manager || !ovl->manager->device) | ||
600 | return 0; | ||
601 | |||
602 | dssdev = ovl->manager->device; | ||
603 | |||
604 | if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { | ||
605 | irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; | ||
606 | channel = OMAP_DSS_CHANNEL_DIGIT; | ||
607 | } else { | ||
608 | if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { | ||
609 | enum omap_dss_update_mode mode; | ||
610 | mode = dssdev->driver->get_update_mode(dssdev); | ||
611 | if (mode != OMAP_DSS_UPDATE_AUTO) | ||
612 | return 0; | ||
613 | |||
614 | irq = DISPC_IRQ_FRAMEDONE; | ||
615 | } else { | ||
616 | irq = DISPC_IRQ_VSYNC; | ||
617 | } | ||
618 | channel = OMAP_DSS_CHANNEL_LCD; | ||
619 | } | ||
620 | |||
621 | oc = &dss_cache.overlay_cache[ovl->id]; | ||
622 | i = 0; | ||
623 | while (1) { | ||
624 | unsigned long flags; | ||
625 | bool shadow_dirty, dirty; | ||
626 | |||
627 | spin_lock_irqsave(&dss_cache.lock, flags); | ||
628 | dirty = oc->dirty; | ||
629 | shadow_dirty = oc->shadow_dirty; | ||
630 | spin_unlock_irqrestore(&dss_cache.lock, flags); | ||
631 | |||
632 | if (!dirty && !shadow_dirty) { | ||
633 | r = 0; | ||
634 | break; | ||
635 | } | ||
636 | |||
637 | /* 4 iterations is the worst case: | ||
638 | * 1 - initial iteration, dirty = true (between VFP and VSYNC) | ||
639 | * 2 - first VSYNC, dirty = true | ||
640 | * 3 - dirty = false, shadow_dirty = true | ||
641 | * 4 - shadow_dirty = false */ | ||
642 | if (i++ == 3) { | ||
643 | DSSERR("ovl(%d)->wait_for_go() not finishing\n", | ||
644 | ovl->id); | ||
645 | r = 0; | ||
646 | break; | ||
647 | } | ||
648 | |||
649 | r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); | ||
650 | if (r == -ERESTARTSYS) | ||
651 | break; | ||
652 | |||
653 | if (r) { | ||
654 | DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id); | ||
655 | break; | ||
656 | } | ||
657 | } | ||
658 | |||
659 | return r; | ||
660 | } | ||
661 | |||
662 | static int overlay_enabled(struct omap_overlay *ovl) | ||
663 | { | ||
664 | return ovl->info.enabled && ovl->manager && ovl->manager->device; | ||
665 | } | ||
666 | |||
667 | /* Is rect1 a subset of rect2? */ | ||
668 | static bool rectangle_subset(int x1, int y1, int w1, int h1, | ||
669 | int x2, int y2, int w2, int h2) | ||
670 | { | ||
671 | if (x1 < x2 || y1 < y2) | ||
672 | return false; | ||
673 | |||
674 | if (x1 + w1 > x2 + w2) | ||
675 | return false; | ||
676 | |||
677 | if (y1 + h1 > y2 + h2) | ||
678 | return false; | ||
679 | |||
680 | return true; | ||
681 | } | ||
682 | |||
683 | /* Do rect1 and rect2 overlap? */ | ||
684 | static bool rectangle_intersects(int x1, int y1, int w1, int h1, | ||
685 | int x2, int y2, int w2, int h2) | ||
686 | { | ||
687 | if (x1 >= x2 + w2) | ||
688 | return false; | ||
689 | |||
690 | if (x2 >= x1 + w1) | ||
691 | return false; | ||
692 | |||
693 | if (y1 >= y2 + h2) | ||
694 | return false; | ||
695 | |||
696 | if (y2 >= y1 + h1) | ||
697 | return false; | ||
698 | |||
699 | return true; | ||
700 | } | ||
701 | |||
702 | static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc) | ||
703 | { | ||
704 | if (oc->out_width != 0 && oc->width != oc->out_width) | ||
705 | return true; | ||
706 | |||
707 | if (oc->out_height != 0 && oc->height != oc->out_height) | ||
708 | return true; | ||
709 | |||
710 | return false; | ||
711 | } | ||
712 | |||
713 | static int configure_overlay(enum omap_plane plane) | ||
714 | { | ||
715 | struct overlay_cache_data *c; | ||
716 | struct manager_cache_data *mc; | ||
717 | u16 outw, outh; | ||
718 | u16 x, y, w, h; | ||
719 | u32 paddr; | ||
720 | int r; | ||
721 | |||
722 | DSSDBGF("%d", plane); | ||
723 | |||
724 | c = &dss_cache.overlay_cache[plane]; | ||
725 | |||
726 | if (!c->enabled) { | ||
727 | dispc_enable_plane(plane, 0); | ||
728 | return 0; | ||
729 | } | ||
730 | |||
731 | mc = &dss_cache.manager_cache[c->channel]; | ||
732 | |||
733 | x = c->pos_x; | ||
734 | y = c->pos_y; | ||
735 | w = c->width; | ||
736 | h = c->height; | ||
737 | outw = c->out_width == 0 ? c->width : c->out_width; | ||
738 | outh = c->out_height == 0 ? c->height : c->out_height; | ||
739 | paddr = c->paddr; | ||
740 | |||
741 | if (c->manual_update && mc->do_manual_update) { | ||
742 | unsigned bpp; | ||
743 | /* If the overlay is outside the update region, disable it */ | ||
744 | if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h, | ||
745 | x, y, outw, outh)) { | ||
746 | dispc_enable_plane(plane, 0); | ||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | switch (c->color_mode) { | ||
751 | case OMAP_DSS_COLOR_RGB16: | ||
752 | case OMAP_DSS_COLOR_ARGB16: | ||
753 | case OMAP_DSS_COLOR_YUV2: | ||
754 | case OMAP_DSS_COLOR_UYVY: | ||
755 | bpp = 16; | ||
756 | break; | ||
757 | |||
758 | case OMAP_DSS_COLOR_RGB24P: | ||
759 | bpp = 24; | ||
760 | break; | ||
761 | |||
762 | case OMAP_DSS_COLOR_RGB24U: | ||
763 | case OMAP_DSS_COLOR_ARGB32: | ||
764 | case OMAP_DSS_COLOR_RGBA32: | ||
765 | case OMAP_DSS_COLOR_RGBX32: | ||
766 | bpp = 32; | ||
767 | break; | ||
768 | |||
769 | default: | ||
770 | BUG(); | ||
771 | } | ||
772 | |||
773 | if (dispc_is_overlay_scaled(c)) { | ||
774 | /* If the overlay is scaled, the update area has | ||
775 | * already been enlarged to cover the whole overlay. We | ||
776 | * only need to adjust x/y here */ | ||
777 | x = c->pos_x - mc->x; | ||
778 | y = c->pos_y - mc->y; | ||
779 | } else { | ||
780 | if (mc->x > c->pos_x) { | ||
781 | x = 0; | ||
782 | w -= (mc->x - c->pos_x); | ||
783 | paddr += (mc->x - c->pos_x) * bpp / 8; | ||
784 | } else { | ||
785 | x = c->pos_x - mc->x; | ||
786 | } | ||
787 | |||
788 | if (mc->y > c->pos_y) { | ||
789 | y = 0; | ||
790 | h -= (mc->y - c->pos_y); | ||
791 | paddr += (mc->y - c->pos_y) * c->screen_width * | ||
792 | bpp / 8; | ||
793 | } else { | ||
794 | y = c->pos_y - mc->y; | ||
795 | } | ||
796 | |||
797 | if (mc->w < (x+w)) | ||
798 | w -= (x+w) - (mc->w); | ||
799 | |||
800 | if (mc->h < (y+h)) | ||
801 | h -= (y+h) - (mc->h); | ||
802 | |||
803 | outw = w; | ||
804 | outh = h; | ||
805 | } | ||
806 | } | ||
807 | |||
808 | r = dispc_setup_plane(plane, | ||
809 | paddr, | ||
810 | c->screen_width, | ||
811 | x, y, | ||
812 | w, h, | ||
813 | outw, outh, | ||
814 | c->color_mode, | ||
815 | c->ilace, | ||
816 | c->rotation_type, | ||
817 | c->rotation, | ||
818 | c->mirror, | ||
819 | c->global_alpha); | ||
820 | |||
821 | if (r) { | ||
822 | /* this shouldn't happen */ | ||
823 | DSSERR("dispc_setup_plane failed for ovl %d\n", plane); | ||
824 | dispc_enable_plane(plane, 0); | ||
825 | return r; | ||
826 | } | ||
827 | |||
828 | dispc_enable_replication(plane, c->replication); | ||
829 | |||
830 | dispc_set_burst_size(plane, c->burst_size); | ||
831 | dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); | ||
832 | |||
833 | dispc_enable_plane(plane, 1); | ||
834 | |||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | static void configure_manager(enum omap_channel channel) | ||
839 | { | ||
840 | struct manager_cache_data *c; | ||
841 | |||
842 | DSSDBGF("%d", channel); | ||
843 | |||
844 | c = &dss_cache.manager_cache[channel]; | ||
845 | |||
846 | dispc_set_trans_key(channel, c->trans_key_type, c->trans_key); | ||
847 | dispc_enable_trans_key(channel, c->trans_enabled); | ||
848 | dispc_enable_alpha_blending(channel, c->alpha_enabled); | ||
849 | } | ||
850 | |||
851 | /* configure_dispc() tries to write values from cache to shadow registers. | ||
852 | * It writes only to those managers/overlays that are not busy. | ||
853 | * returns 0 if everything could be written to shadow registers. | ||
854 | * returns 1 if not everything could be written to shadow registers. */ | ||
855 | static int configure_dispc(void) | ||
856 | { | ||
857 | struct overlay_cache_data *oc; | ||
858 | struct manager_cache_data *mc; | ||
859 | const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); | ||
860 | const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); | ||
861 | int i; | ||
862 | int r; | ||
863 | bool mgr_busy[2]; | ||
864 | bool mgr_go[2]; | ||
865 | bool busy; | ||
866 | |||
867 | r = 0; | ||
868 | busy = false; | ||
869 | |||
870 | mgr_busy[0] = dispc_go_busy(0); | ||
871 | mgr_busy[1] = dispc_go_busy(1); | ||
872 | mgr_go[0] = false; | ||
873 | mgr_go[1] = false; | ||
874 | |||
875 | /* Commit overlay settings */ | ||
876 | for (i = 0; i < num_ovls; ++i) { | ||
877 | oc = &dss_cache.overlay_cache[i]; | ||
878 | mc = &dss_cache.manager_cache[oc->channel]; | ||
879 | |||
880 | if (!oc->dirty) | ||
881 | continue; | ||
882 | |||
883 | if (oc->manual_update && !mc->do_manual_update) | ||
884 | continue; | ||
885 | |||
886 | if (mgr_busy[oc->channel]) { | ||
887 | busy = true; | ||
888 | continue; | ||
889 | } | ||
890 | |||
891 | r = configure_overlay(i); | ||
892 | if (r) | ||
893 | DSSERR("configure_overlay %d failed\n", i); | ||
894 | |||
895 | oc->dirty = false; | ||
896 | oc->shadow_dirty = true; | ||
897 | mgr_go[oc->channel] = true; | ||
898 | } | ||
899 | |||
900 | /* Commit manager settings */ | ||
901 | for (i = 0; i < num_mgrs; ++i) { | ||
902 | mc = &dss_cache.manager_cache[i]; | ||
903 | |||
904 | if (!mc->dirty) | ||
905 | continue; | ||
906 | |||
907 | if (mc->manual_update && !mc->do_manual_update) | ||
908 | continue; | ||
909 | |||
910 | if (mgr_busy[i]) { | ||
911 | busy = true; | ||
912 | continue; | ||
913 | } | ||
914 | |||
915 | configure_manager(i); | ||
916 | mc->dirty = false; | ||
917 | mc->shadow_dirty = true; | ||
918 | mgr_go[i] = true; | ||
919 | } | ||
920 | |||
921 | /* set GO */ | ||
922 | for (i = 0; i < num_mgrs; ++i) { | ||
923 | mc = &dss_cache.manager_cache[i]; | ||
924 | |||
925 | if (!mgr_go[i]) | ||
926 | continue; | ||
927 | |||
928 | /* We don't need GO with manual update display. LCD iface will | ||
929 | * always be turned off after frame, and new settings will be | ||
930 | * taken in to use at next update */ | ||
931 | if (!mc->manual_upd_display) | ||
932 | dispc_go(i); | ||
933 | } | ||
934 | |||
935 | if (busy) | ||
936 | r = 1; | ||
937 | else | ||
938 | r = 0; | ||
939 | |||
940 | return r; | ||
941 | } | ||
942 | |||
943 | /* Configure dispc for partial update. Return possibly modified update | ||
944 | * area */ | ||
945 | void dss_setup_partial_planes(struct omap_dss_device *dssdev, | ||
946 | u16 *xi, u16 *yi, u16 *wi, u16 *hi) | ||
947 | { | ||
948 | struct overlay_cache_data *oc; | ||
949 | struct manager_cache_data *mc; | ||
950 | const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); | ||
951 | struct omap_overlay_manager *mgr; | ||
952 | int i; | ||
953 | u16 x, y, w, h; | ||
954 | unsigned long flags; | ||
955 | |||
956 | x = *xi; | ||
957 | y = *yi; | ||
958 | w = *wi; | ||
959 | h = *hi; | ||
960 | |||
961 | DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n", | ||
962 | *xi, *yi, *wi, *hi); | ||
963 | |||
964 | mgr = dssdev->manager; | ||
965 | |||
966 | if (!mgr) { | ||
967 | DSSDBG("no manager\n"); | ||
968 | return; | ||
969 | } | ||
970 | |||
971 | spin_lock_irqsave(&dss_cache.lock, flags); | ||
972 | |||
973 | /* We need to show the whole overlay if it is scaled. So look for | ||
974 | * those, and make the update area larger if found. | ||
975 | * Also mark the overlay cache dirty */ | ||
976 | for (i = 0; i < num_ovls; ++i) { | ||
977 | unsigned x1, y1, x2, y2; | ||
978 | unsigned outw, outh; | ||
979 | |||
980 | oc = &dss_cache.overlay_cache[i]; | ||
981 | |||
982 | if (oc->channel != mgr->id) | ||
983 | continue; | ||
984 | |||
985 | oc->dirty = true; | ||
986 | |||
987 | if (!oc->enabled) | ||
988 | continue; | ||
989 | |||
990 | if (!dispc_is_overlay_scaled(oc)) | ||
991 | continue; | ||
992 | |||
993 | outw = oc->out_width == 0 ? oc->width : oc->out_width; | ||
994 | outh = oc->out_height == 0 ? oc->height : oc->out_height; | ||
995 | |||
996 | /* is the overlay outside the update region? */ | ||
997 | if (!rectangle_intersects(x, y, w, h, | ||
998 | oc->pos_x, oc->pos_y, | ||
999 | outw, outh)) | ||
1000 | continue; | ||
1001 | |||
1002 | /* if the overlay totally inside the update region? */ | ||
1003 | if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, | ||
1004 | x, y, w, h)) | ||
1005 | continue; | ||
1006 | |||
1007 | if (x > oc->pos_x) | ||
1008 | x1 = oc->pos_x; | ||
1009 | else | ||
1010 | x1 = x; | ||
1011 | |||
1012 | if (y > oc->pos_y) | ||
1013 | y1 = oc->pos_y; | ||
1014 | else | ||
1015 | y1 = y; | ||
1016 | |||
1017 | if ((x + w) < (oc->pos_x + outw)) | ||
1018 | x2 = oc->pos_x + outw; | ||
1019 | else | ||
1020 | x2 = x + w; | ||
1021 | |||
1022 | if ((y + h) < (oc->pos_y + outh)) | ||
1023 | y2 = oc->pos_y + outh; | ||
1024 | else | ||
1025 | y2 = y + h; | ||
1026 | |||
1027 | x = x1; | ||
1028 | y = y1; | ||
1029 | w = x2 - x1; | ||
1030 | h = y2 - y1; | ||
1031 | |||
1032 | DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n", | ||
1033 | i, x, y, w, h); | ||
1034 | } | ||
1035 | |||
1036 | mc = &dss_cache.manager_cache[mgr->id]; | ||
1037 | mc->do_manual_update = true; | ||
1038 | mc->x = x; | ||
1039 | mc->y = y; | ||
1040 | mc->w = w; | ||
1041 | mc->h = h; | ||
1042 | |||
1043 | configure_dispc(); | ||
1044 | |||
1045 | mc->do_manual_update = false; | ||
1046 | |||
1047 | spin_unlock_irqrestore(&dss_cache.lock, flags); | ||
1048 | |||
1049 | *xi = x; | ||
1050 | *yi = y; | ||
1051 | *wi = w; | ||
1052 | *hi = h; | ||
1053 | } | ||
1054 | |||
1055 | void dss_start_update(struct omap_dss_device *dssdev) | ||
1056 | { | ||
1057 | struct manager_cache_data *mc; | ||
1058 | struct overlay_cache_data *oc; | ||
1059 | const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); | ||
1060 | const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); | ||
1061 | struct omap_overlay_manager *mgr; | ||
1062 | int i; | ||
1063 | |||
1064 | mgr = dssdev->manager; | ||
1065 | |||
1066 | for (i = 0; i < num_ovls; ++i) { | ||
1067 | oc = &dss_cache.overlay_cache[i]; | ||
1068 | if (oc->channel != mgr->id) | ||
1069 | continue; | ||
1070 | |||
1071 | oc->shadow_dirty = false; | ||
1072 | } | ||
1073 | |||
1074 | for (i = 0; i < num_mgrs; ++i) { | ||
1075 | mc = &dss_cache.manager_cache[i]; | ||
1076 | if (mgr->id != i) | ||
1077 | continue; | ||
1078 | |||
1079 | mc->shadow_dirty = false; | ||
1080 | } | ||
1081 | |||
1082 | dssdev->manager->enable(dssdev->manager); | ||
1083 | } | ||
1084 | |||
1085 | static void dss_apply_irq_handler(void *data, u32 mask) | ||
1086 | { | ||
1087 | struct manager_cache_data *mc; | ||
1088 | struct overlay_cache_data *oc; | ||
1089 | const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); | ||
1090 | const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); | ||
1091 | int i, r; | ||
1092 | bool mgr_busy[2]; | ||
1093 | |||
1094 | mgr_busy[0] = dispc_go_busy(0); | ||
1095 | mgr_busy[1] = dispc_go_busy(1); | ||
1096 | |||
1097 | spin_lock(&dss_cache.lock); | ||
1098 | |||
1099 | for (i = 0; i < num_ovls; ++i) { | ||
1100 | oc = &dss_cache.overlay_cache[i]; | ||
1101 | if (!mgr_busy[oc->channel]) | ||
1102 | oc->shadow_dirty = false; | ||
1103 | } | ||
1104 | |||
1105 | for (i = 0; i < num_mgrs; ++i) { | ||
1106 | mc = &dss_cache.manager_cache[i]; | ||
1107 | if (!mgr_busy[i]) | ||
1108 | mc->shadow_dirty = false; | ||
1109 | } | ||
1110 | |||
1111 | r = configure_dispc(); | ||
1112 | if (r == 1) | ||
1113 | goto end; | ||
1114 | |||
1115 | /* re-read busy flags */ | ||
1116 | mgr_busy[0] = dispc_go_busy(0); | ||
1117 | mgr_busy[1] = dispc_go_busy(1); | ||
1118 | |||
1119 | /* keep running as long as there are busy managers, so that | ||
1120 | * we can collect overlay-applied information */ | ||
1121 | for (i = 0; i < num_mgrs; ++i) { | ||
1122 | if (mgr_busy[i]) | ||
1123 | goto end; | ||
1124 | } | ||
1125 | |||
1126 | omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, | ||
1127 | DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | | ||
1128 | DISPC_IRQ_EVSYNC_EVEN); | ||
1129 | dss_cache.irq_enabled = false; | ||
1130 | |||
1131 | end: | ||
1132 | spin_unlock(&dss_cache.lock); | ||
1133 | } | ||
1134 | |||
1135 | static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) | ||
1136 | { | ||
1137 | struct overlay_cache_data *oc; | ||
1138 | struct manager_cache_data *mc; | ||
1139 | int i; | ||
1140 | struct omap_overlay *ovl; | ||
1141 | int num_planes_enabled = 0; | ||
1142 | bool use_fifomerge; | ||
1143 | unsigned long flags; | ||
1144 | int r; | ||
1145 | |||
1146 | DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); | ||
1147 | |||
1148 | spin_lock_irqsave(&dss_cache.lock, flags); | ||
1149 | |||
1150 | /* Configure overlays */ | ||
1151 | for (i = 0; i < omap_dss_get_num_overlays(); ++i) { | ||
1152 | struct omap_dss_device *dssdev; | ||
1153 | |||
1154 | ovl = omap_dss_get_overlay(i); | ||
1155 | |||
1156 | if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) | ||
1157 | continue; | ||
1158 | |||
1159 | oc = &dss_cache.overlay_cache[ovl->id]; | ||
1160 | |||
1161 | if (!overlay_enabled(ovl)) { | ||
1162 | if (oc->enabled) { | ||
1163 | oc->enabled = false; | ||
1164 | oc->dirty = true; | ||
1165 | } | ||
1166 | continue; | ||
1167 | } | ||
1168 | |||
1169 | if (!ovl->info_dirty) { | ||
1170 | if (oc->enabled) | ||
1171 | ++num_planes_enabled; | ||
1172 | continue; | ||
1173 | } | ||
1174 | |||
1175 | dssdev = ovl->manager->device; | ||
1176 | |||
1177 | if (dss_check_overlay(ovl, dssdev)) { | ||
1178 | if (oc->enabled) { | ||
1179 | oc->enabled = false; | ||
1180 | oc->dirty = true; | ||
1181 | } | ||
1182 | continue; | ||
1183 | } | ||
1184 | |||
1185 | ovl->info_dirty = false; | ||
1186 | oc->dirty = true; | ||
1187 | |||
1188 | oc->paddr = ovl->info.paddr; | ||
1189 | oc->vaddr = ovl->info.vaddr; | ||
1190 | oc->screen_width = ovl->info.screen_width; | ||
1191 | oc->width = ovl->info.width; | ||
1192 | oc->height = ovl->info.height; | ||
1193 | oc->color_mode = ovl->info.color_mode; | ||
1194 | oc->rotation = ovl->info.rotation; | ||
1195 | oc->rotation_type = ovl->info.rotation_type; | ||
1196 | oc->mirror = ovl->info.mirror; | ||
1197 | oc->pos_x = ovl->info.pos_x; | ||
1198 | oc->pos_y = ovl->info.pos_y; | ||
1199 | oc->out_width = ovl->info.out_width; | ||
1200 | oc->out_height = ovl->info.out_height; | ||
1201 | oc->global_alpha = ovl->info.global_alpha; | ||
1202 | |||
1203 | oc->replication = | ||
1204 | dss_use_replication(dssdev, ovl->info.color_mode); | ||
1205 | |||
1206 | oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC; | ||
1207 | |||
1208 | oc->channel = ovl->manager->id; | ||
1209 | |||
1210 | oc->enabled = true; | ||
1211 | |||
1212 | oc->manual_update = | ||
1213 | dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && | ||
1214 | dssdev->driver->get_update_mode(dssdev) != | ||
1215 | OMAP_DSS_UPDATE_AUTO; | ||
1216 | |||
1217 | ++num_planes_enabled; | ||
1218 | } | ||
1219 | |||
1220 | /* Configure managers */ | ||
1221 | list_for_each_entry(mgr, &manager_list, list) { | ||
1222 | struct omap_dss_device *dssdev; | ||
1223 | |||
1224 | if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC)) | ||
1225 | continue; | ||
1226 | |||
1227 | mc = &dss_cache.manager_cache[mgr->id]; | ||
1228 | |||
1229 | if (mgr->device_changed) { | ||
1230 | mgr->device_changed = false; | ||
1231 | mgr->info_dirty = true; | ||
1232 | } | ||
1233 | |||
1234 | if (!mgr->info_dirty) | ||
1235 | continue; | ||
1236 | |||
1237 | if (!mgr->device) | ||
1238 | continue; | ||
1239 | |||
1240 | dssdev = mgr->device; | ||
1241 | |||
1242 | mgr->info_dirty = false; | ||
1243 | mc->dirty = true; | ||
1244 | |||
1245 | mc->default_color = mgr->info.default_color; | ||
1246 | mc->trans_key_type = mgr->info.trans_key_type; | ||
1247 | mc->trans_key = mgr->info.trans_key; | ||
1248 | mc->trans_enabled = mgr->info.trans_enabled; | ||
1249 | mc->alpha_enabled = mgr->info.alpha_enabled; | ||
1250 | |||
1251 | mc->manual_upd_display = | ||
1252 | dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; | ||
1253 | |||
1254 | mc->manual_update = | ||
1255 | dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && | ||
1256 | dssdev->driver->get_update_mode(dssdev) != | ||
1257 | OMAP_DSS_UPDATE_AUTO; | ||
1258 | } | ||
1259 | |||
1260 | /* XXX TODO: Try to get fifomerge working. The problem is that it | ||
1261 | * affects both managers, not individually but at the same time. This | ||
1262 | * means the change has to be well synchronized. I guess the proper way | ||
1263 | * is to have a two step process for fifo merge: | ||
1264 | * fifomerge enable: | ||
1265 | * 1. disable other planes, leaving one plane enabled | ||
1266 | * 2. wait until the planes are disabled on HW | ||
1267 | * 3. config merged fifo thresholds, enable fifomerge | ||
1268 | * fifomerge disable: | ||
1269 | * 1. config unmerged fifo thresholds, disable fifomerge | ||
1270 | * 2. wait until fifo changes are in HW | ||
1271 | * 3. enable planes | ||
1272 | */ | ||
1273 | use_fifomerge = false; | ||
1274 | |||
1275 | /* Configure overlay fifos */ | ||
1276 | for (i = 0; i < omap_dss_get_num_overlays(); ++i) { | ||
1277 | struct omap_dss_device *dssdev; | ||
1278 | u32 size; | ||
1279 | |||
1280 | ovl = omap_dss_get_overlay(i); | ||
1281 | |||
1282 | if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) | ||
1283 | continue; | ||
1284 | |||
1285 | oc = &dss_cache.overlay_cache[ovl->id]; | ||
1286 | |||
1287 | if (!oc->enabled) | ||
1288 | continue; | ||
1289 | |||
1290 | dssdev = ovl->manager->device; | ||
1291 | |||
1292 | size = dispc_get_plane_fifo_size(ovl->id); | ||
1293 | if (use_fifomerge) | ||
1294 | size *= 3; | ||
1295 | |||
1296 | switch (dssdev->type) { | ||
1297 | case OMAP_DISPLAY_TYPE_DPI: | ||
1298 | case OMAP_DISPLAY_TYPE_DBI: | ||
1299 | case OMAP_DISPLAY_TYPE_SDI: | ||
1300 | case OMAP_DISPLAY_TYPE_VENC: | ||
1301 | default_get_overlay_fifo_thresholds(ovl->id, size, | ||
1302 | &oc->burst_size, &oc->fifo_low, | ||
1303 | &oc->fifo_high); | ||
1304 | break; | ||
1305 | #ifdef CONFIG_OMAP2_DSS_DSI | ||
1306 | case OMAP_DISPLAY_TYPE_DSI: | ||
1307 | dsi_get_overlay_fifo_thresholds(ovl->id, size, | ||
1308 | &oc->burst_size, &oc->fifo_low, | ||
1309 | &oc->fifo_high); | ||
1310 | break; | ||
1311 | #endif | ||
1312 | default: | ||
1313 | BUG(); | ||
1314 | } | ||
1315 | } | ||
1316 | |||
1317 | r = 0; | ||
1318 | dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); | ||
1319 | if (!dss_cache.irq_enabled) { | ||
1320 | r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, | ||
1321 | DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | | ||
1322 | DISPC_IRQ_EVSYNC_EVEN); | ||
1323 | dss_cache.irq_enabled = true; | ||
1324 | } | ||
1325 | configure_dispc(); | ||
1326 | dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); | ||
1327 | |||
1328 | spin_unlock_irqrestore(&dss_cache.lock, flags); | ||
1329 | |||
1330 | return r; | ||
1331 | } | ||
1332 | |||
1333 | static int dss_check_manager(struct omap_overlay_manager *mgr) | ||
1334 | { | ||
1335 | /* OMAP supports only graphics source transparency color key and alpha | ||
1336 | * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */ | ||
1337 | |||
1338 | if (mgr->info.alpha_enabled && mgr->info.trans_enabled && | ||
1339 | mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) | ||
1340 | return -EINVAL; | ||
1341 | |||
1342 | return 0; | ||
1343 | } | ||
1344 | |||
1345 | static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr, | ||
1346 | struct omap_overlay_manager_info *info) | ||
1347 | { | ||
1348 | int r; | ||
1349 | struct omap_overlay_manager_info old_info; | ||
1350 | |||
1351 | old_info = mgr->info; | ||
1352 | mgr->info = *info; | ||
1353 | |||
1354 | r = dss_check_manager(mgr); | ||
1355 | if (r) { | ||
1356 | mgr->info = old_info; | ||
1357 | return r; | ||
1358 | } | ||
1359 | |||
1360 | mgr->info_dirty = true; | ||
1361 | |||
1362 | return 0; | ||
1363 | } | ||
1364 | |||
1365 | static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr, | ||
1366 | struct omap_overlay_manager_info *info) | ||
1367 | { | ||
1368 | *info = mgr->info; | ||
1369 | } | ||
1370 | |||
1371 | static int dss_mgr_enable(struct omap_overlay_manager *mgr) | ||
1372 | { | ||
1373 | dispc_enable_channel(mgr->id, 1); | ||
1374 | return 0; | ||
1375 | } | ||
1376 | |||
1377 | static int dss_mgr_disable(struct omap_overlay_manager *mgr) | ||
1378 | { | ||
1379 | dispc_enable_channel(mgr->id, 0); | ||
1380 | return 0; | ||
1381 | } | ||
1382 | |||
1383 | static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager) | ||
1384 | { | ||
1385 | ++num_managers; | ||
1386 | list_add_tail(&manager->list, &manager_list); | ||
1387 | } | ||
1388 | |||
1389 | int dss_init_overlay_managers(struct platform_device *pdev) | ||
1390 | { | ||
1391 | int i, r; | ||
1392 | |||
1393 | spin_lock_init(&dss_cache.lock); | ||
1394 | |||
1395 | INIT_LIST_HEAD(&manager_list); | ||
1396 | |||
1397 | num_managers = 0; | ||
1398 | |||
1399 | for (i = 0; i < 2; ++i) { | ||
1400 | struct omap_overlay_manager *mgr; | ||
1401 | mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); | ||
1402 | |||
1403 | BUG_ON(mgr == NULL); | ||
1404 | |||
1405 | switch (i) { | ||
1406 | case 0: | ||
1407 | mgr->name = "lcd"; | ||
1408 | mgr->id = OMAP_DSS_CHANNEL_LCD; | ||
1409 | mgr->supported_displays = | ||
1410 | OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | | ||
1411 | OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI; | ||
1412 | break; | ||
1413 | case 1: | ||
1414 | mgr->name = "tv"; | ||
1415 | mgr->id = OMAP_DSS_CHANNEL_DIGIT; | ||
1416 | mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC; | ||
1417 | break; | ||
1418 | } | ||
1419 | |||
1420 | mgr->set_device = &omap_dss_set_device; | ||
1421 | mgr->unset_device = &omap_dss_unset_device; | ||
1422 | mgr->apply = &omap_dss_mgr_apply; | ||
1423 | mgr->set_manager_info = &omap_dss_mgr_set_info; | ||
1424 | mgr->get_manager_info = &omap_dss_mgr_get_info; | ||
1425 | mgr->wait_for_go = &dss_mgr_wait_for_go; | ||
1426 | mgr->wait_for_vsync = &dss_mgr_wait_for_vsync; | ||
1427 | |||
1428 | mgr->enable = &dss_mgr_enable; | ||
1429 | mgr->disable = &dss_mgr_disable; | ||
1430 | |||
1431 | mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC; | ||
1432 | |||
1433 | dss_overlay_setup_dispc_manager(mgr); | ||
1434 | |||
1435 | omap_dss_add_overlay_manager(mgr); | ||
1436 | |||
1437 | r = kobject_init_and_add(&mgr->kobj, &manager_ktype, | ||
1438 | &pdev->dev.kobj, "manager%d", i); | ||
1439 | |||
1440 | if (r) { | ||
1441 | DSSERR("failed to create sysfs file\n"); | ||
1442 | continue; | ||
1443 | } | ||
1444 | } | ||
1445 | |||
1446 | #ifdef L4_EXAMPLE | ||
1447 | { | ||
1448 | int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr) | ||
1449 | { | ||
1450 | DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name); | ||
1451 | |||
1452 | return 0; | ||
1453 | } | ||
1454 | |||
1455 | struct omap_overlay_manager *mgr; | ||
1456 | mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); | ||
1457 | |||
1458 | BUG_ON(mgr == NULL); | ||
1459 | |||
1460 | mgr->name = "l4"; | ||
1461 | mgr->supported_displays = | ||
1462 | OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI; | ||
1463 | |||
1464 | mgr->set_device = &omap_dss_set_device; | ||
1465 | mgr->unset_device = &omap_dss_unset_device; | ||
1466 | mgr->apply = &omap_dss_mgr_apply_l4; | ||
1467 | mgr->set_manager_info = &omap_dss_mgr_set_info; | ||
1468 | mgr->get_manager_info = &omap_dss_mgr_get_info; | ||
1469 | |||
1470 | dss_overlay_setup_l4_manager(mgr); | ||
1471 | |||
1472 | omap_dss_add_overlay_manager(mgr); | ||
1473 | |||
1474 | r = kobject_init_and_add(&mgr->kobj, &manager_ktype, | ||
1475 | &pdev->dev.kobj, "managerl4"); | ||
1476 | |||
1477 | if (r) | ||
1478 | DSSERR("failed to create sysfs file\n"); | ||
1479 | } | ||
1480 | #endif | ||
1481 | |||
1482 | return 0; | ||
1483 | } | ||
1484 | |||
1485 | void dss_uninit_overlay_managers(struct platform_device *pdev) | ||
1486 | { | ||
1487 | struct omap_overlay_manager *mgr; | ||
1488 | |||
1489 | while (!list_empty(&manager_list)) { | ||
1490 | mgr = list_first_entry(&manager_list, | ||
1491 | struct omap_overlay_manager, list); | ||
1492 | list_del(&mgr->list); | ||
1493 | kobject_del(&mgr->kobj); | ||
1494 | kobject_put(&mgr->kobj); | ||
1495 | kfree(mgr); | ||
1496 | } | ||
1497 | |||
1498 | num_managers = 0; | ||
1499 | } | ||
1500 | |||
1501 | int omap_dss_get_num_overlay_managers(void) | ||
1502 | { | ||
1503 | return num_managers; | ||
1504 | } | ||
1505 | EXPORT_SYMBOL(omap_dss_get_num_overlay_managers); | ||
1506 | |||
1507 | struct omap_overlay_manager *omap_dss_get_overlay_manager(int num) | ||
1508 | { | ||
1509 | int i = 0; | ||
1510 | struct omap_overlay_manager *mgr; | ||
1511 | |||
1512 | list_for_each_entry(mgr, &manager_list, list) { | ||
1513 | if (i++ == num) | ||
1514 | return mgr; | ||
1515 | } | ||
1516 | |||
1517 | return NULL; | ||
1518 | } | ||
1519 | EXPORT_SYMBOL(omap_dss_get_overlay_manager); | ||
1520 | |||