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