Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
cd5351f4 | 2 | /* |
bb5cdf8d | 3 | * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ |
cd5351f4 | 4 | * Author: Rob Clark <rob@ti.com> |
cd5351f4 RC |
5 | */ |
6 | ||
81f6156c SR |
7 | #include <linux/math64.h> |
8 | ||
69a12263 LP |
9 | #include <drm/drm_atomic.h> |
10 | #include <drm/drm_atomic_helper.h> | |
2d278f54 | 11 | #include <drm/drm_crtc.h> |
b9ed9f0e | 12 | #include <drm/drm_mode.h> |
3cb9ae4f | 13 | #include <drm/drm_plane_helper.h> |
81f6156c | 14 | #include <drm/drm_vblank.h> |
2d278f54 LP |
15 | |
16 | #include "omap_drv.h" | |
cd5351f4 | 17 | |
3dfeb631 ML |
18 | #define to_omap_crtc_state(x) container_of(x, struct omap_crtc_state, base) |
19 | ||
20 | struct omap_crtc_state { | |
21 | /* Must be first. */ | |
22 | struct drm_crtc_state base; | |
23 | /* Shadow values for legacy userspace support. */ | |
24 | unsigned int rotation; | |
25 | unsigned int zpos; | |
1bb418bf | 26 | bool manually_updated; |
3dfeb631 ML |
27 | }; |
28 | ||
cd5351f4 RC |
29 | #define to_omap_crtc(x) container_of(x, struct omap_crtc, base) |
30 | ||
31 | struct omap_crtc { | |
32 | struct drm_crtc base; | |
f5f9454c | 33 | |
bb5c2d9a | 34 | const char *name; |
67dfd2d3 | 35 | struct omap_drm_pipeline *pipe; |
f5f9454c | 36 | enum omap_channel channel; |
f5f9454c | 37 | |
da11bbbb | 38 | struct videomode vm; |
f5f9454c | 39 | |
a36af73f | 40 | bool ignore_digit_sync_lost; |
5f741b39 | 41 | |
f933a3a9 | 42 | bool enabled; |
5f741b39 TV |
43 | bool pending; |
44 | wait_queue_head_t pending_wait; | |
577d3983 | 45 | struct drm_pending_vblank_event *event; |
1bb418bf | 46 | struct delayed_work update_work; |
47103a80 SR |
47 | |
48 | void (*framedone_handler)(void *); | |
49 | void *framedone_handler_data; | |
f5f9454c RC |
50 | }; |
51 | ||
971fb3e5 LP |
52 | /* ----------------------------------------------------------------------------- |
53 | * Helper Functions | |
54 | */ | |
55 | ||
4520ff28 | 56 | struct videomode *omap_crtc_timings(struct drm_crtc *crtc) |
971fb3e5 LP |
57 | { |
58 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
da11bbbb | 59 | return &omap_crtc->vm; |
971fb3e5 LP |
60 | } |
61 | ||
62 | enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) | |
63 | { | |
64 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
65 | return omap_crtc->channel; | |
66 | } | |
67 | ||
d173d3dc LP |
68 | static bool omap_crtc_is_pending(struct drm_crtc *crtc) |
69 | { | |
70 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
71 | unsigned long flags; | |
72 | bool pending; | |
73 | ||
74 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | |
75 | pending = omap_crtc->pending; | |
76 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | |
77 | ||
78 | return pending; | |
79 | } | |
80 | ||
5f741b39 TV |
81 | int omap_crtc_wait_pending(struct drm_crtc *crtc) |
82 | { | |
83 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
84 | ||
61f3c40b TV |
85 | /* |
86 | * Timeout is set to a "sufficiently" high value, which should cover | |
87 | * a single frame refresh even on slower displays. | |
88 | */ | |
5f741b39 | 89 | return wait_event_timeout(omap_crtc->pending_wait, |
d173d3dc | 90 | !omap_crtc_is_pending(crtc), |
61f3c40b | 91 | msecs_to_jiffies(250)); |
5f741b39 TV |
92 | } |
93 | ||
971fb3e5 LP |
94 | /* ----------------------------------------------------------------------------- |
95 | * DSS Manager Functions | |
96 | */ | |
97 | ||
f5f9454c RC |
98 | /* |
99 | * Manager-ops, callbacks from output when they need to configure | |
100 | * the upstream part of the video pipe. | |
f5f9454c RC |
101 | */ |
102 | ||
64cb8179 LP |
103 | static void omap_crtc_dss_start_update(struct omap_drm_private *priv, |
104 | enum omap_channel channel) | |
f5f9454c | 105 | { |
1bb418bf | 106 | priv->dispc_ops->mgr_enable(priv->dispc, channel, true); |
f5f9454c RC |
107 | } |
108 | ||
4029755e | 109 | /* Called only from the encoder enable/disable and suspend/resume handlers. */ |
8472b570 LP |
110 | static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) |
111 | { | |
1bb418bf | 112 | struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); |
8472b570 | 113 | struct drm_device *dev = crtc->dev; |
9f759225 | 114 | struct omap_drm_private *priv = dev->dev_private; |
8472b570 LP |
115 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
116 | enum omap_channel channel = omap_crtc->channel; | |
117 | struct omap_irq_wait *wait; | |
118 | u32 framedone_irq, vsync_irq; | |
119 | int ret; | |
120 | ||
03af8157 LP |
121 | if (WARN_ON(omap_crtc->enabled == enable)) |
122 | return; | |
123 | ||
1bb418bf SR |
124 | if (omap_state->manually_updated) { |
125 | omap_irq_enable_framedone(crtc, enable); | |
126 | omap_crtc->enabled = enable; | |
127 | return; | |
128 | } | |
129 | ||
0dbfc396 | 130 | if (omap_crtc->pipe->output->type == OMAP_DISPLAY_TYPE_HDMI) { |
50638ae5 | 131 | priv->dispc_ops->mgr_enable(priv->dispc, channel, enable); |
f933a3a9 | 132 | omap_crtc->enabled = enable; |
4e4b53ce TV |
133 | return; |
134 | } | |
135 | ||
ef422283 TV |
136 | if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { |
137 | /* | |
138 | * Digit output produces some sync lost interrupts during the | |
139 | * first frame when enabling, so we need to ignore those. | |
140 | */ | |
141 | omap_crtc->ignore_digit_sync_lost = true; | |
142 | } | |
8472b570 | 143 | |
50638ae5 LP |
144 | framedone_irq = priv->dispc_ops->mgr_get_framedone_irq(priv->dispc, |
145 | channel); | |
146 | vsync_irq = priv->dispc_ops->mgr_get_vsync_irq(priv->dispc, channel); | |
8472b570 LP |
147 | |
148 | if (enable) { | |
149 | wait = omap_irq_wait_init(dev, vsync_irq, 1); | |
150 | } else { | |
151 | /* | |
152 | * When we disable the digit output, we need to wait for | |
153 | * FRAMEDONE to know that DISPC has finished with the output. | |
154 | * | |
155 | * OMAP2/3 does not have FRAMEDONE irq for digit output, and in | |
156 | * that case we need to use vsync interrupt, and wait for both | |
157 | * even and odd frames. | |
158 | */ | |
159 | ||
160 | if (framedone_irq) | |
161 | wait = omap_irq_wait_init(dev, framedone_irq, 1); | |
162 | else | |
163 | wait = omap_irq_wait_init(dev, vsync_irq, 2); | |
164 | } | |
165 | ||
50638ae5 | 166 | priv->dispc_ops->mgr_enable(priv->dispc, channel, enable); |
f933a3a9 | 167 | omap_crtc->enabled = enable; |
8472b570 LP |
168 | |
169 | ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); | |
170 | if (ret) { | |
171 | dev_err(dev->dev, "%s: timeout waiting for %s\n", | |
172 | omap_crtc->name, enable ? "enable" : "disable"); | |
173 | } | |
174 | ||
ef422283 TV |
175 | if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { |
176 | omap_crtc->ignore_digit_sync_lost = false; | |
177 | /* make sure the irq handler sees the value above */ | |
178 | mb(); | |
179 | } | |
8472b570 LP |
180 | } |
181 | ||
506096a1 | 182 | |
64cb8179 LP |
183 | static int omap_crtc_dss_enable(struct omap_drm_private *priv, |
184 | enum omap_channel channel) | |
f5f9454c | 185 | { |
e48f9f16 LP |
186 | struct drm_crtc *crtc = priv->channels[channel]->crtc; |
187 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
506096a1 | 188 | |
50638ae5 LP |
189 | priv->dispc_ops->mgr_set_timings(priv->dispc, omap_crtc->channel, |
190 | &omap_crtc->vm); | |
8472b570 | 191 | omap_crtc_set_enabled(&omap_crtc->base, true); |
506096a1 | 192 | |
f5f9454c RC |
193 | return 0; |
194 | } | |
195 | ||
64cb8179 LP |
196 | static void omap_crtc_dss_disable(struct omap_drm_private *priv, |
197 | enum omap_channel channel) | |
f5f9454c | 198 | { |
e48f9f16 LP |
199 | struct drm_crtc *crtc = priv->channels[channel]->crtc; |
200 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
506096a1 | 201 | |
8472b570 | 202 | omap_crtc_set_enabled(&omap_crtc->base, false); |
f5f9454c RC |
203 | } |
204 | ||
64cb8179 LP |
205 | static void omap_crtc_dss_set_timings(struct omap_drm_private *priv, |
206 | enum omap_channel channel, | |
da11bbbb | 207 | const struct videomode *vm) |
f5f9454c | 208 | { |
e48f9f16 LP |
209 | struct drm_crtc *crtc = priv->channels[channel]->crtc; |
210 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
211 | ||
f5f9454c | 212 | DBG("%s", omap_crtc->name); |
da11bbbb | 213 | omap_crtc->vm = *vm; |
f5f9454c RC |
214 | } |
215 | ||
64cb8179 LP |
216 | static void omap_crtc_dss_set_lcd_config(struct omap_drm_private *priv, |
217 | enum omap_channel channel, | |
f5f9454c RC |
218 | const struct dss_lcd_mgr_config *config) |
219 | { | |
e48f9f16 LP |
220 | struct drm_crtc *crtc = priv->channels[channel]->crtc; |
221 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
9f759225 | 222 | |
f5f9454c | 223 | DBG("%s", omap_crtc->name); |
50638ae5 LP |
224 | priv->dispc_ops->mgr_set_lcd_config(priv->dispc, omap_crtc->channel, |
225 | config); | |
f5f9454c RC |
226 | } |
227 | ||
4343f0f8 | 228 | static int omap_crtc_dss_register_framedone( |
64cb8179 | 229 | struct omap_drm_private *priv, enum omap_channel channel, |
f5f9454c RC |
230 | void (*handler)(void *), void *data) |
231 | { | |
47103a80 SR |
232 | struct drm_crtc *crtc = priv->channels[channel]->crtc; |
233 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
234 | struct drm_device *dev = omap_crtc->base.dev; | |
235 | ||
236 | if (omap_crtc->framedone_handler) | |
237 | return -EBUSY; | |
238 | ||
239 | dev_dbg(dev->dev, "register framedone %s", omap_crtc->name); | |
240 | ||
241 | omap_crtc->framedone_handler = handler; | |
242 | omap_crtc->framedone_handler_data = data; | |
243 | ||
f5f9454c RC |
244 | return 0; |
245 | } | |
246 | ||
4343f0f8 | 247 | static void omap_crtc_dss_unregister_framedone( |
64cb8179 | 248 | struct omap_drm_private *priv, enum omap_channel channel, |
f5f9454c RC |
249 | void (*handler)(void *), void *data) |
250 | { | |
47103a80 SR |
251 | struct drm_crtc *crtc = priv->channels[channel]->crtc; |
252 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
253 | struct drm_device *dev = omap_crtc->base.dev; | |
254 | ||
255 | dev_dbg(dev->dev, "unregister framedone %s", omap_crtc->name); | |
256 | ||
257 | WARN_ON(omap_crtc->framedone_handler != handler); | |
258 | WARN_ON(omap_crtc->framedone_handler_data != data); | |
259 | ||
260 | omap_crtc->framedone_handler = NULL; | |
261 | omap_crtc->framedone_handler_data = NULL; | |
f5f9454c RC |
262 | } |
263 | ||
264 | static const struct dss_mgr_ops mgr_ops = { | |
4343f0f8 LP |
265 | .start_update = omap_crtc_dss_start_update, |
266 | .enable = omap_crtc_dss_enable, | |
267 | .disable = omap_crtc_dss_disable, | |
268 | .set_timings = omap_crtc_dss_set_timings, | |
269 | .set_lcd_config = omap_crtc_dss_set_lcd_config, | |
270 | .register_framedone_handler = omap_crtc_dss_register_framedone, | |
271 | .unregister_framedone_handler = omap_crtc_dss_unregister_framedone, | |
cd5351f4 RC |
272 | }; |
273 | ||
971fb3e5 | 274 | /* ----------------------------------------------------------------------------- |
1d5e5ea1 | 275 | * Setup, Flush and Page Flip |
971fb3e5 LP |
276 | */ |
277 | ||
dfe9cfcc | 278 | void omap_crtc_error_irq(struct drm_crtc *crtc, u32 irqstatus) |
971fb3e5 | 279 | { |
e0519af7 | 280 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
a36af73f TV |
281 | |
282 | if (omap_crtc->ignore_digit_sync_lost) { | |
283 | irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; | |
284 | if (!irqstatus) | |
285 | return; | |
286 | } | |
287 | ||
3b143fc8 | 288 | DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); |
971fb3e5 LP |
289 | } |
290 | ||
14389a37 | 291 | void omap_crtc_vblank_irq(struct drm_crtc *crtc) |
971fb3e5 | 292 | { |
14389a37 | 293 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
9f759225 TV |
294 | struct drm_device *dev = omap_crtc->base.dev; |
295 | struct omap_drm_private *priv = dev->dev_private; | |
14389a37 | 296 | bool pending; |
971fb3e5 | 297 | |
14389a37 LP |
298 | spin_lock(&crtc->dev->event_lock); |
299 | /* | |
300 | * If the dispc is busy we're racing the flush operation. Try again on | |
301 | * the next vblank interrupt. | |
302 | */ | |
50638ae5 | 303 | if (priv->dispc_ops->mgr_go_busy(priv->dispc, omap_crtc->channel)) { |
14389a37 | 304 | spin_unlock(&crtc->dev->event_lock); |
a42133a7 | 305 | return; |
14389a37 | 306 | } |
a42133a7 | 307 | |
14389a37 LP |
308 | /* Send the vblank event if one has been requested. */ |
309 | if (omap_crtc->event) { | |
310 | drm_crtc_send_vblank_event(crtc, omap_crtc->event); | |
311 | omap_crtc->event = NULL; | |
312 | } | |
a42133a7 | 313 | |
14389a37 | 314 | pending = omap_crtc->pending; |
5f741b39 | 315 | omap_crtc->pending = false; |
d173d3dc | 316 | spin_unlock(&crtc->dev->event_lock); |
5f741b39 | 317 | |
14389a37 LP |
318 | if (pending) |
319 | drm_crtc_vblank_put(crtc); | |
a42133a7 | 320 | |
14389a37 | 321 | /* Wake up omap_atomic_complete. */ |
5f741b39 | 322 | wake_up(&omap_crtc->pending_wait); |
14389a37 LP |
323 | |
324 | DBG("%s: apply done", omap_crtc->name); | |
971fb3e5 LP |
325 | } |
326 | ||
47103a80 SR |
327 | void omap_crtc_framedone_irq(struct drm_crtc *crtc, uint32_t irqstatus) |
328 | { | |
329 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
330 | ||
331 | if (!omap_crtc->framedone_handler) | |
332 | return; | |
333 | ||
334 | omap_crtc->framedone_handler(omap_crtc->framedone_handler_data); | |
335 | ||
336 | spin_lock(&crtc->dev->event_lock); | |
337 | /* Send the vblank event if one has been requested. */ | |
338 | if (omap_crtc->event) { | |
339 | drm_crtc_send_vblank_event(crtc, omap_crtc->event); | |
340 | omap_crtc->event = NULL; | |
341 | } | |
342 | omap_crtc->pending = false; | |
343 | spin_unlock(&crtc->dev->event_lock); | |
344 | ||
345 | /* Wake up omap_atomic_complete. */ | |
346 | wake_up(&omap_crtc->pending_wait); | |
347 | } | |
348 | ||
1bb418bf SR |
349 | void omap_crtc_flush(struct drm_crtc *crtc) |
350 | { | |
351 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
352 | struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); | |
353 | ||
354 | if (!omap_state->manually_updated) | |
355 | return; | |
356 | ||
357 | if (!delayed_work_pending(&omap_crtc->update_work)) | |
358 | schedule_delayed_work(&omap_crtc->update_work, 0); | |
359 | } | |
360 | ||
361 | static void omap_crtc_manual_display_update(struct work_struct *data) | |
362 | { | |
363 | struct omap_crtc *omap_crtc = | |
364 | container_of(data, struct omap_crtc, update_work.work); | |
365 | struct drm_display_mode *mode = &omap_crtc->pipe->crtc->mode; | |
366 | struct omap_dss_device *dssdev = omap_crtc->pipe->output->next; | |
367 | struct drm_device *dev = omap_crtc->base.dev; | |
368 | const struct omap_dss_driver *dssdrv; | |
369 | int ret; | |
370 | ||
371 | if (!dssdev) { | |
372 | dev_err_once(dev->dev, "missing display dssdev!"); | |
373 | return; | |
374 | } | |
375 | ||
376 | dssdrv = dssdev->driver; | |
377 | if (!dssdrv || !dssdrv->update) { | |
378 | dev_err_once(dev->dev, "missing or incorrect dssdrv!"); | |
379 | return; | |
380 | } | |
381 | ||
382 | if (dssdrv->sync) | |
383 | dssdrv->sync(dssdev); | |
384 | ||
385 | ret = dssdrv->update(dssdev, 0, 0, mode->hdisplay, mode->vdisplay); | |
386 | if (ret < 0) { | |
387 | spin_lock_irq(&dev->event_lock); | |
388 | omap_crtc->pending = false; | |
389 | spin_unlock_irq(&dev->event_lock); | |
390 | wake_up(&omap_crtc->pending_wait); | |
391 | } | |
392 | } | |
393 | ||
7e3d9274 TV |
394 | static void omap_crtc_write_crtc_properties(struct drm_crtc *crtc) |
395 | { | |
9f759225 | 396 | struct omap_drm_private *priv = crtc->dev->dev_private; |
7e3d9274 TV |
397 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
398 | struct omap_overlay_manager_info info; | |
399 | ||
400 | memset(&info, 0, sizeof(info)); | |
401 | ||
402 | info.default_color = 0x000000; | |
403 | info.trans_enabled = false; | |
404 | info.partial_alpha_enabled = false; | |
405 | info.cpr_enable = false; | |
406 | ||
50638ae5 | 407 | priv->dispc_ops->mgr_setup(priv->dispc, omap_crtc->channel, &info); |
7e3d9274 TV |
408 | } |
409 | ||
971fb3e5 LP |
410 | /* ----------------------------------------------------------------------------- |
411 | * CRTC Functions | |
f5f9454c RC |
412 | */ |
413 | ||
cd5351f4 RC |
414 | static void omap_crtc_destroy(struct drm_crtc *crtc) |
415 | { | |
416 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
f5f9454c RC |
417 | |
418 | DBG("%s", omap_crtc->name); | |
419 | ||
cd5351f4 | 420 | drm_crtc_cleanup(crtc); |
f5f9454c | 421 | |
cd5351f4 RC |
422 | kfree(omap_crtc); |
423 | } | |
424 | ||
ce9a8f1a LP |
425 | static void omap_crtc_arm_event(struct drm_crtc *crtc) |
426 | { | |
427 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
428 | ||
429 | WARN_ON(omap_crtc->pending); | |
430 | omap_crtc->pending = true; | |
431 | ||
432 | if (crtc->state->event) { | |
433 | omap_crtc->event = crtc->state->event; | |
434 | crtc->state->event = NULL; | |
435 | } | |
436 | } | |
437 | ||
0b20a0f8 | 438 | static void omap_crtc_atomic_enable(struct drm_crtc *crtc, |
351f950d | 439 | struct drm_atomic_state *state) |
cd5351f4 | 440 | { |
24ec84e8 | 441 | struct omap_drm_private *priv = crtc->dev->dev_private; |
cd5351f4 | 442 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
1bb418bf | 443 | struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); |
14389a37 | 444 | int ret; |
cd5351f4 | 445 | |
f1d57fb5 | 446 | DBG("%s", omap_crtc->name); |
f5f9454c | 447 | |
24ec84e8 LP |
448 | priv->dispc_ops->runtime_get(priv->dispc); |
449 | ||
1bb418bf SR |
450 | /* manual updated display will not trigger vsync irq */ |
451 | if (omap_state->manually_updated) | |
452 | return; | |
453 | ||
14389a37 | 454 | drm_crtc_vblank_on(crtc); |
7fd5b254 | 455 | |
14389a37 LP |
456 | ret = drm_crtc_vblank_get(crtc); |
457 | WARN_ON(ret != 0); | |
458 | ||
7fd5b254 | 459 | spin_lock_irq(&crtc->dev->event_lock); |
ce9a8f1a | 460 | omap_crtc_arm_event(crtc); |
d173d3dc | 461 | spin_unlock_irq(&crtc->dev->event_lock); |
cd5351f4 RC |
462 | } |
463 | ||
64581714 | 464 | static void omap_crtc_atomic_disable(struct drm_crtc *crtc, |
351f950d | 465 | struct drm_atomic_state *state) |
cd5351f4 | 466 | { |
24ec84e8 | 467 | struct omap_drm_private *priv = crtc->dev->dev_private; |
f1d57fb5 | 468 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
1bb418bf | 469 | struct drm_device *dev = crtc->dev; |
f1d57fb5 LP |
470 | |
471 | DBG("%s", omap_crtc->name); | |
472 | ||
ce9a8f1a LP |
473 | spin_lock_irq(&crtc->dev->event_lock); |
474 | if (crtc->state->event) { | |
475 | drm_crtc_send_vblank_event(crtc, crtc->state->event); | |
476 | crtc->state->event = NULL; | |
477 | } | |
478 | spin_unlock_irq(&crtc->dev->event_lock); | |
479 | ||
1bb418bf SR |
480 | cancel_delayed_work(&omap_crtc->update_work); |
481 | ||
482 | if (!omap_crtc_wait_pending(crtc)) | |
483 | dev_warn(dev->dev, "manual display update did not finish!"); | |
484 | ||
f1d57fb5 | 485 | drm_crtc_vblank_off(crtc); |
24ec84e8 LP |
486 | |
487 | priv->dispc_ops->runtime_put(priv->dispc); | |
cd5351f4 RC |
488 | } |
489 | ||
a7631c4b PU |
490 | static enum drm_mode_status omap_crtc_mode_valid(struct drm_crtc *crtc, |
491 | const struct drm_display_mode *mode) | |
492 | { | |
493 | struct omap_drm_private *priv = crtc->dev->dev_private; | |
116c7721 LP |
494 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
495 | struct videomode vm = {0}; | |
496 | int r; | |
497 | ||
498 | drm_display_mode_to_videomode(mode, &vm); | |
ad9df7d9 SR |
499 | |
500 | /* | |
501 | * DSI might not call this, since the supplied mode is not a | |
502 | * valid DISPC mode. DSI will calculate and configure the | |
503 | * proper DISPC mode later. | |
504 | */ | |
505 | if (omap_crtc->pipe->output->next == NULL || | |
506 | omap_crtc->pipe->output->next->type != OMAP_DISPLAY_TYPE_DSI) { | |
507 | r = priv->dispc_ops->mgr_check_timings(priv->dispc, | |
508 | omap_crtc->channel, | |
509 | &vm); | |
510 | if (r) | |
511 | return r; | |
512 | } | |
a7631c4b PU |
513 | |
514 | /* Check for bandwidth limit */ | |
515 | if (priv->max_bandwidth) { | |
516 | /* | |
517 | * Estimation for the bandwidth need of a given mode with one | |
518 | * full screen plane: | |
519 | * bandwidth = resolution * 32bpp * (pclk / (vtotal * htotal)) | |
520 | * ^^ Refresh rate ^^ | |
521 | * | |
522 | * The interlaced mode is taken into account by using the | |
523 | * pixelclock in the calculation. | |
524 | * | |
525 | * The equation is rearranged for 64bit arithmetic. | |
526 | */ | |
527 | uint64_t bandwidth = mode->clock * 1000; | |
528 | unsigned int bpp = 4; | |
529 | ||
530 | bandwidth = bandwidth * mode->hdisplay * mode->vdisplay * bpp; | |
531 | bandwidth = div_u64(bandwidth, mode->htotal * mode->vtotal); | |
532 | ||
533 | /* | |
534 | * Reject modes which would need more bandwidth if used with one | |
535 | * full resolution plane (most common use case). | |
536 | */ | |
537 | if (priv->max_bandwidth < bandwidth) | |
538 | return MODE_BAD; | |
539 | } | |
540 | ||
541 | return MODE_OK; | |
542 | } | |
543 | ||
f7a73b65 | 544 | static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) |
cd5351f4 RC |
545 | { |
546 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
f7a73b65 | 547 | struct drm_display_mode *mode = &crtc->state->adjusted_mode; |
f5f9454c | 548 | |
c39ff7ea SM |
549 | DBG("%s: set mode: " DRM_MODE_FMT, |
550 | omap_crtc->name, DRM_MODE_ARG(mode)); | |
f5f9454c | 551 | |
8e9c1c66 | 552 | drm_display_mode_to_videomode(mode, &omap_crtc->vm); |
cd5351f4 RC |
553 | } |
554 | ||
1bb418bf SR |
555 | static bool omap_crtc_is_manually_updated(struct drm_crtc *crtc) |
556 | { | |
557 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
558 | struct omap_dss_device *display = omap_crtc->pipe->output->next; | |
559 | ||
560 | if (!display) | |
561 | return false; | |
562 | ||
563 | if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { | |
564 | DBG("detected manually updated display!"); | |
565 | return true; | |
566 | } | |
567 | ||
568 | return false; | |
569 | } | |
570 | ||
492a426a | 571 | static int omap_crtc_atomic_check(struct drm_crtc *crtc, |
29b77ad7 | 572 | struct drm_atomic_state *state) |
492a426a | 573 | { |
29b77ad7 MR |
574 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, |
575 | crtc); | |
3dfeb631 ML |
576 | struct drm_plane_state *pri_state; |
577 | ||
29b77ad7 MR |
578 | if (crtc_state->color_mgmt_changed && crtc_state->gamma_lut) { |
579 | unsigned int length = crtc_state->gamma_lut->length / | |
492a426a JS |
580 | sizeof(struct drm_color_lut); |
581 | ||
582 | if (length < 2) | |
583 | return -EINVAL; | |
584 | } | |
585 | ||
29b77ad7 MR |
586 | pri_state = drm_atomic_get_new_plane_state(crtc_state->state, |
587 | crtc->primary); | |
3dfeb631 ML |
588 | if (pri_state) { |
589 | struct omap_crtc_state *omap_crtc_state = | |
29b77ad7 | 590 | to_omap_crtc_state(crtc_state); |
3dfeb631 ML |
591 | |
592 | /* Mirror new values for zpos and rotation in omap_crtc_state */ | |
593 | omap_crtc_state->zpos = pri_state->zpos; | |
594 | omap_crtc_state->rotation = pri_state->rotation; | |
1bb418bf SR |
595 | |
596 | /* Check if this CRTC is for a manually updated display */ | |
597 | omap_crtc_state->manually_updated = omap_crtc_is_manually_updated(crtc); | |
3dfeb631 ML |
598 | } |
599 | ||
492a426a JS |
600 | return 0; |
601 | } | |
602 | ||
c201d00f | 603 | static void omap_crtc_atomic_begin(struct drm_crtc *crtc, |
577d3983 | 604 | struct drm_crtc_state *old_crtc_state) |
de8e4100 | 605 | { |
fa16d262 | 606 | } |
cd5351f4 | 607 | |
c201d00f | 608 | static void omap_crtc_atomic_flush(struct drm_crtc *crtc, |
577d3983 | 609 | struct drm_crtc_state *old_crtc_state) |
fa16d262 | 610 | { |
9f759225 | 611 | struct omap_drm_private *priv = crtc->dev->dev_private; |
6646dfd0 | 612 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
1bb418bf | 613 | struct omap_crtc_state *omap_crtc_state = to_omap_crtc_state(crtc->state); |
14389a37 | 614 | int ret; |
6646dfd0 | 615 | |
492a426a JS |
616 | if (crtc->state->color_mgmt_changed) { |
617 | struct drm_color_lut *lut = NULL; | |
dfe9cfcc | 618 | unsigned int length = 0; |
492a426a JS |
619 | |
620 | if (crtc->state->gamma_lut) { | |
621 | lut = (struct drm_color_lut *) | |
622 | crtc->state->gamma_lut->data; | |
623 | length = crtc->state->gamma_lut->length / | |
624 | sizeof(*lut); | |
625 | } | |
50638ae5 LP |
626 | priv->dispc_ops->mgr_set_gamma(priv->dispc, omap_crtc->channel, |
627 | lut, length); | |
492a426a JS |
628 | } |
629 | ||
7e3d9274 TV |
630 | omap_crtc_write_crtc_properties(crtc); |
631 | ||
e025d386 | 632 | /* Only flush the CRTC if it is currently enabled. */ |
f933a3a9 LP |
633 | if (!omap_crtc->enabled) |
634 | return; | |
5f741b39 | 635 | |
f933a3a9 | 636 | DBG("%s: GO", omap_crtc->name); |
6646dfd0 | 637 | |
1bb418bf SR |
638 | if (omap_crtc_state->manually_updated) { |
639 | /* send new image for page flips and modeset changes */ | |
640 | spin_lock_irq(&crtc->dev->event_lock); | |
641 | omap_crtc_flush(crtc); | |
642 | omap_crtc_arm_event(crtc); | |
643 | spin_unlock_irq(&crtc->dev->event_lock); | |
644 | return; | |
645 | } | |
646 | ||
14389a37 LP |
647 | ret = drm_crtc_vblank_get(crtc); |
648 | WARN_ON(ret != 0); | |
649 | ||
d173d3dc | 650 | spin_lock_irq(&crtc->dev->event_lock); |
50638ae5 | 651 | priv->dispc_ops->mgr_go(priv->dispc, omap_crtc->channel); |
ce9a8f1a | 652 | omap_crtc_arm_event(crtc); |
d173d3dc | 653 | spin_unlock_irq(&crtc->dev->event_lock); |
cd5351f4 RC |
654 | } |
655 | ||
afc34932 LP |
656 | static int omap_crtc_atomic_set_property(struct drm_crtc *crtc, |
657 | struct drm_crtc_state *state, | |
658 | struct drm_property *property, | |
dfe9cfcc | 659 | u64 val) |
3c810c61 | 660 | { |
3dfeb631 ML |
661 | struct omap_drm_private *priv = crtc->dev->dev_private; |
662 | struct drm_plane_state *plane_state; | |
afc34932 | 663 | |
3dfeb631 ML |
664 | /* |
665 | * Delegate property set to the primary plane. Get the plane state and | |
666 | * set the property directly, the shadow copy will be assigned in the | |
667 | * omap_crtc_atomic_check callback. This way updates to plane state will | |
668 | * always be mirrored in the crtc state correctly. | |
669 | */ | |
670 | plane_state = drm_atomic_get_plane_state(state->state, crtc->primary); | |
671 | if (IS_ERR(plane_state)) | |
672 | return PTR_ERR(plane_state); | |
673 | ||
674 | if (property == crtc->primary->rotation_property) | |
675 | plane_state->rotation = val; | |
676 | else if (property == priv->zorder_prop) | |
677 | plane_state->zpos = val; | |
678 | else | |
679 | return -EINVAL; | |
6bdad6cf | 680 | |
3dfeb631 | 681 | return 0; |
afc34932 | 682 | } |
1e0fdfc2 | 683 | |
afc34932 LP |
684 | static int omap_crtc_atomic_get_property(struct drm_crtc *crtc, |
685 | const struct drm_crtc_state *state, | |
686 | struct drm_property *property, | |
dfe9cfcc | 687 | u64 *val) |
afc34932 | 688 | { |
3dfeb631 ML |
689 | struct omap_drm_private *priv = crtc->dev->dev_private; |
690 | struct omap_crtc_state *omap_state = to_omap_crtc_state(state); | |
691 | ||
692 | if (property == crtc->primary->rotation_property) | |
693 | *val = omap_state->rotation; | |
694 | else if (property == priv->zorder_prop) | |
695 | *val = omap_state->zpos; | |
696 | else | |
697 | return -EINVAL; | |
698 | ||
699 | return 0; | |
700 | } | |
701 | ||
702 | static void omap_crtc_reset(struct drm_crtc *crtc) | |
703 | { | |
51f644b4 DV |
704 | struct omap_crtc_state *state; |
705 | ||
3dfeb631 ML |
706 | if (crtc->state) |
707 | __drm_atomic_helper_crtc_destroy_state(crtc->state); | |
708 | ||
709 | kfree(crtc->state); | |
3dfeb631 | 710 | |
51f644b4 DV |
711 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
712 | if (state) | |
713 | __drm_atomic_helper_crtc_reset(crtc, &state->base); | |
3dfeb631 ML |
714 | } |
715 | ||
716 | static struct drm_crtc_state * | |
717 | omap_crtc_duplicate_state(struct drm_crtc *crtc) | |
718 | { | |
719 | struct omap_crtc_state *state, *current_state; | |
720 | ||
721 | if (WARN_ON(!crtc->state)) | |
722 | return NULL; | |
723 | ||
724 | current_state = to_omap_crtc_state(crtc->state); | |
725 | ||
726 | state = kmalloc(sizeof(*state), GFP_KERNEL); | |
2419672f DC |
727 | if (!state) |
728 | return NULL; | |
729 | ||
730 | __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); | |
3dfeb631 ML |
731 | |
732 | state->zpos = current_state->zpos; | |
733 | state->rotation = current_state->rotation; | |
1bb418bf | 734 | state->manually_updated = current_state->manually_updated; |
6bdad6cf | 735 | |
3dfeb631 | 736 | return &state->base; |
3c810c61 RC |
737 | } |
738 | ||
cd5351f4 | 739 | static const struct drm_crtc_funcs omap_crtc_funcs = { |
3dfeb631 | 740 | .reset = omap_crtc_reset, |
9416c9df | 741 | .set_config = drm_atomic_helper_set_config, |
cd5351f4 | 742 | .destroy = omap_crtc_destroy, |
fa16d262 | 743 | .page_flip = drm_atomic_helper_page_flip, |
492a426a | 744 | .gamma_set = drm_atomic_helper_legacy_gamma_set, |
3dfeb631 | 745 | .atomic_duplicate_state = omap_crtc_duplicate_state, |
69a12263 | 746 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
afc34932 LP |
747 | .atomic_set_property = omap_crtc_atomic_set_property, |
748 | .atomic_get_property = omap_crtc_atomic_get_property, | |
0396162a TV |
749 | .enable_vblank = omap_irq_enable_vblank, |
750 | .disable_vblank = omap_irq_disable_vblank, | |
cd5351f4 RC |
751 | }; |
752 | ||
753 | static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { | |
f7a73b65 | 754 | .mode_set_nofb = omap_crtc_mode_set_nofb, |
492a426a | 755 | .atomic_check = omap_crtc_atomic_check, |
de8e4100 LP |
756 | .atomic_begin = omap_crtc_atomic_begin, |
757 | .atomic_flush = omap_crtc_atomic_flush, | |
0b20a0f8 | 758 | .atomic_enable = omap_crtc_atomic_enable, |
64581714 | 759 | .atomic_disable = omap_crtc_atomic_disable, |
a7631c4b | 760 | .mode_valid = omap_crtc_mode_valid, |
cd5351f4 RC |
761 | }; |
762 | ||
971fb3e5 LP |
763 | /* ----------------------------------------------------------------------------- |
764 | * Init and Cleanup | |
765 | */ | |
e2f8fd74 | 766 | |
f5f9454c | 767 | static const char *channel_names[] = { |
222025e4 LP |
768 | [OMAP_DSS_CHANNEL_LCD] = "lcd", |
769 | [OMAP_DSS_CHANNEL_DIGIT] = "tv", | |
770 | [OMAP_DSS_CHANNEL_LCD2] = "lcd2", | |
771 | [OMAP_DSS_CHANNEL_LCD3] = "lcd3", | |
f5f9454c RC |
772 | }; |
773 | ||
64cb8179 | 774 | void omap_crtc_pre_init(struct omap_drm_private *priv) |
04b1fc02 | 775 | { |
845417b3 | 776 | dss_install_mgr_ops(priv->dss, &mgr_ops, priv); |
04b1fc02 TV |
777 | } |
778 | ||
845417b3 | 779 | void omap_crtc_pre_uninit(struct omap_drm_private *priv) |
3a01ab25 | 780 | { |
845417b3 | 781 | dss_uninstall_mgr_ops(priv->dss); |
3a01ab25 AT |
782 | } |
783 | ||
cd5351f4 RC |
784 | /* initialize crtc */ |
785 | struct drm_crtc *omap_crtc_init(struct drm_device *dev, | |
00b30e79 LP |
786 | struct omap_drm_pipeline *pipe, |
787 | struct drm_plane *plane) | |
cd5351f4 | 788 | { |
9f759225 | 789 | struct omap_drm_private *priv = dev->dev_private; |
cd5351f4 | 790 | struct drm_crtc *crtc = NULL; |
f5f9454c | 791 | struct omap_crtc *omap_crtc; |
e8e13b15 | 792 | enum omap_channel channel; |
ef6b0e02 | 793 | int ret; |
f5f9454c | 794 | |
00b30e79 | 795 | channel = pipe->output->dispc_channel; |
e8e13b15 | 796 | |
f5f9454c | 797 | DBG("%s", channel_names[channel]); |
cd5351f4 | 798 | |
f5f9454c | 799 | omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); |
78110bb8 | 800 | if (!omap_crtc) |
e8e13b15 | 801 | return ERR_PTR(-ENOMEM); |
cd5351f4 | 802 | |
cd5351f4 | 803 | crtc = &omap_crtc->base; |
bb5c2d9a | 804 | |
5f741b39 | 805 | init_waitqueue_head(&omap_crtc->pending_wait); |
f5f9454c | 806 | |
67dfd2d3 | 807 | omap_crtc->pipe = pipe; |
0d8f371f | 808 | omap_crtc->channel = channel; |
0d8f371f | 809 | omap_crtc->name = channel_names[channel]; |
0d8f371f | 810 | |
1bb418bf SR |
811 | /* |
812 | * We want to refresh manually updated displays from dirty callback, | |
813 | * which is called quite often (e.g. for each drawn line). This will | |
814 | * be used to do the display update asynchronously to avoid blocking | |
815 | * the rendering process and merges multiple dirty calls into one | |
816 | * update if they arrive very fast. We also call this function for | |
817 | * atomic display updates (e.g. for page flips), which means we do | |
818 | * not need extra locking. Atomic updates should be synchronous, but | |
819 | * need to wait for the framedone interrupt anyways. | |
820 | */ | |
821 | INIT_DELAYED_WORK(&omap_crtc->update_work, | |
822 | omap_crtc_manual_display_update); | |
823 | ||
ef6b0e02 | 824 | ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, |
f9882876 | 825 | &omap_crtc_funcs, NULL); |
ef6b0e02 | 826 | if (ret < 0) { |
e8e13b15 | 827 | dev_err(dev->dev, "%s(): could not init crtc for: %s\n", |
79107f27 | 828 | __func__, pipe->output->name); |
ef6b0e02 | 829 | kfree(omap_crtc); |
e8e13b15 | 830 | return ERR_PTR(ret); |
ef6b0e02 LP |
831 | } |
832 | ||
cd5351f4 RC |
833 | drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); |
834 | ||
492a426a JS |
835 | /* The dispc API adapts to what ever size, but the HW supports |
836 | * 256 element gamma table for LCDs and 1024 element table for | |
837 | * OMAP_DSS_CHANNEL_DIGIT. X server assumes 256 element gamma | |
838 | * tables so lets use that. Size of HW gamma table can be | |
839 | * extracted with dispc_mgr_gamma_size(). If it returns 0 | |
bdc19ba6 | 840 | * gamma table is not supported. |
492a426a | 841 | */ |
50638ae5 | 842 | if (priv->dispc_ops->mgr_gamma_size(priv->dispc, channel)) { |
dfe9cfcc | 843 | unsigned int gamma_lut_size = 256; |
492a426a JS |
844 | |
845 | drm_crtc_enable_color_mgmt(crtc, 0, false, gamma_lut_size); | |
846 | drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size); | |
847 | } | |
848 | ||
ef6b0e02 | 849 | omap_plane_install_properties(crtc->primary, &crtc->base); |
3c810c61 | 850 | |
cd5351f4 | 851 | return crtc; |
cd5351f4 | 852 | } |