Commit | Line | Data |
---|---|---|
cd5351f4 | 1 | /* |
bb5cdf8d | 2 | * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ |
cd5351f4 RC |
3 | * Author: Rob Clark <rob@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 | ||
69a12263 LP |
18 | #include <drm/drm_atomic.h> |
19 | #include <drm/drm_atomic_helper.h> | |
2d278f54 LP |
20 | #include <drm/drm_crtc.h> |
21 | #include <drm/drm_crtc_helper.h> | |
b9ed9f0e | 22 | #include <drm/drm_mode.h> |
3cb9ae4f | 23 | #include <drm/drm_plane_helper.h> |
a7631c4b | 24 | #include <linux/math64.h> |
2d278f54 LP |
25 | |
26 | #include "omap_drv.h" | |
cd5351f4 | 27 | |
3dfeb631 ML |
28 | #define to_omap_crtc_state(x) container_of(x, struct omap_crtc_state, base) |
29 | ||
30 | struct omap_crtc_state { | |
31 | /* Must be first. */ | |
32 | struct drm_crtc_state base; | |
33 | /* Shadow values for legacy userspace support. */ | |
34 | unsigned int rotation; | |
35 | unsigned int zpos; | |
36 | }; | |
37 | ||
cd5351f4 RC |
38 | #define to_omap_crtc(x) container_of(x, struct omap_crtc, base) |
39 | ||
40 | struct omap_crtc { | |
41 | struct drm_crtc base; | |
f5f9454c | 42 | |
bb5c2d9a | 43 | const char *name; |
f5f9454c | 44 | enum omap_channel channel; |
f5f9454c | 45 | |
da11bbbb | 46 | struct videomode vm; |
f5f9454c | 47 | |
a36af73f | 48 | bool ignore_digit_sync_lost; |
5f741b39 | 49 | |
f933a3a9 | 50 | bool enabled; |
5f741b39 TV |
51 | bool pending; |
52 | wait_queue_head_t pending_wait; | |
577d3983 | 53 | struct drm_pending_vblank_event *event; |
f5f9454c RC |
54 | }; |
55 | ||
971fb3e5 LP |
56 | /* ----------------------------------------------------------------------------- |
57 | * Helper Functions | |
58 | */ | |
59 | ||
4520ff28 | 60 | struct videomode *omap_crtc_timings(struct drm_crtc *crtc) |
971fb3e5 LP |
61 | { |
62 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
da11bbbb | 63 | return &omap_crtc->vm; |
971fb3e5 LP |
64 | } |
65 | ||
66 | enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) | |
67 | { | |
68 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
69 | return omap_crtc->channel; | |
70 | } | |
71 | ||
d173d3dc LP |
72 | static bool omap_crtc_is_pending(struct drm_crtc *crtc) |
73 | { | |
74 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
75 | unsigned long flags; | |
76 | bool pending; | |
77 | ||
78 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | |
79 | pending = omap_crtc->pending; | |
80 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | |
81 | ||
82 | return pending; | |
83 | } | |
84 | ||
5f741b39 TV |
85 | int omap_crtc_wait_pending(struct drm_crtc *crtc) |
86 | { | |
87 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
88 | ||
61f3c40b TV |
89 | /* |
90 | * Timeout is set to a "sufficiently" high value, which should cover | |
91 | * a single frame refresh even on slower displays. | |
92 | */ | |
5f741b39 | 93 | return wait_event_timeout(omap_crtc->pending_wait, |
d173d3dc | 94 | !omap_crtc_is_pending(crtc), |
61f3c40b | 95 | msecs_to_jiffies(250)); |
5f741b39 TV |
96 | } |
97 | ||
971fb3e5 LP |
98 | /* ----------------------------------------------------------------------------- |
99 | * DSS Manager Functions | |
100 | */ | |
101 | ||
f5f9454c RC |
102 | /* |
103 | * Manager-ops, callbacks from output when they need to configure | |
104 | * the upstream part of the video pipe. | |
105 | * | |
106 | * Most of these we can ignore until we add support for command-mode | |
107 | * panels.. for video-mode the crtc-helpers already do an adequate | |
108 | * job of sequencing the setup of the video pipe in the proper order | |
109 | */ | |
110 | ||
04b1fc02 TV |
111 | /* ovl-mgr-id -> crtc */ |
112 | static struct omap_crtc *omap_crtcs[8]; | |
3a924138 | 113 | static struct omap_dss_device *omap_crtc_output[8]; |
04b1fc02 | 114 | |
f5f9454c | 115 | /* we can probably ignore these until we support command-mode panels: */ |
64cb8179 LP |
116 | static int omap_crtc_dss_connect(struct omap_drm_private *priv, |
117 | enum omap_channel channel, | |
1f68d9c4 | 118 | struct omap_dss_device *dst) |
a7e71e7f | 119 | { |
50638ae5 LP |
120 | const struct dispc_ops *dispc_ops = priv->dispc_ops; |
121 | struct dispc_device *dispc = priv->dispc; | |
122 | ||
e5cbb6e8 | 123 | if (omap_crtc_output[channel]) |
a7e71e7f TV |
124 | return -EINVAL; |
125 | ||
50638ae5 | 126 | if (!(dispc_ops->mgr_get_supported_outputs(dispc, channel) & dst->id)) |
a7e71e7f TV |
127 | return -EINVAL; |
128 | ||
e5cbb6e8 | 129 | omap_crtc_output[channel] = dst; |
49239503 | 130 | dst->dispc_channel_connected = true; |
a7e71e7f TV |
131 | |
132 | return 0; | |
133 | } | |
134 | ||
64cb8179 LP |
135 | static void omap_crtc_dss_disconnect(struct omap_drm_private *priv, |
136 | enum omap_channel channel, | |
1f68d9c4 | 137 | struct omap_dss_device *dst) |
a7e71e7f | 138 | { |
e5cbb6e8 | 139 | omap_crtc_output[channel] = NULL; |
49239503 | 140 | dst->dispc_channel_connected = false; |
a7e71e7f TV |
141 | } |
142 | ||
64cb8179 LP |
143 | static void omap_crtc_dss_start_update(struct omap_drm_private *priv, |
144 | enum omap_channel channel) | |
f5f9454c RC |
145 | { |
146 | } | |
147 | ||
4029755e | 148 | /* Called only from the encoder enable/disable and suspend/resume handlers. */ |
8472b570 LP |
149 | static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) |
150 | { | |
151 | struct drm_device *dev = crtc->dev; | |
9f759225 | 152 | struct omap_drm_private *priv = dev->dev_private; |
8472b570 LP |
153 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
154 | enum omap_channel channel = omap_crtc->channel; | |
155 | struct omap_irq_wait *wait; | |
156 | u32 framedone_irq, vsync_irq; | |
157 | int ret; | |
158 | ||
03af8157 LP |
159 | if (WARN_ON(omap_crtc->enabled == enable)) |
160 | return; | |
161 | ||
3a924138 | 162 | if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) { |
50638ae5 | 163 | priv->dispc_ops->mgr_enable(priv->dispc, channel, enable); |
f933a3a9 | 164 | omap_crtc->enabled = enable; |
4e4b53ce TV |
165 | return; |
166 | } | |
167 | ||
ef422283 TV |
168 | if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { |
169 | /* | |
170 | * Digit output produces some sync lost interrupts during the | |
171 | * first frame when enabling, so we need to ignore those. | |
172 | */ | |
173 | omap_crtc->ignore_digit_sync_lost = true; | |
174 | } | |
8472b570 | 175 | |
50638ae5 LP |
176 | framedone_irq = priv->dispc_ops->mgr_get_framedone_irq(priv->dispc, |
177 | channel); | |
178 | vsync_irq = priv->dispc_ops->mgr_get_vsync_irq(priv->dispc, channel); | |
8472b570 LP |
179 | |
180 | if (enable) { | |
181 | wait = omap_irq_wait_init(dev, vsync_irq, 1); | |
182 | } else { | |
183 | /* | |
184 | * When we disable the digit output, we need to wait for | |
185 | * FRAMEDONE to know that DISPC has finished with the output. | |
186 | * | |
187 | * OMAP2/3 does not have FRAMEDONE irq for digit output, and in | |
188 | * that case we need to use vsync interrupt, and wait for both | |
189 | * even and odd frames. | |
190 | */ | |
191 | ||
192 | if (framedone_irq) | |
193 | wait = omap_irq_wait_init(dev, framedone_irq, 1); | |
194 | else | |
195 | wait = omap_irq_wait_init(dev, vsync_irq, 2); | |
196 | } | |
197 | ||
50638ae5 | 198 | priv->dispc_ops->mgr_enable(priv->dispc, channel, enable); |
f933a3a9 | 199 | omap_crtc->enabled = enable; |
8472b570 LP |
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 | ||
ef422283 TV |
207 | 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 | } | |
8472b570 LP |
212 | } |
213 | ||
506096a1 | 214 | |
64cb8179 LP |
215 | static int omap_crtc_dss_enable(struct omap_drm_private *priv, |
216 | enum omap_channel channel) | |
f5f9454c | 217 | { |
e5cbb6e8 | 218 | struct omap_crtc *omap_crtc = omap_crtcs[channel]; |
506096a1 | 219 | |
50638ae5 LP |
220 | priv->dispc_ops->mgr_set_timings(priv->dispc, omap_crtc->channel, |
221 | &omap_crtc->vm); | |
8472b570 | 222 | omap_crtc_set_enabled(&omap_crtc->base, true); |
506096a1 | 223 | |
f5f9454c RC |
224 | return 0; |
225 | } | |
226 | ||
64cb8179 LP |
227 | static void omap_crtc_dss_disable(struct omap_drm_private *priv, |
228 | enum omap_channel channel) | |
f5f9454c | 229 | { |
e5cbb6e8 | 230 | struct omap_crtc *omap_crtc = omap_crtcs[channel]; |
506096a1 | 231 | |
8472b570 | 232 | omap_crtc_set_enabled(&omap_crtc->base, false); |
f5f9454c RC |
233 | } |
234 | ||
64cb8179 LP |
235 | static void omap_crtc_dss_set_timings(struct omap_drm_private *priv, |
236 | enum omap_channel channel, | |
da11bbbb | 237 | const struct videomode *vm) |
f5f9454c | 238 | { |
e5cbb6e8 | 239 | struct omap_crtc *omap_crtc = omap_crtcs[channel]; |
f5f9454c | 240 | DBG("%s", omap_crtc->name); |
da11bbbb | 241 | omap_crtc->vm = *vm; |
f5f9454c RC |
242 | } |
243 | ||
64cb8179 LP |
244 | static void omap_crtc_dss_set_lcd_config(struct omap_drm_private *priv, |
245 | enum omap_channel channel, | |
f5f9454c RC |
246 | const struct dss_lcd_mgr_config *config) |
247 | { | |
e5cbb6e8 | 248 | struct omap_crtc *omap_crtc = omap_crtcs[channel]; |
9f759225 | 249 | |
f5f9454c | 250 | DBG("%s", omap_crtc->name); |
50638ae5 LP |
251 | priv->dispc_ops->mgr_set_lcd_config(priv->dispc, omap_crtc->channel, |
252 | config); | |
f5f9454c RC |
253 | } |
254 | ||
4343f0f8 | 255 | static int omap_crtc_dss_register_framedone( |
64cb8179 | 256 | struct omap_drm_private *priv, enum omap_channel channel, |
f5f9454c RC |
257 | void (*handler)(void *), void *data) |
258 | { | |
259 | return 0; | |
260 | } | |
261 | ||
4343f0f8 | 262 | static void omap_crtc_dss_unregister_framedone( |
64cb8179 | 263 | struct omap_drm_private *priv, enum omap_channel channel, |
f5f9454c RC |
264 | void (*handler)(void *), void *data) |
265 | { | |
266 | } | |
267 | ||
268 | static const struct dss_mgr_ops mgr_ops = { | |
4343f0f8 LP |
269 | .connect = omap_crtc_dss_connect, |
270 | .disconnect = omap_crtc_dss_disconnect, | |
271 | .start_update = omap_crtc_dss_start_update, | |
272 | .enable = omap_crtc_dss_enable, | |
273 | .disable = omap_crtc_dss_disable, | |
274 | .set_timings = omap_crtc_dss_set_timings, | |
275 | .set_lcd_config = omap_crtc_dss_set_lcd_config, | |
276 | .register_framedone_handler = omap_crtc_dss_register_framedone, | |
277 | .unregister_framedone_handler = omap_crtc_dss_unregister_framedone, | |
cd5351f4 RC |
278 | }; |
279 | ||
971fb3e5 | 280 | /* ----------------------------------------------------------------------------- |
1d5e5ea1 | 281 | * Setup, Flush and Page Flip |
971fb3e5 LP |
282 | */ |
283 | ||
dfe9cfcc | 284 | void omap_crtc_error_irq(struct drm_crtc *crtc, u32 irqstatus) |
971fb3e5 | 285 | { |
e0519af7 | 286 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
a36af73f TV |
287 | |
288 | if (omap_crtc->ignore_digit_sync_lost) { | |
289 | irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; | |
290 | if (!irqstatus) | |
291 | return; | |
292 | } | |
293 | ||
3b143fc8 | 294 | DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); |
971fb3e5 LP |
295 | } |
296 | ||
14389a37 | 297 | void omap_crtc_vblank_irq(struct drm_crtc *crtc) |
971fb3e5 | 298 | { |
14389a37 | 299 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
9f759225 TV |
300 | struct drm_device *dev = omap_crtc->base.dev; |
301 | struct omap_drm_private *priv = dev->dev_private; | |
14389a37 | 302 | bool pending; |
971fb3e5 | 303 | |
14389a37 LP |
304 | spin_lock(&crtc->dev->event_lock); |
305 | /* | |
306 | * If the dispc is busy we're racing the flush operation. Try again on | |
307 | * the next vblank interrupt. | |
308 | */ | |
50638ae5 | 309 | if (priv->dispc_ops->mgr_go_busy(priv->dispc, omap_crtc->channel)) { |
14389a37 | 310 | spin_unlock(&crtc->dev->event_lock); |
a42133a7 | 311 | return; |
14389a37 | 312 | } |
a42133a7 | 313 | |
14389a37 LP |
314 | /* Send the vblank event if one has been requested. */ |
315 | if (omap_crtc->event) { | |
316 | drm_crtc_send_vblank_event(crtc, omap_crtc->event); | |
317 | omap_crtc->event = NULL; | |
318 | } | |
a42133a7 | 319 | |
14389a37 | 320 | pending = omap_crtc->pending; |
5f741b39 | 321 | omap_crtc->pending = false; |
d173d3dc | 322 | spin_unlock(&crtc->dev->event_lock); |
5f741b39 | 323 | |
14389a37 LP |
324 | if (pending) |
325 | drm_crtc_vblank_put(crtc); | |
a42133a7 | 326 | |
14389a37 | 327 | /* Wake up omap_atomic_complete. */ |
5f741b39 | 328 | wake_up(&omap_crtc->pending_wait); |
14389a37 LP |
329 | |
330 | DBG("%s: apply done", omap_crtc->name); | |
971fb3e5 LP |
331 | } |
332 | ||
7e3d9274 TV |
333 | static void omap_crtc_write_crtc_properties(struct drm_crtc *crtc) |
334 | { | |
9f759225 | 335 | struct omap_drm_private *priv = crtc->dev->dev_private; |
7e3d9274 TV |
336 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
337 | struct omap_overlay_manager_info info; | |
338 | ||
339 | memset(&info, 0, sizeof(info)); | |
340 | ||
341 | info.default_color = 0x000000; | |
342 | info.trans_enabled = false; | |
343 | info.partial_alpha_enabled = false; | |
344 | info.cpr_enable = false; | |
345 | ||
50638ae5 | 346 | priv->dispc_ops->mgr_setup(priv->dispc, omap_crtc->channel, &info); |
7e3d9274 TV |
347 | } |
348 | ||
971fb3e5 LP |
349 | /* ----------------------------------------------------------------------------- |
350 | * CRTC Functions | |
f5f9454c RC |
351 | */ |
352 | ||
cd5351f4 RC |
353 | static void omap_crtc_destroy(struct drm_crtc *crtc) |
354 | { | |
355 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
f5f9454c RC |
356 | |
357 | DBG("%s", omap_crtc->name); | |
358 | ||
cd5351f4 | 359 | drm_crtc_cleanup(crtc); |
f5f9454c | 360 | |
cd5351f4 RC |
361 | kfree(omap_crtc); |
362 | } | |
363 | ||
ce9a8f1a LP |
364 | static void omap_crtc_arm_event(struct drm_crtc *crtc) |
365 | { | |
366 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
367 | ||
368 | WARN_ON(omap_crtc->pending); | |
369 | omap_crtc->pending = true; | |
370 | ||
371 | if (crtc->state->event) { | |
372 | omap_crtc->event = crtc->state->event; | |
373 | crtc->state->event = NULL; | |
374 | } | |
375 | } | |
376 | ||
0b20a0f8 LP |
377 | static void omap_crtc_atomic_enable(struct drm_crtc *crtc, |
378 | struct drm_crtc_state *old_state) | |
cd5351f4 RC |
379 | { |
380 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
14389a37 | 381 | int ret; |
cd5351f4 | 382 | |
f1d57fb5 | 383 | DBG("%s", omap_crtc->name); |
f5f9454c | 384 | |
d173d3dc | 385 | spin_lock_irq(&crtc->dev->event_lock); |
14389a37 LP |
386 | drm_crtc_vblank_on(crtc); |
387 | ret = drm_crtc_vblank_get(crtc); | |
388 | WARN_ON(ret != 0); | |
389 | ||
ce9a8f1a | 390 | omap_crtc_arm_event(crtc); |
d173d3dc | 391 | spin_unlock_irq(&crtc->dev->event_lock); |
cd5351f4 RC |
392 | } |
393 | ||
64581714 LP |
394 | static void omap_crtc_atomic_disable(struct drm_crtc *crtc, |
395 | struct drm_crtc_state *old_state) | |
cd5351f4 | 396 | { |
f1d57fb5 | 397 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
f1d57fb5 LP |
398 | |
399 | DBG("%s", omap_crtc->name); | |
400 | ||
ce9a8f1a LP |
401 | spin_lock_irq(&crtc->dev->event_lock); |
402 | if (crtc->state->event) { | |
403 | drm_crtc_send_vblank_event(crtc, crtc->state->event); | |
404 | crtc->state->event = NULL; | |
405 | } | |
406 | spin_unlock_irq(&crtc->dev->event_lock); | |
407 | ||
f1d57fb5 | 408 | drm_crtc_vblank_off(crtc); |
cd5351f4 RC |
409 | } |
410 | ||
a7631c4b PU |
411 | static enum drm_mode_status omap_crtc_mode_valid(struct drm_crtc *crtc, |
412 | const struct drm_display_mode *mode) | |
413 | { | |
414 | struct omap_drm_private *priv = crtc->dev->dev_private; | |
415 | ||
416 | /* Check for bandwidth limit */ | |
417 | if (priv->max_bandwidth) { | |
418 | /* | |
419 | * Estimation for the bandwidth need of a given mode with one | |
420 | * full screen plane: | |
421 | * bandwidth = resolution * 32bpp * (pclk / (vtotal * htotal)) | |
422 | * ^^ Refresh rate ^^ | |
423 | * | |
424 | * The interlaced mode is taken into account by using the | |
425 | * pixelclock in the calculation. | |
426 | * | |
427 | * The equation is rearranged for 64bit arithmetic. | |
428 | */ | |
429 | uint64_t bandwidth = mode->clock * 1000; | |
430 | unsigned int bpp = 4; | |
431 | ||
432 | bandwidth = bandwidth * mode->hdisplay * mode->vdisplay * bpp; | |
433 | bandwidth = div_u64(bandwidth, mode->htotal * mode->vtotal); | |
434 | ||
435 | /* | |
436 | * Reject modes which would need more bandwidth if used with one | |
437 | * full resolution plane (most common use case). | |
438 | */ | |
439 | if (priv->max_bandwidth < bandwidth) | |
440 | return MODE_BAD; | |
441 | } | |
442 | ||
443 | return MODE_OK; | |
444 | } | |
445 | ||
f7a73b65 | 446 | static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) |
cd5351f4 RC |
447 | { |
448 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
f7a73b65 | 449 | struct drm_display_mode *mode = &crtc->state->adjusted_mode; |
50fa9f0b TV |
450 | struct omap_drm_private *priv = crtc->dev->dev_private; |
451 | const u32 flags_mask = DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_DE_LOW | | |
452 | DISPLAY_FLAGS_PIXDATA_POSEDGE | DISPLAY_FLAGS_PIXDATA_NEGEDGE | | |
453 | DISPLAY_FLAGS_SYNC_POSEDGE | DISPLAY_FLAGS_SYNC_NEGEDGE; | |
454 | unsigned int i; | |
f5f9454c RC |
455 | |
456 | DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", | |
f7a73b65 LP |
457 | omap_crtc->name, mode->base.id, mode->name, |
458 | mode->vrefresh, mode->clock, | |
459 | mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, | |
460 | mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, | |
461 | mode->type, mode->flags); | |
f5f9454c | 462 | |
da11bbbb | 463 | drm_display_mode_to_videomode(mode, &omap_crtc->vm); |
50fa9f0b TV |
464 | |
465 | /* | |
466 | * HACK: This fixes the vm flags. | |
467 | * struct drm_display_mode does not contain the VSYNC/HSYNC/DE flags | |
468 | * and they get lost when converting back and forth between | |
469 | * struct drm_display_mode and struct videomode. The hack below | |
470 | * goes and fetches the missing flags from the panel drivers. | |
471 | * | |
472 | * Correct solution would be to use DRM's bus-flags, but that's not | |
473 | * easily possible before the omapdrm's panel/encoder driver model | |
474 | * has been changed to the DRM model. | |
475 | */ | |
476 | ||
477 | for (i = 0; i < priv->num_encoders; ++i) { | |
478 | struct drm_encoder *encoder = priv->encoders[i]; | |
479 | ||
480 | if (encoder->crtc == crtc) { | |
481 | struct omap_dss_device *dssdev; | |
482 | ||
483 | dssdev = omap_encoder_get_dssdev(encoder); | |
484 | ||
485 | if (dssdev) { | |
486 | struct videomode vm = {0}; | |
487 | ||
488 | dssdev->driver->get_timings(dssdev, &vm); | |
489 | ||
490 | omap_crtc->vm.flags |= vm.flags & flags_mask; | |
491 | } | |
492 | ||
493 | break; | |
494 | } | |
495 | } | |
cd5351f4 RC |
496 | } |
497 | ||
492a426a JS |
498 | static int omap_crtc_atomic_check(struct drm_crtc *crtc, |
499 | struct drm_crtc_state *state) | |
500 | { | |
3dfeb631 ML |
501 | struct drm_plane_state *pri_state; |
502 | ||
492a426a | 503 | if (state->color_mgmt_changed && state->gamma_lut) { |
dfe9cfcc | 504 | unsigned int length = state->gamma_lut->length / |
492a426a JS |
505 | sizeof(struct drm_color_lut); |
506 | ||
507 | if (length < 2) | |
508 | return -EINVAL; | |
509 | } | |
510 | ||
3dfeb631 ML |
511 | pri_state = drm_atomic_get_new_plane_state(state->state, crtc->primary); |
512 | if (pri_state) { | |
513 | struct omap_crtc_state *omap_crtc_state = | |
514 | to_omap_crtc_state(state); | |
515 | ||
516 | /* Mirror new values for zpos and rotation in omap_crtc_state */ | |
517 | omap_crtc_state->zpos = pri_state->zpos; | |
518 | omap_crtc_state->rotation = pri_state->rotation; | |
519 | } | |
520 | ||
492a426a JS |
521 | return 0; |
522 | } | |
523 | ||
c201d00f | 524 | static void omap_crtc_atomic_begin(struct drm_crtc *crtc, |
577d3983 | 525 | struct drm_crtc_state *old_crtc_state) |
de8e4100 | 526 | { |
fa16d262 | 527 | } |
cd5351f4 | 528 | |
c201d00f | 529 | static void omap_crtc_atomic_flush(struct drm_crtc *crtc, |
577d3983 | 530 | struct drm_crtc_state *old_crtc_state) |
fa16d262 | 531 | { |
9f759225 | 532 | struct omap_drm_private *priv = crtc->dev->dev_private; |
6646dfd0 | 533 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
14389a37 | 534 | int ret; |
6646dfd0 | 535 | |
492a426a JS |
536 | if (crtc->state->color_mgmt_changed) { |
537 | struct drm_color_lut *lut = NULL; | |
dfe9cfcc | 538 | unsigned int length = 0; |
492a426a JS |
539 | |
540 | if (crtc->state->gamma_lut) { | |
541 | lut = (struct drm_color_lut *) | |
542 | crtc->state->gamma_lut->data; | |
543 | length = crtc->state->gamma_lut->length / | |
544 | sizeof(*lut); | |
545 | } | |
50638ae5 LP |
546 | priv->dispc_ops->mgr_set_gamma(priv->dispc, omap_crtc->channel, |
547 | lut, length); | |
492a426a JS |
548 | } |
549 | ||
7e3d9274 TV |
550 | omap_crtc_write_crtc_properties(crtc); |
551 | ||
e025d386 | 552 | /* Only flush the CRTC if it is currently enabled. */ |
f933a3a9 LP |
553 | if (!omap_crtc->enabled) |
554 | return; | |
5f741b39 | 555 | |
f933a3a9 | 556 | DBG("%s: GO", omap_crtc->name); |
6646dfd0 | 557 | |
14389a37 LP |
558 | ret = drm_crtc_vblank_get(crtc); |
559 | WARN_ON(ret != 0); | |
560 | ||
d173d3dc | 561 | spin_lock_irq(&crtc->dev->event_lock); |
50638ae5 | 562 | priv->dispc_ops->mgr_go(priv->dispc, omap_crtc->channel); |
ce9a8f1a | 563 | omap_crtc_arm_event(crtc); |
d173d3dc | 564 | spin_unlock_irq(&crtc->dev->event_lock); |
cd5351f4 RC |
565 | } |
566 | ||
afc34932 LP |
567 | static int omap_crtc_atomic_set_property(struct drm_crtc *crtc, |
568 | struct drm_crtc_state *state, | |
569 | struct drm_property *property, | |
dfe9cfcc | 570 | u64 val) |
3c810c61 | 571 | { |
3dfeb631 ML |
572 | struct omap_drm_private *priv = crtc->dev->dev_private; |
573 | struct drm_plane_state *plane_state; | |
afc34932 | 574 | |
3dfeb631 ML |
575 | /* |
576 | * Delegate property set to the primary plane. Get the plane state and | |
577 | * set the property directly, the shadow copy will be assigned in the | |
578 | * omap_crtc_atomic_check callback. This way updates to plane state will | |
579 | * always be mirrored in the crtc state correctly. | |
580 | */ | |
581 | plane_state = drm_atomic_get_plane_state(state->state, crtc->primary); | |
582 | if (IS_ERR(plane_state)) | |
583 | return PTR_ERR(plane_state); | |
584 | ||
585 | if (property == crtc->primary->rotation_property) | |
586 | plane_state->rotation = val; | |
587 | else if (property == priv->zorder_prop) | |
588 | plane_state->zpos = val; | |
589 | else | |
590 | return -EINVAL; | |
6bdad6cf | 591 | |
3dfeb631 | 592 | return 0; |
afc34932 | 593 | } |
1e0fdfc2 | 594 | |
afc34932 LP |
595 | static int omap_crtc_atomic_get_property(struct drm_crtc *crtc, |
596 | const struct drm_crtc_state *state, | |
597 | struct drm_property *property, | |
dfe9cfcc | 598 | u64 *val) |
afc34932 | 599 | { |
3dfeb631 ML |
600 | struct omap_drm_private *priv = crtc->dev->dev_private; |
601 | struct omap_crtc_state *omap_state = to_omap_crtc_state(state); | |
602 | ||
603 | if (property == crtc->primary->rotation_property) | |
604 | *val = omap_state->rotation; | |
605 | else if (property == priv->zorder_prop) | |
606 | *val = omap_state->zpos; | |
607 | else | |
608 | return -EINVAL; | |
609 | ||
610 | return 0; | |
611 | } | |
612 | ||
613 | static void omap_crtc_reset(struct drm_crtc *crtc) | |
614 | { | |
615 | if (crtc->state) | |
616 | __drm_atomic_helper_crtc_destroy_state(crtc->state); | |
617 | ||
618 | kfree(crtc->state); | |
619 | crtc->state = kzalloc(sizeof(struct omap_crtc_state), GFP_KERNEL); | |
620 | ||
621 | if (crtc->state) | |
622 | crtc->state->crtc = crtc; | |
623 | } | |
624 | ||
625 | static struct drm_crtc_state * | |
626 | omap_crtc_duplicate_state(struct drm_crtc *crtc) | |
627 | { | |
628 | struct omap_crtc_state *state, *current_state; | |
629 | ||
630 | if (WARN_ON(!crtc->state)) | |
631 | return NULL; | |
632 | ||
633 | current_state = to_omap_crtc_state(crtc->state); | |
634 | ||
635 | state = kmalloc(sizeof(*state), GFP_KERNEL); | |
2419672f DC |
636 | if (!state) |
637 | return NULL; | |
638 | ||
639 | __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); | |
3dfeb631 ML |
640 | |
641 | state->zpos = current_state->zpos; | |
642 | state->rotation = current_state->rotation; | |
6bdad6cf | 643 | |
3dfeb631 | 644 | return &state->base; |
3c810c61 RC |
645 | } |
646 | ||
cd5351f4 | 647 | static const struct drm_crtc_funcs omap_crtc_funcs = { |
3dfeb631 | 648 | .reset = omap_crtc_reset, |
9416c9df | 649 | .set_config = drm_atomic_helper_set_config, |
cd5351f4 | 650 | .destroy = omap_crtc_destroy, |
fa16d262 | 651 | .page_flip = drm_atomic_helper_page_flip, |
492a426a | 652 | .gamma_set = drm_atomic_helper_legacy_gamma_set, |
3dfeb631 | 653 | .atomic_duplicate_state = omap_crtc_duplicate_state, |
69a12263 | 654 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
afc34932 LP |
655 | .atomic_set_property = omap_crtc_atomic_set_property, |
656 | .atomic_get_property = omap_crtc_atomic_get_property, | |
0396162a TV |
657 | .enable_vblank = omap_irq_enable_vblank, |
658 | .disable_vblank = omap_irq_disable_vblank, | |
cd5351f4 RC |
659 | }; |
660 | ||
661 | static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { | |
f7a73b65 | 662 | .mode_set_nofb = omap_crtc_mode_set_nofb, |
492a426a | 663 | .atomic_check = omap_crtc_atomic_check, |
de8e4100 LP |
664 | .atomic_begin = omap_crtc_atomic_begin, |
665 | .atomic_flush = omap_crtc_atomic_flush, | |
0b20a0f8 | 666 | .atomic_enable = omap_crtc_atomic_enable, |
64581714 | 667 | .atomic_disable = omap_crtc_atomic_disable, |
a7631c4b | 668 | .mode_valid = omap_crtc_mode_valid, |
cd5351f4 RC |
669 | }; |
670 | ||
971fb3e5 LP |
671 | /* ----------------------------------------------------------------------------- |
672 | * Init and Cleanup | |
673 | */ | |
e2f8fd74 | 674 | |
f5f9454c | 675 | static const char *channel_names[] = { |
222025e4 LP |
676 | [OMAP_DSS_CHANNEL_LCD] = "lcd", |
677 | [OMAP_DSS_CHANNEL_DIGIT] = "tv", | |
678 | [OMAP_DSS_CHANNEL_LCD2] = "lcd2", | |
679 | [OMAP_DSS_CHANNEL_LCD3] = "lcd3", | |
f5f9454c RC |
680 | }; |
681 | ||
64cb8179 | 682 | void omap_crtc_pre_init(struct omap_drm_private *priv) |
04b1fc02 | 683 | { |
e8e13b15 JS |
684 | memset(omap_crtcs, 0, sizeof(omap_crtcs)); |
685 | ||
64cb8179 | 686 | dss_install_mgr_ops(&mgr_ops, priv); |
04b1fc02 TV |
687 | } |
688 | ||
3a01ab25 AT |
689 | void omap_crtc_pre_uninit(void) |
690 | { | |
691 | dss_uninstall_mgr_ops(); | |
692 | } | |
693 | ||
cd5351f4 RC |
694 | /* initialize crtc */ |
695 | struct drm_crtc *omap_crtc_init(struct drm_device *dev, | |
e8e13b15 | 696 | struct drm_plane *plane, struct omap_dss_device *dssdev) |
cd5351f4 | 697 | { |
9f759225 | 698 | struct omap_drm_private *priv = dev->dev_private; |
cd5351f4 | 699 | struct drm_crtc *crtc = NULL; |
f5f9454c | 700 | struct omap_crtc *omap_crtc; |
e8e13b15 JS |
701 | enum omap_channel channel; |
702 | struct omap_dss_device *out; | |
ef6b0e02 | 703 | int ret; |
f5f9454c | 704 | |
e8e13b15 JS |
705 | out = omapdss_find_output_from_display(dssdev); |
706 | channel = out->dispc_channel; | |
707 | omap_dss_put_device(out); | |
708 | ||
f5f9454c | 709 | DBG("%s", channel_names[channel]); |
cd5351f4 | 710 | |
e8e13b15 JS |
711 | /* Multiple displays on same channel is not allowed */ |
712 | if (WARN_ON(omap_crtcs[channel] != NULL)) | |
713 | return ERR_PTR(-EINVAL); | |
714 | ||
f5f9454c | 715 | omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); |
78110bb8 | 716 | if (!omap_crtc) |
e8e13b15 | 717 | return ERR_PTR(-ENOMEM); |
cd5351f4 | 718 | |
cd5351f4 | 719 | crtc = &omap_crtc->base; |
bb5c2d9a | 720 | |
5f741b39 | 721 | init_waitqueue_head(&omap_crtc->pending_wait); |
f5f9454c | 722 | |
0d8f371f | 723 | omap_crtc->channel = channel; |
0d8f371f | 724 | omap_crtc->name = channel_names[channel]; |
0d8f371f | 725 | |
ef6b0e02 | 726 | ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, |
f9882876 | 727 | &omap_crtc_funcs, NULL); |
ef6b0e02 | 728 | if (ret < 0) { |
e8e13b15 JS |
729 | dev_err(dev->dev, "%s(): could not init crtc for: %s\n", |
730 | __func__, dssdev->name); | |
ef6b0e02 | 731 | kfree(omap_crtc); |
e8e13b15 | 732 | return ERR_PTR(ret); |
ef6b0e02 LP |
733 | } |
734 | ||
cd5351f4 RC |
735 | drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); |
736 | ||
492a426a JS |
737 | /* The dispc API adapts to what ever size, but the HW supports |
738 | * 256 element gamma table for LCDs and 1024 element table for | |
739 | * OMAP_DSS_CHANNEL_DIGIT. X server assumes 256 element gamma | |
740 | * tables so lets use that. Size of HW gamma table can be | |
741 | * extracted with dispc_mgr_gamma_size(). If it returns 0 | |
742 | * gamma table is not supprted. | |
743 | */ | |
50638ae5 | 744 | if (priv->dispc_ops->mgr_gamma_size(priv->dispc, channel)) { |
dfe9cfcc | 745 | unsigned int gamma_lut_size = 256; |
492a426a JS |
746 | |
747 | drm_crtc_enable_color_mgmt(crtc, 0, false, gamma_lut_size); | |
748 | drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size); | |
749 | } | |
750 | ||
ef6b0e02 | 751 | omap_plane_install_properties(crtc->primary, &crtc->base); |
3c810c61 | 752 | |
04b1fc02 TV |
753 | omap_crtcs[channel] = omap_crtc; |
754 | ||
cd5351f4 | 755 | return crtc; |
cd5351f4 | 756 | } |