blob: 936571b7aace5f2842cd517dbe228b2a1fd3c3b1 [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;
89};
90
91static struct {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +020092 struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +020093 struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
Tomi Valkeinen58f255482011-11-04 09:48:54 +020094
95 bool irq_enabled;
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +020096} dss_data;
Tomi Valkeinen58f255482011-11-04 09:48:54 +020097
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +020098/* protects dss_data */
Tomi Valkeinen063fd702011-11-15 12:04:10 +020099static spinlock_t data_lock;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200100/* lock for blocking functions */
101static DEFINE_MUTEX(apply_lock);
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200102
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200103static struct ovl_priv_data *get_ovl_priv(struct omap_overlay *ovl)
104{
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200105 return &dss_data.ovl_priv_data_array[ovl->id];
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200106}
107
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200108static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
109{
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200110 return &dss_data.mgr_priv_data_array[mgr->id];
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200111}
112
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200113void dss_apply_init(void)
114{
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200115 spin_lock_init(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200116}
117
118static bool ovl_manual_update(struct omap_overlay *ovl)
119{
120 return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
121}
122
123static bool mgr_manual_update(struct omap_overlay_manager *mgr)
124{
125 return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
126}
127
128static int overlay_enabled(struct omap_overlay *ovl)
129{
130 return ovl->info.enabled && ovl->manager && ovl->manager->device;
131}
132
133int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
134{
135 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200136 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200137 u32 irq;
138 int r;
139 int i;
140 struct omap_dss_device *dssdev = mgr->device;
141
142 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
143 return 0;
144
145 if (mgr_manual_update(mgr))
146 return 0;
147
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200148 irq = dispc_mgr_get_vsync_irq(mgr->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200149
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200150 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200151 i = 0;
152 while (1) {
153 unsigned long flags;
154 bool shadow_dirty, dirty;
155
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200156 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200157 dirty = mp->dirty;
158 shadow_dirty = mp->shadow_dirty;
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200159 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200160
161 if (!dirty && !shadow_dirty) {
162 r = 0;
163 break;
164 }
165
166 /* 4 iterations is the worst case:
167 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
168 * 2 - first VSYNC, dirty = true
169 * 3 - dirty = false, shadow_dirty = true
170 * 4 - shadow_dirty = false */
171 if (i++ == 3) {
172 DSSERR("mgr(%d)->wait_for_go() not finishing\n",
173 mgr->id);
174 r = 0;
175 break;
176 }
177
178 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
179 if (r == -ERESTARTSYS)
180 break;
181
182 if (r) {
183 DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
184 break;
185 }
186 }
187
188 return r;
189}
190
191int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
192{
193 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200194 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200195 struct omap_dss_device *dssdev;
196 u32 irq;
197 int r;
198 int i;
199
200 if (!ovl->manager)
201 return 0;
202
203 dssdev = ovl->manager->device;
204
205 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
206 return 0;
207
208 if (ovl_manual_update(ovl))
209 return 0;
210
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200211 irq = dispc_mgr_get_vsync_irq(ovl->manager->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200212
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200213 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200214 i = 0;
215 while (1) {
216 unsigned long flags;
217 bool shadow_dirty, dirty;
218
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200219 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200220 dirty = op->dirty;
221 shadow_dirty = op->shadow_dirty;
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200222 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200223
224 if (!dirty && !shadow_dirty) {
225 r = 0;
226 break;
227 }
228
229 /* 4 iterations is the worst case:
230 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
231 * 2 - first VSYNC, dirty = true
232 * 3 - dirty = false, shadow_dirty = true
233 * 4 - shadow_dirty = false */
234 if (i++ == 3) {
235 DSSERR("ovl(%d)->wait_for_go() not finishing\n",
236 ovl->id);
237 r = 0;
238 break;
239 }
240
241 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
242 if (r == -ERESTARTSYS)
243 break;
244
245 if (r) {
246 DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
247 break;
248 }
249 }
250
251 return r;
252}
253
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200254static int dss_ovl_write_regs(struct omap_overlay *ovl)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200255{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200256 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200257 struct omap_overlay_info *oi;
258 bool ilace, replication;
259 int r;
260
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200261 DSSDBGF("%d", ovl->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200262
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200263 op = get_ovl_priv(ovl);
264 oi = &op->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200265
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200266 if (!op->enabled) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200267 dispc_ovl_enable(ovl->id, 0);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200268 return 0;
269 }
270
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200271 replication = dss_use_replication(ovl->manager->device, oi->color_mode);
272
273 ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;
274
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200275 dispc_ovl_set_channel_out(ovl->id, op->channel);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200276
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200277 r = dispc_ovl_setup(ovl->id, oi, ilace, replication);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200278 if (r) {
279 /* this shouldn't happen */
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200280 DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
281 dispc_ovl_enable(ovl->id, 0);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200282 return r;
283 }
284
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200285 dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200286
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200287 dispc_ovl_enable(ovl->id, 1);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200288
289 return 0;
290}
291
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200292static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200293{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200294 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200295 struct omap_overlay_manager_info *mi;
296
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200297 DSSDBGF("%d", mgr->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200298
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200299 mp = get_mgr_priv(mgr);
300 mi = &mp->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200301
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200302 dispc_mgr_setup(mgr->id, mi);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200303}
304
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200305/* dss_write_regs() tries to write values from cache to shadow registers.
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200306 * It writes only to those managers/overlays that are not busy.
307 * returns 0 if everything could be written to shadow registers.
308 * returns 1 if not everything could be written to shadow registers. */
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200309static int dss_write_regs(void)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200310{
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200311 struct omap_overlay *ovl;
312 struct omap_overlay_manager *mgr;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200313 struct ovl_priv_data *op;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200314 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200315 const int num_ovls = dss_feat_get_num_ovls();
316 const int num_mgrs = dss_feat_get_num_mgrs();
317 int i;
318 int r;
319 bool mgr_busy[MAX_DSS_MANAGERS];
320 bool mgr_go[MAX_DSS_MANAGERS];
321 bool busy;
322
323 r = 0;
324 busy = false;
325
326 for (i = 0; i < num_mgrs; i++) {
327 mgr_busy[i] = dispc_mgr_go_busy(i);
328 mgr_go[i] = false;
329 }
330
331 /* Commit overlay settings */
332 for (i = 0; i < num_ovls; ++i) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200333 ovl = omap_dss_get_overlay(i);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200334 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200335
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200336 if (!op->dirty)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200337 continue;
338
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200339 mp = get_mgr_priv(ovl->manager);
340
341 if (mp->manual_update && !mp->do_manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200342 continue;
343
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200344 if (mgr_busy[op->channel]) {
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200345 busy = true;
346 continue;
347 }
348
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200349 r = dss_ovl_write_regs(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200350 if (r)
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200351 DSSERR("dss_ovl_write_regs %d failed\n", i);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200352
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200353 op->dirty = false;
354 op->shadow_dirty = true;
355 mgr_go[op->channel] = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200356 }
357
358 /* Commit manager settings */
359 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200360 mgr = omap_dss_get_overlay_manager(i);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200361 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200362
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200363 if (!mp->dirty)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200364 continue;
365
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200366 if (mp->manual_update && !mp->do_manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200367 continue;
368
369 if (mgr_busy[i]) {
370 busy = true;
371 continue;
372 }
373
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200374 dss_mgr_write_regs(mgr);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200375 mp->dirty = false;
376 mp->shadow_dirty = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200377 mgr_go[i] = true;
378 }
379
380 /* set GO */
381 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200382 mgr = omap_dss_get_overlay_manager(i);
383 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200384
385 if (!mgr_go[i])
386 continue;
387
388 /* We don't need GO with manual update display. LCD iface will
389 * always be turned off after frame, and new settings will be
390 * taken in to use at next update */
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200391 if (!mp->manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200392 dispc_mgr_go(i);
393 }
394
395 if (busy)
396 r = 1;
397 else
398 r = 0;
399
400 return r;
401}
402
403void dss_mgr_start_update(struct omap_overlay_manager *mgr)
404{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200405 struct mgr_priv_data *mp = get_mgr_priv(mgr);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200406 struct ovl_priv_data *op;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200407 struct omap_overlay *ovl;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200408 unsigned long flags;
409
410 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200411
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200412 mp->do_manual_update = true;
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200413 dss_write_regs();
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200414 mp->do_manual_update = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200415
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200416 list_for_each_entry(ovl, &mgr->overlays, list) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200417 op = get_ovl_priv(ovl);
418 op->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200419 }
420
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200421 mp->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200422
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200423 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200424
425 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200426}
427
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200428static void dss_apply_irq_handler(void *data, u32 mask);
429
430static void dss_register_vsync_isr(void)
431{
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200432 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200433 u32 mask;
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200434 int r, i;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200435
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200436 mask = 0;
437 for (i = 0; i < num_mgrs; ++i)
438 mask |= dispc_mgr_get_vsync_irq(i);
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200439
440 r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
441 WARN_ON(r);
442
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200443 dss_data.irq_enabled = true;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200444}
445
446static void dss_unregister_vsync_isr(void)
447{
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200448 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200449 u32 mask;
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200450 int r, i;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200451
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200452 mask = 0;
453 for (i = 0; i < num_mgrs; ++i)
454 mask |= dispc_mgr_get_vsync_irq(i);
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200455
456 r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
457 WARN_ON(r);
458
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200459 dss_data.irq_enabled = false;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200460}
461
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200462static void dss_apply_irq_handler(void *data, u32 mask)
463{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200464 struct omap_overlay *ovl;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200465 struct omap_overlay_manager *mgr;
466 struct mgr_priv_data *mp;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200467 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200468 const int num_ovls = dss_feat_get_num_ovls();
469 const int num_mgrs = dss_feat_get_num_mgrs();
470 int i, r;
471 bool mgr_busy[MAX_DSS_MANAGERS];
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200472
473 for (i = 0; i < num_mgrs; i++)
474 mgr_busy[i] = dispc_mgr_go_busy(i);
475
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200476 spin_lock(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200477
478 for (i = 0; i < num_ovls; ++i) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200479 ovl = omap_dss_get_overlay(i);
480 op = get_ovl_priv(ovl);
481 if (!mgr_busy[op->channel])
482 op->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200483 }
484
485 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200486 mgr = omap_dss_get_overlay_manager(i);
487 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200488 if (!mgr_busy[i])
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200489 mp->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200490 }
491
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200492 r = dss_write_regs();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200493 if (r == 1)
494 goto end;
495
496 /* re-read busy flags */
497 for (i = 0; i < num_mgrs; i++)
498 mgr_busy[i] = dispc_mgr_go_busy(i);
499
500 /* keep running as long as there are busy managers, so that
501 * we can collect overlay-applied information */
502 for (i = 0; i < num_mgrs; ++i) {
503 if (mgr_busy[i])
504 goto end;
505 }
506
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200507 dss_unregister_vsync_isr();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200508
509end:
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200510 spin_unlock(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200511}
512
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200513static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200514{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200515 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200516
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200517 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200518
519 if (ovl->manager_changed) {
520 ovl->manager_changed = false;
521 ovl->info_dirty = true;
522 }
523
524 if (!overlay_enabled(ovl)) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200525 if (op->enabled) {
526 op->enabled = false;
527 op->dirty = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200528 }
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200529 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200530 }
531
532 if (!ovl->info_dirty)
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200533 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200534
535 ovl->info_dirty = false;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200536 op->dirty = true;
537 op->info = ovl->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200538
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200539 op->channel = ovl->manager->id;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200540
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200541 op->enabled = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200542}
543
544static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr)
545{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200546 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200547
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200548 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200549
550 if (mgr->device_changed) {
551 mgr->device_changed = false;
552 mgr->info_dirty = true;
553 }
554
555 if (!mgr->info_dirty)
556 return;
557
558 if (!mgr->device)
559 return;
560
561 mgr->info_dirty = false;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200562 mp->dirty = true;
563 mp->info = mgr->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200564
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200565 mp->manual_update = mgr_manual_update(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200566}
567
568static void omap_dss_mgr_apply_ovl_fifos(struct omap_overlay *ovl)
569{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200570 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200571 struct omap_dss_device *dssdev;
572 u32 size, burst_size;
573
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200574 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200575
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200576 if (!op->enabled)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200577 return;
578
579 dssdev = ovl->manager->device;
580
581 size = dispc_ovl_get_fifo_size(ovl->id);
582
583 burst_size = dispc_ovl_get_burst_size(ovl->id);
584
585 switch (dssdev->type) {
586 case OMAP_DISPLAY_TYPE_DPI:
587 case OMAP_DISPLAY_TYPE_DBI:
588 case OMAP_DISPLAY_TYPE_SDI:
589 case OMAP_DISPLAY_TYPE_VENC:
590 case OMAP_DISPLAY_TYPE_HDMI:
591 default_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200592 burst_size, &op->fifo_low,
593 &op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200594 break;
595#ifdef CONFIG_OMAP2_DSS_DSI
596 case OMAP_DISPLAY_TYPE_DSI:
597 dsi_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200598 burst_size, &op->fifo_low,
599 &op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200600 break;
601#endif
602 default:
603 BUG();
604 }
605}
606
607int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
608{
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200609 int r;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200610 unsigned long flags;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200611 struct omap_overlay *ovl;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200612
613 DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
614
615 r = dispc_runtime_get();
616 if (r)
617 return r;
618
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200619 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200620
621 /* Configure overlays */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200622 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200623 omap_dss_mgr_apply_ovl(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200624
625 /* Configure manager */
626 omap_dss_mgr_apply_mgr(mgr);
627
628 /* Configure overlay fifos */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200629 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200630 omap_dss_mgr_apply_ovl_fifos(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200631
632 r = 0;
Tomi Valkeinen04f66432011-11-07 15:04:01 +0200633 if (mgr->enabled && !mgr_manual_update(mgr)) {
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200634 if (!dss_data.irq_enabled)
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200635 dss_register_vsync_isr();
Tomi Valkeinen18135ea2011-11-04 09:35:59 +0200636
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200637 dss_write_regs();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200638 }
639
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200640 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200641
642 dispc_runtime_put();
643
644 return r;
645}
646
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200647void dss_mgr_enable(struct omap_overlay_manager *mgr)
648{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200649 mutex_lock(&apply_lock);
650
Tomi Valkeinen9a147a62011-11-09 15:30:11 +0200651 if (!mgr_manual_update(mgr))
652 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinenbe729172011-11-04 10:30:47 +0200653 mgr->enabled = true;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200654
655 mutex_unlock(&apply_lock);
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200656}
657
658void dss_mgr_disable(struct omap_overlay_manager *mgr)
659{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200660 mutex_lock(&apply_lock);
661
Tomi Valkeinen9a147a62011-11-09 15:30:11 +0200662 if (!mgr_manual_update(mgr))
663 dispc_mgr_enable(mgr->id, false);
Tomi Valkeinenbe729172011-11-04 10:30:47 +0200664 mgr->enabled = false;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200665
666 mutex_unlock(&apply_lock);
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200667}
668
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200669int dss_mgr_set_info(struct omap_overlay_manager *mgr,
670 struct omap_overlay_manager_info *info)
671{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200672 unsigned long flags;
673
674 spin_lock_irqsave(&data_lock, flags);
675
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200676 mgr->info = *info;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200677 mgr->info_dirty = true;
678
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200679 spin_unlock_irqrestore(&data_lock, flags);
680
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200681 return 0;
682}
683
684void dss_mgr_get_info(struct omap_overlay_manager *mgr,
685 struct omap_overlay_manager_info *info)
686{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200687 unsigned long flags;
688
689 spin_lock_irqsave(&data_lock, flags);
690
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200691 *info = mgr->info;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200692
693 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200694}
695
696int dss_mgr_set_device(struct omap_overlay_manager *mgr,
697 struct omap_dss_device *dssdev)
698{
699 int r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200700
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200701 mutex_lock(&apply_lock);
702
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200703 if (dssdev->manager) {
704 DSSERR("display '%s' already has a manager '%s'\n",
705 dssdev->name, dssdev->manager->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200706 r = -EINVAL;
707 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200708 }
709
710 if ((mgr->supported_displays & dssdev->type) == 0) {
711 DSSERR("display '%s' does not support manager '%s'\n",
712 dssdev->name, mgr->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200713 r = -EINVAL;
714 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200715 }
716
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200717 dssdev->manager = mgr;
718 mgr->device = dssdev;
719 mgr->device_changed = true;
720
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200721 mutex_unlock(&apply_lock);
722
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200723 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200724err:
725 mutex_unlock(&apply_lock);
726 return r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200727}
728
729int dss_mgr_unset_device(struct omap_overlay_manager *mgr)
730{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200731 int r;
732
733 mutex_lock(&apply_lock);
734
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200735 if (!mgr->device) {
736 DSSERR("failed to unset display, display not set.\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200737 r = -EINVAL;
738 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200739 }
740
741 /*
742 * Don't allow currently enabled displays to have the overlay manager
743 * pulled out from underneath them
744 */
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200745 if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED) {
746 r = -EINVAL;
747 goto err;
748 }
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200749
750 mgr->device->manager = NULL;
751 mgr->device = NULL;
752 mgr->device_changed = true;
753
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200754 mutex_unlock(&apply_lock);
755
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200756 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200757err:
758 mutex_unlock(&apply_lock);
759 return r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200760}
761
762
763
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200764int dss_ovl_set_info(struct omap_overlay *ovl,
765 struct omap_overlay_info *info)
766{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200767 unsigned long flags;
768
769 spin_lock_irqsave(&data_lock, flags);
770
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200771 ovl->info = *info;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200772 ovl->info_dirty = true;
773
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200774 spin_unlock_irqrestore(&data_lock, flags);
775
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200776 return 0;
777}
778
779void dss_ovl_get_info(struct omap_overlay *ovl,
780 struct omap_overlay_info *info)
781{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200782 unsigned long flags;
783
784 spin_lock_irqsave(&data_lock, flags);
785
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200786 *info = ovl->info;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200787
788 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200789}
790
791int dss_ovl_set_manager(struct omap_overlay *ovl,
792 struct omap_overlay_manager *mgr)
793{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200794 int r;
795
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200796 if (!mgr)
797 return -EINVAL;
798
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200799 mutex_lock(&apply_lock);
800
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200801 if (ovl->manager) {
802 DSSERR("overlay '%s' already has a manager '%s'\n",
803 ovl->name, ovl->manager->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200804 r = -EINVAL;
805 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200806 }
807
808 if (ovl->info.enabled) {
809 DSSERR("overlay has to be disabled to change the manager\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200810 r = -EINVAL;
811 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200812 }
813
814 ovl->manager = mgr;
815 list_add_tail(&ovl->list, &mgr->overlays);
816 ovl->manager_changed = true;
817
818 /* XXX: When there is an overlay on a DSI manual update display, and
819 * the overlay is first disabled, then moved to tv, and enabled, we
820 * seem to get SYNC_LOST_DIGIT error.
821 *
822 * Waiting doesn't seem to help, but updating the manual update display
823 * after disabling the overlay seems to fix this. This hints that the
824 * overlay is perhaps somehow tied to the LCD output until the output
825 * is updated.
826 *
827 * Userspace workaround for this is to update the LCD after disabling
828 * the overlay, but before moving the overlay to TV.
829 */
830
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200831 mutex_unlock(&apply_lock);
832
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200833 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200834err:
835 mutex_unlock(&apply_lock);
836 return r;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200837}
838
839int dss_ovl_unset_manager(struct omap_overlay *ovl)
840{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200841 int r;
842
843 mutex_lock(&apply_lock);
844
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200845 if (!ovl->manager) {
846 DSSERR("failed to detach overlay: manager not set\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200847 r = -EINVAL;
848 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200849 }
850
851 if (ovl->info.enabled) {
852 DSSERR("overlay has to be disabled to unset the manager\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200853 r = -EINVAL;
854 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200855 }
856
857 ovl->manager = NULL;
858 list_del(&ovl->list);
859 ovl->manager_changed = true;
860
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200861 mutex_unlock(&apply_lock);
862
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200863 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200864err:
865 mutex_unlock(&apply_lock);
866 return r;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200867}
868