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