Linux-libre 3.4.28-gnu1
[librecmc/linux-libre.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/jiffies.h>
30
31 #include <video/omapdss.h>
32
33 #include "dss.h"
34 #include "dss_features.h"
35
36 static int num_managers;
37 static struct omap_overlay_manager *managers;
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         struct omap_overlay_manager_info info;
108
109         mgr->get_manager_info(mgr, &info);
110
111         return snprintf(buf, PAGE_SIZE, "%#x\n", info.default_color);
112 }
113
114 static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr,
115                                            const char *buf, size_t size)
116 {
117         struct omap_overlay_manager_info info;
118         u32 color;
119         int r;
120
121         r = kstrtouint(buf, 0, &color);
122         if (r)
123                 return r;
124
125         mgr->get_manager_info(mgr, &info);
126
127         info.default_color = color;
128
129         r = mgr->set_manager_info(mgr, &info);
130         if (r)
131                 return r;
132
133         r = mgr->apply(mgr);
134         if (r)
135                 return r;
136
137         return size;
138 }
139
140 static const char *trans_key_type_str[] = {
141         "gfx-destination",
142         "video-source",
143 };
144
145 static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr,
146                                            char *buf)
147 {
148         enum omap_dss_trans_key_type key_type;
149         struct omap_overlay_manager_info info;
150
151         mgr->get_manager_info(mgr, &info);
152
153         key_type = info.trans_key_type;
154         BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str));
155
156         return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]);
157 }
158
159 static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr,
160                                             const char *buf, size_t size)
161 {
162         enum omap_dss_trans_key_type key_type;
163         struct omap_overlay_manager_info info;
164         int r;
165
166         for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
167                         key_type < ARRAY_SIZE(trans_key_type_str); key_type++) {
168                 if (sysfs_streq(buf, trans_key_type_str[key_type]))
169                         break;
170         }
171
172         if (key_type == ARRAY_SIZE(trans_key_type_str))
173                 return -EINVAL;
174
175         mgr->get_manager_info(mgr, &info);
176
177         info.trans_key_type = key_type;
178
179         r = mgr->set_manager_info(mgr, &info);
180         if (r)
181                 return r;
182
183         r = mgr->apply(mgr);
184         if (r)
185                 return r;
186
187         return size;
188 }
189
190 static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr,
191                                             char *buf)
192 {
193         struct omap_overlay_manager_info info;
194
195         mgr->get_manager_info(mgr, &info);
196
197         return snprintf(buf, PAGE_SIZE, "%#x\n", info.trans_key);
198 }
199
200 static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr,
201                                              const char *buf, size_t size)
202 {
203         struct omap_overlay_manager_info info;
204         u32 key_value;
205         int r;
206
207         r = kstrtouint(buf, 0, &key_value);
208         if (r)
209                 return r;
210
211         mgr->get_manager_info(mgr, &info);
212
213         info.trans_key = key_value;
214
215         r = mgr->set_manager_info(mgr, &info);
216         if (r)
217                 return r;
218
219         r = mgr->apply(mgr);
220         if (r)
221                 return r;
222
223         return size;
224 }
225
226 static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr,
227                                               char *buf)
228 {
229         struct omap_overlay_manager_info info;
230
231         mgr->get_manager_info(mgr, &info);
232
233         return snprintf(buf, PAGE_SIZE, "%d\n", info.trans_enabled);
234 }
235
236 static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
237                                                const char *buf, size_t size)
238 {
239         struct omap_overlay_manager_info info;
240         bool enable;
241         int r;
242
243         r = strtobool(buf, &enable);
244         if (r)
245                 return r;
246
247         mgr->get_manager_info(mgr, &info);
248
249         info.trans_enabled = enable;
250
251         r = mgr->set_manager_info(mgr, &info);
252         if (r)
253                 return r;
254
255         r = mgr->apply(mgr);
256         if (r)
257                 return r;
258
259         return size;
260 }
261
262 static ssize_t manager_alpha_blending_enabled_show(
263                 struct omap_overlay_manager *mgr, char *buf)
264 {
265         struct omap_overlay_manager_info info;
266
267         mgr->get_manager_info(mgr, &info);
268
269         WARN_ON(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER));
270
271         return snprintf(buf, PAGE_SIZE, "%d\n",
272                 info.partial_alpha_enabled);
273 }
274
275 static ssize_t manager_alpha_blending_enabled_store(
276                 struct omap_overlay_manager *mgr,
277                 const char *buf, size_t size)
278 {
279         struct omap_overlay_manager_info info;
280         bool enable;
281         int r;
282
283         WARN_ON(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER));
284
285         r = strtobool(buf, &enable);
286         if (r)
287                 return r;
288
289         mgr->get_manager_info(mgr, &info);
290
291         info.partial_alpha_enabled = enable;
292
293         r = mgr->set_manager_info(mgr, &info);
294         if (r)
295                 return r;
296
297         r = mgr->apply(mgr);
298         if (r)
299                 return r;
300
301         return size;
302 }
303
304 static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr,
305                 char *buf)
306 {
307         struct omap_overlay_manager_info info;
308
309         mgr->get_manager_info(mgr, &info);
310
311         return snprintf(buf, PAGE_SIZE, "%d\n", info.cpr_enable);
312 }
313
314 static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
315                 const char *buf, size_t size)
316 {
317         struct omap_overlay_manager_info info;
318         int r;
319         bool enable;
320
321         if (!dss_has_feature(FEAT_CPR))
322                 return -ENODEV;
323
324         r = strtobool(buf, &enable);
325         if (r)
326                 return r;
327
328         mgr->get_manager_info(mgr, &info);
329
330         if (info.cpr_enable == enable)
331                 return size;
332
333         info.cpr_enable = enable;
334
335         r = mgr->set_manager_info(mgr, &info);
336         if (r)
337                 return r;
338
339         r = mgr->apply(mgr);
340         if (r)
341                 return r;
342
343         return size;
344 }
345
346 static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr,
347                 char *buf)
348 {
349         struct omap_overlay_manager_info info;
350
351         mgr->get_manager_info(mgr, &info);
352
353         return snprintf(buf, PAGE_SIZE,
354                         "%d %d %d %d %d %d %d %d %d\n",
355                         info.cpr_coefs.rr,
356                         info.cpr_coefs.rg,
357                         info.cpr_coefs.rb,
358                         info.cpr_coefs.gr,
359                         info.cpr_coefs.gg,
360                         info.cpr_coefs.gb,
361                         info.cpr_coefs.br,
362                         info.cpr_coefs.bg,
363                         info.cpr_coefs.bb);
364 }
365
366 static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr,
367                 const char *buf, size_t size)
368 {
369         struct omap_overlay_manager_info info;
370         struct omap_dss_cpr_coefs coefs;
371         int r, i;
372         s16 *arr;
373
374         if (!dss_has_feature(FEAT_CPR))
375                 return -ENODEV;
376
377         if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd",
378                                 &coefs.rr, &coefs.rg, &coefs.rb,
379                                 &coefs.gr, &coefs.gg, &coefs.gb,
380                                 &coefs.br, &coefs.bg, &coefs.bb) != 9)
381                 return -EINVAL;
382
383         arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb,
384                 coefs.gr, coefs.gg, coefs.gb,
385                 coefs.br, coefs.bg, coefs.bb };
386
387         for (i = 0; i < 9; ++i) {
388                 if (arr[i] < -512 || arr[i] > 511)
389                         return -EINVAL;
390         }
391
392         mgr->get_manager_info(mgr, &info);
393
394         info.cpr_coefs = coefs;
395
396         r = mgr->set_manager_info(mgr, &info);
397         if (r)
398                 return r;
399
400         r = mgr->apply(mgr);
401         if (r)
402                 return r;
403
404         return size;
405 }
406
407 struct manager_attribute {
408         struct attribute attr;
409         ssize_t (*show)(struct omap_overlay_manager *, char *);
410         ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t);
411 };
412
413 #define MANAGER_ATTR(_name, _mode, _show, _store) \
414         struct manager_attribute manager_attr_##_name = \
415         __ATTR(_name, _mode, _show, _store)
416
417 static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL);
418 static MANAGER_ATTR(display, S_IRUGO|S_IWUSR,
419                 manager_display_show, manager_display_store);
420 static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR,
421                 manager_default_color_show, manager_default_color_store);
422 static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR,
423                 manager_trans_key_type_show, manager_trans_key_type_store);
424 static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR,
425                 manager_trans_key_value_show, manager_trans_key_value_store);
426 static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,
427                 manager_trans_key_enabled_show,
428                 manager_trans_key_enabled_store);
429 static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
430                 manager_alpha_blending_enabled_show,
431                 manager_alpha_blending_enabled_store);
432 static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR,
433                 manager_cpr_enable_show,
434                 manager_cpr_enable_store);
435 static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR,
436                 manager_cpr_coef_show,
437                 manager_cpr_coef_store);
438
439
440 static struct attribute *manager_sysfs_attrs[] = {
441         &manager_attr_name.attr,
442         &manager_attr_display.attr,
443         &manager_attr_default_color.attr,
444         &manager_attr_trans_key_type.attr,
445         &manager_attr_trans_key_value.attr,
446         &manager_attr_trans_key_enabled.attr,
447         &manager_attr_alpha_blending_enabled.attr,
448         &manager_attr_cpr_enable.attr,
449         &manager_attr_cpr_coef.attr,
450         NULL
451 };
452
453 static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr,
454                 char *buf)
455 {
456         struct omap_overlay_manager *manager;
457         struct manager_attribute *manager_attr;
458
459         manager = container_of(kobj, struct omap_overlay_manager, kobj);
460         manager_attr = container_of(attr, struct manager_attribute, attr);
461
462         if (!manager_attr->show)
463                 return -ENOENT;
464
465         return manager_attr->show(manager, buf);
466 }
467
468 static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr,
469                 const char *buf, size_t size)
470 {
471         struct omap_overlay_manager *manager;
472         struct manager_attribute *manager_attr;
473
474         manager = container_of(kobj, struct omap_overlay_manager, kobj);
475         manager_attr = container_of(attr, struct manager_attribute, attr);
476
477         if (!manager_attr->store)
478                 return -ENOENT;
479
480         return manager_attr->store(manager, buf, size);
481 }
482
483 static const struct sysfs_ops manager_sysfs_ops = {
484         .show = manager_attr_show,
485         .store = manager_attr_store,
486 };
487
488 static struct kobj_type manager_ktype = {
489         .sysfs_ops = &manager_sysfs_ops,
490         .default_attrs = manager_sysfs_attrs,
491 };
492
493 static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
494 {
495         unsigned long timeout = msecs_to_jiffies(500);
496         u32 irq;
497         int r;
498
499         r = dispc_runtime_get();
500         if (r)
501                 return r;
502
503         if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
504                 irq = DISPC_IRQ_EVSYNC_ODD;
505         } else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) {
506                 irq = DISPC_IRQ_EVSYNC_EVEN;
507         } else {
508                 if (mgr->id == OMAP_DSS_CHANNEL_LCD)
509                         irq = DISPC_IRQ_VSYNC;
510                 else
511                         irq = DISPC_IRQ_VSYNC2;
512         }
513
514         r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
515
516         dispc_runtime_put();
517
518         return r;
519 }
520
521 int dss_init_overlay_managers(struct platform_device *pdev)
522 {
523         int i, r;
524
525         num_managers = dss_feat_get_num_mgrs();
526
527         managers = kzalloc(sizeof(struct omap_overlay_manager) * num_managers,
528                         GFP_KERNEL);
529
530         BUG_ON(managers == NULL);
531
532         for (i = 0; i < num_managers; ++i) {
533                 struct omap_overlay_manager *mgr = &managers[i];
534
535                 switch (i) {
536                 case 0:
537                         mgr->name = "lcd";
538                         mgr->id = OMAP_DSS_CHANNEL_LCD;
539                         break;
540                 case 1:
541                         mgr->name = "tv";
542                         mgr->id = OMAP_DSS_CHANNEL_DIGIT;
543                         break;
544                 case 2:
545                         mgr->name = "lcd2";
546                         mgr->id = OMAP_DSS_CHANNEL_LCD2;
547                         break;
548                 }
549
550                 mgr->set_device = &dss_mgr_set_device;
551                 mgr->unset_device = &dss_mgr_unset_device;
552                 mgr->apply = &omap_dss_mgr_apply;
553                 mgr->set_manager_info = &dss_mgr_set_info;
554                 mgr->get_manager_info = &dss_mgr_get_info;
555                 mgr->wait_for_go = &dss_mgr_wait_for_go;
556                 mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
557
558                 mgr->caps = 0;
559                 mgr->supported_displays =
560                         dss_feat_get_supported_displays(mgr->id);
561
562                 INIT_LIST_HEAD(&mgr->overlays);
563
564                 r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
565                                 &pdev->dev.kobj, "manager%d", i);
566
567                 if (r)
568                         DSSERR("failed to create sysfs file\n");
569         }
570
571         return 0;
572 }
573
574 void dss_uninit_overlay_managers(struct platform_device *pdev)
575 {
576         int i;
577
578         for (i = 0; i < num_managers; ++i) {
579                 struct omap_overlay_manager *mgr = &managers[i];
580
581                 kobject_del(&mgr->kobj);
582                 kobject_put(&mgr->kobj);
583         }
584
585         kfree(managers);
586         managers = NULL;
587         num_managers = 0;
588 }
589
590 int omap_dss_get_num_overlay_managers(void)
591 {
592         return num_managers;
593 }
594 EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
595
596 struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
597 {
598         if (num >= num_managers)
599                 return NULL;
600
601         return &managers[num];
602 }
603 EXPORT_SYMBOL(omap_dss_get_overlay_manager);
604
605 int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
606                 const struct omap_overlay_manager_info *info)
607 {
608         if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) {
609                 /*
610                  * OMAP3 supports only graphics source transparency color key
611                  * and alpha blending simultaneously. See TRM 15.4.2.4.2.2
612                  * Alpha Mode.
613                  */
614                 if (info->partial_alpha_enabled && info->trans_enabled
615                         && info->trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) {
616                         DSSERR("check_manager: illegal transparency key\n");
617                         return -EINVAL;
618                 }
619         }
620
621         return 0;
622 }
623
624 static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr,
625                 struct omap_overlay_info **overlay_infos)
626 {
627         struct omap_overlay *ovl1, *ovl2;
628         struct omap_overlay_info *info1, *info2;
629
630         list_for_each_entry(ovl1, &mgr->overlays, list) {
631                 info1 = overlay_infos[ovl1->id];
632
633                 if (info1 == NULL)
634                         continue;
635
636                 list_for_each_entry(ovl2, &mgr->overlays, list) {
637                         if (ovl1 == ovl2)
638                                 continue;
639
640                         info2 = overlay_infos[ovl2->id];
641
642                         if (info2 == NULL)
643                                 continue;
644
645                         if (info1->zorder == info2->zorder) {
646                                 DSSERR("overlays %d and %d have the same "
647                                                 "zorder %d\n",
648                                         ovl1->id, ovl2->id, info1->zorder);
649                                 return -EINVAL;
650                         }
651                 }
652         }
653
654         return 0;
655 }
656
657 int dss_mgr_check(struct omap_overlay_manager *mgr,
658                 struct omap_dss_device *dssdev,
659                 struct omap_overlay_manager_info *info,
660                 struct omap_overlay_info **overlay_infos)
661 {
662         struct omap_overlay *ovl;
663         int r;
664
665         if (dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) {
666                 r = dss_mgr_check_zorder(mgr, overlay_infos);
667                 if (r)
668                         return r;
669         }
670
671         list_for_each_entry(ovl, &mgr->overlays, list) {
672                 struct omap_overlay_info *oi;
673                 int r;
674
675                 oi = overlay_infos[ovl->id];
676
677                 if (oi == NULL)
678                         continue;
679
680                 r = dss_ovl_check(ovl, oi, dssdev);
681                 if (r)
682                         return r;
683         }
684
685         return 0;
686 }