1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. |
4 | * |
5 | * Parts of this file were based on sources as follows: |
6 | * |
7 | * Copyright (c) 2006-2008 Intel Corporation |
8 | * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> |
9 | * Copyright (C) 2011 Texas Instruments |
10 | */ |
11 | |
12 | #include <linux/clk.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/dma-buf.h> |
15 | #include <linux/media-bus-format.h> |
16 | #include <linux/of_graph.h> |
17 | |
18 | #include <drm/drm_fb_dma_helper.h> |
19 | #include <drm/drm_fourcc.h> |
20 | #include <drm/drm_framebuffer.h> |
21 | #include <drm/drm_gem_atomic_helper.h> |
22 | #include <drm/drm_gem_dma_helper.h> |
23 | #include <drm/drm_vblank.h> |
24 | |
25 | #include "pl111_drm.h" |
26 | |
27 | irqreturn_t pl111_irq(int irq, void *data) |
28 | { |
29 | struct pl111_drm_dev_private *priv = data; |
30 | u32 irq_stat; |
31 | irqreturn_t status = IRQ_NONE; |
32 | |
33 | irq_stat = readl(addr: priv->regs + CLCD_PL111_MIS); |
34 | |
35 | if (!irq_stat) |
36 | return IRQ_NONE; |
37 | |
38 | if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) { |
39 | drm_crtc_handle_vblank(crtc: &priv->pipe.crtc); |
40 | |
41 | status = IRQ_HANDLED; |
42 | } |
43 | |
44 | /* Clear the interrupt once done */ |
45 | writel(val: irq_stat, addr: priv->regs + CLCD_PL111_ICR); |
46 | |
47 | return status; |
48 | } |
49 | |
50 | static enum drm_mode_status |
51 | pl111_mode_valid(struct drm_simple_display_pipe *pipe, |
52 | const struct drm_display_mode *mode) |
53 | { |
54 | struct drm_device *drm = pipe->crtc.dev; |
55 | struct pl111_drm_dev_private *priv = drm->dev_private; |
56 | u32 cpp = DIV_ROUND_UP(priv->variant->fb_depth, 8); |
57 | u64 bw; |
58 | |
59 | /* |
60 | * We use the pixelclock to also account for interlaced modes, the |
61 | * resulting bandwidth is in bytes per second. |
62 | */ |
63 | bw = mode->clock * 1000ULL; /* In Hz */ |
64 | bw = bw * mode->hdisplay * mode->vdisplay * cpp; |
65 | bw = div_u64(dividend: bw, divisor: mode->htotal * mode->vtotal); |
66 | |
67 | /* |
68 | * If no bandwidth constraints, anything goes, else |
69 | * check if we are too fast. |
70 | */ |
71 | if (priv->memory_bw && (bw > priv->memory_bw)) { |
72 | DRM_DEBUG_KMS("%d x %d @ %d Hz, %d cpp, bw %llu too fast\n" , |
73 | mode->hdisplay, mode->vdisplay, |
74 | mode->clock * 1000, cpp, bw); |
75 | |
76 | return MODE_BAD; |
77 | } |
78 | DRM_DEBUG_KMS("%d x %d @ %d Hz, %d cpp, bw %llu bytes/s OK\n" , |
79 | mode->hdisplay, mode->vdisplay, |
80 | mode->clock * 1000, cpp, bw); |
81 | |
82 | return MODE_OK; |
83 | } |
84 | |
85 | static int pl111_display_check(struct drm_simple_display_pipe *pipe, |
86 | struct drm_plane_state *pstate, |
87 | struct drm_crtc_state *cstate) |
88 | { |
89 | const struct drm_display_mode *mode = &cstate->mode; |
90 | struct drm_framebuffer *old_fb = pipe->plane.state->fb; |
91 | struct drm_framebuffer *fb = pstate->fb; |
92 | |
93 | if (mode->hdisplay % 16) |
94 | return -EINVAL; |
95 | |
96 | if (fb) { |
97 | u32 offset = drm_fb_dma_get_gem_addr(fb, state: pstate, plane: 0); |
98 | |
99 | /* FB base address must be dword aligned. */ |
100 | if (offset & 3) |
101 | return -EINVAL; |
102 | |
103 | /* There's no pitch register -- the mode's hdisplay |
104 | * controls it. |
105 | */ |
106 | if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) |
107 | return -EINVAL; |
108 | |
109 | /* We can't change the FB format in a flicker-free |
110 | * manner (and only update it during CRTC enable). |
111 | */ |
112 | if (old_fb && old_fb->format != fb->format) |
113 | cstate->mode_changed = true; |
114 | } |
115 | |
116 | return 0; |
117 | } |
118 | |
119 | static void pl111_display_enable(struct drm_simple_display_pipe *pipe, |
120 | struct drm_crtc_state *cstate, |
121 | struct drm_plane_state *plane_state) |
122 | { |
123 | struct drm_crtc *crtc = &pipe->crtc; |
124 | struct drm_plane *plane = &pipe->plane; |
125 | struct drm_device *drm = crtc->dev; |
126 | struct pl111_drm_dev_private *priv = drm->dev_private; |
127 | const struct drm_display_mode *mode = &cstate->mode; |
128 | struct drm_framebuffer *fb = plane->state->fb; |
129 | struct drm_connector *connector = priv->connector; |
130 | struct drm_bridge *bridge = priv->bridge; |
131 | bool grayscale = false; |
132 | u32 cntl; |
133 | u32 ppl, hsw, hfp, hbp; |
134 | u32 lpp, vsw, vfp, vbp; |
135 | u32 cpl, tim2; |
136 | int ret; |
137 | |
138 | ret = clk_set_rate(clk: priv->clk, rate: mode->clock * 1000); |
139 | if (ret) { |
140 | dev_err(drm->dev, |
141 | "Failed to set pixel clock rate to %d: %d\n" , |
142 | mode->clock * 1000, ret); |
143 | } |
144 | |
145 | clk_prepare_enable(clk: priv->clk); |
146 | |
147 | ppl = (mode->hdisplay / 16) - 1; |
148 | hsw = mode->hsync_end - mode->hsync_start - 1; |
149 | hfp = mode->hsync_start - mode->hdisplay - 1; |
150 | hbp = mode->htotal - mode->hsync_end - 1; |
151 | |
152 | lpp = mode->vdisplay - 1; |
153 | vsw = mode->vsync_end - mode->vsync_start - 1; |
154 | vfp = mode->vsync_start - mode->vdisplay; |
155 | vbp = mode->vtotal - mode->vsync_end; |
156 | |
157 | cpl = mode->hdisplay - 1; |
158 | |
159 | writel(val: (ppl << 2) | |
160 | (hsw << 8) | |
161 | (hfp << 16) | |
162 | (hbp << 24), |
163 | addr: priv->regs + CLCD_TIM0); |
164 | writel(val: lpp | |
165 | (vsw << 10) | |
166 | (vfp << 16) | |
167 | (vbp << 24), |
168 | addr: priv->regs + CLCD_TIM1); |
169 | |
170 | spin_lock(lock: &priv->tim2_lock); |
171 | |
172 | tim2 = readl(addr: priv->regs + CLCD_TIM2); |
173 | tim2 &= (TIM2_BCD | TIM2_PCD_LO_MASK | TIM2_PCD_HI_MASK); |
174 | |
175 | if (priv->variant->broken_clockdivider) |
176 | tim2 |= TIM2_BCD; |
177 | |
178 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) |
179 | tim2 |= TIM2_IHS; |
180 | |
181 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) |
182 | tim2 |= TIM2_IVS; |
183 | |
184 | if (connector) { |
185 | if (connector->display_info.bus_flags & DRM_BUS_FLAG_DE_LOW) |
186 | tim2 |= TIM2_IOE; |
187 | |
188 | if (connector->display_info.bus_flags & |
189 | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) |
190 | tim2 |= TIM2_IPC; |
191 | |
192 | if (connector->display_info.num_bus_formats == 1 && |
193 | connector->display_info.bus_formats[0] == |
194 | MEDIA_BUS_FMT_Y8_1X8) |
195 | grayscale = true; |
196 | |
197 | /* |
198 | * The AC pin bias frequency is set to max count when using |
199 | * grayscale so at least once in a while we will reverse |
200 | * polarity and get rid of any DC built up that could |
201 | * damage the display. |
202 | */ |
203 | if (grayscale) |
204 | tim2 |= TIM2_ACB_MASK; |
205 | } |
206 | |
207 | if (bridge) { |
208 | const struct drm_bridge_timings *btimings = bridge->timings; |
209 | |
210 | /* |
211 | * Here is when things get really fun. Sometimes the bridge |
212 | * timings are such that the signal out from PL11x is not |
213 | * stable before the receiving bridge (such as a dumb VGA DAC |
214 | * or similar) samples it. If that happens, we compensate by |
215 | * the only method we have: output the data on the opposite |
216 | * edge of the clock so it is for sure stable when it gets |
217 | * sampled. |
218 | * |
219 | * The PL111 manual does not contain proper timining diagrams |
220 | * or data for these details, but we know from experiments |
221 | * that the setup time is more than 3000 picoseconds (3 ns). |
222 | * If we have a bridge that requires the signal to be stable |
223 | * earlier than 3000 ps before the clock pulse, we have to |
224 | * output the data on the opposite edge to avoid flicker. |
225 | */ |
226 | if (btimings && btimings->setup_time_ps >= 3000) |
227 | tim2 ^= TIM2_IPC; |
228 | } |
229 | |
230 | tim2 |= cpl << 16; |
231 | writel(val: tim2, addr: priv->regs + CLCD_TIM2); |
232 | spin_unlock(lock: &priv->tim2_lock); |
233 | |
234 | writel(val: 0, addr: priv->regs + CLCD_TIM3); |
235 | |
236 | /* |
237 | * Detect grayscale bus format. We do not support a grayscale mode |
238 | * toward userspace, instead we expose an RGB24 buffer and then the |
239 | * hardware will activate its grayscaler to convert to the grayscale |
240 | * format. |
241 | */ |
242 | if (grayscale) |
243 | cntl = CNTL_LCDEN | CNTL_LCDMONO8; |
244 | else |
245 | /* Else we assume TFT display */ |
246 | cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDVCOMP(1); |
247 | |
248 | /* On the ST Micro variant, assume all 24 bits are connected */ |
249 | if (priv->variant->st_bitmux_control) |
250 | cntl |= CNTL_ST_CDWID_24; |
251 | |
252 | /* |
253 | * Note that the ARM hardware's format reader takes 'r' from |
254 | * the low bit, while DRM formats list channels from high bit |
255 | * to low bit as you read left to right. The ST Micro version of |
256 | * the PL110 (LCDC) however uses the standard DRM format. |
257 | */ |
258 | switch (fb->format->format) { |
259 | case DRM_FORMAT_BGR888: |
260 | /* Only supported on the ST Micro variant */ |
261 | if (priv->variant->st_bitmux_control) |
262 | cntl |= CNTL_ST_LCDBPP24_PACKED | CNTL_BGR; |
263 | break; |
264 | case DRM_FORMAT_RGB888: |
265 | /* Only supported on the ST Micro variant */ |
266 | if (priv->variant->st_bitmux_control) |
267 | cntl |= CNTL_ST_LCDBPP24_PACKED; |
268 | break; |
269 | case DRM_FORMAT_ABGR8888: |
270 | case DRM_FORMAT_XBGR8888: |
271 | if (priv->variant->st_bitmux_control) |
272 | cntl |= CNTL_LCDBPP24 | CNTL_BGR; |
273 | else |
274 | cntl |= CNTL_LCDBPP24; |
275 | break; |
276 | case DRM_FORMAT_ARGB8888: |
277 | case DRM_FORMAT_XRGB8888: |
278 | if (priv->variant->st_bitmux_control) |
279 | cntl |= CNTL_LCDBPP24; |
280 | else |
281 | cntl |= CNTL_LCDBPP24 | CNTL_BGR; |
282 | break; |
283 | case DRM_FORMAT_BGR565: |
284 | if (priv->variant->is_pl110) |
285 | cntl |= CNTL_LCDBPP16; |
286 | else if (priv->variant->st_bitmux_control) |
287 | cntl |= CNTL_LCDBPP16 | CNTL_ST_1XBPP_565 | CNTL_BGR; |
288 | else |
289 | cntl |= CNTL_LCDBPP16_565; |
290 | break; |
291 | case DRM_FORMAT_RGB565: |
292 | if (priv->variant->is_pl110) |
293 | cntl |= CNTL_LCDBPP16 | CNTL_BGR; |
294 | else if (priv->variant->st_bitmux_control) |
295 | cntl |= CNTL_LCDBPP16 | CNTL_ST_1XBPP_565; |
296 | else |
297 | cntl |= CNTL_LCDBPP16_565 | CNTL_BGR; |
298 | break; |
299 | case DRM_FORMAT_ABGR1555: |
300 | case DRM_FORMAT_XBGR1555: |
301 | cntl |= CNTL_LCDBPP16; |
302 | if (priv->variant->st_bitmux_control) |
303 | cntl |= CNTL_ST_1XBPP_5551 | CNTL_BGR; |
304 | break; |
305 | case DRM_FORMAT_ARGB1555: |
306 | case DRM_FORMAT_XRGB1555: |
307 | cntl |= CNTL_LCDBPP16; |
308 | if (priv->variant->st_bitmux_control) |
309 | cntl |= CNTL_ST_1XBPP_5551; |
310 | else |
311 | cntl |= CNTL_BGR; |
312 | break; |
313 | case DRM_FORMAT_ABGR4444: |
314 | case DRM_FORMAT_XBGR4444: |
315 | cntl |= CNTL_LCDBPP16_444; |
316 | if (priv->variant->st_bitmux_control) |
317 | cntl |= CNTL_ST_1XBPP_444 | CNTL_BGR; |
318 | break; |
319 | case DRM_FORMAT_ARGB4444: |
320 | case DRM_FORMAT_XRGB4444: |
321 | cntl |= CNTL_LCDBPP16_444; |
322 | if (priv->variant->st_bitmux_control) |
323 | cntl |= CNTL_ST_1XBPP_444; |
324 | else |
325 | cntl |= CNTL_BGR; |
326 | break; |
327 | default: |
328 | WARN_ONCE(true, "Unknown FB format 0x%08x\n" , |
329 | fb->format->format); |
330 | break; |
331 | } |
332 | |
333 | /* The PL110 in Integrator/Versatile does the BGR routing externally */ |
334 | if (priv->variant->external_bgr) |
335 | cntl &= ~CNTL_BGR; |
336 | |
337 | /* Power sequence: first enable and chill */ |
338 | writel(val: cntl, addr: priv->regs + priv->ctrl); |
339 | |
340 | /* |
341 | * We expect this delay to stabilize the contrast |
342 | * voltage Vee as stipulated by the manual |
343 | */ |
344 | msleep(msecs: 20); |
345 | |
346 | if (priv->variant_display_enable) |
347 | priv->variant_display_enable(drm, fb->format->format); |
348 | |
349 | /* Power Up */ |
350 | cntl |= CNTL_LCDPWR; |
351 | writel(val: cntl, addr: priv->regs + priv->ctrl); |
352 | |
353 | if (!priv->variant->broken_vblank) |
354 | drm_crtc_vblank_on(crtc); |
355 | } |
356 | |
357 | static void pl111_display_disable(struct drm_simple_display_pipe *pipe) |
358 | { |
359 | struct drm_crtc *crtc = &pipe->crtc; |
360 | struct drm_device *drm = crtc->dev; |
361 | struct pl111_drm_dev_private *priv = drm->dev_private; |
362 | u32 cntl; |
363 | |
364 | if (!priv->variant->broken_vblank) |
365 | drm_crtc_vblank_off(crtc); |
366 | |
367 | /* Power Down */ |
368 | cntl = readl(addr: priv->regs + priv->ctrl); |
369 | if (cntl & CNTL_LCDPWR) { |
370 | cntl &= ~CNTL_LCDPWR; |
371 | writel(val: cntl, addr: priv->regs + priv->ctrl); |
372 | } |
373 | |
374 | /* |
375 | * We expect this delay to stabilize the contrast voltage Vee as |
376 | * stipulated by the manual |
377 | */ |
378 | msleep(msecs: 20); |
379 | |
380 | if (priv->variant_display_disable) |
381 | priv->variant_display_disable(drm); |
382 | |
383 | /* Disable */ |
384 | writel(val: 0, addr: priv->regs + priv->ctrl); |
385 | |
386 | clk_disable_unprepare(clk: priv->clk); |
387 | } |
388 | |
389 | static void pl111_display_update(struct drm_simple_display_pipe *pipe, |
390 | struct drm_plane_state *old_pstate) |
391 | { |
392 | struct drm_crtc *crtc = &pipe->crtc; |
393 | struct drm_device *drm = crtc->dev; |
394 | struct pl111_drm_dev_private *priv = drm->dev_private; |
395 | struct drm_pending_vblank_event *event = crtc->state->event; |
396 | struct drm_plane *plane = &pipe->plane; |
397 | struct drm_plane_state *pstate = plane->state; |
398 | struct drm_framebuffer *fb = pstate->fb; |
399 | |
400 | if (fb) { |
401 | u32 addr = drm_fb_dma_get_gem_addr(fb, state: pstate, plane: 0); |
402 | |
403 | writel(val: addr, addr: priv->regs + CLCD_UBAS); |
404 | } |
405 | |
406 | if (event) { |
407 | crtc->state->event = NULL; |
408 | |
409 | spin_lock_irq(lock: &crtc->dev->event_lock); |
410 | if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0) |
411 | drm_crtc_arm_vblank_event(crtc, e: event); |
412 | else |
413 | drm_crtc_send_vblank_event(crtc, e: event); |
414 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
415 | } |
416 | } |
417 | |
418 | static int pl111_display_enable_vblank(struct drm_simple_display_pipe *pipe) |
419 | { |
420 | struct drm_crtc *crtc = &pipe->crtc; |
421 | struct drm_device *drm = crtc->dev; |
422 | struct pl111_drm_dev_private *priv = drm->dev_private; |
423 | |
424 | writel(CLCD_IRQ_NEXTBASE_UPDATE, addr: priv->regs + priv->ienb); |
425 | |
426 | return 0; |
427 | } |
428 | |
429 | static void pl111_display_disable_vblank(struct drm_simple_display_pipe *pipe) |
430 | { |
431 | struct drm_crtc *crtc = &pipe->crtc; |
432 | struct drm_device *drm = crtc->dev; |
433 | struct pl111_drm_dev_private *priv = drm->dev_private; |
434 | |
435 | writel(val: 0, addr: priv->regs + priv->ienb); |
436 | } |
437 | |
438 | static struct drm_simple_display_pipe_funcs pl111_display_funcs = { |
439 | .mode_valid = pl111_mode_valid, |
440 | .check = pl111_display_check, |
441 | .enable = pl111_display_enable, |
442 | .disable = pl111_display_disable, |
443 | .update = pl111_display_update, |
444 | }; |
445 | |
446 | static int pl111_clk_div_choose_div(struct clk_hw *hw, unsigned long rate, |
447 | unsigned long *prate, bool set_parent) |
448 | { |
449 | int best_div = 1, div; |
450 | struct clk_hw *parent = clk_hw_get_parent(hw); |
451 | unsigned long best_prate = 0; |
452 | unsigned long best_diff = ~0ul; |
453 | int max_div = (1 << (TIM2_PCD_LO_BITS + TIM2_PCD_HI_BITS)) - 1; |
454 | |
455 | for (div = 1; div < max_div; div++) { |
456 | unsigned long this_prate, div_rate, diff; |
457 | |
458 | if (set_parent) |
459 | this_prate = clk_hw_round_rate(hw: parent, rate: rate * div); |
460 | else |
461 | this_prate = *prate; |
462 | div_rate = DIV_ROUND_UP_ULL(this_prate, div); |
463 | diff = abs(rate - div_rate); |
464 | |
465 | if (diff < best_diff) { |
466 | best_div = div; |
467 | best_diff = diff; |
468 | best_prate = this_prate; |
469 | } |
470 | } |
471 | |
472 | *prate = best_prate; |
473 | return best_div; |
474 | } |
475 | |
476 | static long pl111_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, |
477 | unsigned long *prate) |
478 | { |
479 | int div = pl111_clk_div_choose_div(hw, rate, prate, set_parent: true); |
480 | |
481 | return DIV_ROUND_UP_ULL(*prate, div); |
482 | } |
483 | |
484 | static unsigned long pl111_clk_div_recalc_rate(struct clk_hw *hw, |
485 | unsigned long prate) |
486 | { |
487 | struct pl111_drm_dev_private *priv = |
488 | container_of(hw, struct pl111_drm_dev_private, clk_div); |
489 | u32 tim2 = readl(addr: priv->regs + CLCD_TIM2); |
490 | int div; |
491 | |
492 | if (tim2 & TIM2_BCD) |
493 | return prate; |
494 | |
495 | div = tim2 & TIM2_PCD_LO_MASK; |
496 | div |= (tim2 & TIM2_PCD_HI_MASK) >> |
497 | (TIM2_PCD_HI_SHIFT - TIM2_PCD_LO_BITS); |
498 | div += 2; |
499 | |
500 | return DIV_ROUND_UP_ULL(prate, div); |
501 | } |
502 | |
503 | static int pl111_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, |
504 | unsigned long prate) |
505 | { |
506 | struct pl111_drm_dev_private *priv = |
507 | container_of(hw, struct pl111_drm_dev_private, clk_div); |
508 | int div = pl111_clk_div_choose_div(hw, rate, prate: &prate, set_parent: false); |
509 | u32 tim2; |
510 | |
511 | spin_lock(lock: &priv->tim2_lock); |
512 | tim2 = readl(addr: priv->regs + CLCD_TIM2); |
513 | tim2 &= ~(TIM2_BCD | TIM2_PCD_LO_MASK | TIM2_PCD_HI_MASK); |
514 | |
515 | if (div == 1) { |
516 | tim2 |= TIM2_BCD; |
517 | } else { |
518 | div -= 2; |
519 | tim2 |= div & TIM2_PCD_LO_MASK; |
520 | tim2 |= (div >> TIM2_PCD_LO_BITS) << TIM2_PCD_HI_SHIFT; |
521 | } |
522 | |
523 | writel(val: tim2, addr: priv->regs + CLCD_TIM2); |
524 | spin_unlock(lock: &priv->tim2_lock); |
525 | |
526 | return 0; |
527 | } |
528 | |
529 | static const struct clk_ops pl111_clk_div_ops = { |
530 | .recalc_rate = pl111_clk_div_recalc_rate, |
531 | .round_rate = pl111_clk_div_round_rate, |
532 | .set_rate = pl111_clk_div_set_rate, |
533 | }; |
534 | |
535 | static int |
536 | pl111_init_clock_divider(struct drm_device *drm) |
537 | { |
538 | struct pl111_drm_dev_private *priv = drm->dev_private; |
539 | struct clk *parent = devm_clk_get(dev: drm->dev, id: "clcdclk" ); |
540 | struct clk_hw *div = &priv->clk_div; |
541 | const char *parent_name; |
542 | struct clk_init_data init = { |
543 | .name = "pl111_div" , |
544 | .ops = &pl111_clk_div_ops, |
545 | .parent_names = &parent_name, |
546 | .num_parents = 1, |
547 | .flags = CLK_SET_RATE_PARENT, |
548 | }; |
549 | int ret; |
550 | |
551 | if (IS_ERR(ptr: parent)) { |
552 | dev_err(drm->dev, "CLCD: unable to get clcdclk.\n" ); |
553 | return PTR_ERR(ptr: parent); |
554 | } |
555 | |
556 | spin_lock_init(&priv->tim2_lock); |
557 | |
558 | /* If the clock divider is broken, use the parent directly */ |
559 | if (priv->variant->broken_clockdivider) { |
560 | priv->clk = parent; |
561 | return 0; |
562 | } |
563 | parent_name = __clk_get_name(clk: parent); |
564 | div->init = &init; |
565 | |
566 | ret = devm_clk_hw_register(dev: drm->dev, hw: div); |
567 | |
568 | priv->clk = div->clk; |
569 | return ret; |
570 | } |
571 | |
572 | int pl111_display_init(struct drm_device *drm) |
573 | { |
574 | struct pl111_drm_dev_private *priv = drm->dev_private; |
575 | int ret; |
576 | |
577 | ret = pl111_init_clock_divider(drm); |
578 | if (ret) |
579 | return ret; |
580 | |
581 | if (!priv->variant->broken_vblank) { |
582 | pl111_display_funcs.enable_vblank = pl111_display_enable_vblank; |
583 | pl111_display_funcs.disable_vblank = pl111_display_disable_vblank; |
584 | } |
585 | |
586 | ret = drm_simple_display_pipe_init(dev: drm, pipe: &priv->pipe, |
587 | funcs: &pl111_display_funcs, |
588 | formats: priv->variant->formats, |
589 | format_count: priv->variant->nformats, |
590 | NULL, |
591 | connector: priv->connector); |
592 | if (ret) |
593 | return ret; |
594 | |
595 | return 0; |
596 | } |
597 | |