DSS2: OMAP2/3 Display Subsystem driver
[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/module.h>
27 #include <linux/platform_device.h>
28
29 #include <mach/display.h>
30
31 #include "dss.h"
32
33 static int num_managers;
34 static struct list_head manager_list;
35
36 static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf)
37 {
38         return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name);
39 }
40
41 static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf)
42 {
43         return snprintf(buf, PAGE_SIZE, "%s\n",
44                         mgr->display ? mgr->display->name : "<none>");
45 }
46
47 static ssize_t manager_display_store(struct omap_overlay_manager *mgr, const char *buf, size_t size)
48 {
49         int r, i;
50         int len = size;
51         struct omap_display *display = NULL;
52
53         if (buf[size-1] == '\n')
54                 --len;
55
56         if (len > 0) {
57                 for (i = 0; i < omap_dss_get_num_displays(); ++i) {
58                         display = dss_get_display(i);
59
60                         if (strncmp(buf, display->name, len) == 0)
61                                 break;
62
63                         display = NULL;
64                 }
65         }
66
67         if (len > 0 && display == NULL)
68                 return -EINVAL;
69
70         if (display)
71                 DSSDBG("display %s found\n", display->name);
72
73         if (mgr->display) {
74                 r = mgr->unset_display(mgr);
75                 if (r) {
76                         DSSERR("failed to unset display\n");
77                         return r;
78                 }
79         }
80
81         if (display) {
82                 r = mgr->set_display(mgr, display);
83                 if (r) {
84                         DSSERR("failed to set manager\n");
85                         return r;
86                 }
87
88                 r = mgr->apply(mgr);
89                 if (r) {
90                         DSSERR("failed to apply dispc config\n");
91                         return r;
92                 }
93         }
94
95         return size;
96 }
97
98 static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr,
99                                           char *buf)
100 {
101         u32 default_color;
102
103         default_color = dispc_get_default_color(mgr->id);
104         return snprintf(buf, PAGE_SIZE, "%d", default_color);
105 }
106
107 static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr,
108                                            const char *buf, size_t size)
109 {
110         u32 default_color;
111
112         if (sscanf(buf, "%d", &default_color) != 1)
113                 return -EINVAL;
114         dispc_set_default_color(mgr->id, default_color);
115
116         return size;
117 }
118
119 static const char *color_key_type_str[] = {
120         "gfx-destination",
121         "video-source",
122 };
123
124 static ssize_t manager_color_key_type_show(struct omap_overlay_manager *mgr,
125                                            char *buf)
126 {
127         enum omap_dss_color_key_type key_type;
128
129         dispc_get_trans_key(mgr->id, &key_type, NULL);
130         BUG_ON(key_type >= ARRAY_SIZE(color_key_type_str));
131
132         return snprintf(buf, PAGE_SIZE, "%s\n", color_key_type_str[key_type]);
133 }
134
135 static ssize_t manager_color_key_type_store(struct omap_overlay_manager *mgr,
136                                             const char *buf, size_t size)
137 {
138         enum omap_dss_color_key_type key_type;
139         u32 key_value;
140
141         for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
142              key_type < ARRAY_SIZE(color_key_type_str); key_type++) {
143                 if (sysfs_streq(buf, color_key_type_str[key_type]))
144                         break;
145         }
146         if (key_type == ARRAY_SIZE(color_key_type_str))
147                 return -EINVAL;
148         dispc_get_trans_key(mgr->id, NULL, &key_value);
149         dispc_set_trans_key(mgr->id, key_type, key_value);
150
151         return size;
152 }
153
154 static ssize_t manager_color_key_value_show(struct omap_overlay_manager *mgr,
155                                             char *buf)
156 {
157         u32 key_value;
158
159         dispc_get_trans_key(mgr->id, NULL, &key_value);
160
161         return snprintf(buf, PAGE_SIZE, "%d\n", key_value);
162 }
163
164 static ssize_t manager_color_key_value_store(struct omap_overlay_manager *mgr,
165                                              const char *buf, size_t size)
166 {
167         enum omap_dss_color_key_type key_type;
168         u32 key_value;
169
170         if (sscanf(buf, "%d", &key_value) != 1)
171                 return -EINVAL;
172         dispc_get_trans_key(mgr->id, &key_type, NULL);
173         dispc_set_trans_key(mgr->id, key_type, key_value);
174
175         return size;
176 }
177
178 static ssize_t manager_color_key_enabled_show(struct omap_overlay_manager *mgr,
179                                               char *buf)
180 {
181         return snprintf(buf, PAGE_SIZE, "%d\n",
182                         dispc_trans_key_enabled(mgr->id));
183 }
184
185 static ssize_t manager_color_key_enabled_store(struct omap_overlay_manager *mgr,
186                                                const char *buf, size_t size)
187 {
188         int enable;
189
190         if (sscanf(buf, "%d", &enable) != 1)
191                 return -EINVAL;
192
193         dispc_enable_trans_key(mgr->id, enable);
194
195         return size;
196 }
197
198
199 struct manager_attribute {
200         struct attribute attr;
201         ssize_t (*show)(struct omap_overlay_manager *, char *);
202         ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t);
203 };
204
205 #define MANAGER_ATTR(_name, _mode, _show, _store) \
206         struct manager_attribute manager_attr_##_name = \
207         __ATTR(_name, _mode, _show, _store)
208
209 static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL);
210 static MANAGER_ATTR(display, S_IRUGO|S_IWUSR,
211                 manager_display_show, manager_display_store);
212 static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR,
213                 manager_default_color_show, manager_default_color_store);
214 static MANAGER_ATTR(color_key_type, S_IRUGO|S_IWUSR,
215                 manager_color_key_type_show, manager_color_key_type_store);
216 static MANAGER_ATTR(color_key_value, S_IRUGO|S_IWUSR,
217                 manager_color_key_value_show, manager_color_key_value_store);
218 static MANAGER_ATTR(color_key_enabled, S_IRUGO|S_IWUSR,
219                 manager_color_key_enabled_show, manager_color_key_enabled_store);
220
221 static struct attribute *manager_sysfs_attrs[] = {
222         &manager_attr_name.attr,
223         &manager_attr_display.attr,
224         &manager_attr_default_color.attr,
225         &manager_attr_color_key_type.attr,
226         &manager_attr_color_key_value.attr,
227         &manager_attr_color_key_enabled.attr,
228         NULL
229 };
230
231 static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
232 {
233         struct omap_overlay_manager *manager;
234         struct manager_attribute *manager_attr;
235
236         manager = container_of(kobj, struct omap_overlay_manager, kobj);
237         manager_attr = container_of(attr, struct manager_attribute, attr);
238
239         if (!manager_attr->show)
240                 return -ENOENT;
241
242         return manager_attr->show(manager, buf);
243 }
244
245 static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr,
246                 const char *buf, size_t size)
247 {
248         struct omap_overlay_manager *manager;
249         struct manager_attribute *manager_attr;
250
251         manager = container_of(kobj, struct omap_overlay_manager, kobj);
252         manager_attr = container_of(attr, struct manager_attribute, attr);
253
254         if (!manager_attr->store)
255                 return -ENOENT;
256
257         return manager_attr->store(manager, buf, size);
258 }
259
260 static struct sysfs_ops manager_sysfs_ops = {
261         .show = manager_attr_show,
262         .store = manager_attr_store,
263 };
264
265 static struct kobj_type manager_ktype = {
266         .sysfs_ops = &manager_sysfs_ops,
267         .default_attrs = manager_sysfs_attrs,
268 };
269
270 static int omap_dss_set_display(struct omap_overlay_manager *mgr,
271                 struct omap_display *display)
272 {
273         int i;
274         int r;
275
276         if (display->manager) {
277                 DSSERR("display '%s' already has a manager '%s'\n",
278                                display->name, display->manager->name);
279                 return -EINVAL;
280         }
281
282         if ((mgr->supported_displays & display->type) == 0) {
283                 DSSERR("display '%s' does not support manager '%s'\n",
284                                display->name, mgr->name);
285                 return -EINVAL;
286         }
287
288         for (i = 0; i < mgr->num_overlays; i++) {
289                 struct omap_overlay *ovl = mgr->overlays[i];
290
291                 if (ovl->manager != mgr || !ovl->info.enabled)
292                         continue;
293
294                 r = dss_check_overlay(ovl, display);
295                 if (r)
296                         return r;
297         }
298
299         display->manager = mgr;
300         mgr->display = display;
301
302         return 0;
303 }
304
305 static int omap_dss_unset_display(struct omap_overlay_manager *mgr)
306 {
307         if (!mgr->display) {
308                 DSSERR("failed to unset display, display not set.\n");
309                 return -EINVAL;
310         }
311
312         mgr->display->manager = NULL;
313         mgr->display = NULL;
314
315         return 0;
316 }
317
318
319 static int overlay_enabled(struct omap_overlay *ovl)
320 {
321         return ovl->info.enabled && ovl->manager && ovl->manager->display;
322 }
323
324 /* We apply settings to both managers here so that we can use optimizations
325  * like fifomerge. Shadow registers can be changed first and the non-shadowed
326  * should be changed last, at the same time with GO */
327 static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
328 {
329         int i;
330         int ret = 0;
331         enum omap_dss_update_mode mode;
332         struct omap_display *display;
333         struct omap_overlay *ovl;
334         bool ilace = 0;
335         int outw, outh;
336         int r;
337         int num_planes_enabled = 0;
338
339         DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
340
341         dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
342
343         /* Configure normal overlay parameters and disable unused overlays */
344         for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
345                 ovl = omap_dss_get_overlay(i);
346
347                 if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
348                         continue;
349
350                 if (!overlay_enabled(ovl)) {
351                         dispc_enable_plane(ovl->id, 0);
352                         continue;
353                 }
354
355                 display = ovl->manager->display;
356
357                 if (dss_check_overlay(ovl, display)) {
358                         dispc_enable_plane(ovl->id, 0);
359                         continue;
360                 }
361
362                 ++num_planes_enabled;
363
364                 /* On a manual update display, in manual update mode, update()
365                  * handles configuring planes */
366                 mode = OMAP_DSS_UPDATE_AUTO;
367                 if (display->get_update_mode)
368                         mode = display->get_update_mode(mgr->display);
369
370                 if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
371                                 mode != OMAP_DSS_UPDATE_AUTO)
372                         continue;
373
374                 if (display->type == OMAP_DISPLAY_TYPE_VENC)
375                         ilace = 1;
376
377                 if (ovl->info.out_width == 0)
378                         outw = ovl->info.width;
379                 else
380                         outw = ovl->info.out_width;
381
382                 if (ovl->info.out_height == 0)
383                         outh = ovl->info.height;
384                 else
385                         outh = ovl->info.out_height;
386
387                 r = dispc_setup_plane(ovl->id, ovl->manager->id,
388                                 ovl->info.paddr,
389                                 ovl->info.screen_width,
390                                 ovl->info.pos_x,
391                                 ovl->info.pos_y,
392                                 ovl->info.width,
393                                 ovl->info.height,
394                                 outw,
395                                 outh,
396                                 ovl->info.color_mode,
397                                 ilace,
398                                 ovl->info.rotation,
399                                 ovl->info.mirror);
400
401                 if (r) {
402                         DSSERR("dispc_setup_plane failed for ovl %d\n",
403                                         ovl->id);
404                         dispc_enable_plane(ovl->id, 0);
405                         continue;
406                 }
407
408                 dispc_enable_plane(ovl->id, 1);
409         }
410
411         /* Enable fifo merge if possible */
412         dispc_enable_fifomerge(num_planes_enabled == 1);
413
414         /* Go through overlays again. This time we configure fifos.  We have to
415          * do this after enabling/disabling fifomerge so that we have correct
416          * knowledge of fifo sizes */
417         for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
418                 ovl = omap_dss_get_overlay(i);
419
420                 if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
421                         continue;
422
423                 if (!overlay_enabled(ovl)) {
424                         continue;
425                 }
426
427                 ovl->manager->display->configure_overlay(ovl);
428         }
429
430         /* Issue GO for managers */
431         list_for_each_entry(mgr, &manager_list, list) {
432                 if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC))
433                         continue;
434
435                 display = mgr->display;
436
437                 if (!display)
438                         continue;
439
440                 /* We don't need GO with manual update display. LCD iface will
441                  * always be turned off after frame, and new settings will
442                  * be taken in to use at next update */
443                 if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
444                         continue;
445
446                 dispc_go(mgr->id);
447         }
448
449         dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
450
451         return ret;
452 }
453
454 static void omap_dss_mgr_set_def_color(struct omap_overlay_manager *mgr,
455                 u32 color)
456 {
457         dispc_set_default_color(mgr->id, color);
458 }
459
460 static void omap_dss_mgr_set_trans_key(struct omap_overlay_manager *mgr,
461                 enum omap_dss_color_key_type type,
462                 u32 trans_key)
463 {
464         dispc_set_trans_key(mgr->id, type, trans_key);
465 }
466
467 static void omap_dss_mgr_enable_trans_key(struct omap_overlay_manager *mgr,
468                 bool enable)
469 {
470         dispc_enable_trans_key(mgr->id, enable);
471 }
472
473 static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager)
474 {
475         ++num_managers;
476         list_add_tail(&manager->list, &manager_list);
477 }
478
479 int dss_init_overlay_managers(struct platform_device *pdev)
480 {
481         int i, r;
482
483         INIT_LIST_HEAD(&manager_list);
484
485         num_managers = 0;
486
487         for (i = 0; i < 2; ++i) {
488                 struct omap_overlay_manager *mgr;
489                 mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
490
491                 BUG_ON(mgr == NULL);
492
493                 switch (i) {
494                 case 0:
495                         mgr->name = "lcd";
496                         mgr->id = OMAP_DSS_CHANNEL_LCD;
497                         mgr->supported_displays =
498                                 OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
499                                 OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI;
500                         break;
501                 case 1:
502                         mgr->name = "tv";
503                         mgr->id = OMAP_DSS_CHANNEL_DIGIT;
504                         mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC;
505                         break;
506                 }
507
508                 mgr->set_display = &omap_dss_set_display,
509                 mgr->unset_display = &omap_dss_unset_display,
510                 mgr->apply = &omap_dss_mgr_apply,
511                 mgr->set_default_color = &omap_dss_mgr_set_def_color,
512                 mgr->set_trans_key = &omap_dss_mgr_set_trans_key,
513                 mgr->enable_trans_key = &omap_dss_mgr_enable_trans_key,
514                 mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC,
515
516                 dss_overlay_setup_dispc_manager(mgr);
517
518                 omap_dss_add_overlay_manager(mgr);
519
520                 r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
521                                 &pdev->dev.kobj, "manager%d", i);
522
523                 if (r) {
524                         DSSERR("failed to create sysfs file\n");
525                         continue;
526                 }
527         }
528
529         return 0;
530 }
531
532 void dss_uninit_overlay_managers(struct platform_device *pdev)
533 {
534         struct omap_overlay_manager *mgr;
535
536         while (!list_empty(&manager_list)) {
537                 mgr = list_first_entry(&manager_list,
538                                 struct omap_overlay_manager, list);
539                 list_del(&mgr->list);
540                 kobject_del(&mgr->kobj);
541                 kobject_put(&mgr->kobj);
542                 kfree(mgr);
543         }
544
545         num_managers = 0;
546 }
547
548 int omap_dss_get_num_overlay_managers(void)
549 {
550         return num_managers;
551 }
552 EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
553
554 struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
555 {
556         int i = 0;
557         struct omap_overlay_manager *mgr;
558
559         list_for_each_entry(mgr, &manager_list, list) {
560                 if (i++ == num)
561                         return mgr;
562         }
563
564         return NULL;
565 }
566 EXPORT_SYMBOL(omap_dss_get_overlay_manager);
567
568 #ifdef L4_EXAMPLE
569 static int ovl_mgr_apply_l4(struct omap_overlay_manager *mgr)
570 {
571         DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name);
572
573         return 0;
574 }
575 #endif
576