]>
Commit | Line | Data |
---|---|---|
eed07e0e TV |
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> | |
5a0e3ad6 | 26 | #include <linux/slab.h> |
eed07e0e TV |
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 | ||
52cf25d0 | 345 | static const struct sysfs_ops manager_sysfs_ops = { |
eed07e0e TV |
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 | ||
3f71cbe7 TV |
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 | ||
eed07e0e TV |
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; | |
446f7bff | 526 | struct omap_dss_device *dssdev = mgr->device; |
eed07e0e | 527 | |
446f7bff | 528 | if (!dssdev) |
eed07e0e TV |
529 | return 0; |
530 | ||
446f7bff | 531 | if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { |
eed07e0e TV |
532 | irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; |
533 | channel = OMAP_DSS_CHANNEL_DIGIT; | |
534 | } else { | |
446f7bff | 535 | if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { |
eed07e0e | 536 | enum omap_dss_update_mode mode; |
446f7bff | 537 | mode = dssdev->driver->get_update_mode(dssdev); |
eed07e0e TV |
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; | |
446f7bff | 610 | mode = dssdev->driver->get_update_mode(dssdev); |
eed07e0e TV |
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 | ||
a2faee84 | 1082 | dssdev->manager->enable(dssdev->manager); |
eed07e0e TV |
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 && | |
446f7bff TV |
1214 | dssdev->driver->get_update_mode(dssdev) != |
1215 | OMAP_DSS_UPDATE_AUTO; | |
eed07e0e TV |
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 && | |
446f7bff TV |
1256 | dssdev->driver->get_update_mode(dssdev) != |
1257 | OMAP_DSS_UPDATE_AUTO; | |
eed07e0e TV |
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 | ||
a2faee84 TV |
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 | ||
eed07e0e TV |
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; | |
3f71cbe7 | 1426 | mgr->wait_for_vsync = &dss_mgr_wait_for_vsync; |
eed07e0e | 1427 | |
a2faee84 TV |
1428 | mgr->enable = &dss_mgr_enable; |
1429 | mgr->disable = &dss_mgr_disable; | |
1430 | ||
eed07e0e TV |
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 |