blob: f1c334c275e2927baf54847cbd53cef22e7bda6f [file] [log] [blame]
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001/*
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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090026#include <linux/slab.h>
Tomi Valkeineneed07e02009-08-07 13:43:20 +030027#include <linux/module.h>
28#include <linux/platform_device.h>
29#include <linux/spinlock.h>
30#include <linux/jiffies.h>
31
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030032#include <video/omapdss.h>
Tomi Valkeineneed07e02009-08-07 13:43:20 +030033#include <plat/cpu.h>
34
35#include "dss.h"
Archit Tanejaa0acb552010-09-15 19:20:00 +053036#include "dss_features.h"
Tomi Valkeineneed07e02009-08-07 13:43:20 +030037
38static int num_managers;
39static struct list_head manager_list;
40
41static 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
46static 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
52static 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
99put_device:
100 if (dssdev)
101 omap_dss_put_device(dssdev);
102
103 return r ? r : size;
104}
105
106static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr,
107 char *buf)
108{
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300109 return snprintf(buf, PAGE_SIZE, "%#x\n", mgr->info.default_color);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300110}
111
112static 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
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300119 r = kstrtouint(buf, 0, &color);
120 if (r)
121 return r;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300122
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
138static const char *trans_key_type_str[] = {
139 "gfx-destination",
140 "video-source",
141};
142
143static 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
154static 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
185static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr,
186 char *buf)
187{
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300188 return snprintf(buf, PAGE_SIZE, "%#x\n", mgr->info.trans_key);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300189}
190
191static 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
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300198 r = kstrtouint(buf, 0, &key_value);
199 if (r)
200 return r;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300201
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
217static 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
223static 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;
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300227 bool enable;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300228 int r;
229
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300230 r = strtobool(buf, &enable);
231 if (r)
232 return r;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300233
234 mgr->get_manager_info(mgr, &info);
235
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300236 info.trans_enabled = enable;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300237
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
249static ssize_t manager_alpha_blending_enabled_show(
250 struct omap_overlay_manager *mgr, char *buf)
251{
252 return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled);
253}
254
255static ssize_t manager_alpha_blending_enabled_store(
256 struct omap_overlay_manager *mgr,
257 const char *buf, size_t size)
258{
259 struct omap_overlay_manager_info info;
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300260 bool enable;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300261 int r;
262
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300263 r = strtobool(buf, &enable);
264 if (r)
265 return r;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300266
267 mgr->get_manager_info(mgr, &info);
268
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300269 info.alpha_enabled = enable;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300270
271 r = mgr->set_manager_info(mgr, &info);
272 if (r)
273 return r;
274
275 r = mgr->apply(mgr);
276 if (r)
277 return r;
278
279 return size;
280}
281
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300282static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr,
283 char *buf)
284{
285 return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.cpr_enable);
286}
287
288static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
289 const char *buf, size_t size)
290{
291 struct omap_overlay_manager_info info;
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300292 int r;
293 bool enable;
294
295 if (!dss_has_feature(FEAT_CPR))
296 return -ENODEV;
297
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300298 r = strtobool(buf, &enable);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300299 if (r)
300 return r;
301
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300302 mgr->get_manager_info(mgr, &info);
303
304 if (info.cpr_enable == enable)
305 return size;
306
307 info.cpr_enable = enable;
308
309 r = mgr->set_manager_info(mgr, &info);
310 if (r)
311 return r;
312
313 r = mgr->apply(mgr);
314 if (r)
315 return r;
316
317 return size;
318}
319
320static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr,
321 char *buf)
322{
323 struct omap_overlay_manager_info info;
324
325 mgr->get_manager_info(mgr, &info);
326
327 return snprintf(buf, PAGE_SIZE,
328 "%d %d %d %d %d %d %d %d %d\n",
329 info.cpr_coefs.rr,
330 info.cpr_coefs.rg,
331 info.cpr_coefs.rb,
332 info.cpr_coefs.gr,
333 info.cpr_coefs.gg,
334 info.cpr_coefs.gb,
335 info.cpr_coefs.br,
336 info.cpr_coefs.bg,
337 info.cpr_coefs.bb);
338}
339
340static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr,
341 const char *buf, size_t size)
342{
343 struct omap_overlay_manager_info info;
344 struct omap_dss_cpr_coefs coefs;
345 int r, i;
346 s16 *arr;
347
348 if (!dss_has_feature(FEAT_CPR))
349 return -ENODEV;
350
351 if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd",
352 &coefs.rr, &coefs.rg, &coefs.rb,
353 &coefs.gr, &coefs.gg, &coefs.gb,
354 &coefs.br, &coefs.bg, &coefs.bb) != 9)
355 return -EINVAL;
356
357 arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb,
358 coefs.gr, coefs.gg, coefs.gb,
359 coefs.br, coefs.bg, coefs.bb };
360
361 for (i = 0; i < 9; ++i) {
362 if (arr[i] < -512 || arr[i] > 511)
363 return -EINVAL;
364 }
365
366 mgr->get_manager_info(mgr, &info);
367
368 info.cpr_coefs = coefs;
369
370 r = mgr->set_manager_info(mgr, &info);
371 if (r)
372 return r;
373
374 r = mgr->apply(mgr);
375 if (r)
376 return r;
377
378 return size;
379}
380
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300381struct manager_attribute {
382 struct attribute attr;
383 ssize_t (*show)(struct omap_overlay_manager *, char *);
384 ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t);
385};
386
387#define MANAGER_ATTR(_name, _mode, _show, _store) \
388 struct manager_attribute manager_attr_##_name = \
389 __ATTR(_name, _mode, _show, _store)
390
391static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL);
392static MANAGER_ATTR(display, S_IRUGO|S_IWUSR,
393 manager_display_show, manager_display_store);
394static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR,
395 manager_default_color_show, manager_default_color_store);
396static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR,
397 manager_trans_key_type_show, manager_trans_key_type_store);
398static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR,
399 manager_trans_key_value_show, manager_trans_key_value_store);
400static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,
401 manager_trans_key_enabled_show,
402 manager_trans_key_enabled_store);
403static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
404 manager_alpha_blending_enabled_show,
405 manager_alpha_blending_enabled_store);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300406static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR,
407 manager_cpr_enable_show,
408 manager_cpr_enable_store);
409static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR,
410 manager_cpr_coef_show,
411 manager_cpr_coef_store);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300412
413
414static struct attribute *manager_sysfs_attrs[] = {
415 &manager_attr_name.attr,
416 &manager_attr_display.attr,
417 &manager_attr_default_color.attr,
418 &manager_attr_trans_key_type.attr,
419 &manager_attr_trans_key_value.attr,
420 &manager_attr_trans_key_enabled.attr,
421 &manager_attr_alpha_blending_enabled.attr,
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300422 &manager_attr_cpr_enable.attr,
423 &manager_attr_cpr_coef.attr,
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300424 NULL
425};
426
427static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr,
428 char *buf)
429{
430 struct omap_overlay_manager *manager;
431 struct manager_attribute *manager_attr;
432
433 manager = container_of(kobj, struct omap_overlay_manager, kobj);
434 manager_attr = container_of(attr, struct manager_attribute, attr);
435
436 if (!manager_attr->show)
437 return -ENOENT;
438
439 return manager_attr->show(manager, buf);
440}
441
442static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr,
443 const char *buf, size_t size)
444{
445 struct omap_overlay_manager *manager;
446 struct manager_attribute *manager_attr;
447
448 manager = container_of(kobj, struct omap_overlay_manager, kobj);
449 manager_attr = container_of(attr, struct manager_attribute, attr);
450
451 if (!manager_attr->store)
452 return -ENOENT;
453
454 return manager_attr->store(manager, buf, size);
455}
456
Emese Revfy52cf25d2010-01-19 02:58:23 +0100457static const struct sysfs_ops manager_sysfs_ops = {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300458 .show = manager_attr_show,
459 .store = manager_attr_store,
460};
461
462static struct kobj_type manager_ktype = {
463 .sysfs_ops = &manager_sysfs_ops,
464 .default_attrs = manager_sysfs_attrs,
465};
466
467/*
468 * We have 4 levels of cache for the dispc settings. First two are in SW and
469 * the latter two in HW.
470 *
471 * +--------------------+
472 * |overlay/manager_info|
473 * +--------------------+
474 * v
475 * apply()
476 * v
477 * +--------------------+
478 * | dss_cache |
479 * +--------------------+
480 * v
481 * configure()
482 * v
483 * +--------------------+
484 * | shadow registers |
485 * +--------------------+
486 * v
487 * VFP or lcd/digit_enable
488 * v
489 * +--------------------+
490 * | registers |
491 * +--------------------+
492 */
493
494struct overlay_cache_data {
495 /* If true, cache changed, but not written to shadow registers. Set
496 * in apply(), cleared when registers written. */
497 bool dirty;
498 /* If true, shadow registers contain changed values not yet in real
499 * registers. Set when writing to shadow registers, cleared at
500 * VSYNC/EVSYNC */
501 bool shadow_dirty;
502
503 bool enabled;
504
Nishant Kamat4df9d102011-06-20 20:39:28 +0530505 struct omap_overlay_info info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300506
507 enum omap_channel channel;
508 bool replication;
509 bool ilace;
510
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300511 u32 fifo_low;
512 u32 fifo_high;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300513};
514
515struct manager_cache_data {
516 /* If true, cache changed, but not written to shadow registers. Set
517 * in apply(), cleared when registers written. */
518 bool dirty;
519 /* If true, shadow registers contain changed values not yet in real
520 * registers. Set when writing to shadow registers, cleared at
521 * VSYNC/EVSYNC */
522 bool shadow_dirty;
523
Nishant Kamat4df9d102011-06-20 20:39:28 +0530524 struct omap_overlay_manager_info info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300525
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300526 bool manual_update;
527 bool do_manual_update;
528
529 /* manual update region */
530 u16 x, y, w, h;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300531
532 /* enlarge the update area if the update area contains scaled
533 * overlays */
534 bool enlarge_update_area;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300535};
536
537static struct {
538 spinlock_t lock;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530539 struct overlay_cache_data overlay_cache[MAX_DSS_OVERLAYS];
540 struct manager_cache_data manager_cache[MAX_DSS_MANAGERS];
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300541
542 bool irq_enabled;
543} dss_cache;
544
545
546
547static int omap_dss_set_device(struct omap_overlay_manager *mgr,
548 struct omap_dss_device *dssdev)
549{
550 int i;
551 int r;
552
553 if (dssdev->manager) {
554 DSSERR("display '%s' already has a manager '%s'\n",
555 dssdev->name, dssdev->manager->name);
556 return -EINVAL;
557 }
558
559 if ((mgr->supported_displays & dssdev->type) == 0) {
560 DSSERR("display '%s' does not support manager '%s'\n",
561 dssdev->name, mgr->name);
562 return -EINVAL;
563 }
564
565 for (i = 0; i < mgr->num_overlays; i++) {
566 struct omap_overlay *ovl = mgr->overlays[i];
567
568 if (ovl->manager != mgr || !ovl->info.enabled)
569 continue;
570
571 r = dss_check_overlay(ovl, dssdev);
572 if (r)
573 return r;
574 }
575
576 dssdev->manager = mgr;
577 mgr->device = dssdev;
578 mgr->device_changed = true;
579
580 return 0;
581}
582
583static int omap_dss_unset_device(struct omap_overlay_manager *mgr)
584{
585 if (!mgr->device) {
586 DSSERR("failed to unset display, display not set.\n");
587 return -EINVAL;
588 }
589
Daniel Morsing0f770b42011-08-03 22:10:51 +0200590 /*
591 * Don't allow currently enabled displays to have the overlay manager
592 * pulled out from underneath them
593 */
594 if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED)
595 return -EINVAL;
596
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300597 mgr->device->manager = NULL;
598 mgr->device = NULL;
599 mgr->device_changed = true;
600
601 return 0;
602}
603
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +0200604static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
605{
606 unsigned long timeout = msecs_to_jiffies(500);
607 u32 irq;
608
Sumit Semwal18faa1b2010-12-02 11:27:14 +0000609 if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +0200610 irq = DISPC_IRQ_EVSYNC_ODD;
Mythri P Kb1196012011-03-08 17:15:54 +0530611 } else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) {
612 irq = DISPC_IRQ_EVSYNC_EVEN;
Sumit Semwal18faa1b2010-12-02 11:27:14 +0000613 } else {
614 if (mgr->id == OMAP_DSS_CHANNEL_LCD)
615 irq = DISPC_IRQ_VSYNC;
616 else
617 irq = DISPC_IRQ_VSYNC2;
618 }
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +0200619 return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
620}
621
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300622static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
623{
624 unsigned long timeout = msecs_to_jiffies(500);
625 struct manager_cache_data *mc;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300626 u32 irq;
627 int r;
628 int i;
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +0200629 struct omap_dss_device *dssdev = mgr->device;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300630
Ville Syrjäläa74b2602010-03-04 16:03:56 +0200631 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300632 return 0;
633
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300634 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
635 return 0;
636
Mythri P Kb1196012011-03-08 17:15:54 +0530637 if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
638 || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300639 irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300640 } else {
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300641 irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
642 DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300643 }
644
645 mc = &dss_cache.manager_cache[mgr->id];
646 i = 0;
647 while (1) {
648 unsigned long flags;
649 bool shadow_dirty, dirty;
650
651 spin_lock_irqsave(&dss_cache.lock, flags);
652 dirty = mc->dirty;
653 shadow_dirty = mc->shadow_dirty;
654 spin_unlock_irqrestore(&dss_cache.lock, flags);
655
656 if (!dirty && !shadow_dirty) {
657 r = 0;
658 break;
659 }
660
661 /* 4 iterations is the worst case:
662 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
663 * 2 - first VSYNC, dirty = true
664 * 3 - dirty = false, shadow_dirty = true
665 * 4 - shadow_dirty = false */
666 if (i++ == 3) {
667 DSSERR("mgr(%d)->wait_for_go() not finishing\n",
668 mgr->id);
669 r = 0;
670 break;
671 }
672
673 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
674 if (r == -ERESTARTSYS)
675 break;
676
677 if (r) {
678 DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
679 break;
680 }
681 }
682
683 return r;
684}
685
686int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
687{
688 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300689 struct overlay_cache_data *oc;
690 struct omap_dss_device *dssdev;
691 u32 irq;
692 int r;
693 int i;
694
Ville Syrjäläa74b2602010-03-04 16:03:56 +0200695 if (!ovl->manager)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300696 return 0;
697
698 dssdev = ovl->manager->device;
699
Ville Syrjäläa74b2602010-03-04 16:03:56 +0200700 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
701 return 0;
702
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300703 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
704 return 0;
705
Mythri P Kb1196012011-03-08 17:15:54 +0530706 if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
707 || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300708 irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300709 } else {
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300710 irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
711 DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300712 }
713
714 oc = &dss_cache.overlay_cache[ovl->id];
715 i = 0;
716 while (1) {
717 unsigned long flags;
718 bool shadow_dirty, dirty;
719
720 spin_lock_irqsave(&dss_cache.lock, flags);
721 dirty = oc->dirty;
722 shadow_dirty = oc->shadow_dirty;
723 spin_unlock_irqrestore(&dss_cache.lock, flags);
724
725 if (!dirty && !shadow_dirty) {
726 r = 0;
727 break;
728 }
729
730 /* 4 iterations is the worst case:
731 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
732 * 2 - first VSYNC, dirty = true
733 * 3 - dirty = false, shadow_dirty = true
734 * 4 - shadow_dirty = false */
735 if (i++ == 3) {
736 DSSERR("ovl(%d)->wait_for_go() not finishing\n",
737 ovl->id);
738 r = 0;
739 break;
740 }
741
742 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
743 if (r == -ERESTARTSYS)
744 break;
745
746 if (r) {
747 DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
748 break;
749 }
750 }
751
752 return r;
753}
754
755static int overlay_enabled(struct omap_overlay *ovl)
756{
757 return ovl->info.enabled && ovl->manager && ovl->manager->device;
758}
759
760/* Is rect1 a subset of rect2? */
761static bool rectangle_subset(int x1, int y1, int w1, int h1,
762 int x2, int y2, int w2, int h2)
763{
764 if (x1 < x2 || y1 < y2)
765 return false;
766
767 if (x1 + w1 > x2 + w2)
768 return false;
769
770 if (y1 + h1 > y2 + h2)
771 return false;
772
773 return true;
774}
775
776/* Do rect1 and rect2 overlap? */
777static bool rectangle_intersects(int x1, int y1, int w1, int h1,
778 int x2, int y2, int w2, int h2)
779{
780 if (x1 >= x2 + w2)
781 return false;
782
783 if (x2 >= x1 + w1)
784 return false;
785
786 if (y1 >= y2 + h2)
787 return false;
788
789 if (y2 >= y1 + h1)
790 return false;
791
792 return true;
793}
794
795static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc)
796{
Nishant Kamat4df9d102011-06-20 20:39:28 +0530797 struct omap_overlay_info *oi = &oc->info;
798
799 if (oi->out_width != 0 && oi->width != oi->out_width)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300800 return true;
801
Nishant Kamat4df9d102011-06-20 20:39:28 +0530802 if (oi->out_height != 0 && oi->height != oi->out_height)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300803 return true;
804
805 return false;
806}
807
808static int configure_overlay(enum omap_plane plane)
809{
810 struct overlay_cache_data *c;
811 struct manager_cache_data *mc;
Archit Tanejaa4273b72011-09-14 11:10:10 +0530812 struct omap_overlay_info *oi, new_oi;
Nishant Kamat4df9d102011-06-20 20:39:28 +0530813 struct omap_overlay_manager_info *mi;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300814 u16 outw, outh;
815 u16 x, y, w, h;
816 u32 paddr;
817 int r;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300818 u16 orig_w, orig_h, orig_outw, orig_outh;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300819
820 DSSDBGF("%d", plane);
821
822 c = &dss_cache.overlay_cache[plane];
Nishant Kamat4df9d102011-06-20 20:39:28 +0530823 oi = &c->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300824
825 if (!c->enabled) {
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300826 dispc_ovl_enable(plane, 0);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300827 return 0;
828 }
829
830 mc = &dss_cache.manager_cache[c->channel];
Nishant Kamat4df9d102011-06-20 20:39:28 +0530831 mi = &mc->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300832
Nishant Kamat4df9d102011-06-20 20:39:28 +0530833 x = oi->pos_x;
834 y = oi->pos_y;
835 w = oi->width;
836 h = oi->height;
837 outw = oi->out_width == 0 ? oi->width : oi->out_width;
838 outh = oi->out_height == 0 ? oi->height : oi->out_height;
839 paddr = oi->paddr;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300840
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300841 orig_w = w;
842 orig_h = h;
843 orig_outw = outw;
844 orig_outh = outh;
845
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300846 if (mc->manual_update && mc->do_manual_update) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300847 unsigned bpp;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300848 unsigned scale_x_m = w, scale_x_d = outw;
849 unsigned scale_y_m = h, scale_y_d = outh;
850
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300851 /* If the overlay is outside the update region, disable it */
852 if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h,
853 x, y, outw, outh)) {
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300854 dispc_ovl_enable(plane, 0);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300855 return 0;
856 }
857
Nishant Kamat4df9d102011-06-20 20:39:28 +0530858 switch (oi->color_mode) {
Amber Jainf20e4222011-05-19 19:47:50 +0530859 case OMAP_DSS_COLOR_NV12:
860 bpp = 8;
861 break;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300862 case OMAP_DSS_COLOR_RGB16:
863 case OMAP_DSS_COLOR_ARGB16:
864 case OMAP_DSS_COLOR_YUV2:
865 case OMAP_DSS_COLOR_UYVY:
Amber Jainf20e4222011-05-19 19:47:50 +0530866 case OMAP_DSS_COLOR_RGBA16:
867 case OMAP_DSS_COLOR_RGBX16:
868 case OMAP_DSS_COLOR_ARGB16_1555:
869 case OMAP_DSS_COLOR_XRGB16_1555:
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300870 bpp = 16;
871 break;
872
873 case OMAP_DSS_COLOR_RGB24P:
874 bpp = 24;
875 break;
876
877 case OMAP_DSS_COLOR_RGB24U:
878 case OMAP_DSS_COLOR_ARGB32:
879 case OMAP_DSS_COLOR_RGBA32:
880 case OMAP_DSS_COLOR_RGBX32:
881 bpp = 32;
882 break;
883
884 default:
885 BUG();
886 }
887
Nishant Kamat4df9d102011-06-20 20:39:28 +0530888 if (mc->x > oi->pos_x) {
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300889 x = 0;
Nishant Kamat4df9d102011-06-20 20:39:28 +0530890 outw -= (mc->x - oi->pos_x);
891 paddr += (mc->x - oi->pos_x) *
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300892 scale_x_m / scale_x_d * bpp / 8;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300893 } else {
Nishant Kamat4df9d102011-06-20 20:39:28 +0530894 x = oi->pos_x - mc->x;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300895 }
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300896
Nishant Kamat4df9d102011-06-20 20:39:28 +0530897 if (mc->y > oi->pos_y) {
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300898 y = 0;
Nishant Kamat4df9d102011-06-20 20:39:28 +0530899 outh -= (mc->y - oi->pos_y);
900 paddr += (mc->y - oi->pos_y) *
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300901 scale_y_m / scale_y_d *
Nishant Kamat4df9d102011-06-20 20:39:28 +0530902 oi->screen_width * bpp / 8;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300903 } else {
Nishant Kamat4df9d102011-06-20 20:39:28 +0530904 y = oi->pos_y - mc->y;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300905 }
906
907 if (mc->w < (x + outw))
908 outw -= (x + outw) - (mc->w);
909
910 if (mc->h < (y + outh))
911 outh -= (y + outh) - (mc->h);
912
913 w = w * outw / orig_outw;
914 h = h * outh / orig_outh;
Tomi Valkeinenf55fdcf2010-06-03 16:27:46 +0300915
916 /* YUV mode overlay's input width has to be even and the
917 * algorithm above may adjust the width to be odd.
918 *
919 * Here we adjust the width if needed, preferring to increase
920 * the width if the original width was bigger.
921 */
922 if ((w & 1) &&
Nishant Kamat4df9d102011-06-20 20:39:28 +0530923 (oi->color_mode == OMAP_DSS_COLOR_YUV2 ||
924 oi->color_mode == OMAP_DSS_COLOR_UYVY)) {
Tomi Valkeinenf55fdcf2010-06-03 16:27:46 +0300925 if (orig_w > w)
926 w += 1;
927 else
928 w -= 1;
929 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300930 }
931
Archit Tanejaa4273b72011-09-14 11:10:10 +0530932 new_oi = *oi;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300933
Archit Tanejaa4273b72011-09-14 11:10:10 +0530934 /* update new_oi members which could have been possibly updated */
935 new_oi.pos_x = x;
936 new_oi.pos_y = y;
937 new_oi.width = w;
938 new_oi.height = h;
939 new_oi.out_width = outw;
940 new_oi.out_height = outh;
941 new_oi.paddr = paddr;
942
943 r = dispc_ovl_setup(plane, &new_oi, c->ilace, c->channel);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300944 if (r) {
945 /* this shouldn't happen */
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300946 DSSERR("dispc_ovl_setup failed for ovl %d\n", plane);
947 dispc_ovl_enable(plane, 0);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300948 return r;
949 }
950
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300951 dispc_ovl_enable_replication(plane, c->replication);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300952
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300953 dispc_ovl_set_fifo_threshold(plane, c->fifo_low, c->fifo_high);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300954
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300955 dispc_ovl_enable(plane, 1);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300956
957 return 0;
958}
959
960static void configure_manager(enum omap_channel channel)
961{
Nishant Kamat4df9d102011-06-20 20:39:28 +0530962 struct omap_overlay_manager_info *mi;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300963
964 DSSDBGF("%d", channel);
965
Nishant Kamat4df9d102011-06-20 20:39:28 +0530966 /* picking info from the cache */
967 mi = &dss_cache.manager_cache[channel].info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300968
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +0300969 dispc_mgr_set_default_color(channel, mi->default_color);
970 dispc_mgr_set_trans_key(channel, mi->trans_key_type, mi->trans_key);
971 dispc_mgr_enable_trans_key(channel, mi->trans_enabled);
972 dispc_mgr_enable_alpha_blending(channel, mi->alpha_enabled);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300973 if (dss_has_feature(FEAT_CPR)) {
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +0300974 dispc_mgr_enable_cpr(channel, mi->cpr_enable);
975 dispc_mgr_set_cpr_coef(channel, &mi->cpr_coefs);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300976 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300977}
978
979/* configure_dispc() tries to write values from cache to shadow registers.
980 * It writes only to those managers/overlays that are not busy.
981 * returns 0 if everything could be written to shadow registers.
982 * returns 1 if not everything could be written to shadow registers. */
983static int configure_dispc(void)
984{
985 struct overlay_cache_data *oc;
986 struct manager_cache_data *mc;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530987 const int num_ovls = dss_feat_get_num_ovls();
988 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300989 int i;
990 int r;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530991 bool mgr_busy[MAX_DSS_MANAGERS];
992 bool mgr_go[MAX_DSS_MANAGERS];
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300993 bool busy;
994
995 r = 0;
996 busy = false;
997
Sumit Semwal18faa1b2010-12-02 11:27:14 +0000998 for (i = 0; i < num_mgrs; i++) {
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +0300999 mgr_busy[i] = dispc_mgr_go_busy(i);
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001000 mgr_go[i] = false;
1001 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001002
1003 /* Commit overlay settings */
1004 for (i = 0; i < num_ovls; ++i) {
1005 oc = &dss_cache.overlay_cache[i];
1006 mc = &dss_cache.manager_cache[oc->channel];
1007
1008 if (!oc->dirty)
1009 continue;
1010
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001011 if (mc->manual_update && !mc->do_manual_update)
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001012 continue;
1013
1014 if (mgr_busy[oc->channel]) {
1015 busy = true;
1016 continue;
1017 }
1018
1019 r = configure_overlay(i);
1020 if (r)
1021 DSSERR("configure_overlay %d failed\n", i);
1022
1023 oc->dirty = false;
1024 oc->shadow_dirty = true;
1025 mgr_go[oc->channel] = true;
1026 }
1027
1028 /* Commit manager settings */
1029 for (i = 0; i < num_mgrs; ++i) {
1030 mc = &dss_cache.manager_cache[i];
1031
1032 if (!mc->dirty)
1033 continue;
1034
1035 if (mc->manual_update && !mc->do_manual_update)
1036 continue;
1037
1038 if (mgr_busy[i]) {
1039 busy = true;
1040 continue;
1041 }
1042
1043 configure_manager(i);
1044 mc->dirty = false;
1045 mc->shadow_dirty = true;
1046 mgr_go[i] = true;
1047 }
1048
1049 /* set GO */
1050 for (i = 0; i < num_mgrs; ++i) {
1051 mc = &dss_cache.manager_cache[i];
1052
1053 if (!mgr_go[i])
1054 continue;
1055
1056 /* We don't need GO with manual update display. LCD iface will
1057 * always be turned off after frame, and new settings will be
1058 * taken in to use at next update */
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001059 if (!mc->manual_update)
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001060 dispc_mgr_go(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001061 }
1062
1063 if (busy)
1064 r = 1;
1065 else
1066 r = 0;
1067
1068 return r;
1069}
1070
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001071/* Make the coordinates even. There are some strange problems with OMAP and
1072 * partial DSI update when the update widths are odd. */
1073static void make_even(u16 *x, u16 *w)
1074{
1075 u16 x1, x2;
1076
1077 x1 = *x;
1078 x2 = *x + *w;
1079
1080 x1 &= ~1;
1081 x2 = ALIGN(x2, 2);
1082
1083 *x = x1;
1084 *w = x2 - x1;
1085}
1086
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001087/* Configure dispc for partial update. Return possibly modified update
1088 * area */
1089void dss_setup_partial_planes(struct omap_dss_device *dssdev,
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001090 u16 *xi, u16 *yi, u16 *wi, u16 *hi, bool enlarge_update_area)
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001091{
1092 struct overlay_cache_data *oc;
1093 struct manager_cache_data *mc;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301094 struct omap_overlay_info *oi;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301095 const int num_ovls = dss_feat_get_num_ovls();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001096 struct omap_overlay_manager *mgr;
1097 int i;
1098 u16 x, y, w, h;
1099 unsigned long flags;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001100 bool area_changed;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001101
1102 x = *xi;
1103 y = *yi;
1104 w = *wi;
1105 h = *hi;
1106
1107 DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n",
1108 *xi, *yi, *wi, *hi);
1109
1110 mgr = dssdev->manager;
1111
1112 if (!mgr) {
1113 DSSDBG("no manager\n");
1114 return;
1115 }
1116
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001117 make_even(&x, &w);
1118
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001119 spin_lock_irqsave(&dss_cache.lock, flags);
1120
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001121 /*
1122 * Execute the outer loop until the inner loop has completed
1123 * once without increasing the update area. This will ensure that
1124 * all scaled overlays end up completely within the update area.
1125 */
1126 do {
1127 area_changed = false;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001128
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001129 /* We need to show the whole overlay if it is scaled. So look
1130 * for those, and make the update area larger if found.
1131 * Also mark the overlay cache dirty */
1132 for (i = 0; i < num_ovls; ++i) {
1133 unsigned x1, y1, x2, y2;
1134 unsigned outw, outh;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001135
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001136 oc = &dss_cache.overlay_cache[i];
Nishant Kamat4df9d102011-06-20 20:39:28 +05301137 oi = &oc->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001138
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001139 if (oc->channel != mgr->id)
1140 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001141
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001142 oc->dirty = true;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001143
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001144 if (!enlarge_update_area)
1145 continue;
1146
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001147 if (!oc->enabled)
1148 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001149
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001150 if (!dispc_is_overlay_scaled(oc))
1151 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001152
Nishant Kamat4df9d102011-06-20 20:39:28 +05301153 outw = oi->out_width == 0 ?
1154 oi->width : oi->out_width;
1155 outh = oi->out_height == 0 ?
1156 oi->height : oi->out_height;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001157
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001158 /* is the overlay outside the update region? */
1159 if (!rectangle_intersects(x, y, w, h,
Nishant Kamat4df9d102011-06-20 20:39:28 +05301160 oi->pos_x, oi->pos_y,
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001161 outw, outh))
1162 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001163
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001164 /* if the overlay totally inside the update region? */
Nishant Kamat4df9d102011-06-20 20:39:28 +05301165 if (rectangle_subset(oi->pos_x, oi->pos_y, outw, outh,
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001166 x, y, w, h))
1167 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001168
Nishant Kamat4df9d102011-06-20 20:39:28 +05301169 if (x > oi->pos_x)
1170 x1 = oi->pos_x;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001171 else
1172 x1 = x;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001173
Nishant Kamat4df9d102011-06-20 20:39:28 +05301174 if (y > oi->pos_y)
1175 y1 = oi->pos_y;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001176 else
1177 y1 = y;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001178
Nishant Kamat4df9d102011-06-20 20:39:28 +05301179 if ((x + w) < (oi->pos_x + outw))
1180 x2 = oi->pos_x + outw;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001181 else
1182 x2 = x + w;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001183
Nishant Kamat4df9d102011-06-20 20:39:28 +05301184 if ((y + h) < (oi->pos_y + outh))
1185 y2 = oi->pos_y + outh;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001186 else
1187 y2 = y + h;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001188
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001189 x = x1;
1190 y = y1;
1191 w = x2 - x1;
1192 h = y2 - y1;
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001193
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001194 make_even(&x, &w);
1195
1196 DSSDBG("changing upd area due to ovl(%d) "
1197 "scaling %d,%d %dx%d\n",
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001198 i, x, y, w, h);
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001199
1200 area_changed = true;
1201 }
1202 } while (area_changed);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001203
1204 mc = &dss_cache.manager_cache[mgr->id];
1205 mc->do_manual_update = true;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001206 mc->enlarge_update_area = enlarge_update_area;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001207 mc->x = x;
1208 mc->y = y;
1209 mc->w = w;
1210 mc->h = h;
1211
1212 configure_dispc();
1213
1214 mc->do_manual_update = false;
1215
1216 spin_unlock_irqrestore(&dss_cache.lock, flags);
1217
1218 *xi = x;
1219 *yi = y;
1220 *wi = w;
1221 *hi = h;
1222}
1223
1224void dss_start_update(struct omap_dss_device *dssdev)
1225{
1226 struct manager_cache_data *mc;
1227 struct overlay_cache_data *oc;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301228 const int num_ovls = dss_feat_get_num_ovls();
1229 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001230 struct omap_overlay_manager *mgr;
1231 int i;
1232
1233 mgr = dssdev->manager;
1234
1235 for (i = 0; i < num_ovls; ++i) {
1236 oc = &dss_cache.overlay_cache[i];
1237 if (oc->channel != mgr->id)
1238 continue;
1239
1240 oc->shadow_dirty = false;
1241 }
1242
1243 for (i = 0; i < num_mgrs; ++i) {
1244 mc = &dss_cache.manager_cache[i];
1245 if (mgr->id != i)
1246 continue;
1247
1248 mc->shadow_dirty = false;
1249 }
1250
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001251 dssdev->manager->enable(dssdev->manager);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001252}
1253
1254static void dss_apply_irq_handler(void *data, u32 mask)
1255{
1256 struct manager_cache_data *mc;
1257 struct overlay_cache_data *oc;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301258 const int num_ovls = dss_feat_get_num_ovls();
1259 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001260 int i, r;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301261 bool mgr_busy[MAX_DSS_MANAGERS];
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001262 u32 irq_mask;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001263
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001264 for (i = 0; i < num_mgrs; i++)
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001265 mgr_busy[i] = dispc_mgr_go_busy(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001266
1267 spin_lock(&dss_cache.lock);
1268
1269 for (i = 0; i < num_ovls; ++i) {
1270 oc = &dss_cache.overlay_cache[i];
1271 if (!mgr_busy[oc->channel])
1272 oc->shadow_dirty = false;
1273 }
1274
1275 for (i = 0; i < num_mgrs; ++i) {
1276 mc = &dss_cache.manager_cache[i];
1277 if (!mgr_busy[i])
1278 mc->shadow_dirty = false;
1279 }
1280
1281 r = configure_dispc();
1282 if (r == 1)
1283 goto end;
1284
1285 /* re-read busy flags */
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001286 for (i = 0; i < num_mgrs; i++)
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001287 mgr_busy[i] = dispc_mgr_go_busy(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001288
1289 /* keep running as long as there are busy managers, so that
1290 * we can collect overlay-applied information */
1291 for (i = 0; i < num_mgrs; ++i) {
1292 if (mgr_busy[i])
1293 goto end;
1294 }
1295
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001296 irq_mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
1297 DISPC_IRQ_EVSYNC_EVEN;
1298 if (dss_has_feature(FEAT_MGR_LCD2))
1299 irq_mask |= DISPC_IRQ_VSYNC2;
1300
1301 omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, irq_mask);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001302 dss_cache.irq_enabled = false;
1303
1304end:
1305 spin_unlock(&dss_cache.lock);
1306}
1307
1308static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
1309{
1310 struct overlay_cache_data *oc;
1311 struct manager_cache_data *mc;
1312 int i;
1313 struct omap_overlay *ovl;
1314 int num_planes_enabled = 0;
1315 bool use_fifomerge;
1316 unsigned long flags;
1317 int r;
1318
1319 DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
1320
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001321 r = dispc_runtime_get();
1322 if (r)
1323 return r;
1324
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001325 spin_lock_irqsave(&dss_cache.lock, flags);
1326
1327 /* Configure overlays */
1328 for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
1329 struct omap_dss_device *dssdev;
1330
1331 ovl = omap_dss_get_overlay(i);
1332
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001333 oc = &dss_cache.overlay_cache[ovl->id];
1334
Tomi Valkeinen8fa80312011-08-16 12:56:19 +03001335 if (ovl->manager_changed) {
1336 ovl->manager_changed = false;
1337 ovl->info_dirty = true;
1338 }
1339
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001340 if (!overlay_enabled(ovl)) {
1341 if (oc->enabled) {
1342 oc->enabled = false;
1343 oc->dirty = true;
1344 }
1345 continue;
1346 }
1347
1348 if (!ovl->info_dirty) {
1349 if (oc->enabled)
1350 ++num_planes_enabled;
1351 continue;
1352 }
1353
1354 dssdev = ovl->manager->device;
1355
1356 if (dss_check_overlay(ovl, dssdev)) {
1357 if (oc->enabled) {
1358 oc->enabled = false;
1359 oc->dirty = true;
1360 }
1361 continue;
1362 }
1363
1364 ovl->info_dirty = false;
1365 oc->dirty = true;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301366 oc->info = ovl->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001367
1368 oc->replication =
1369 dss_use_replication(dssdev, ovl->info.color_mode);
1370
1371 oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC;
1372
1373 oc->channel = ovl->manager->id;
1374
1375 oc->enabled = true;
1376
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001377 ++num_planes_enabled;
1378 }
1379
1380 /* Configure managers */
1381 list_for_each_entry(mgr, &manager_list, list) {
1382 struct omap_dss_device *dssdev;
1383
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001384 mc = &dss_cache.manager_cache[mgr->id];
1385
1386 if (mgr->device_changed) {
1387 mgr->device_changed = false;
1388 mgr->info_dirty = true;
1389 }
1390
1391 if (!mgr->info_dirty)
1392 continue;
1393
1394 if (!mgr->device)
1395 continue;
1396
1397 dssdev = mgr->device;
1398
1399 mgr->info_dirty = false;
1400 mc->dirty = true;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301401 mc->info = mgr->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001402
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001403 mc->manual_update =
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001404 dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001405 }
1406
1407 /* XXX TODO: Try to get fifomerge working. The problem is that it
1408 * affects both managers, not individually but at the same time. This
1409 * means the change has to be well synchronized. I guess the proper way
1410 * is to have a two step process for fifo merge:
1411 * fifomerge enable:
1412 * 1. disable other planes, leaving one plane enabled
1413 * 2. wait until the planes are disabled on HW
1414 * 3. config merged fifo thresholds, enable fifomerge
1415 * fifomerge disable:
1416 * 1. config unmerged fifo thresholds, disable fifomerge
1417 * 2. wait until fifo changes are in HW
1418 * 3. enable planes
1419 */
1420 use_fifomerge = false;
1421
1422 /* Configure overlay fifos */
1423 for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
1424 struct omap_dss_device *dssdev;
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001425 u32 size, burst_size;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001426
1427 ovl = omap_dss_get_overlay(i);
1428
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001429 oc = &dss_cache.overlay_cache[ovl->id];
1430
1431 if (!oc->enabled)
1432 continue;
1433
1434 dssdev = ovl->manager->device;
1435
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +03001436 size = dispc_ovl_get_fifo_size(ovl->id);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001437 if (use_fifomerge)
1438 size *= 3;
1439
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +03001440 burst_size = dispc_ovl_get_burst_size(ovl->id);
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001441
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001442 switch (dssdev->type) {
1443 case OMAP_DISPLAY_TYPE_DPI:
1444 case OMAP_DISPLAY_TYPE_DBI:
1445 case OMAP_DISPLAY_TYPE_SDI:
1446 case OMAP_DISPLAY_TYPE_VENC:
Mythri P Kb1196012011-03-08 17:15:54 +05301447 case OMAP_DISPLAY_TYPE_HDMI:
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001448 default_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001449 burst_size, &oc->fifo_low,
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001450 &oc->fifo_high);
1451 break;
1452#ifdef CONFIG_OMAP2_DSS_DSI
1453 case OMAP_DISPLAY_TYPE_DSI:
1454 dsi_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001455 burst_size, &oc->fifo_low,
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001456 &oc->fifo_high);
1457 break;
1458#endif
1459 default:
1460 BUG();
1461 }
1462 }
1463
1464 r = 0;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001465 if (!dss_cache.irq_enabled) {
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001466 u32 mask;
1467
1468 mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
1469 DISPC_IRQ_EVSYNC_EVEN;
1470 if (dss_has_feature(FEAT_MGR_LCD2))
1471 mask |= DISPC_IRQ_VSYNC2;
1472
1473 r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001474 dss_cache.irq_enabled = true;
1475 }
1476 configure_dispc();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001477
1478 spin_unlock_irqrestore(&dss_cache.lock, flags);
1479
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001480 dispc_runtime_put();
1481
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001482 return r;
1483}
1484
1485static int dss_check_manager(struct omap_overlay_manager *mgr)
1486{
1487 /* OMAP supports only graphics source transparency color key and alpha
1488 * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */
1489
1490 if (mgr->info.alpha_enabled && mgr->info.trans_enabled &&
1491 mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST)
1492 return -EINVAL;
1493
1494 return 0;
1495}
1496
1497static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr,
1498 struct omap_overlay_manager_info *info)
1499{
1500 int r;
1501 struct omap_overlay_manager_info old_info;
1502
1503 old_info = mgr->info;
1504 mgr->info = *info;
1505
1506 r = dss_check_manager(mgr);
1507 if (r) {
1508 mgr->info = old_info;
1509 return r;
1510 }
1511
1512 mgr->info_dirty = true;
1513
1514 return 0;
1515}
1516
1517static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr,
1518 struct omap_overlay_manager_info *info)
1519{
1520 *info = mgr->info;
1521}
1522
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001523static int dss_mgr_enable(struct omap_overlay_manager *mgr)
1524{
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001525 dispc_mgr_enable(mgr->id, 1);
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001526 return 0;
1527}
1528
1529static int dss_mgr_disable(struct omap_overlay_manager *mgr)
1530{
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001531 dispc_mgr_enable(mgr->id, 0);
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001532 return 0;
1533}
1534
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001535static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager)
1536{
1537 ++num_managers;
1538 list_add_tail(&manager->list, &manager_list);
1539}
1540
1541int dss_init_overlay_managers(struct platform_device *pdev)
1542{
1543 int i, r;
1544
1545 spin_lock_init(&dss_cache.lock);
1546
1547 INIT_LIST_HEAD(&manager_list);
1548
1549 num_managers = 0;
1550
Archit Tanejaa0acb552010-09-15 19:20:00 +05301551 for (i = 0; i < dss_feat_get_num_mgrs(); ++i) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001552 struct omap_overlay_manager *mgr;
1553 mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
1554
1555 BUG_ON(mgr == NULL);
1556
1557 switch (i) {
1558 case 0:
1559 mgr->name = "lcd";
1560 mgr->id = OMAP_DSS_CHANNEL_LCD;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001561 break;
1562 case 1:
1563 mgr->name = "tv";
1564 mgr->id = OMAP_DSS_CHANNEL_DIGIT;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001565 break;
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001566 case 2:
1567 mgr->name = "lcd2";
1568 mgr->id = OMAP_DSS_CHANNEL_LCD2;
1569 break;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001570 }
1571
1572 mgr->set_device = &omap_dss_set_device;
1573 mgr->unset_device = &omap_dss_unset_device;
1574 mgr->apply = &omap_dss_mgr_apply;
1575 mgr->set_manager_info = &omap_dss_mgr_set_info;
1576 mgr->get_manager_info = &omap_dss_mgr_get_info;
1577 mgr->wait_for_go = &dss_mgr_wait_for_go;
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +02001578 mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001579
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001580 mgr->enable = &dss_mgr_enable;
1581 mgr->disable = &dss_mgr_disable;
1582
Tomi Valkeinen4a9e78a2011-08-15 11:22:21 +03001583 mgr->caps = 0;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301584 mgr->supported_displays =
1585 dss_feat_get_supported_displays(mgr->id);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001586
1587 dss_overlay_setup_dispc_manager(mgr);
1588
1589 omap_dss_add_overlay_manager(mgr);
1590
1591 r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
1592 &pdev->dev.kobj, "manager%d", i);
1593
1594 if (r) {
1595 DSSERR("failed to create sysfs file\n");
1596 continue;
1597 }
1598 }
1599
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001600 return 0;
1601}
1602
1603void dss_uninit_overlay_managers(struct platform_device *pdev)
1604{
1605 struct omap_overlay_manager *mgr;
1606
1607 while (!list_empty(&manager_list)) {
1608 mgr = list_first_entry(&manager_list,
1609 struct omap_overlay_manager, list);
1610 list_del(&mgr->list);
1611 kobject_del(&mgr->kobj);
1612 kobject_put(&mgr->kobj);
1613 kfree(mgr);
1614 }
1615
1616 num_managers = 0;
1617}
1618
1619int omap_dss_get_num_overlay_managers(void)
1620{
1621 return num_managers;
1622}
1623EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
1624
1625struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
1626{
1627 int i = 0;
1628 struct omap_overlay_manager *mgr;
1629
1630 list_for_each_entry(mgr, &manager_list, list) {
1631 if (i++ == num)
1632 return mgr;
1633 }
1634
1635 return NULL;
1636}
1637EXPORT_SYMBOL(omap_dss_get_overlay_manager);
1638