blob: c086f72e488d7c6f6408a147d4f4c921fb06e4af [file] [log] [blame]
Rob Clarkcd5351f2011-11-12 12:09:40 -06001/*
Rob Clark8bb0daf2013-02-11 12:43:09 -05002 * drivers/gpu/drm/omapdrm/omap_crtc.c
Rob Clarkcd5351f2011-11-12 12:09:40 -06003 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
Laurent Pincharta42133a2015-01-17 19:09:26 +020020#include <linux/completion.h>
21
Rob Clarkcd5351f2011-11-12 12:09:40 -060022#include "omap_drv.h"
23
Andy Grossb9ed9f02012-10-16 00:17:40 -050024#include <drm/drm_mode.h>
Daniel Vetter3cb9ae42014-10-29 10:03:57 +010025#include <drm/drm_plane_helper.h>
Rob Clarkcd5351f2011-11-12 12:09:40 -060026#include "drm_crtc.h"
27#include "drm_crtc_helper.h"
28
29#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
30
Laurent Pinchart15d02e92015-01-25 22:42:30 +020031enum omap_page_flip_state {
32 OMAP_PAGE_FLIP_IDLE,
33 OMAP_PAGE_FLIP_WAIT,
34 OMAP_PAGE_FLIP_QUEUED,
35 OMAP_PAGE_FLIP_CANCELLED,
36};
37
Rob Clarkcd5351f2011-11-12 12:09:40 -060038struct omap_crtc {
39 struct drm_crtc base;
Rob Clarkf5f94542012-12-04 13:59:12 -060040
Rob Clarkbb5c2d92012-01-16 12:51:16 -060041 const char *name;
Rob Clarkf5f94542012-12-04 13:59:12 -060042 int pipe;
43 enum omap_channel channel;
44 struct omap_overlay_manager_info info;
Tomi Valkeinenc7aef122014-04-03 16:30:03 +030045 struct drm_encoder *current_encoder;
Rob Clarkf5f94542012-12-04 13:59:12 -060046
47 /*
48 * Temporary: eventually this will go away, but it is needed
49 * for now to keep the output's happy. (They only need
50 * mgr->id.) Eventually this will be replaced w/ something
51 * more common-panel-framework-y
52 */
Tomi Valkeinen04b1fc02013-05-14 10:55:19 +030053 struct omap_overlay_manager *mgr;
Rob Clarkf5f94542012-12-04 13:59:12 -060054
55 struct omap_video_timings timings;
56 bool enabled;
Rob Clarkf5f94542012-12-04 13:59:12 -060057
Laurent Pincharta42133a2015-01-17 19:09:26 +020058 struct omap_drm_irq vblank_irq;
Rob Clarkf5f94542012-12-04 13:59:12 -060059 struct omap_drm_irq error_irq;
60
Laurent Pincharta42133a2015-01-17 19:09:26 +020061 /* list of framebuffers to unpin */
62 struct list_head pending_unpins;
Rob Clarkcd5351f2011-11-12 12:09:40 -060063
Laurent Pinchart42fb61c2015-01-26 02:58:51 +020064 /*
Laurent Pinchart15d02e92015-01-25 22:42:30 +020065 * flip_state flag indicates the current page flap state: IDLE if no
66 * page queue has been submitted, WAIT when waiting for GEM async
67 * completion, QUEUED when the page flip has been queued to the hardware
68 * or CANCELLED when the CRTC is turned off before the flip gets queued
69 * to the hardware. The flip event, if any, is stored in flip_event. The
70 * flip_wait wait queue is used to wait for page flip completion.
Laurent Pinchart42fb61c2015-01-26 02:58:51 +020071 *
72 * The flip_work work queue handles page flip requests without caring
73 * about what context the GEM async callback is called from. Possibly we
74 * should just make omap_gem always call the cb from the worker so we
75 * don't have to care about this.
76 */
Laurent Pinchart15d02e92015-01-25 22:42:30 +020077 enum omap_page_flip_state flip_state;
Laurent Pinchart42fb61c2015-01-26 02:58:51 +020078 struct drm_pending_vblank_event *flip_event;
79 struct work_struct flip_work;
Rob Clarkf5f94542012-12-04 13:59:12 -060080
Laurent Pincharta42133a2015-01-17 19:09:26 +020081 struct completion completion;
82
Tomi Valkeinena36af732015-02-26 15:20:24 +020083 bool ignore_digit_sync_lost;
Rob Clarkcd5351f2011-11-12 12:09:40 -060084};
85
Laurent Pincharta42133a2015-01-17 19:09:26 +020086struct omap_framebuffer_unpin {
87 struct list_head list;
88 struct drm_framebuffer *fb;
89};
90
Laurent Pinchart971fb3e2015-01-18 01:12:59 +020091/* -----------------------------------------------------------------------------
92 * Helper Functions
93 */
94
Archit Taneja0d8f3712013-03-26 19:15:19 +053095uint32_t pipe2vbl(struct drm_crtc *crtc)
96{
97 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
98
99 return dispc_mgr_get_vsync_irq(omap_crtc->channel);
100}
101
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200102const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
103{
104 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
105 return &omap_crtc->timings;
106}
107
108enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
109{
110 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
111 return omap_crtc->channel;
112}
113
114/* -----------------------------------------------------------------------------
115 * DSS Manager Functions
116 */
117
Rob Clarkf5f94542012-12-04 13:59:12 -0600118/*
119 * Manager-ops, callbacks from output when they need to configure
120 * the upstream part of the video pipe.
121 *
122 * Most of these we can ignore until we add support for command-mode
123 * panels.. for video-mode the crtc-helpers already do an adequate
124 * job of sequencing the setup of the video pipe in the proper order
125 */
126
Tomi Valkeinen04b1fc02013-05-14 10:55:19 +0300127/* ovl-mgr-id -> crtc */
128static struct omap_crtc *omap_crtcs[8];
129
Rob Clarkf5f94542012-12-04 13:59:12 -0600130/* we can probably ignore these until we support command-mode panels: */
Tomi Valkeinena7e71e72013-05-08 16:23:32 +0300131static int omap_crtc_connect(struct omap_overlay_manager *mgr,
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300132 struct omap_dss_device *dst)
Tomi Valkeinena7e71e72013-05-08 16:23:32 +0300133{
134 if (mgr->output)
135 return -EINVAL;
136
137 if ((mgr->supported_outputs & dst->id) == 0)
138 return -EINVAL;
139
140 dst->manager = mgr;
141 mgr->output = dst;
142
143 return 0;
144}
145
146static void omap_crtc_disconnect(struct omap_overlay_manager *mgr,
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300147 struct omap_dss_device *dst)
Tomi Valkeinena7e71e72013-05-08 16:23:32 +0300148{
149 mgr->output->manager = NULL;
150 mgr->output = NULL;
151}
152
Rob Clarkf5f94542012-12-04 13:59:12 -0600153static void omap_crtc_start_update(struct omap_overlay_manager *mgr)
154{
155}
156
Laurent Pincharta42133a2015-01-17 19:09:26 +0200157/* Called only from omap_crtc_setup and suspend/resume handlers. */
Laurent Pinchart8472b572015-01-15 00:45:17 +0200158static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
159{
160 struct drm_device *dev = crtc->dev;
161 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
162 enum omap_channel channel = omap_crtc->channel;
163 struct omap_irq_wait *wait;
164 u32 framedone_irq, vsync_irq;
165 int ret;
166
167 if (dispc_mgr_is_enabled(channel) == enable)
168 return;
169
Tomi Valkeinenef422282015-02-26 15:20:25 +0200170 if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
171 /*
172 * Digit output produces some sync lost interrupts during the
173 * first frame when enabling, so we need to ignore those.
174 */
175 omap_crtc->ignore_digit_sync_lost = true;
176 }
Laurent Pinchart8472b572015-01-15 00:45:17 +0200177
178 framedone_irq = dispc_mgr_get_framedone_irq(channel);
179 vsync_irq = dispc_mgr_get_vsync_irq(channel);
180
181 if (enable) {
182 wait = omap_irq_wait_init(dev, vsync_irq, 1);
183 } else {
184 /*
185 * When we disable the digit output, we need to wait for
186 * FRAMEDONE to know that DISPC has finished with the output.
187 *
188 * OMAP2/3 does not have FRAMEDONE irq for digit output, and in
189 * that case we need to use vsync interrupt, and wait for both
190 * even and odd frames.
191 */
192
193 if (framedone_irq)
194 wait = omap_irq_wait_init(dev, framedone_irq, 1);
195 else
196 wait = omap_irq_wait_init(dev, vsync_irq, 2);
197 }
198
199 dispc_mgr_enable(channel, enable);
200
201 ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
202 if (ret) {
203 dev_err(dev->dev, "%s: timeout waiting for %s\n",
204 omap_crtc->name, enable ? "enable" : "disable");
205 }
206
Tomi Valkeinenef422282015-02-26 15:20:25 +0200207 if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
208 omap_crtc->ignore_digit_sync_lost = false;
209 /* make sure the irq handler sees the value above */
210 mb();
211 }
Laurent Pinchart8472b572015-01-15 00:45:17 +0200212}
213
Tomi Valkeinen506096a2014-04-03 13:11:54 +0300214
Rob Clarkf5f94542012-12-04 13:59:12 -0600215static int omap_crtc_enable(struct omap_overlay_manager *mgr)
216{
Tomi Valkeinen506096a2014-04-03 13:11:54 +0300217 struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
218
219 dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
220 dispc_mgr_set_timings(omap_crtc->channel,
221 &omap_crtc->timings);
Laurent Pinchart8472b572015-01-15 00:45:17 +0200222 omap_crtc_set_enabled(&omap_crtc->base, true);
Tomi Valkeinen506096a2014-04-03 13:11:54 +0300223
Rob Clarkf5f94542012-12-04 13:59:12 -0600224 return 0;
225}
226
227static void omap_crtc_disable(struct omap_overlay_manager *mgr)
228{
Tomi Valkeinen506096a2014-04-03 13:11:54 +0300229 struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
230
Laurent Pinchart8472b572015-01-15 00:45:17 +0200231 omap_crtc_set_enabled(&omap_crtc->base, false);
Rob Clarkf5f94542012-12-04 13:59:12 -0600232}
233
234static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
235 const struct omap_video_timings *timings)
236{
Tomi Valkeinen04b1fc02013-05-14 10:55:19 +0300237 struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
Rob Clarkf5f94542012-12-04 13:59:12 -0600238 DBG("%s", omap_crtc->name);
239 omap_crtc->timings = *timings;
Rob Clarkf5f94542012-12-04 13:59:12 -0600240}
241
242static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr,
243 const struct dss_lcd_mgr_config *config)
244{
Tomi Valkeinen04b1fc02013-05-14 10:55:19 +0300245 struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
Rob Clarkf5f94542012-12-04 13:59:12 -0600246 DBG("%s", omap_crtc->name);
247 dispc_mgr_set_lcd_config(omap_crtc->channel, config);
248}
249
250static int omap_crtc_register_framedone_handler(
251 struct omap_overlay_manager *mgr,
252 void (*handler)(void *), void *data)
253{
254 return 0;
255}
256
257static void omap_crtc_unregister_framedone_handler(
258 struct omap_overlay_manager *mgr,
259 void (*handler)(void *), void *data)
260{
261}
262
263static const struct dss_mgr_ops mgr_ops = {
Laurent Pinchart222025e2015-01-11 00:02:07 +0200264 .connect = omap_crtc_connect,
265 .disconnect = omap_crtc_disconnect,
266 .start_update = omap_crtc_start_update,
267 .enable = omap_crtc_enable,
268 .disable = omap_crtc_disable,
269 .set_timings = omap_crtc_set_timings,
270 .set_lcd_config = omap_crtc_set_lcd_config,
271 .register_framedone_handler = omap_crtc_register_framedone_handler,
272 .unregister_framedone_handler = omap_crtc_unregister_framedone_handler,
Rob Clarkf5f94542012-12-04 13:59:12 -0600273};
274
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200275/* -----------------------------------------------------------------------------
Laurent Pinchart1d5e5ea2015-01-18 16:57:36 +0200276 * Setup, Flush and Page Flip
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200277 */
278
Laurent Pinchart1d5e5ea2015-01-18 16:57:36 +0200279void omap_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
280{
281 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
282 struct drm_device *dev = crtc->dev;
283 unsigned long flags;
284
285 spin_lock_irqsave(&dev->event_lock, flags);
286
287 /* Only complete events queued for our file handle. */
288 if (omap_crtc->flip_event &&
289 file == omap_crtc->flip_event->base.file_priv) {
290 drm_send_vblank_event(dev, omap_crtc->pipe,
291 omap_crtc->flip_event);
292 omap_crtc->flip_event = NULL;
293 }
294
295 spin_unlock_irqrestore(&dev->event_lock, flags);
296}
297
Laurent Pinchart15d02e92015-01-25 22:42:30 +0200298/* Must be called with dev->event_lock locked. */
299static void omap_crtc_complete_page_flip(struct drm_crtc *crtc,
300 enum omap_page_flip_state state)
301{
302 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
303 struct drm_device *dev = crtc->dev;
304
305 if (omap_crtc->flip_event) {
306 drm_send_vblank_event(dev, omap_crtc->pipe,
307 omap_crtc->flip_event);
308 omap_crtc->flip_event = NULL;
309 }
310
311 omap_crtc->flip_state = state;
312}
313
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200314static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
315{
316 struct omap_crtc *omap_crtc =
317 container_of(irq, struct omap_crtc, error_irq);
Tomi Valkeinena36af732015-02-26 15:20:24 +0200318
319 if (omap_crtc->ignore_digit_sync_lost) {
320 irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
321 if (!irqstatus)
322 return;
323 }
324
Tomi Valkeinen3b143fc2014-11-19 12:50:13 +0200325 DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus);
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200326}
327
Laurent Pincharta42133a2015-01-17 19:09:26 +0200328static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200329{
330 struct omap_crtc *omap_crtc =
Laurent Pincharta42133a2015-01-17 19:09:26 +0200331 container_of(irq, struct omap_crtc, vblank_irq);
332 struct drm_device *dev = omap_crtc->base.dev;
333 unsigned long flags;
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200334
Laurent Pincharta42133a2015-01-17 19:09:26 +0200335 if (dispc_mgr_go_busy(omap_crtc->channel))
336 return;
337
338 DBG("%s: apply done", omap_crtc->name);
339 __omap_irq_unregister(dev, &omap_crtc->vblank_irq);
340
Laurent Pincharta42133a2015-01-17 19:09:26 +0200341 /* wakeup userspace */
Laurent Pinchart15d02e92015-01-25 22:42:30 +0200342 spin_lock_irqsave(&dev->event_lock, flags);
343 omap_crtc_complete_page_flip(&omap_crtc->base, OMAP_PAGE_FLIP_IDLE);
Laurent Pincharta42133a2015-01-17 19:09:26 +0200344 spin_unlock_irqrestore(&dev->event_lock, flags);
345
346 complete(&omap_crtc->completion);
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200347}
348
Laurent Pincharta42133a2015-01-17 19:09:26 +0200349int omap_crtc_flush(struct drm_crtc *crtc)
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200350{
351 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
Laurent Pincharta42133a2015-01-17 19:09:26 +0200352 struct omap_framebuffer_unpin *fb, *next;
353
354 DBG("%s: GO", omap_crtc->name);
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200355
356 WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
Laurent Pincharta42133a2015-01-17 19:09:26 +0200357 WARN_ON(omap_crtc->vblank_irq.registered);
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200358
Laurent Pincharta42133a2015-01-17 19:09:26 +0200359 dispc_runtime_get();
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200360
Laurent Pincharta42133a2015-01-17 19:09:26 +0200361 if (dispc_mgr_is_enabled(omap_crtc->channel)) {
362 dispc_mgr_go(omap_crtc->channel);
363 omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200364
Laurent Pincharta42133a2015-01-17 19:09:26 +0200365 WARN_ON(!wait_for_completion_timeout(&omap_crtc->completion,
366 msecs_to_jiffies(100)));
367 reinit_completion(&omap_crtc->completion);
368 }
369
370 dispc_runtime_put();
371
372 /* Unpin and unreference pending framebuffers. */
373 list_for_each_entry_safe(fb, next, &omap_crtc->pending_unpins, list) {
374 omap_framebuffer_unpin(fb->fb);
375 drm_framebuffer_unreference(fb->fb);
376 list_del(&fb->list);
377 kfree(fb);
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200378 }
379
380 return 0;
381}
382
Laurent Pincharta42133a2015-01-17 19:09:26 +0200383int omap_crtc_queue_unpin(struct drm_crtc *crtc, struct drm_framebuffer *fb)
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200384{
Laurent Pincharta42133a2015-01-17 19:09:26 +0200385 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
386 struct omap_framebuffer_unpin *unpin;
387
388 unpin = kzalloc(sizeof(*unpin), GFP_KERNEL);
389 if (!unpin)
390 return -ENOMEM;
391
392 unpin->fb = fb;
393 list_add_tail(&unpin->list, &omap_crtc->pending_unpins);
394
395 return 0;
396}
397
398static void omap_crtc_setup(struct drm_crtc *crtc)
399{
400 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200401 struct omap_drm_private *priv = crtc->dev->dev_private;
402 struct drm_encoder *encoder = NULL;
403 unsigned int i;
404
405 DBG("%s: enabled=%d", omap_crtc->name, omap_crtc->enabled);
406
Laurent Pincharta42133a2015-01-17 19:09:26 +0200407 dispc_runtime_get();
408
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200409 for (i = 0; i < priv->num_encoders; i++) {
410 if (priv->encoders[i]->crtc == crtc) {
411 encoder = priv->encoders[i];
412 break;
413 }
414 }
415
416 if (omap_crtc->current_encoder && encoder != omap_crtc->current_encoder)
417 omap_encoder_set_enabled(omap_crtc->current_encoder, false);
418
419 omap_crtc->current_encoder = encoder;
420
421 if (!omap_crtc->enabled) {
422 if (encoder)
423 omap_encoder_set_enabled(encoder, false);
424 } else {
425 if (encoder) {
426 omap_encoder_set_enabled(encoder, false);
427 omap_encoder_update(encoder, omap_crtc->mgr,
428 &omap_crtc->timings);
429 omap_encoder_set_enabled(encoder, true);
430 }
431 }
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200432
Laurent Pincharta42133a2015-01-17 19:09:26 +0200433 dispc_runtime_put();
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200434}
435
436/* -----------------------------------------------------------------------------
437 * CRTC Functions
Rob Clarkf5f94542012-12-04 13:59:12 -0600438 */
439
Rob Clarkcd5351f2011-11-12 12:09:40 -0600440static void omap_crtc_destroy(struct drm_crtc *crtc)
441{
442 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
Rob Clarkf5f94542012-12-04 13:59:12 -0600443
444 DBG("%s", omap_crtc->name);
445
Laurent Pincharta42133a2015-01-17 19:09:26 +0200446 WARN_ON(omap_crtc->vblank_irq.registered);
Rob Clarkf5f94542012-12-04 13:59:12 -0600447 omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
448
Rob Clarkcd5351f2011-11-12 12:09:40 -0600449 drm_crtc_cleanup(crtc);
Rob Clarkf5f94542012-12-04 13:59:12 -0600450
Rob Clarkcd5351f2011-11-12 12:09:40 -0600451 kfree(omap_crtc);
452}
453
454static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
455{
Rob Clarkbb5c2d92012-01-16 12:51:16 -0600456 struct omap_drm_private *priv = crtc->dev->dev_private;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600457 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
Rob Clarkf5f94542012-12-04 13:59:12 -0600458 bool enabled = (mode == DRM_MODE_DPMS_ON);
Rob Clarkbb5c2d92012-01-16 12:51:16 -0600459 int i;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600460
Rob Clarkf5f94542012-12-04 13:59:12 -0600461 DBG("%s: %d", omap_crtc->name, mode);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600462
Laurent Pincharta42133a2015-01-17 19:09:26 +0200463 if (enabled == omap_crtc->enabled)
464 return;
Rob Clarkf5f94542012-12-04 13:59:12 -0600465
Laurent Pincharta42133a2015-01-17 19:09:26 +0200466 /* Enable/disable all planes associated with the CRTC. */
467 for (i = 0; i < priv->num_planes; i++) {
468 struct drm_plane *plane = priv->planes[i];
469
470 if (plane->crtc == crtc)
471 WARN_ON(omap_plane_set_enable(plane, enabled));
Rob Clarkcd5351f2011-11-12 12:09:40 -0600472 }
Laurent Pincharta42133a2015-01-17 19:09:26 +0200473
474 omap_crtc->enabled = enabled;
475
476 omap_crtc_setup(crtc);
477 omap_crtc_flush(crtc);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600478}
479
480static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
Laurent Pincharte811f5a2012-07-17 17:56:50 +0200481 const struct drm_display_mode *mode,
Rob Clarkbb5c2d92012-01-16 12:51:16 -0600482 struct drm_display_mode *adjusted_mode)
Rob Clarkcd5351f2011-11-12 12:09:40 -0600483{
Rob Clarkcd5351f2011-11-12 12:09:40 -0600484 return true;
485}
486
487static int omap_crtc_mode_set(struct drm_crtc *crtc,
Rob Clarkbb5c2d92012-01-16 12:51:16 -0600488 struct drm_display_mode *mode,
489 struct drm_display_mode *adjusted_mode,
490 int x, int y,
491 struct drm_framebuffer *old_fb)
Rob Clarkcd5351f2011-11-12 12:09:40 -0600492{
493 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
494
Rob Clarkf5f94542012-12-04 13:59:12 -0600495 mode = adjusted_mode;
496
497 DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
498 omap_crtc->name, mode->base.id, mode->name,
499 mode->vrefresh, mode->clock,
500 mode->hdisplay, mode->hsync_start,
501 mode->hsync_end, mode->htotal,
502 mode->vdisplay, mode->vsync_start,
503 mode->vsync_end, mode->vtotal,
504 mode->type, mode->flags);
505
506 copy_timings_drm_to_omap(&omap_crtc->timings, mode);
Rob Clarkf5f94542012-12-04 13:59:12 -0600507
Laurent Pinchartef6b0e02015-01-11 00:11:18 +0200508 /*
509 * The primary plane CRTC can be reset if the plane is disabled directly
510 * through the universal plane API. Set it again here.
511 */
512 crtc->primary->crtc = crtc;
513
514 return omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb,
Laurent Pincharta350da82015-01-17 22:31:42 +0200515 0, 0, mode->hdisplay, mode->vdisplay,
Laurent Pincharta42133a2015-01-17 19:09:26 +0200516 x, y, mode->hdisplay, mode->vdisplay);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600517}
518
519static void omap_crtc_prepare(struct drm_crtc *crtc)
520{
521 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
Rob Clarkbb5c2d92012-01-16 12:51:16 -0600522 DBG("%s", omap_crtc->name);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600523 omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
524}
525
526static void omap_crtc_commit(struct drm_crtc *crtc)
527{
528 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
Rob Clarkbb5c2d92012-01-16 12:51:16 -0600529 DBG("%s", omap_crtc->name);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600530 omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
531}
532
533static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
Rob Clarkbb5c2d92012-01-16 12:51:16 -0600534 struct drm_framebuffer *old_fb)
Rob Clarkcd5351f2011-11-12 12:09:40 -0600535{
Laurent Pinchartef6b0e02015-01-11 00:11:18 +0200536 struct drm_plane *plane = crtc->primary;
Rob Clarkbb5c2d92012-01-16 12:51:16 -0600537 struct drm_display_mode *mode = &crtc->mode;
Laurent Pincharta42133a2015-01-17 19:09:26 +0200538 int ret;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600539
Laurent Pincharta42133a2015-01-17 19:09:26 +0200540 ret = omap_plane_mode_set(plane, crtc, crtc->primary->fb,
541 0, 0, mode->hdisplay, mode->vdisplay,
542 x, y, mode->hdisplay, mode->vdisplay);
543 if (ret < 0)
544 return ret;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600545
Laurent Pincharta42133a2015-01-17 19:09:26 +0200546 return omap_crtc_flush(crtc);
Rob Clarkf5f94542012-12-04 13:59:12 -0600547}
548
549static void page_flip_worker(struct work_struct *work)
550{
551 struct omap_crtc *omap_crtc =
Laurent Pinchart42fb61c2015-01-26 02:58:51 +0200552 container_of(work, struct omap_crtc, flip_work);
Rob Clarkf5f94542012-12-04 13:59:12 -0600553 struct drm_crtc *crtc = &omap_crtc->base;
Rob Clarkf5f94542012-12-04 13:59:12 -0600554 struct drm_display_mode *mode = &crtc->mode;
Laurent Pinchart15d02e92015-01-25 22:42:30 +0200555 struct drm_device *dev = crtc->dev;
556 struct drm_framebuffer *fb;
Rob Clarkf5f94542012-12-04 13:59:12 -0600557 struct drm_gem_object *bo;
Laurent Pinchart15d02e92015-01-25 22:42:30 +0200558 unsigned long flags;
559 bool queue_flip;
Rob Clarkf5f94542012-12-04 13:59:12 -0600560
Rob Clark51fd3712013-11-19 12:10:12 -0500561 drm_modeset_lock(&crtc->mutex, NULL);
Laurent Pinchart15d02e92015-01-25 22:42:30 +0200562
563 spin_lock_irqsave(&dev->event_lock, flags);
564 /*
565 * The page flip could have been cancelled while waiting for the GEM
566 * async operation to complete. Don't queue the flip in that case.
567 */
568 if (omap_crtc->flip_state == OMAP_PAGE_FLIP_WAIT) {
569 omap_crtc->flip_state = OMAP_PAGE_FLIP_QUEUED;
570 queue_flip = true;
571 } else {
572 omap_crtc->flip_state = OMAP_PAGE_FLIP_IDLE;
573 queue_flip = false;
574 }
575 spin_unlock_irqrestore(&dev->event_lock, flags);
576
577 fb = crtc->primary->fb;
578
579 if (queue_flip) {
580 omap_plane_mode_set(crtc->primary, crtc, fb,
581 0, 0, mode->hdisplay, mode->vdisplay,
582 crtc->x, crtc->y, mode->hdisplay,
583 mode->vdisplay);
584 omap_crtc_flush(crtc);
585 }
586
Rob Clark51fd3712013-11-19 12:10:12 -0500587 drm_modeset_unlock(&crtc->mutex);
Rob Clarkf5f94542012-12-04 13:59:12 -0600588
Laurent Pinchart15d02e92015-01-25 22:42:30 +0200589 bo = omap_framebuffer_bo(fb, 0);
Rob Clarkf5f94542012-12-04 13:59:12 -0600590 drm_gem_object_unreference_unlocked(bo);
Laurent Pincharta42133a2015-01-17 19:09:26 +0200591 drm_framebuffer_unreference(crtc->primary->fb);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600592}
593
Rob Clark72d0c332012-03-11 21:11:21 -0500594static void page_flip_cb(void *arg)
595{
596 struct drm_crtc *crtc = arg;
597 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
Rob Clarkf5f94542012-12-04 13:59:12 -0600598 struct omap_drm_private *priv = crtc->dev->dev_private;
Rob Clark72d0c332012-03-11 21:11:21 -0500599
Rob Clarkf5f94542012-12-04 13:59:12 -0600600 /* avoid assumptions about what ctxt we are called from: */
Laurent Pinchart42fb61c2015-01-26 02:58:51 +0200601 queue_work(priv->wq, &omap_crtc->flip_work);
Rob Clark72d0c332012-03-11 21:11:21 -0500602}
603
Laurent Pinchart077db4d2015-01-18 16:36:19 +0200604static int omap_crtc_page_flip(struct drm_crtc *crtc,
605 struct drm_framebuffer *fb,
606 struct drm_pending_vblank_event *event,
607 uint32_t page_flip_flags)
Rob Clarkcd5351f2011-11-12 12:09:40 -0600608{
609 struct drm_device *dev = crtc->dev;
610 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
Matt Roperf4510a22014-04-01 15:22:40 -0700611 struct drm_plane *primary = crtc->primary;
Rob Clark119c0812012-09-04 17:46:22 -0500612 struct drm_gem_object *bo;
Archit Taneja38e55972014-04-11 12:53:35 +0530613 unsigned long flags;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600614
Matt Roperf4510a22014-04-01 15:22:40 -0700615 DBG("%d -> %d (event=%p)", primary->fb ? primary->fb->base.id : -1,
Rob Clarkf5f94542012-12-04 13:59:12 -0600616 fb->base.id, event);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600617
Archit Taneja38e55972014-04-11 12:53:35 +0530618 spin_lock_irqsave(&dev->event_lock, flags);
619
Laurent Pinchart15d02e92015-01-25 22:42:30 +0200620 if (omap_crtc->flip_state != OMAP_PAGE_FLIP_IDLE) {
Archit Taneja38e55972014-04-11 12:53:35 +0530621 spin_unlock_irqrestore(&dev->event_lock, flags);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600622 dev_err(dev->dev, "already a pending flip\n");
Tomi Valkeinen549a7542014-09-03 19:25:50 +0000623 return -EBUSY;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600624 }
625
Laurent Pinchart42fb61c2015-01-26 02:58:51 +0200626 omap_crtc->flip_event = event;
Laurent Pinchart15d02e92015-01-25 22:42:30 +0200627 omap_crtc->flip_state = OMAP_PAGE_FLIP_WAIT;
Laurent Pinchart42fb61c2015-01-26 02:58:51 +0200628 primary->fb = fb;
629 drm_framebuffer_reference(fb);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600630
Archit Taneja38e55972014-04-11 12:53:35 +0530631 spin_unlock_irqrestore(&dev->event_lock, flags);
632
Rob Clark119c0812012-09-04 17:46:22 -0500633 /*
634 * Hold a reference temporarily until the crtc is updated
635 * and takes the reference to the bo. This avoids it
636 * getting freed from under us:
637 */
638 bo = omap_framebuffer_bo(fb, 0);
639 drm_gem_object_reference(bo);
640
641 omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600642
643 return 0;
644}
645
Rob Clark3c810c62012-08-15 15:18:01 -0500646static int omap_crtc_set_property(struct drm_crtc *crtc,
647 struct drm_property *property, uint64_t val)
648{
Laurent Pincharte2cd09b2015-03-06 17:16:43 +0200649 if (property == crtc->dev->mode_config.rotation_property) {
Rob Clark1e0fdfc2012-09-04 11:36:20 -0500650 crtc->invert_dimensions =
651 !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
652 }
653
Laurent Pinchartef6b0e02015-01-11 00:11:18 +0200654 return omap_plane_set_property(crtc->primary, property, val);
Rob Clark3c810c62012-08-15 15:18:01 -0500655}
656
Rob Clarkcd5351f2011-11-12 12:09:40 -0600657static const struct drm_crtc_funcs omap_crtc_funcs = {
Rob Clarkcd5351f2011-11-12 12:09:40 -0600658 .set_config = drm_crtc_helper_set_config,
659 .destroy = omap_crtc_destroy,
Laurent Pinchart077db4d2015-01-18 16:36:19 +0200660 .page_flip = omap_crtc_page_flip,
Rob Clark3c810c62012-08-15 15:18:01 -0500661 .set_property = omap_crtc_set_property,
Rob Clarkcd5351f2011-11-12 12:09:40 -0600662};
663
664static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
665 .dpms = omap_crtc_dpms,
666 .mode_fixup = omap_crtc_mode_fixup,
667 .mode_set = omap_crtc_mode_set,
668 .prepare = omap_crtc_prepare,
669 .commit = omap_crtc_commit,
670 .mode_set_base = omap_crtc_mode_set_base,
Rob Clarkcd5351f2011-11-12 12:09:40 -0600671};
672
Laurent Pinchart971fb3e2015-01-18 01:12:59 +0200673/* -----------------------------------------------------------------------------
674 * Init and Cleanup
675 */
Tomi Valkeinene2f8fd72014-04-02 14:31:57 +0300676
Rob Clarkf5f94542012-12-04 13:59:12 -0600677static const char *channel_names[] = {
Laurent Pinchart222025e2015-01-11 00:02:07 +0200678 [OMAP_DSS_CHANNEL_LCD] = "lcd",
679 [OMAP_DSS_CHANNEL_DIGIT] = "tv",
680 [OMAP_DSS_CHANNEL_LCD2] = "lcd2",
681 [OMAP_DSS_CHANNEL_LCD3] = "lcd3",
Rob Clarkf5f94542012-12-04 13:59:12 -0600682};
683
Tomi Valkeinen04b1fc02013-05-14 10:55:19 +0300684void omap_crtc_pre_init(void)
685{
686 dss_install_mgr_ops(&mgr_ops);
687}
688
Archit Taneja3a01ab22014-01-02 14:49:51 +0530689void omap_crtc_pre_uninit(void)
690{
691 dss_uninstall_mgr_ops();
692}
693
Rob Clarkcd5351f2011-11-12 12:09:40 -0600694/* initialize crtc */
695struct drm_crtc *omap_crtc_init(struct drm_device *dev,
Rob Clarkf5f94542012-12-04 13:59:12 -0600696 struct drm_plane *plane, enum omap_channel channel, int id)
Rob Clarkcd5351f2011-11-12 12:09:40 -0600697{
698 struct drm_crtc *crtc = NULL;
Rob Clarkf5f94542012-12-04 13:59:12 -0600699 struct omap_crtc *omap_crtc;
700 struct omap_overlay_manager_info *info;
Laurent Pinchartef6b0e02015-01-11 00:11:18 +0200701 int ret;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600702
Rob Clarkf5f94542012-12-04 13:59:12 -0600703 DBG("%s", channel_names[channel]);
704
705 omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
Joe Perches78110bb2013-02-11 09:41:29 -0800706 if (!omap_crtc)
Laurent Pinchartef6b0e02015-01-11 00:11:18 +0200707 return NULL;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600708
Rob Clarkcd5351f2011-11-12 12:09:40 -0600709 crtc = &omap_crtc->base;
Rob Clarkbb5c2d92012-01-16 12:51:16 -0600710
Laurent Pinchart42fb61c2015-01-26 02:58:51 +0200711 INIT_WORK(&omap_crtc->flip_work, page_flip_worker);
Rob Clarkf5f94542012-12-04 13:59:12 -0600712
Laurent Pincharta42133a2015-01-17 19:09:26 +0200713 INIT_LIST_HEAD(&omap_crtc->pending_unpins);
Rob Clarkf5f94542012-12-04 13:59:12 -0600714
Laurent Pincharta42133a2015-01-17 19:09:26 +0200715 init_completion(&omap_crtc->completion);
Rob Clarkf5f94542012-12-04 13:59:12 -0600716
Archit Taneja0d8f3712013-03-26 19:15:19 +0530717 omap_crtc->channel = channel;
Archit Taneja0d8f3712013-03-26 19:15:19 +0530718 omap_crtc->name = channel_names[channel];
719 omap_crtc->pipe = id;
720
Laurent Pincharta42133a2015-01-17 19:09:26 +0200721 omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc);
722 omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq;
Rob Clarkf5f94542012-12-04 13:59:12 -0600723
724 omap_crtc->error_irq.irqmask =
725 dispc_mgr_get_sync_lost_irq(channel);
726 omap_crtc->error_irq.irq = omap_crtc_error_irq;
727 omap_irq_register(dev, &omap_crtc->error_irq);
728
Rob Clarkf5f94542012-12-04 13:59:12 -0600729 /* temporary: */
Tomi Valkeinen04b1fc02013-05-14 10:55:19 +0300730 omap_crtc->mgr = omap_dss_get_overlay_manager(channel);
Rob Clarkf5f94542012-12-04 13:59:12 -0600731
732 /* TODO: fix hard-coded setup.. add properties! */
733 info = &omap_crtc->info;
734 info->default_color = 0x00000000;
735 info->trans_key = 0x00000000;
736 info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
737 info->trans_enabled = false;
Rob Clarkbb5c2d92012-01-16 12:51:16 -0600738
Laurent Pinchartef6b0e02015-01-11 00:11:18 +0200739 ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
740 &omap_crtc_funcs);
741 if (ret < 0) {
742 kfree(omap_crtc);
743 return NULL;
744 }
745
Rob Clarkcd5351f2011-11-12 12:09:40 -0600746 drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
747
Laurent Pinchartef6b0e02015-01-11 00:11:18 +0200748 omap_plane_install_properties(crtc->primary, &crtc->base);
Rob Clark3c810c62012-08-15 15:18:01 -0500749
Tomi Valkeinen04b1fc02013-05-14 10:55:19 +0300750 omap_crtcs[channel] = omap_crtc;
751
Rob Clarkcd5351f2011-11-12 12:09:40 -0600752 return crtc;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600753}