blob: 107a4ae6e5acb43f3fe021ff8b5ab355ee2d21bb [file] [log] [blame]
Tomi Valkeinen58f255482011-11-04 09:48:54 +02001/*
2 * Copyright (C) 2011 Texas Instruments
3 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#define DSS_SUBSYS_NAME "APPLY"
19
20#include <linux/kernel.h>
21#include <linux/slab.h>
22#include <linux/spinlock.h>
23#include <linux/jiffies.h>
24
25#include <video/omapdss.h>
26
27#include "dss.h"
28#include "dss_features.h"
29
30/*
31 * We have 4 levels of cache for the dispc settings. First two are in SW and
32 * the latter two in HW.
33 *
34 * +--------------------+
35 * |overlay/manager_info|
36 * +--------------------+
37 * v
38 * apply()
39 * v
40 * +--------------------+
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +020041 * | info |
Tomi Valkeinen58f255482011-11-04 09:48:54 +020042 * +--------------------+
43 * v
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +020044 * write_regs()
Tomi Valkeinen58f255482011-11-04 09:48:54 +020045 * v
46 * +--------------------+
47 * | shadow registers |
48 * +--------------------+
49 * v
50 * VFP or lcd/digit_enable
51 * v
52 * +--------------------+
53 * | registers |
54 * +--------------------+
55 */
56
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +020057struct ovl_priv_data {
Tomi Valkeinen58f255482011-11-04 09:48:54 +020058 /* If true, cache changed, but not written to shadow registers. Set
59 * in apply(), cleared when registers written. */
60 bool dirty;
61 /* If true, shadow registers contain changed values not yet in real
62 * registers. Set when writing to shadow registers, cleared at
63 * VSYNC/EVSYNC */
64 bool shadow_dirty;
65
66 bool enabled;
67
68 struct omap_overlay_info info;
69
70 enum omap_channel channel;
71
72 u32 fifo_low;
73 u32 fifo_high;
74};
75
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +020076struct mgr_priv_data {
Tomi Valkeinen58f255482011-11-04 09:48:54 +020077 /* If true, cache changed, but not written to shadow registers. Set
78 * in apply(), cleared when registers written. */
79 bool dirty;
80 /* If true, shadow registers contain changed values not yet in real
81 * registers. Set when writing to shadow registers, cleared at
82 * VSYNC/EVSYNC */
83 bool shadow_dirty;
84
85 struct omap_overlay_manager_info info;
86
87 bool manual_update;
88 bool do_manual_update;
Tomi Valkeinenbf213522011-11-15 14:43:53 +020089
Tomi Valkeinen43a972d2011-11-15 15:04:25 +020090 /* If true, GO bit is up and shadow registers cannot be written.
91 * Never true for manual update displays */
92 bool busy;
93
Tomi Valkeinenbf213522011-11-15 14:43:53 +020094 /* If true, a display is enabled using this manager */
95 bool enabled;
Tomi Valkeinen58f255482011-11-04 09:48:54 +020096};
97
98static struct {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +020099 struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200100 struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200101
102 bool irq_enabled;
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200103} dss_data;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200104
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200105/* protects dss_data */
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200106static spinlock_t data_lock;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200107/* lock for blocking functions */
108static DEFINE_MUTEX(apply_lock);
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200109
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200110static struct ovl_priv_data *get_ovl_priv(struct omap_overlay *ovl)
111{
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200112 return &dss_data.ovl_priv_data_array[ovl->id];
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200113}
114
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200115static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
116{
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200117 return &dss_data.mgr_priv_data_array[mgr->id];
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200118}
119
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200120void dss_apply_init(void)
121{
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200122 spin_lock_init(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200123}
124
125static bool ovl_manual_update(struct omap_overlay *ovl)
126{
127 return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
128}
129
130static bool mgr_manual_update(struct omap_overlay_manager *mgr)
131{
132 return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
133}
134
135static int overlay_enabled(struct omap_overlay *ovl)
136{
137 return ovl->info.enabled && ovl->manager && ovl->manager->device;
138}
139
140int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
141{
142 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200143 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200144 u32 irq;
145 int r;
146 int i;
147 struct omap_dss_device *dssdev = mgr->device;
148
149 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
150 return 0;
151
152 if (mgr_manual_update(mgr))
153 return 0;
154
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200155 irq = dispc_mgr_get_vsync_irq(mgr->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200156
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200157 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200158 i = 0;
159 while (1) {
160 unsigned long flags;
161 bool shadow_dirty, dirty;
162
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200163 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200164 dirty = mp->dirty;
165 shadow_dirty = mp->shadow_dirty;
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200166 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200167
168 if (!dirty && !shadow_dirty) {
169 r = 0;
170 break;
171 }
172
173 /* 4 iterations is the worst case:
174 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
175 * 2 - first VSYNC, dirty = true
176 * 3 - dirty = false, shadow_dirty = true
177 * 4 - shadow_dirty = false */
178 if (i++ == 3) {
179 DSSERR("mgr(%d)->wait_for_go() not finishing\n",
180 mgr->id);
181 r = 0;
182 break;
183 }
184
185 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
186 if (r == -ERESTARTSYS)
187 break;
188
189 if (r) {
190 DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
191 break;
192 }
193 }
194
195 return r;
196}
197
198int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
199{
200 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200201 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200202 struct omap_dss_device *dssdev;
203 u32 irq;
204 int r;
205 int i;
206
207 if (!ovl->manager)
208 return 0;
209
210 dssdev = ovl->manager->device;
211
212 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
213 return 0;
214
215 if (ovl_manual_update(ovl))
216 return 0;
217
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200218 irq = dispc_mgr_get_vsync_irq(ovl->manager->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200219
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200220 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200221 i = 0;
222 while (1) {
223 unsigned long flags;
224 bool shadow_dirty, dirty;
225
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200226 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200227 dirty = op->dirty;
228 shadow_dirty = op->shadow_dirty;
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200229 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200230
231 if (!dirty && !shadow_dirty) {
232 r = 0;
233 break;
234 }
235
236 /* 4 iterations is the worst case:
237 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
238 * 2 - first VSYNC, dirty = true
239 * 3 - dirty = false, shadow_dirty = true
240 * 4 - shadow_dirty = false */
241 if (i++ == 3) {
242 DSSERR("ovl(%d)->wait_for_go() not finishing\n",
243 ovl->id);
244 r = 0;
245 break;
246 }
247
248 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
249 if (r == -ERESTARTSYS)
250 break;
251
252 if (r) {
253 DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
254 break;
255 }
256 }
257
258 return r;
259}
260
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200261static int dss_ovl_write_regs(struct omap_overlay *ovl)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200262{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200263 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200264 struct omap_overlay_info *oi;
265 bool ilace, replication;
266 int r;
267
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200268 DSSDBGF("%d", ovl->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200269
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200270 op = get_ovl_priv(ovl);
271 oi = &op->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200272
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200273 if (!op->enabled) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200274 dispc_ovl_enable(ovl->id, 0);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200275 return 0;
276 }
277
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200278 replication = dss_use_replication(ovl->manager->device, oi->color_mode);
279
280 ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;
281
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200282 dispc_ovl_set_channel_out(ovl->id, op->channel);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200283
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200284 r = dispc_ovl_setup(ovl->id, oi, ilace, replication);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200285 if (r) {
286 /* this shouldn't happen */
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200287 DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
288 dispc_ovl_enable(ovl->id, 0);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200289 return r;
290 }
291
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200292 dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200293
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200294 dispc_ovl_enable(ovl->id, 1);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200295
296 return 0;
297}
298
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200299static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200300{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200301 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200302 struct omap_overlay_manager_info *mi;
303
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200304 DSSDBGF("%d", mgr->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200305
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200306 mp = get_mgr_priv(mgr);
307 mi = &mp->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200308
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200309 dispc_mgr_setup(mgr->id, mi);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200310}
311
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200312/* dss_write_regs() tries to write values from cache to shadow registers.
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200313 * It writes only to those managers/overlays that are not busy.
314 * returns 0 if everything could be written to shadow registers.
315 * returns 1 if not everything could be written to shadow registers. */
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200316static int dss_write_regs(void)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200317{
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200318 struct omap_overlay *ovl;
319 struct omap_overlay_manager *mgr;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200320 struct ovl_priv_data *op;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200321 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200322 const int num_ovls = dss_feat_get_num_ovls();
323 const int num_mgrs = dss_feat_get_num_mgrs();
324 int i;
325 int r;
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200326 bool mgr_go[MAX_DSS_MANAGERS] = { false };
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200327 bool busy;
328
329 r = 0;
330 busy = false;
331
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200332 /* Commit overlay settings */
333 for (i = 0; i < num_ovls; ++i) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200334 ovl = omap_dss_get_overlay(i);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200335 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200336
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200337 if (!op->dirty)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200338 continue;
339
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200340 mp = get_mgr_priv(ovl->manager);
341
342 if (mp->manual_update && !mp->do_manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200343 continue;
344
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200345 if (mp->busy) {
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200346 busy = true;
347 continue;
348 }
349
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200350 r = dss_ovl_write_regs(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200351 if (r)
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200352 DSSERR("dss_ovl_write_regs %d failed\n", i);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200353
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200354 op->dirty = false;
355 op->shadow_dirty = true;
356 mgr_go[op->channel] = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200357 }
358
359 /* Commit manager settings */
360 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200361 mgr = omap_dss_get_overlay_manager(i);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200362 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200363
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200364 if (!mp->dirty)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200365 continue;
366
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200367 if (mp->manual_update && !mp->do_manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200368 continue;
369
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200370 if (mp->busy) {
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200371 busy = true;
372 continue;
373 }
374
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200375 dss_mgr_write_regs(mgr);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200376 mp->dirty = false;
377 mp->shadow_dirty = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200378 mgr_go[i] = true;
379 }
380
381 /* set GO */
382 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200383 mgr = omap_dss_get_overlay_manager(i);
384 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200385
386 if (!mgr_go[i])
387 continue;
388
389 /* We don't need GO with manual update display. LCD iface will
390 * always be turned off after frame, and new settings will be
391 * taken in to use at next update */
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200392 if (!mp->manual_update) {
393 mp->busy = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200394 dispc_mgr_go(i);
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200395 }
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200396 }
397
398 if (busy)
399 r = 1;
400 else
401 r = 0;
402
403 return r;
404}
405
406void dss_mgr_start_update(struct omap_overlay_manager *mgr)
407{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200408 struct mgr_priv_data *mp = get_mgr_priv(mgr);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200409 struct ovl_priv_data *op;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200410 struct omap_overlay *ovl;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200411 unsigned long flags;
412
413 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200414
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200415 mp->do_manual_update = true;
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200416 dss_write_regs();
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200417 mp->do_manual_update = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200418
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200419 list_for_each_entry(ovl, &mgr->overlays, list) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200420 op = get_ovl_priv(ovl);
421 op->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200422 }
423
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200424 mp->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200425
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200426 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200427
428 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200429}
430
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200431static void dss_apply_irq_handler(void *data, u32 mask);
432
433static void dss_register_vsync_isr(void)
434{
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200435 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200436 u32 mask;
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200437 int r, i;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200438
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200439 mask = 0;
440 for (i = 0; i < num_mgrs; ++i)
441 mask |= dispc_mgr_get_vsync_irq(i);
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200442
443 r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
444 WARN_ON(r);
445
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200446 dss_data.irq_enabled = true;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200447}
448
449static void dss_unregister_vsync_isr(void)
450{
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200451 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200452 u32 mask;
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200453 int r, i;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200454
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200455 mask = 0;
456 for (i = 0; i < num_mgrs; ++i)
457 mask |= dispc_mgr_get_vsync_irq(i);
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200458
459 r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
460 WARN_ON(r);
461
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200462 dss_data.irq_enabled = false;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200463}
464
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200465static void dss_apply_irq_handler(void *data, u32 mask)
466{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200467 struct omap_overlay *ovl;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200468 struct omap_overlay_manager *mgr;
469 struct mgr_priv_data *mp;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200470 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200471 const int num_ovls = dss_feat_get_num_ovls();
472 const int num_mgrs = dss_feat_get_num_mgrs();
473 int i, r;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200474
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200475 spin_lock(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200476
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200477 for (i = 0; i < num_mgrs; i++) {
478 mgr = omap_dss_get_overlay_manager(i);
479 mp = get_mgr_priv(mgr);
480
481 mp->busy = dispc_mgr_go_busy(i);
482 }
483
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200484 for (i = 0; i < num_ovls; ++i) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200485 ovl = omap_dss_get_overlay(i);
486 op = get_ovl_priv(ovl);
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200487
488 if (!op->enabled)
489 continue;
490
491 mp = get_mgr_priv(ovl->manager);
492
493 if (!mp->busy)
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200494 op->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200495 }
496
497 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200498 mgr = omap_dss_get_overlay_manager(i);
499 mp = get_mgr_priv(mgr);
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200500
501 if (!mp->busy)
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200502 mp->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200503 }
504
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200505 r = dss_write_regs();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200506 if (r == 1)
507 goto end;
508
509 /* re-read busy flags */
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200510 for (i = 0; i < num_mgrs; i++) {
511 mgr = omap_dss_get_overlay_manager(i);
512 mp = get_mgr_priv(mgr);
513
514 mp->busy = dispc_mgr_go_busy(i);
515 }
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200516
517 /* keep running as long as there are busy managers, so that
518 * we can collect overlay-applied information */
519 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200520 mgr = omap_dss_get_overlay_manager(i);
521 mp = get_mgr_priv(mgr);
522
523 if (mp->busy)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200524 goto end;
525 }
526
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200527 dss_unregister_vsync_isr();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200528
529end:
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200530 spin_unlock(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200531}
532
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200533static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200534{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200535 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200536
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200537 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200538
539 if (ovl->manager_changed) {
540 ovl->manager_changed = false;
541 ovl->info_dirty = true;
542 }
543
544 if (!overlay_enabled(ovl)) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200545 if (op->enabled) {
546 op->enabled = false;
547 op->dirty = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200548 }
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200549 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200550 }
551
552 if (!ovl->info_dirty)
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200553 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200554
555 ovl->info_dirty = false;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200556 op->dirty = true;
557 op->info = ovl->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200558
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200559 op->channel = ovl->manager->id;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200560
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200561 op->enabled = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200562}
563
564static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr)
565{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200566 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200567
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200568 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200569
570 if (mgr->device_changed) {
571 mgr->device_changed = false;
572 mgr->info_dirty = true;
573 }
574
575 if (!mgr->info_dirty)
576 return;
577
578 if (!mgr->device)
579 return;
580
581 mgr->info_dirty = false;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200582 mp->dirty = true;
583 mp->info = mgr->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200584
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200585 mp->manual_update = mgr_manual_update(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200586}
587
588static void omap_dss_mgr_apply_ovl_fifos(struct omap_overlay *ovl)
589{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200590 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200591 struct omap_dss_device *dssdev;
592 u32 size, burst_size;
593
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200594 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200595
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200596 if (!op->enabled)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200597 return;
598
599 dssdev = ovl->manager->device;
600
601 size = dispc_ovl_get_fifo_size(ovl->id);
602
603 burst_size = dispc_ovl_get_burst_size(ovl->id);
604
605 switch (dssdev->type) {
606 case OMAP_DISPLAY_TYPE_DPI:
607 case OMAP_DISPLAY_TYPE_DBI:
608 case OMAP_DISPLAY_TYPE_SDI:
609 case OMAP_DISPLAY_TYPE_VENC:
610 case OMAP_DISPLAY_TYPE_HDMI:
611 default_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200612 burst_size, &op->fifo_low,
613 &op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200614 break;
615#ifdef CONFIG_OMAP2_DSS_DSI
616 case OMAP_DISPLAY_TYPE_DSI:
617 dsi_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200618 burst_size, &op->fifo_low,
619 &op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200620 break;
621#endif
622 default:
623 BUG();
624 }
625}
626
627int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
628{
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200629 int r;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200630 unsigned long flags;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200631 struct omap_overlay *ovl;
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200632 struct mgr_priv_data *mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200633
634 DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
635
636 r = dispc_runtime_get();
637 if (r)
638 return r;
639
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200640 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200641
642 /* Configure overlays */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200643 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200644 omap_dss_mgr_apply_ovl(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200645
646 /* Configure manager */
647 omap_dss_mgr_apply_mgr(mgr);
648
649 /* Configure overlay fifos */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200650 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200651 omap_dss_mgr_apply_ovl_fifos(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200652
653 r = 0;
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200654 if (mp->enabled && !mgr_manual_update(mgr)) {
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200655 if (!dss_data.irq_enabled)
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200656 dss_register_vsync_isr();
Tomi Valkeinen18135ea2011-11-04 09:35:59 +0200657
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200658 dss_write_regs();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200659 }
660
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200661 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200662
663 dispc_runtime_put();
664
665 return r;
666}
667
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200668void dss_mgr_enable(struct omap_overlay_manager *mgr)
669{
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200670 struct mgr_priv_data *mp = get_mgr_priv(mgr);
671 unsigned long flags;
672
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200673 mutex_lock(&apply_lock);
674
Tomi Valkeinen9a147a62011-11-09 15:30:11 +0200675 if (!mgr_manual_update(mgr))
676 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200677
678 spin_lock_irqsave(&data_lock, flags);
679
680 mp->enabled = true;
681
682 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200683
684 mutex_unlock(&apply_lock);
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200685}
686
687void dss_mgr_disable(struct omap_overlay_manager *mgr)
688{
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200689 struct mgr_priv_data *mp = get_mgr_priv(mgr);
690 unsigned long flags;
691
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200692 mutex_lock(&apply_lock);
693
Tomi Valkeinen9a147a62011-11-09 15:30:11 +0200694 if (!mgr_manual_update(mgr))
695 dispc_mgr_enable(mgr->id, false);
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200696
697 spin_lock_irqsave(&data_lock, flags);
698
699 mp->enabled = false;
700
701 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200702
703 mutex_unlock(&apply_lock);
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200704}
705
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200706int dss_mgr_set_info(struct omap_overlay_manager *mgr,
707 struct omap_overlay_manager_info *info)
708{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200709 unsigned long flags;
710
711 spin_lock_irqsave(&data_lock, flags);
712
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200713 mgr->info = *info;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200714 mgr->info_dirty = true;
715
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200716 spin_unlock_irqrestore(&data_lock, flags);
717
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200718 return 0;
719}
720
721void dss_mgr_get_info(struct omap_overlay_manager *mgr,
722 struct omap_overlay_manager_info *info)
723{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200724 unsigned long flags;
725
726 spin_lock_irqsave(&data_lock, flags);
727
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200728 *info = mgr->info;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200729
730 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200731}
732
733int dss_mgr_set_device(struct omap_overlay_manager *mgr,
734 struct omap_dss_device *dssdev)
735{
736 int r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200737
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200738 mutex_lock(&apply_lock);
739
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200740 if (dssdev->manager) {
741 DSSERR("display '%s' already has a manager '%s'\n",
742 dssdev->name, dssdev->manager->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200743 r = -EINVAL;
744 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200745 }
746
747 if ((mgr->supported_displays & dssdev->type) == 0) {
748 DSSERR("display '%s' does not support manager '%s'\n",
749 dssdev->name, mgr->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200750 r = -EINVAL;
751 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200752 }
753
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200754 dssdev->manager = mgr;
755 mgr->device = dssdev;
756 mgr->device_changed = true;
757
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200758 mutex_unlock(&apply_lock);
759
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200760 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200761err:
762 mutex_unlock(&apply_lock);
763 return r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200764}
765
766int dss_mgr_unset_device(struct omap_overlay_manager *mgr)
767{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200768 int r;
769
770 mutex_lock(&apply_lock);
771
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200772 if (!mgr->device) {
773 DSSERR("failed to unset display, display not set.\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200774 r = -EINVAL;
775 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200776 }
777
778 /*
779 * Don't allow currently enabled displays to have the overlay manager
780 * pulled out from underneath them
781 */
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200782 if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED) {
783 r = -EINVAL;
784 goto err;
785 }
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200786
787 mgr->device->manager = NULL;
788 mgr->device = NULL;
789 mgr->device_changed = true;
790
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200791 mutex_unlock(&apply_lock);
792
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200793 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200794err:
795 mutex_unlock(&apply_lock);
796 return r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200797}
798
799
800
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200801int dss_ovl_set_info(struct omap_overlay *ovl,
802 struct omap_overlay_info *info)
803{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200804 unsigned long flags;
805
806 spin_lock_irqsave(&data_lock, flags);
807
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200808 ovl->info = *info;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200809 ovl->info_dirty = true;
810
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200811 spin_unlock_irqrestore(&data_lock, flags);
812
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200813 return 0;
814}
815
816void dss_ovl_get_info(struct omap_overlay *ovl,
817 struct omap_overlay_info *info)
818{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200819 unsigned long flags;
820
821 spin_lock_irqsave(&data_lock, flags);
822
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200823 *info = ovl->info;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200824
825 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200826}
827
828int dss_ovl_set_manager(struct omap_overlay *ovl,
829 struct omap_overlay_manager *mgr)
830{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200831 int r;
832
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200833 if (!mgr)
834 return -EINVAL;
835
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200836 mutex_lock(&apply_lock);
837
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200838 if (ovl->manager) {
839 DSSERR("overlay '%s' already has a manager '%s'\n",
840 ovl->name, ovl->manager->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200841 r = -EINVAL;
842 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200843 }
844
845 if (ovl->info.enabled) {
846 DSSERR("overlay has to be disabled to change the manager\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200847 r = -EINVAL;
848 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200849 }
850
851 ovl->manager = mgr;
852 list_add_tail(&ovl->list, &mgr->overlays);
853 ovl->manager_changed = true;
854
855 /* XXX: When there is an overlay on a DSI manual update display, and
856 * the overlay is first disabled, then moved to tv, and enabled, we
857 * seem to get SYNC_LOST_DIGIT error.
858 *
859 * Waiting doesn't seem to help, but updating the manual update display
860 * after disabling the overlay seems to fix this. This hints that the
861 * overlay is perhaps somehow tied to the LCD output until the output
862 * is updated.
863 *
864 * Userspace workaround for this is to update the LCD after disabling
865 * the overlay, but before moving the overlay to TV.
866 */
867
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200868 mutex_unlock(&apply_lock);
869
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200870 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200871err:
872 mutex_unlock(&apply_lock);
873 return r;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200874}
875
876int dss_ovl_unset_manager(struct omap_overlay *ovl)
877{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200878 int r;
879
880 mutex_lock(&apply_lock);
881
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200882 if (!ovl->manager) {
883 DSSERR("failed to detach overlay: manager not set\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200884 r = -EINVAL;
885 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200886 }
887
888 if (ovl->info.enabled) {
889 DSSERR("overlay has to be disabled to unset the manager\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200890 r = -EINVAL;
891 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200892 }
893
894 ovl->manager = NULL;
895 list_del(&ovl->list);
896 ovl->manager_changed = true;
897
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200898 mutex_unlock(&apply_lock);
899
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200900 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200901err:
902 mutex_unlock(&apply_lock);
903 return r;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200904}
905