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