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