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