1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2012 Russell King |
4 | * Rewritten from the dovefb driver, and Armada510 manuals. |
5 | */ |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/component.h> |
9 | #include <linux/module.h> |
10 | #include <linux/of.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/property.h> |
13 | |
14 | #include <drm/drm_atomic.h> |
15 | #include <drm/drm_atomic_helper.h> |
16 | #include <drm/drm_probe_helper.h> |
17 | #include <drm/drm_vblank.h> |
18 | |
19 | #include "armada_crtc.h" |
20 | #include "armada_drm.h" |
21 | #include "armada_fb.h" |
22 | #include "armada_gem.h" |
23 | #include "armada_hw.h" |
24 | #include "armada_plane.h" |
25 | #include "armada_trace.h" |
26 | |
27 | /* |
28 | * A note about interlacing. Let's consider HDMI 1920x1080i. |
29 | * The timing parameters we have from X are: |
30 | * Hact HsyA HsyI Htot Vact VsyA VsyI Vtot |
31 | * 1920 2448 2492 2640 1080 1084 1094 1125 |
32 | * Which get translated to: |
33 | * Hact HsyA HsyI Htot Vact VsyA VsyI Vtot |
34 | * 1920 2448 2492 2640 540 542 547 562 |
35 | * |
36 | * This is how it is defined by CEA-861-D - line and pixel numbers are |
37 | * referenced to the rising edge of VSYNC and HSYNC. Total clocks per |
38 | * line: 2640. The odd frame, the first active line is at line 21, and |
39 | * the even frame, the first active line is 584. |
40 | * |
41 | * LN: 560 561 562 563 567 568 569 |
42 | * DE: ~~~|____________________________//__________________________ |
43 | * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____ |
44 | * VSYNC: _________________________|~~~~~~//~~~~~~~~~~~~~~~|__________ |
45 | * 22 blanking lines. VSYNC at 1320 (referenced to the HSYNC rising edge). |
46 | * |
47 | * LN: 1123 1124 1125 1 5 6 7 |
48 | * DE: ~~~|____________________________//__________________________ |
49 | * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____ |
50 | * VSYNC: ____________________|~~~~~~~~~~~//~~~~~~~~~~|_______________ |
51 | * 23 blanking lines |
52 | * |
53 | * The Armada LCD Controller line and pixel numbers are, like X timings, |
54 | * referenced to the top left of the active frame. |
55 | * |
56 | * So, translating these to our LCD controller: |
57 | * Odd frame, 563 total lines, VSYNC at line 543-548, pixel 1128. |
58 | * Even frame, 562 total lines, VSYNC at line 542-547, pixel 2448. |
59 | * Note: Vsync front porch remains constant! |
60 | * |
61 | * if (odd_frame) { |
62 | * vtotal = mode->crtc_vtotal + 1; |
63 | * vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay + 1; |
64 | * vhorizpos = mode->crtc_hsync_start - mode->crtc_htotal / 2 |
65 | * } else { |
66 | * vtotal = mode->crtc_vtotal; |
67 | * vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay; |
68 | * vhorizpos = mode->crtc_hsync_start; |
69 | * } |
70 | * vfrontporch = mode->crtc_vtotal - mode->crtc_vsync_end; |
71 | * |
72 | * So, we need to reprogram these registers on each vsync event: |
73 | * LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL |
74 | * |
75 | * Note: we do not use the frame done interrupts because these appear |
76 | * to happen too early, and lead to jitter on the display (presumably |
77 | * they occur at the end of the last active line, before the vsync back |
78 | * porch, which we're reprogramming.) |
79 | */ |
80 | |
81 | void |
82 | armada_drm_crtc_update_regs(struct armada_crtc *dcrtc, struct armada_regs *regs) |
83 | { |
84 | while (regs->offset != ~0) { |
85 | void __iomem *reg = dcrtc->base + regs->offset; |
86 | uint32_t val; |
87 | |
88 | val = regs->mask; |
89 | if (val != 0) |
90 | val &= readl_relaxed(reg); |
91 | writel_relaxed(val | regs->val, reg); |
92 | ++regs; |
93 | } |
94 | } |
95 | |
96 | static void armada_drm_crtc_update(struct armada_crtc *dcrtc, bool enable) |
97 | { |
98 | uint32_t dumb_ctrl; |
99 | |
100 | dumb_ctrl = dcrtc->cfg_dumb_ctrl; |
101 | |
102 | if (enable) |
103 | dumb_ctrl |= CFG_DUMB_ENA; |
104 | |
105 | /* |
106 | * When the dumb interface isn't in DUMB24_RGB888_0 mode, it might |
107 | * be using SPI or GPIO. If we set this to DUMB_BLANK, we will |
108 | * force LCD_D[23:0] to output blank color, overriding the GPIO or |
109 | * SPI usage. So leave it as-is unless in DUMB24_RGB888_0 mode. |
110 | */ |
111 | if (!enable && (dumb_ctrl & DUMB_MASK) == DUMB24_RGB888_0) { |
112 | dumb_ctrl &= ~DUMB_MASK; |
113 | dumb_ctrl |= DUMB_BLANK; |
114 | } |
115 | |
116 | armada_updatel(val: dumb_ctrl, |
117 | mask: ~(CFG_INV_CSYNC | CFG_INV_HSYNC | CFG_INV_VSYNC), |
118 | ptr: dcrtc->base + LCD_SPU_DUMB_CTRL); |
119 | } |
120 | |
121 | static void armada_drm_crtc_queue_state_event(struct drm_crtc *crtc) |
122 | { |
123 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
124 | struct drm_pending_vblank_event *event; |
125 | |
126 | /* If we have an event, we need vblank events enabled */ |
127 | event = xchg(&crtc->state->event, NULL); |
128 | if (event) { |
129 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); |
130 | dcrtc->event = event; |
131 | } |
132 | } |
133 | |
134 | static void armada_drm_update_gamma(struct drm_crtc *crtc) |
135 | { |
136 | struct drm_property_blob *blob = crtc->state->gamma_lut; |
137 | void __iomem *base = drm_to_armada_crtc(crtc)->base; |
138 | int i; |
139 | |
140 | if (blob) { |
141 | struct drm_color_lut *lut = blob->data; |
142 | |
143 | armada_updatel(val: CFG_CSB_256x8, mask: CFG_CSB_256x8 | CFG_PDWN256x8, |
144 | ptr: base + LCD_SPU_SRAM_PARA1); |
145 | |
146 | for (i = 0; i < 256; i++) { |
147 | writel_relaxed(drm_color_lut_extract(lut[i].red, 8), |
148 | base + LCD_SPU_SRAM_WRDAT); |
149 | writel_relaxed(i | SRAM_WRITE | SRAM_GAMMA_YR, |
150 | base + LCD_SPU_SRAM_CTRL); |
151 | readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN); |
152 | writel_relaxed(drm_color_lut_extract(lut[i].green, 8), |
153 | base + LCD_SPU_SRAM_WRDAT); |
154 | writel_relaxed(i | SRAM_WRITE | SRAM_GAMMA_UG, |
155 | base + LCD_SPU_SRAM_CTRL); |
156 | readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN); |
157 | writel_relaxed(drm_color_lut_extract(lut[i].blue, 8), |
158 | base + LCD_SPU_SRAM_WRDAT); |
159 | writel_relaxed(i | SRAM_WRITE | SRAM_GAMMA_VB, |
160 | base + LCD_SPU_SRAM_CTRL); |
161 | readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN); |
162 | } |
163 | armada_updatel(val: CFG_GAMMA_ENA, mask: CFG_GAMMA_ENA, |
164 | ptr: base + LCD_SPU_DMA_CTRL0); |
165 | } else { |
166 | armada_updatel(val: 0, mask: CFG_GAMMA_ENA, ptr: base + LCD_SPU_DMA_CTRL0); |
167 | armada_updatel(val: CFG_PDWN256x8, mask: CFG_CSB_256x8 | CFG_PDWN256x8, |
168 | ptr: base + LCD_SPU_SRAM_PARA1); |
169 | } |
170 | } |
171 | |
172 | static enum drm_mode_status armada_drm_crtc_mode_valid(struct drm_crtc *crtc, |
173 | const struct drm_display_mode *mode) |
174 | { |
175 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
176 | |
177 | if (mode->vscan > 1) |
178 | return MODE_NO_VSCAN; |
179 | |
180 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) |
181 | return MODE_NO_DBLESCAN; |
182 | |
183 | if (mode->flags & DRM_MODE_FLAG_HSKEW) |
184 | return MODE_H_ILLEGAL; |
185 | |
186 | /* We can't do interlaced modes if we don't have the SPU_ADV_REG */ |
187 | if (!dcrtc->variant->has_spu_adv_reg && |
188 | mode->flags & DRM_MODE_FLAG_INTERLACE) |
189 | return MODE_NO_INTERLACE; |
190 | |
191 | if (mode->flags & (DRM_MODE_FLAG_BCAST | DRM_MODE_FLAG_PIXMUX | |
192 | DRM_MODE_FLAG_CLKDIV2)) |
193 | return MODE_BAD; |
194 | |
195 | return MODE_OK; |
196 | } |
197 | |
198 | /* The mode_config.mutex will be held for this call */ |
199 | static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, |
200 | const struct drm_display_mode *mode, struct drm_display_mode *adj) |
201 | { |
202 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
203 | int ret; |
204 | |
205 | /* |
206 | * Set CRTC modesetting parameters for the adjusted mode. This is |
207 | * applied after the connectors, bridges, and encoders have fixed up |
208 | * this mode, as described above drm_atomic_helper_check_modeset(). |
209 | */ |
210 | drm_mode_set_crtcinfo(p: adj, CRTC_INTERLACE_HALVE_V); |
211 | |
212 | /* |
213 | * Validate the adjusted mode in case an encoder/bridge has set |
214 | * something we don't support. |
215 | */ |
216 | if (armada_drm_crtc_mode_valid(crtc, mode: adj) != MODE_OK) |
217 | return false; |
218 | |
219 | /* Check whether the display mode is possible */ |
220 | ret = dcrtc->variant->compute_clock(dcrtc, adj, NULL); |
221 | if (ret) |
222 | return false; |
223 | |
224 | return true; |
225 | } |
226 | |
227 | /* These are locked by dev->vbl_lock */ |
228 | static void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask) |
229 | { |
230 | if (dcrtc->irq_ena & mask) { |
231 | dcrtc->irq_ena &= ~mask; |
232 | writel(val: dcrtc->irq_ena, addr: dcrtc->base + LCD_SPU_IRQ_ENA); |
233 | } |
234 | } |
235 | |
236 | static void armada_drm_crtc_enable_irq(struct armada_crtc *dcrtc, u32 mask) |
237 | { |
238 | if ((dcrtc->irq_ena & mask) != mask) { |
239 | dcrtc->irq_ena |= mask; |
240 | writel(val: dcrtc->irq_ena, addr: dcrtc->base + LCD_SPU_IRQ_ENA); |
241 | if (readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR) & mask) |
242 | writel(val: 0, addr: dcrtc->base + LCD_SPU_IRQ_ISR); |
243 | } |
244 | } |
245 | |
246 | static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) |
247 | { |
248 | struct drm_pending_vblank_event *event; |
249 | void __iomem *base = dcrtc->base; |
250 | |
251 | if (stat & DMA_FF_UNDERFLOW) |
252 | DRM_ERROR("video underflow on crtc %u\n" , dcrtc->num); |
253 | if (stat & GRA_FF_UNDERFLOW) |
254 | DRM_ERROR("graphics underflow on crtc %u\n" , dcrtc->num); |
255 | |
256 | if (stat & VSYNC_IRQ) |
257 | drm_crtc_handle_vblank(crtc: &dcrtc->crtc); |
258 | |
259 | spin_lock(lock: &dcrtc->irq_lock); |
260 | if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) { |
261 | int i = stat & GRA_FRAME_IRQ0 ? 0 : 1; |
262 | uint32_t val; |
263 | |
264 | writel_relaxed(dcrtc->v[i].spu_v_porch, base + LCD_SPU_V_PORCH); |
265 | writel_relaxed(dcrtc->v[i].spu_v_h_total, |
266 | base + LCD_SPUT_V_H_TOTAL); |
267 | |
268 | val = readl_relaxed(base + LCD_SPU_ADV_REG); |
269 | val &= ~(ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN); |
270 | val |= dcrtc->v[i].spu_adv_reg; |
271 | writel_relaxed(val, base + LCD_SPU_ADV_REG); |
272 | } |
273 | |
274 | if (stat & dcrtc->irq_ena & DUMB_FRAMEDONE) { |
275 | if (dcrtc->update_pending) { |
276 | armada_drm_crtc_update_regs(dcrtc, regs: dcrtc->regs); |
277 | dcrtc->update_pending = false; |
278 | } |
279 | if (dcrtc->cursor_update) { |
280 | writel_relaxed(dcrtc->cursor_hw_pos, |
281 | base + LCD_SPU_HWC_OVSA_HPXL_VLN); |
282 | writel_relaxed(dcrtc->cursor_hw_sz, |
283 | base + LCD_SPU_HWC_HPXL_VLN); |
284 | armada_updatel(val: CFG_HWC_ENA, |
285 | mask: CFG_HWC_ENA | CFG_HWC_1BITMOD | |
286 | CFG_HWC_1BITENA, |
287 | ptr: base + LCD_SPU_DMA_CTRL0); |
288 | dcrtc->cursor_update = false; |
289 | } |
290 | armada_drm_crtc_disable_irq(dcrtc, mask: DUMB_FRAMEDONE_ENA); |
291 | } |
292 | spin_unlock(lock: &dcrtc->irq_lock); |
293 | |
294 | if (stat & VSYNC_IRQ && !dcrtc->update_pending) { |
295 | event = xchg(&dcrtc->event, NULL); |
296 | if (event) { |
297 | spin_lock(lock: &dcrtc->crtc.dev->event_lock); |
298 | drm_crtc_send_vblank_event(crtc: &dcrtc->crtc, e: event); |
299 | spin_unlock(lock: &dcrtc->crtc.dev->event_lock); |
300 | drm_crtc_vblank_put(crtc: &dcrtc->crtc); |
301 | } |
302 | } |
303 | } |
304 | |
305 | static irqreturn_t armada_drm_irq(int irq, void *arg) |
306 | { |
307 | struct armada_crtc *dcrtc = arg; |
308 | u32 v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); |
309 | |
310 | /* |
311 | * Reading the ISR appears to clear bits provided CLEAN_SPU_IRQ_ISR |
312 | * is set. Writing has some other effect to acknowledge the IRQ - |
313 | * without this, we only get a single IRQ. |
314 | */ |
315 | writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); |
316 | |
317 | trace_armada_drm_irq(crtc: &dcrtc->crtc, stat); |
318 | |
319 | /* Mask out those interrupts we haven't enabled */ |
320 | v = stat & dcrtc->irq_ena; |
321 | |
322 | if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) { |
323 | armada_drm_crtc_irq(dcrtc, stat); |
324 | return IRQ_HANDLED; |
325 | } |
326 | return IRQ_NONE; |
327 | } |
328 | |
329 | /* The mode_config.mutex will be held for this call */ |
330 | static void armada_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) |
331 | { |
332 | struct drm_display_mode *adj = &crtc->state->adjusted_mode; |
333 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
334 | struct armada_regs regs[17]; |
335 | uint32_t lm, rm, tm, bm, val, sclk; |
336 | unsigned long flags; |
337 | unsigned i; |
338 | bool interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE); |
339 | |
340 | i = 0; |
341 | rm = adj->crtc_hsync_start - adj->crtc_hdisplay; |
342 | lm = adj->crtc_htotal - adj->crtc_hsync_end; |
343 | bm = adj->crtc_vsync_start - adj->crtc_vdisplay; |
344 | tm = adj->crtc_vtotal - adj->crtc_vsync_end; |
345 | |
346 | DRM_DEBUG_KMS("[CRTC:%d:%s] mode " DRM_MODE_FMT "\n" , |
347 | crtc->base.id, crtc->name, DRM_MODE_ARG(adj)); |
348 | DRM_DEBUG_KMS("lm %d rm %d tm %d bm %d\n" , lm, rm, tm, bm); |
349 | |
350 | /* Now compute the divider for real */ |
351 | dcrtc->variant->compute_clock(dcrtc, adj, &sclk); |
352 | |
353 | armada_reg_queue_set(regs, i, sclk, LCD_CFG_SCLK_DIV); |
354 | |
355 | spin_lock_irqsave(&dcrtc->irq_lock, flags); |
356 | |
357 | dcrtc->interlaced = interlaced; |
358 | /* Even interlaced/progressive frame */ |
359 | dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 | |
360 | adj->crtc_htotal; |
361 | dcrtc->v[1].spu_v_porch = tm << 16 | bm; |
362 | val = adj->crtc_hsync_start; |
363 | dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN; |
364 | |
365 | if (interlaced) { |
366 | /* Odd interlaced frame */ |
367 | val -= adj->crtc_htotal / 2; |
368 | dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN; |
369 | dcrtc->v[0].spu_v_h_total = dcrtc->v[1].spu_v_h_total + |
370 | (1 << 16); |
371 | dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1; |
372 | } else { |
373 | dcrtc->v[0] = dcrtc->v[1]; |
374 | } |
375 | |
376 | val = adj->crtc_vdisplay << 16 | adj->crtc_hdisplay; |
377 | |
378 | armada_reg_queue_set(regs, i, val, LCD_SPU_V_H_ACTIVE); |
379 | armada_reg_queue_set(regs, i, (lm << 16) | rm, LCD_SPU_H_PORCH); |
380 | armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_porch, LCD_SPU_V_PORCH); |
381 | armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total, |
382 | LCD_SPUT_V_H_TOTAL); |
383 | |
384 | if (dcrtc->variant->has_spu_adv_reg) |
385 | armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg, |
386 | ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | |
387 | ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); |
388 | |
389 | val = adj->flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0; |
390 | armada_reg_queue_mod(regs, i, val, CFG_VSYNC_INV, LCD_SPU_DMA_CTRL1); |
391 | |
392 | /* |
393 | * The documentation doesn't indicate what the normal state of |
394 | * the sync signals are. Sebastian Hesselbart kindly probed |
395 | * these signals on his board to determine their state. |
396 | * |
397 | * The non-inverted state of the sync signals is active high. |
398 | * Setting these bits makes the appropriate signal active low. |
399 | */ |
400 | val = 0; |
401 | if (adj->flags & DRM_MODE_FLAG_NCSYNC) |
402 | val |= CFG_INV_CSYNC; |
403 | if (adj->flags & DRM_MODE_FLAG_NHSYNC) |
404 | val |= CFG_INV_HSYNC; |
405 | if (adj->flags & DRM_MODE_FLAG_NVSYNC) |
406 | val |= CFG_INV_VSYNC; |
407 | armada_reg_queue_mod(regs, i, val, CFG_INV_CSYNC | CFG_INV_HSYNC | |
408 | CFG_INV_VSYNC, LCD_SPU_DUMB_CTRL); |
409 | armada_reg_queue_end(regs, i); |
410 | |
411 | armada_drm_crtc_update_regs(dcrtc, regs); |
412 | spin_unlock_irqrestore(lock: &dcrtc->irq_lock, flags); |
413 | } |
414 | |
415 | static int armada_drm_crtc_atomic_check(struct drm_crtc *crtc, |
416 | struct drm_atomic_state *state) |
417 | { |
418 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, |
419 | crtc); |
420 | DRM_DEBUG_KMS("[CRTC:%d:%s]\n" , crtc->base.id, crtc->name); |
421 | |
422 | if (crtc_state->gamma_lut && drm_color_lut_size(blob: crtc_state->gamma_lut) != 256) |
423 | return -EINVAL; |
424 | |
425 | if (crtc_state->color_mgmt_changed) |
426 | crtc_state->planes_changed = true; |
427 | |
428 | return 0; |
429 | } |
430 | |
431 | static void armada_drm_crtc_atomic_begin(struct drm_crtc *crtc, |
432 | struct drm_atomic_state *state) |
433 | { |
434 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, |
435 | crtc); |
436 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
437 | |
438 | DRM_DEBUG_KMS("[CRTC:%d:%s]\n" , crtc->base.id, crtc->name); |
439 | |
440 | if (crtc_state->color_mgmt_changed) |
441 | armada_drm_update_gamma(crtc); |
442 | |
443 | dcrtc->regs_idx = 0; |
444 | dcrtc->regs = dcrtc->atomic_regs; |
445 | } |
446 | |
447 | static void armada_drm_crtc_atomic_flush(struct drm_crtc *crtc, |
448 | struct drm_atomic_state *state) |
449 | { |
450 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, |
451 | crtc); |
452 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
453 | |
454 | DRM_DEBUG_KMS("[CRTC:%d:%s]\n" , crtc->base.id, crtc->name); |
455 | |
456 | armada_reg_queue_end(dcrtc->regs, dcrtc->regs_idx); |
457 | |
458 | /* |
459 | * If we aren't doing a full modeset, then we need to queue |
460 | * the event here. |
461 | */ |
462 | if (!drm_atomic_crtc_needs_modeset(state: crtc_state)) { |
463 | dcrtc->update_pending = true; |
464 | armada_drm_crtc_queue_state_event(crtc); |
465 | spin_lock_irq(lock: &dcrtc->irq_lock); |
466 | armada_drm_crtc_enable_irq(dcrtc, mask: DUMB_FRAMEDONE_ENA); |
467 | spin_unlock_irq(lock: &dcrtc->irq_lock); |
468 | } else { |
469 | spin_lock_irq(lock: &dcrtc->irq_lock); |
470 | armada_drm_crtc_update_regs(dcrtc, regs: dcrtc->regs); |
471 | spin_unlock_irq(lock: &dcrtc->irq_lock); |
472 | } |
473 | } |
474 | |
475 | static void armada_drm_crtc_atomic_disable(struct drm_crtc *crtc, |
476 | struct drm_atomic_state *state) |
477 | { |
478 | struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, |
479 | crtc); |
480 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
481 | struct drm_pending_vblank_event *event; |
482 | |
483 | DRM_DEBUG_KMS("[CRTC:%d:%s]\n" , crtc->base.id, crtc->name); |
484 | |
485 | if (old_state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) |
486 | drm_crtc_vblank_put(crtc); |
487 | |
488 | drm_crtc_vblank_off(crtc); |
489 | armada_drm_crtc_update(dcrtc, enable: false); |
490 | |
491 | if (!crtc->state->active) { |
492 | /* |
493 | * This modeset will be leaving the CRTC disabled, so |
494 | * call the backend to disable upstream clocks etc. |
495 | */ |
496 | if (dcrtc->variant->disable) |
497 | dcrtc->variant->disable(dcrtc); |
498 | |
499 | /* |
500 | * We will not receive any further vblank events. |
501 | * Send the flip_done event manually. |
502 | */ |
503 | event = crtc->state->event; |
504 | crtc->state->event = NULL; |
505 | if (event) { |
506 | spin_lock_irq(lock: &crtc->dev->event_lock); |
507 | drm_crtc_send_vblank_event(crtc, e: event); |
508 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
509 | } |
510 | } |
511 | } |
512 | |
513 | static void armada_drm_crtc_atomic_enable(struct drm_crtc *crtc, |
514 | struct drm_atomic_state *state) |
515 | { |
516 | struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, |
517 | crtc); |
518 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
519 | |
520 | DRM_DEBUG_KMS("[CRTC:%d:%s]\n" , crtc->base.id, crtc->name); |
521 | |
522 | if (!old_state->active) { |
523 | /* |
524 | * This modeset is enabling the CRTC after it having |
525 | * been disabled. Reverse the call to ->disable in |
526 | * the atomic_disable(). |
527 | */ |
528 | if (dcrtc->variant->enable) |
529 | dcrtc->variant->enable(dcrtc, &crtc->state->adjusted_mode); |
530 | } |
531 | armada_drm_crtc_update(dcrtc, enable: true); |
532 | drm_crtc_vblank_on(crtc); |
533 | |
534 | if (crtc->state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) |
535 | WARN_ON(drm_crtc_vblank_get(crtc)); |
536 | |
537 | armada_drm_crtc_queue_state_event(crtc); |
538 | } |
539 | |
540 | static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = { |
541 | .mode_valid = armada_drm_crtc_mode_valid, |
542 | .mode_fixup = armada_drm_crtc_mode_fixup, |
543 | .mode_set_nofb = armada_drm_crtc_mode_set_nofb, |
544 | .atomic_check = armada_drm_crtc_atomic_check, |
545 | .atomic_begin = armada_drm_crtc_atomic_begin, |
546 | .atomic_flush = armada_drm_crtc_atomic_flush, |
547 | .atomic_disable = armada_drm_crtc_atomic_disable, |
548 | .atomic_enable = armada_drm_crtc_atomic_enable, |
549 | }; |
550 | |
551 | static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix, |
552 | unsigned stride, unsigned width, unsigned height) |
553 | { |
554 | uint32_t addr; |
555 | unsigned y; |
556 | |
557 | addr = SRAM_HWC32_RAM1; |
558 | for (y = 0; y < height; y++) { |
559 | uint32_t *p = &pix[y * stride]; |
560 | unsigned x; |
561 | |
562 | for (x = 0; x < width; x++, p++) { |
563 | uint32_t val = *p; |
564 | |
565 | /* |
566 | * In "ARGB888" (HWC32) mode, writing to the SRAM |
567 | * requires these bits to contain: |
568 | * 31:24 = alpha 23:16 = blue 15:8 = green 7:0 = red |
569 | * So, it's actually ABGR8888. This is independent |
570 | * of the SWAPRB bits in DMA control register 0. |
571 | */ |
572 | val = (val & 0xff00ff00) | |
573 | (val & 0x000000ff) << 16 | |
574 | (val & 0x00ff0000) >> 16; |
575 | |
576 | writel_relaxed(val, |
577 | base + LCD_SPU_SRAM_WRDAT); |
578 | writel_relaxed(addr | SRAM_WRITE, |
579 | base + LCD_SPU_SRAM_CTRL); |
580 | readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN); |
581 | addr += 1; |
582 | if ((addr & 0x00ff) == 0) |
583 | addr += 0xf00; |
584 | if ((addr & 0x30ff) == 0) |
585 | addr = SRAM_HWC32_RAM2; |
586 | } |
587 | } |
588 | } |
589 | |
590 | static void armada_drm_crtc_cursor_tran(void __iomem *base) |
591 | { |
592 | unsigned addr; |
593 | |
594 | for (addr = 0; addr < 256; addr++) { |
595 | /* write the default value */ |
596 | writel_relaxed(0x55555555, base + LCD_SPU_SRAM_WRDAT); |
597 | writel_relaxed(addr | SRAM_WRITE | SRAM_HWC32_TRAN, |
598 | base + LCD_SPU_SRAM_CTRL); |
599 | } |
600 | } |
601 | |
602 | static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload) |
603 | { |
604 | uint32_t xoff, xscr, w = dcrtc->cursor_w, s; |
605 | uint32_t yoff, yscr, h = dcrtc->cursor_h; |
606 | uint32_t para1; |
607 | |
608 | /* |
609 | * Calculate the visible width and height of the cursor, |
610 | * screen position, and the position in the cursor bitmap. |
611 | */ |
612 | if (dcrtc->cursor_x < 0) { |
613 | xoff = -dcrtc->cursor_x; |
614 | xscr = 0; |
615 | w -= min(xoff, w); |
616 | } else if (dcrtc->cursor_x + w > dcrtc->crtc.mode.hdisplay) { |
617 | xoff = 0; |
618 | xscr = dcrtc->cursor_x; |
619 | w = max_t(int, dcrtc->crtc.mode.hdisplay - dcrtc->cursor_x, 0); |
620 | } else { |
621 | xoff = 0; |
622 | xscr = dcrtc->cursor_x; |
623 | } |
624 | |
625 | if (dcrtc->cursor_y < 0) { |
626 | yoff = -dcrtc->cursor_y; |
627 | yscr = 0; |
628 | h -= min(yoff, h); |
629 | } else if (dcrtc->cursor_y + h > dcrtc->crtc.mode.vdisplay) { |
630 | yoff = 0; |
631 | yscr = dcrtc->cursor_y; |
632 | h = max_t(int, dcrtc->crtc.mode.vdisplay - dcrtc->cursor_y, 0); |
633 | } else { |
634 | yoff = 0; |
635 | yscr = dcrtc->cursor_y; |
636 | } |
637 | |
638 | /* On interlaced modes, the vertical cursor size must be halved */ |
639 | s = dcrtc->cursor_w; |
640 | if (dcrtc->interlaced) { |
641 | s *= 2; |
642 | yscr /= 2; |
643 | h /= 2; |
644 | } |
645 | |
646 | if (!dcrtc->cursor_obj || !h || !w) { |
647 | spin_lock_irq(lock: &dcrtc->irq_lock); |
648 | dcrtc->cursor_update = false; |
649 | armada_updatel(val: 0, mask: CFG_HWC_ENA, ptr: dcrtc->base + LCD_SPU_DMA_CTRL0); |
650 | spin_unlock_irq(lock: &dcrtc->irq_lock); |
651 | return 0; |
652 | } |
653 | |
654 | spin_lock_irq(lock: &dcrtc->irq_lock); |
655 | para1 = readl_relaxed(dcrtc->base + LCD_SPU_SRAM_PARA1); |
656 | armada_updatel(val: CFG_CSB_256x32, mask: CFG_CSB_256x32 | CFG_PDWN256x32, |
657 | ptr: dcrtc->base + LCD_SPU_SRAM_PARA1); |
658 | spin_unlock_irq(lock: &dcrtc->irq_lock); |
659 | |
660 | /* |
661 | * Initialize the transparency if the SRAM was powered down. |
662 | * We must also reload the cursor data as well. |
663 | */ |
664 | if (!(para1 & CFG_CSB_256x32)) { |
665 | armada_drm_crtc_cursor_tran(base: dcrtc->base); |
666 | reload = true; |
667 | } |
668 | |
669 | if (dcrtc->cursor_hw_sz != (h << 16 | w)) { |
670 | spin_lock_irq(lock: &dcrtc->irq_lock); |
671 | dcrtc->cursor_update = false; |
672 | armada_updatel(val: 0, mask: CFG_HWC_ENA, ptr: dcrtc->base + LCD_SPU_DMA_CTRL0); |
673 | spin_unlock_irq(lock: &dcrtc->irq_lock); |
674 | reload = true; |
675 | } |
676 | if (reload) { |
677 | struct armada_gem_object *obj = dcrtc->cursor_obj; |
678 | uint32_t *pix; |
679 | /* Set the top-left corner of the cursor image */ |
680 | pix = obj->addr; |
681 | pix += yoff * s + xoff; |
682 | armada_load_cursor_argb(base: dcrtc->base, pix, stride: s, width: w, height: h); |
683 | } |
684 | |
685 | /* Reload the cursor position, size and enable in the IRQ handler */ |
686 | spin_lock_irq(lock: &dcrtc->irq_lock); |
687 | dcrtc->cursor_hw_pos = yscr << 16 | xscr; |
688 | dcrtc->cursor_hw_sz = h << 16 | w; |
689 | dcrtc->cursor_update = true; |
690 | armada_drm_crtc_enable_irq(dcrtc, mask: DUMB_FRAMEDONE_ENA); |
691 | spin_unlock_irq(lock: &dcrtc->irq_lock); |
692 | |
693 | return 0; |
694 | } |
695 | |
696 | static void cursor_update(void *data) |
697 | { |
698 | armada_drm_crtc_cursor_update(dcrtc: data, reload: true); |
699 | } |
700 | |
701 | static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc, |
702 | struct drm_file *file, uint32_t handle, uint32_t w, uint32_t h) |
703 | { |
704 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
705 | struct armada_gem_object *obj = NULL; |
706 | int ret; |
707 | |
708 | /* If no cursor support, replicate drm's return value */ |
709 | if (!dcrtc->variant->has_spu_adv_reg) |
710 | return -ENXIO; |
711 | |
712 | if (handle && w > 0 && h > 0) { |
713 | /* maximum size is 64x32 or 32x64 */ |
714 | if (w > 64 || h > 64 || (w > 32 && h > 32)) |
715 | return -ENOMEM; |
716 | |
717 | obj = armada_gem_object_lookup(dfile: file, handle); |
718 | if (!obj) |
719 | return -ENOENT; |
720 | |
721 | /* Must be a kernel-mapped object */ |
722 | if (!obj->addr) { |
723 | drm_gem_object_put(obj: &obj->obj); |
724 | return -EINVAL; |
725 | } |
726 | |
727 | if (obj->obj.size < w * h * 4) { |
728 | DRM_ERROR("buffer is too small\n" ); |
729 | drm_gem_object_put(obj: &obj->obj); |
730 | return -ENOMEM; |
731 | } |
732 | } |
733 | |
734 | if (dcrtc->cursor_obj) { |
735 | dcrtc->cursor_obj->update = NULL; |
736 | dcrtc->cursor_obj->update_data = NULL; |
737 | drm_gem_object_put(obj: &dcrtc->cursor_obj->obj); |
738 | } |
739 | dcrtc->cursor_obj = obj; |
740 | dcrtc->cursor_w = w; |
741 | dcrtc->cursor_h = h; |
742 | ret = armada_drm_crtc_cursor_update(dcrtc, reload: true); |
743 | if (obj) { |
744 | obj->update_data = dcrtc; |
745 | obj->update = cursor_update; |
746 | } |
747 | |
748 | return ret; |
749 | } |
750 | |
751 | static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) |
752 | { |
753 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
754 | int ret; |
755 | |
756 | /* If no cursor support, replicate drm's return value */ |
757 | if (!dcrtc->variant->has_spu_adv_reg) |
758 | return -EFAULT; |
759 | |
760 | dcrtc->cursor_x = x; |
761 | dcrtc->cursor_y = y; |
762 | ret = armada_drm_crtc_cursor_update(dcrtc, reload: false); |
763 | |
764 | return ret; |
765 | } |
766 | |
767 | static void armada_drm_crtc_destroy(struct drm_crtc *crtc) |
768 | { |
769 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
770 | struct armada_private *priv = drm_to_armada_dev(crtc->dev); |
771 | |
772 | if (dcrtc->cursor_obj) |
773 | drm_gem_object_put(obj: &dcrtc->cursor_obj->obj); |
774 | |
775 | priv->dcrtc[dcrtc->num] = NULL; |
776 | drm_crtc_cleanup(crtc: &dcrtc->crtc); |
777 | |
778 | if (dcrtc->variant->disable) |
779 | dcrtc->variant->disable(dcrtc); |
780 | |
781 | writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA); |
782 | |
783 | of_node_put(node: dcrtc->crtc.port); |
784 | |
785 | kfree(objp: dcrtc); |
786 | } |
787 | |
788 | static int armada_drm_crtc_late_register(struct drm_crtc *crtc) |
789 | { |
790 | if (IS_ENABLED(CONFIG_DEBUG_FS)) |
791 | armada_drm_crtc_debugfs_init(drm_to_armada_crtc(crtc)); |
792 | |
793 | return 0; |
794 | } |
795 | |
796 | /* These are called under the vbl_lock. */ |
797 | static int armada_drm_crtc_enable_vblank(struct drm_crtc *crtc) |
798 | { |
799 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
800 | unsigned long flags; |
801 | |
802 | spin_lock_irqsave(&dcrtc->irq_lock, flags); |
803 | armada_drm_crtc_enable_irq(dcrtc, mask: VSYNC_IRQ_ENA); |
804 | spin_unlock_irqrestore(lock: &dcrtc->irq_lock, flags); |
805 | return 0; |
806 | } |
807 | |
808 | static void armada_drm_crtc_disable_vblank(struct drm_crtc *crtc) |
809 | { |
810 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
811 | unsigned long flags; |
812 | |
813 | spin_lock_irqsave(&dcrtc->irq_lock, flags); |
814 | armada_drm_crtc_disable_irq(dcrtc, mask: VSYNC_IRQ_ENA); |
815 | spin_unlock_irqrestore(lock: &dcrtc->irq_lock, flags); |
816 | } |
817 | |
818 | static const struct drm_crtc_funcs armada_crtc_funcs = { |
819 | .reset = drm_atomic_helper_crtc_reset, |
820 | .cursor_set = armada_drm_crtc_cursor_set, |
821 | .cursor_move = armada_drm_crtc_cursor_move, |
822 | .destroy = armada_drm_crtc_destroy, |
823 | .set_config = drm_atomic_helper_set_config, |
824 | .page_flip = drm_atomic_helper_page_flip, |
825 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
826 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
827 | .late_register = armada_drm_crtc_late_register, |
828 | .enable_vblank = armada_drm_crtc_enable_vblank, |
829 | .disable_vblank = armada_drm_crtc_disable_vblank, |
830 | }; |
831 | |
832 | int armada_crtc_select_clock(struct armada_crtc *dcrtc, |
833 | struct armada_clk_result *res, |
834 | const struct armada_clocking_params *params, |
835 | struct clk *clks[], size_t num_clks, |
836 | unsigned long desired_khz) |
837 | { |
838 | unsigned long desired_hz = desired_khz * 1000; |
839 | unsigned long desired_clk_hz; // requested clk input |
840 | unsigned long real_clk_hz; // actual clk input |
841 | unsigned long real_hz; // actual pixel clk |
842 | unsigned long permillage; |
843 | struct clk *clk; |
844 | u32 div; |
845 | int i; |
846 | |
847 | DRM_DEBUG_KMS("[CRTC:%u:%s] desired clock=%luHz\n" , |
848 | dcrtc->crtc.base.id, dcrtc->crtc.name, desired_hz); |
849 | |
850 | for (i = 0; i < num_clks; i++) { |
851 | clk = clks[i]; |
852 | if (!clk) |
853 | continue; |
854 | |
855 | if (params->settable & BIT(i)) { |
856 | real_clk_hz = clk_round_rate(clk, rate: desired_hz); |
857 | desired_clk_hz = desired_hz; |
858 | } else { |
859 | real_clk_hz = clk_get_rate(clk); |
860 | desired_clk_hz = real_clk_hz; |
861 | } |
862 | |
863 | /* If the clock can do exactly the desired rate, we're done */ |
864 | if (real_clk_hz == desired_hz) { |
865 | real_hz = real_clk_hz; |
866 | div = 1; |
867 | goto found; |
868 | } |
869 | |
870 | /* Calculate the divider - if invalid, we can't do this rate */ |
871 | div = DIV_ROUND_CLOSEST(real_clk_hz, desired_hz); |
872 | if (div == 0 || div > params->div_max) |
873 | continue; |
874 | |
875 | /* Calculate the actual rate - HDMI requires -0.6%..+0.5% */ |
876 | real_hz = DIV_ROUND_CLOSEST(real_clk_hz, div); |
877 | |
878 | DRM_DEBUG_KMS("[CRTC:%u:%s] clk=%u %luHz div=%u real=%luHz\n" , |
879 | dcrtc->crtc.base.id, dcrtc->crtc.name, |
880 | i, real_clk_hz, div, real_hz); |
881 | |
882 | /* Avoid repeated division */ |
883 | if (real_hz < desired_hz) { |
884 | permillage = real_hz / desired_khz; |
885 | if (permillage < params->permillage_min) |
886 | continue; |
887 | } else { |
888 | permillage = DIV_ROUND_UP(real_hz, desired_khz); |
889 | if (permillage > params->permillage_max) |
890 | continue; |
891 | } |
892 | goto found; |
893 | } |
894 | |
895 | return -ERANGE; |
896 | |
897 | found: |
898 | DRM_DEBUG_KMS("[CRTC:%u:%s] selected clk=%u %luHz div=%u real=%luHz\n" , |
899 | dcrtc->crtc.base.id, dcrtc->crtc.name, |
900 | i, real_clk_hz, div, real_hz); |
901 | |
902 | res->desired_clk_hz = desired_clk_hz; |
903 | res->clk = clk; |
904 | res->div = div; |
905 | |
906 | return i; |
907 | } |
908 | |
909 | static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, |
910 | struct resource *res, int irq, const struct armada_variant *variant, |
911 | struct device_node *port) |
912 | { |
913 | struct armada_private *priv = drm_to_armada_dev(drm); |
914 | struct armada_crtc *dcrtc; |
915 | struct drm_plane *primary; |
916 | void __iomem *base; |
917 | int ret; |
918 | |
919 | base = devm_ioremap_resource(dev, res); |
920 | if (IS_ERR(ptr: base)) |
921 | return PTR_ERR(ptr: base); |
922 | |
923 | dcrtc = kzalloc(size: sizeof(*dcrtc), GFP_KERNEL); |
924 | if (!dcrtc) { |
925 | DRM_ERROR("failed to allocate Armada crtc\n" ); |
926 | return -ENOMEM; |
927 | } |
928 | |
929 | if (dev != drm->dev) |
930 | dev_set_drvdata(dev, data: dcrtc); |
931 | |
932 | dcrtc->variant = variant; |
933 | dcrtc->base = base; |
934 | dcrtc->num = drm->mode_config.num_crtc; |
935 | dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0; |
936 | dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24; |
937 | spin_lock_init(&dcrtc->irq_lock); |
938 | dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR; |
939 | |
940 | /* Initialize some registers which we don't otherwise set */ |
941 | writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV); |
942 | writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_BLANKCOLOR); |
943 | writel_relaxed(dcrtc->spu_iopad_ctrl, |
944 | dcrtc->base + LCD_SPU_IOPAD_CONTROL); |
945 | writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_SRAM_PARA0); |
946 | writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 | |
947 | CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 | |
948 | CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); |
949 | writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1); |
950 | writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); |
951 | readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); |
952 | writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); |
953 | |
954 | ret = devm_request_irq(dev, irq, handler: armada_drm_irq, irqflags: 0, devname: "armada_drm_crtc" , |
955 | dev_id: dcrtc); |
956 | if (ret < 0) |
957 | goto err_crtc; |
958 | |
959 | if (dcrtc->variant->init) { |
960 | ret = dcrtc->variant->init(dcrtc, dev); |
961 | if (ret) |
962 | goto err_crtc; |
963 | } |
964 | |
965 | /* Ensure AXI pipeline is enabled */ |
966 | armada_updatel(val: CFG_ARBFAST_ENA, mask: 0, ptr: dcrtc->base + LCD_SPU_DMA_CTRL0); |
967 | |
968 | priv->dcrtc[dcrtc->num] = dcrtc; |
969 | |
970 | dcrtc->crtc.port = port; |
971 | |
972 | primary = kzalloc(size: sizeof(*primary), GFP_KERNEL); |
973 | if (!primary) { |
974 | ret = -ENOMEM; |
975 | goto err_crtc; |
976 | } |
977 | |
978 | ret = armada_drm_primary_plane_init(drm, primary); |
979 | if (ret) { |
980 | kfree(objp: primary); |
981 | goto err_crtc; |
982 | } |
983 | |
984 | ret = drm_crtc_init_with_planes(dev: drm, crtc: &dcrtc->crtc, primary, NULL, |
985 | funcs: &armada_crtc_funcs, NULL); |
986 | if (ret) |
987 | goto err_crtc_init; |
988 | |
989 | drm_crtc_helper_add(crtc: &dcrtc->crtc, funcs: &armada_crtc_helper_funcs); |
990 | |
991 | ret = drm_mode_crtc_set_gamma_size(crtc: &dcrtc->crtc, gamma_size: 256); |
992 | if (ret) |
993 | return ret; |
994 | |
995 | drm_crtc_enable_color_mgmt(crtc: &dcrtc->crtc, degamma_lut_size: 0, has_ctm: false, gamma_lut_size: 256); |
996 | |
997 | return armada_overlay_plane_create(drm, 1 << dcrtc->num); |
998 | |
999 | err_crtc_init: |
1000 | primary->funcs->destroy(primary); |
1001 | err_crtc: |
1002 | kfree(objp: dcrtc); |
1003 | |
1004 | return ret; |
1005 | } |
1006 | |
1007 | static int |
1008 | armada_lcd_bind(struct device *dev, struct device *master, void *data) |
1009 | { |
1010 | struct platform_device *pdev = to_platform_device(dev); |
1011 | struct drm_device *drm = data; |
1012 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1013 | int irq = platform_get_irq(pdev, 0); |
1014 | const struct armada_variant *variant; |
1015 | struct device_node *port = NULL; |
1016 | struct device_node *np, *parent = dev->of_node; |
1017 | |
1018 | if (irq < 0) |
1019 | return irq; |
1020 | |
1021 | |
1022 | variant = device_get_match_data(dev); |
1023 | if (!variant) |
1024 | return -ENXIO; |
1025 | |
1026 | if (parent) { |
1027 | np = of_get_child_by_name(node: parent, name: "ports" ); |
1028 | if (np) |
1029 | parent = np; |
1030 | port = of_get_child_by_name(node: parent, name: "port" ); |
1031 | of_node_put(node: np); |
1032 | if (!port) { |
1033 | dev_err(dev, "no port node found in %pOF\n" , parent); |
1034 | return -ENXIO; |
1035 | } |
1036 | } |
1037 | |
1038 | return armada_drm_crtc_create(drm, dev, res, irq, variant, port); |
1039 | } |
1040 | |
1041 | static void |
1042 | armada_lcd_unbind(struct device *dev, struct device *master, void *data) |
1043 | { |
1044 | struct armada_crtc *dcrtc = dev_get_drvdata(dev); |
1045 | |
1046 | armada_drm_crtc_destroy(crtc: &dcrtc->crtc); |
1047 | } |
1048 | |
1049 | static const struct component_ops armada_lcd_ops = { |
1050 | .bind = armada_lcd_bind, |
1051 | .unbind = armada_lcd_unbind, |
1052 | }; |
1053 | |
1054 | static int armada_lcd_probe(struct platform_device *pdev) |
1055 | { |
1056 | return component_add(&pdev->dev, &armada_lcd_ops); |
1057 | } |
1058 | |
1059 | static void armada_lcd_remove(struct platform_device *pdev) |
1060 | { |
1061 | component_del(&pdev->dev, &armada_lcd_ops); |
1062 | } |
1063 | |
1064 | static const struct of_device_id armada_lcd_of_match[] = { |
1065 | { |
1066 | .compatible = "marvell,dove-lcd" , |
1067 | .data = &armada510_ops, |
1068 | }, |
1069 | {} |
1070 | }; |
1071 | MODULE_DEVICE_TABLE(of, armada_lcd_of_match); |
1072 | |
1073 | static const struct platform_device_id armada_lcd_platform_ids[] = { |
1074 | { |
1075 | .name = "armada-lcd" , |
1076 | .driver_data = (unsigned long)&armada510_ops, |
1077 | }, { |
1078 | .name = "armada-510-lcd" , |
1079 | .driver_data = (unsigned long)&armada510_ops, |
1080 | }, |
1081 | { }, |
1082 | }; |
1083 | MODULE_DEVICE_TABLE(platform, armada_lcd_platform_ids); |
1084 | |
1085 | struct platform_driver armada_lcd_platform_driver = { |
1086 | .probe = armada_lcd_probe, |
1087 | .remove_new = armada_lcd_remove, |
1088 | .driver = { |
1089 | .name = "armada-lcd" , |
1090 | .owner = THIS_MODULE, |
1091 | .of_match_table = armada_lcd_of_match, |
1092 | }, |
1093 | .id_table = armada_lcd_platform_ids, |
1094 | }; |
1095 | |