Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[sfrench/cifs-2.6.git] / drivers / video / omap2 / dss / overlay.c
1 /*
2  * linux/drivers/video/omap2/dss/overlay.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 "OVERLAY"
24
25 #include <linux/kernel.h>
26 #include <linux/module.h>
27 #include <linux/err.h>
28 #include <linux/sysfs.h>
29 #include <linux/kobject.h>
30 #include <linux/platform_device.h>
31 #include <linux/delay.h>
32 #include <linux/slab.h>
33
34 #include <plat/display.h>
35 #include <plat/cpu.h>
36
37 #include "dss.h"
38
39 static int num_overlays;
40 static struct list_head overlay_list;
41
42 static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf)
43 {
44         return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name);
45 }
46
47 static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf)
48 {
49         return snprintf(buf, PAGE_SIZE, "%s\n",
50                         ovl->manager ? ovl->manager->name : "<none>");
51 }
52
53 static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
54                 size_t size)
55 {
56         int i, r;
57         struct omap_overlay_manager *mgr = NULL;
58         struct omap_overlay_manager *old_mgr;
59         int len = size;
60
61         if (buf[size-1] == '\n')
62                 --len;
63
64         if (len > 0) {
65                 for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
66                         mgr = omap_dss_get_overlay_manager(i);
67
68                         if (sysfs_streq(buf, mgr->name))
69                                 break;
70
71                         mgr = NULL;
72                 }
73         }
74
75         if (len > 0 && mgr == NULL)
76                 return -EINVAL;
77
78         if (mgr)
79                 DSSDBG("manager %s found\n", mgr->name);
80
81         if (mgr == ovl->manager)
82                 return size;
83
84         old_mgr = ovl->manager;
85
86         /* detach old manager */
87         if (old_mgr) {
88                 r = ovl->unset_manager(ovl);
89                 if (r) {
90                         DSSERR("detach failed\n");
91                         return r;
92                 }
93
94                 r = old_mgr->apply(old_mgr);
95                 if (r)
96                         return r;
97         }
98
99         if (mgr) {
100                 r = ovl->set_manager(ovl, mgr);
101                 if (r) {
102                         DSSERR("Failed to attach overlay\n");
103                         return r;
104                 }
105
106                 r = mgr->apply(mgr);
107                 if (r)
108                         return r;
109         }
110
111         return size;
112 }
113
114 static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf)
115 {
116         return snprintf(buf, PAGE_SIZE, "%d,%d\n",
117                         ovl->info.width, ovl->info.height);
118 }
119
120 static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf)
121 {
122         return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width);
123 }
124
125 static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf)
126 {
127         return snprintf(buf, PAGE_SIZE, "%d,%d\n",
128                         ovl->info.pos_x, ovl->info.pos_y);
129 }
130
131 static ssize_t overlay_position_store(struct omap_overlay *ovl,
132                 const char *buf, size_t size)
133 {
134         int r;
135         char *last;
136         struct omap_overlay_info info;
137
138         ovl->get_overlay_info(ovl, &info);
139
140         info.pos_x = simple_strtoul(buf, &last, 10);
141         ++last;
142         if (last - buf >= size)
143                 return -EINVAL;
144
145         info.pos_y = simple_strtoul(last, &last, 10);
146
147         r = ovl->set_overlay_info(ovl, &info);
148         if (r)
149                 return r;
150
151         if (ovl->manager) {
152                 r = ovl->manager->apply(ovl->manager);
153                 if (r)
154                         return r;
155         }
156
157         return size;
158 }
159
160 static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf)
161 {
162         return snprintf(buf, PAGE_SIZE, "%d,%d\n",
163                         ovl->info.out_width, ovl->info.out_height);
164 }
165
166 static ssize_t overlay_output_size_store(struct omap_overlay *ovl,
167                 const char *buf, size_t size)
168 {
169         int r;
170         char *last;
171         struct omap_overlay_info info;
172
173         ovl->get_overlay_info(ovl, &info);
174
175         info.out_width = simple_strtoul(buf, &last, 10);
176         ++last;
177         if (last - buf >= size)
178                 return -EINVAL;
179
180         info.out_height = simple_strtoul(last, &last, 10);
181
182         r = ovl->set_overlay_info(ovl, &info);
183         if (r)
184                 return r;
185
186         if (ovl->manager) {
187                 r = ovl->manager->apply(ovl->manager);
188                 if (r)
189                         return r;
190         }
191
192         return size;
193 }
194
195 static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf)
196 {
197         return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.enabled);
198 }
199
200 static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
201                 size_t size)
202 {
203         int r;
204         struct omap_overlay_info info;
205
206         ovl->get_overlay_info(ovl, &info);
207
208         info.enabled = simple_strtoul(buf, NULL, 10);
209
210         r = ovl->set_overlay_info(ovl, &info);
211         if (r)
212                 return r;
213
214         if (ovl->manager) {
215                 r = ovl->manager->apply(ovl->manager);
216                 if (r)
217                         return r;
218         }
219
220         return size;
221 }
222
223 static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf)
224 {
225         return snprintf(buf, PAGE_SIZE, "%d\n",
226                         ovl->info.global_alpha);
227 }
228
229 static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
230                 const char *buf, size_t size)
231 {
232         int r;
233         struct omap_overlay_info info;
234
235         ovl->get_overlay_info(ovl, &info);
236
237         /* Video1 plane does not support global alpha
238          * to always make it 255 completely opaque
239          */
240         if (ovl->id == OMAP_DSS_VIDEO1)
241                 info.global_alpha = 255;
242         else
243                 info.global_alpha = simple_strtoul(buf, NULL, 10);
244
245         r = ovl->set_overlay_info(ovl, &info);
246         if (r)
247                 return r;
248
249         if (ovl->manager) {
250                 r = ovl->manager->apply(ovl->manager);
251                 if (r)
252                         return r;
253         }
254
255         return size;
256 }
257
258 struct overlay_attribute {
259         struct attribute attr;
260         ssize_t (*show)(struct omap_overlay *, char *);
261         ssize_t (*store)(struct omap_overlay *, const char *, size_t);
262 };
263
264 #define OVERLAY_ATTR(_name, _mode, _show, _store) \
265         struct overlay_attribute overlay_attr_##_name = \
266         __ATTR(_name, _mode, _show, _store)
267
268 static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL);
269 static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR,
270                 overlay_manager_show, overlay_manager_store);
271 static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL);
272 static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL);
273 static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR,
274                 overlay_position_show, overlay_position_store);
275 static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR,
276                 overlay_output_size_show, overlay_output_size_store);
277 static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
278                 overlay_enabled_show, overlay_enabled_store);
279 static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR,
280                 overlay_global_alpha_show, overlay_global_alpha_store);
281
282 static struct attribute *overlay_sysfs_attrs[] = {
283         &overlay_attr_name.attr,
284         &overlay_attr_manager.attr,
285         &overlay_attr_input_size.attr,
286         &overlay_attr_screen_width.attr,
287         &overlay_attr_position.attr,
288         &overlay_attr_output_size.attr,
289         &overlay_attr_enabled.attr,
290         &overlay_attr_global_alpha.attr,
291         NULL
292 };
293
294 static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr,
295                 char *buf)
296 {
297         struct omap_overlay *overlay;
298         struct overlay_attribute *overlay_attr;
299
300         overlay = container_of(kobj, struct omap_overlay, kobj);
301         overlay_attr = container_of(attr, struct overlay_attribute, attr);
302
303         if (!overlay_attr->show)
304                 return -ENOENT;
305
306         return overlay_attr->show(overlay, buf);
307 }
308
309 static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr,
310                 const char *buf, size_t size)
311 {
312         struct omap_overlay *overlay;
313         struct overlay_attribute *overlay_attr;
314
315         overlay = container_of(kobj, struct omap_overlay, kobj);
316         overlay_attr = container_of(attr, struct overlay_attribute, attr);
317
318         if (!overlay_attr->store)
319                 return -ENOENT;
320
321         return overlay_attr->store(overlay, buf, size);
322 }
323
324 static const struct sysfs_ops overlay_sysfs_ops = {
325         .show = overlay_attr_show,
326         .store = overlay_attr_store,
327 };
328
329 static struct kobj_type overlay_ktype = {
330         .sysfs_ops = &overlay_sysfs_ops,
331         .default_attrs = overlay_sysfs_attrs,
332 };
333
334 /* Check if overlay parameters are compatible with display */
335 int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev)
336 {
337         struct omap_overlay_info *info;
338         u16 outw, outh;
339         u16 dw, dh;
340
341         if (!dssdev)
342                 return 0;
343
344         if (!ovl->info.enabled)
345                 return 0;
346
347         info = &ovl->info;
348
349         if (info->paddr == 0) {
350                 DSSDBG("check_overlay failed: paddr 0\n");
351                 return -EINVAL;
352         }
353
354         dssdev->driver->get_resolution(dssdev, &dw, &dh);
355
356         DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n",
357                         ovl->id,
358                         info->pos_x, info->pos_y,
359                         info->width, info->height,
360                         info->out_width, info->out_height,
361                         dw, dh);
362
363         if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
364                 outw = info->width;
365                 outh = info->height;
366         } else {
367                 if (info->out_width == 0)
368                         outw = info->width;
369                 else
370                         outw = info->out_width;
371
372                 if (info->out_height == 0)
373                         outh = info->height;
374                 else
375                         outh = info->out_height;
376         }
377
378         if (dw < info->pos_x + outw) {
379                 DSSDBG("check_overlay failed 1: %d < %d + %d\n",
380                                 dw, info->pos_x, outw);
381                 return -EINVAL;
382         }
383
384         if (dh < info->pos_y + outh) {
385                 DSSDBG("check_overlay failed 2: %d < %d + %d\n",
386                                 dh, info->pos_y, outh);
387                 return -EINVAL;
388         }
389
390         if ((ovl->supported_modes & info->color_mode) == 0) {
391                 DSSERR("overlay doesn't support mode %d\n", info->color_mode);
392                 return -EINVAL;
393         }
394
395         return 0;
396 }
397
398 static int dss_ovl_set_overlay_info(struct omap_overlay *ovl,
399                 struct omap_overlay_info *info)
400 {
401         int r;
402         struct omap_overlay_info old_info;
403
404         old_info = ovl->info;
405         ovl->info = *info;
406
407         if (ovl->manager) {
408                 r = dss_check_overlay(ovl, ovl->manager->device);
409                 if (r) {
410                         ovl->info = old_info;
411                         return r;
412                 }
413         }
414
415         ovl->info_dirty = true;
416
417         return 0;
418 }
419
420 static void dss_ovl_get_overlay_info(struct omap_overlay *ovl,
421                 struct omap_overlay_info *info)
422 {
423         *info = ovl->info;
424 }
425
426 static int dss_ovl_wait_for_go(struct omap_overlay *ovl)
427 {
428         return dss_mgr_wait_for_go_ovl(ovl);
429 }
430
431 static int omap_dss_set_manager(struct omap_overlay *ovl,
432                 struct omap_overlay_manager *mgr)
433 {
434         if (!mgr)
435                 return -EINVAL;
436
437         if (ovl->manager) {
438                 DSSERR("overlay '%s' already has a manager '%s'\n",
439                                 ovl->name, ovl->manager->name);
440                 return -EINVAL;
441         }
442
443         if (ovl->info.enabled) {
444                 DSSERR("overlay has to be disabled to change the manager\n");
445                 return -EINVAL;
446         }
447
448         ovl->manager = mgr;
449
450         dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
451         /* XXX: on manual update display, in auto update mode, a bug happens
452          * here. When an overlay is first enabled on LCD, then it's disabled,
453          * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT
454          * errors. Waiting before changing the channel_out fixes it. I'm
455          * guessing that the overlay is still somehow being used for the LCD,
456          * but I don't understand how or why. */
457         msleep(40);
458         dispc_set_channel_out(ovl->id, mgr->id);
459         dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
460
461         return 0;
462 }
463
464 static int omap_dss_unset_manager(struct omap_overlay *ovl)
465 {
466         int r;
467
468         if (!ovl->manager) {
469                 DSSERR("failed to detach overlay: manager not set\n");
470                 return -EINVAL;
471         }
472
473         if (ovl->info.enabled) {
474                 DSSERR("overlay has to be disabled to unset the manager\n");
475                 return -EINVAL;
476         }
477
478         r = ovl->wait_for_go(ovl);
479         if (r)
480                 return r;
481
482         ovl->manager = NULL;
483
484         return 0;
485 }
486
487 int omap_dss_get_num_overlays(void)
488 {
489         return num_overlays;
490 }
491 EXPORT_SYMBOL(omap_dss_get_num_overlays);
492
493 struct omap_overlay *omap_dss_get_overlay(int num)
494 {
495         int i = 0;
496         struct omap_overlay *ovl;
497
498         list_for_each_entry(ovl, &overlay_list, list) {
499                 if (i++ == num)
500                         return ovl;
501         }
502
503         return NULL;
504 }
505 EXPORT_SYMBOL(omap_dss_get_overlay);
506
507 static void omap_dss_add_overlay(struct omap_overlay *overlay)
508 {
509         ++num_overlays;
510         list_add_tail(&overlay->list, &overlay_list);
511 }
512
513 static struct omap_overlay *dispc_overlays[3];
514
515 void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr)
516 {
517         mgr->num_overlays = 3;
518         mgr->overlays = dispc_overlays;
519 }
520
521 #ifdef L4_EXAMPLE
522 static struct omap_overlay *l4_overlays[1];
523 void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr)
524 {
525         mgr->num_overlays = 1;
526         mgr->overlays = l4_overlays;
527 }
528 #endif
529
530 void dss_init_overlays(struct platform_device *pdev)
531 {
532         int i, r;
533
534         INIT_LIST_HEAD(&overlay_list);
535
536         num_overlays = 0;
537
538         for (i = 0; i < 3; ++i) {
539                 struct omap_overlay *ovl;
540                 ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
541
542                 BUG_ON(ovl == NULL);
543
544                 switch (i) {
545                 case 0:
546                         ovl->name = "gfx";
547                         ovl->id = OMAP_DSS_GFX;
548                         ovl->supported_modes = cpu_is_omap34xx() ?
549                                 OMAP_DSS_COLOR_GFX_OMAP3 :
550                                 OMAP_DSS_COLOR_GFX_OMAP2;
551                         ovl->caps = OMAP_DSS_OVL_CAP_DISPC;
552                         ovl->info.global_alpha = 255;
553                         break;
554                 case 1:
555                         ovl->name = "vid1";
556                         ovl->id = OMAP_DSS_VIDEO1;
557                         ovl->supported_modes = cpu_is_omap34xx() ?
558                                 OMAP_DSS_COLOR_VID1_OMAP3 :
559                                 OMAP_DSS_COLOR_VID_OMAP2;
560                         ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
561                                 OMAP_DSS_OVL_CAP_DISPC;
562                         ovl->info.global_alpha = 255;
563                         break;
564                 case 2:
565                         ovl->name = "vid2";
566                         ovl->id = OMAP_DSS_VIDEO2;
567                         ovl->supported_modes = cpu_is_omap34xx() ?
568                                 OMAP_DSS_COLOR_VID2_OMAP3 :
569                                 OMAP_DSS_COLOR_VID_OMAP2;
570                         ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
571                                 OMAP_DSS_OVL_CAP_DISPC;
572                         ovl->info.global_alpha = 255;
573                         break;
574                 }
575
576                 ovl->set_manager = &omap_dss_set_manager;
577                 ovl->unset_manager = &omap_dss_unset_manager;
578                 ovl->set_overlay_info = &dss_ovl_set_overlay_info;
579                 ovl->get_overlay_info = &dss_ovl_get_overlay_info;
580                 ovl->wait_for_go = &dss_ovl_wait_for_go;
581
582                 omap_dss_add_overlay(ovl);
583
584                 r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
585                                 &pdev->dev.kobj, "overlay%d", i);
586
587                 if (r) {
588                         DSSERR("failed to create sysfs file\n");
589                         continue;
590                 }
591
592                 dispc_overlays[i] = ovl;
593         }
594
595 #ifdef L4_EXAMPLE
596         {
597                 struct omap_overlay *ovl;
598                 ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
599
600                 BUG_ON(ovl == NULL);
601
602                 ovl->name = "l4";
603                 ovl->supported_modes = OMAP_DSS_COLOR_RGB24U;
604
605                 ovl->set_manager = &omap_dss_set_manager;
606                 ovl->unset_manager = &omap_dss_unset_manager;
607                 ovl->set_overlay_info = &dss_ovl_set_overlay_info;
608                 ovl->get_overlay_info = &dss_ovl_get_overlay_info;
609
610                 omap_dss_add_overlay(ovl);
611
612                 r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
613                                 &pdev->dev.kobj, "overlayl4");
614
615                 if (r)
616                         DSSERR("failed to create sysfs file\n");
617
618                 l4_overlays[0] = ovl;
619         }
620 #endif
621 }
622
623 /* connect overlays to the new device, if not already connected. if force
624  * selected, connect always. */
625 void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
626 {
627         int i;
628         struct omap_overlay_manager *lcd_mgr;
629         struct omap_overlay_manager *tv_mgr;
630         struct omap_overlay_manager *mgr = NULL;
631
632         lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD);
633         tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV);
634
635         if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) {
636                 if (!lcd_mgr->device || force) {
637                         if (lcd_mgr->device)
638                                 lcd_mgr->unset_device(lcd_mgr);
639                         lcd_mgr->set_device(lcd_mgr, dssdev);
640                         mgr = lcd_mgr;
641                 }
642         }
643
644         if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
645                 if (!tv_mgr->device || force) {
646                         if (tv_mgr->device)
647                                 tv_mgr->unset_device(tv_mgr);
648                         tv_mgr->set_device(tv_mgr, dssdev);
649                         mgr = tv_mgr;
650                 }
651         }
652
653         if (mgr) {
654                 for (i = 0; i < 3; i++) {
655                         struct omap_overlay *ovl;
656                         ovl = omap_dss_get_overlay(i);
657                         if (!ovl->manager || force) {
658                                 if (ovl->manager)
659                                         omap_dss_unset_manager(ovl);
660                                 omap_dss_set_manager(ovl, mgr);
661                         }
662                 }
663         }
664 }
665
666 void dss_uninit_overlays(struct platform_device *pdev)
667 {
668         struct omap_overlay *ovl;
669
670         while (!list_empty(&overlay_list)) {
671                 ovl = list_first_entry(&overlay_list,
672                                 struct omap_overlay, list);
673                 list_del(&ovl->list);
674                 kobject_del(&ovl->kobj);
675                 kobject_put(&ovl->kobj);
676                 kfree(ovl);
677         }
678
679         num_overlays = 0;
680 }
681