1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * Copyright (C) 2020 Unisoc Inc. |
4 | */ |
5 | |
6 | #include <linux/component.h> |
7 | #include <linux/delay.h> |
8 | #include <linux/dma-buf.h> |
9 | #include <linux/io.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/of_graph.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/wait.h> |
15 | #include <linux/workqueue.h> |
16 | |
17 | #include <drm/drm_atomic_helper.h> |
18 | #include <drm/drm_blend.h> |
19 | #include <drm/drm_fb_dma_helper.h> |
20 | #include <drm/drm_framebuffer.h> |
21 | #include <drm/drm_gem_dma_helper.h> |
22 | #include <drm/drm_gem_framebuffer_helper.h> |
23 | |
24 | #include "sprd_drm.h" |
25 | #include "sprd_dpu.h" |
26 | #include "sprd_dsi.h" |
27 | |
28 | /* Global control registers */ |
29 | #define REG_DPU_CTRL 0x04 |
30 | #define REG_DPU_CFG0 0x08 |
31 | #define REG_PANEL_SIZE 0x20 |
32 | #define REG_BLEND_SIZE 0x24 |
33 | #define REG_BG_COLOR 0x2C |
34 | |
35 | /* Layer0 control registers */ |
36 | #define REG_LAY_BASE_ADDR0 0x30 |
37 | #define REG_LAY_BASE_ADDR1 0x34 |
38 | #define REG_LAY_BASE_ADDR2 0x38 |
39 | #define REG_LAY_CTRL 0x40 |
40 | #define REG_LAY_SIZE 0x44 |
41 | #define REG_LAY_PITCH 0x48 |
42 | #define REG_LAY_POS 0x4C |
43 | #define REG_LAY_ALPHA 0x50 |
44 | #define REG_LAY_CROP_START 0x5C |
45 | |
46 | /* Interrupt control registers */ |
47 | #define REG_DPU_INT_EN 0x1E0 |
48 | #define REG_DPU_INT_CLR 0x1E4 |
49 | #define REG_DPU_INT_STS 0x1E8 |
50 | |
51 | /* DPI control registers */ |
52 | #define REG_DPI_CTRL 0x1F0 |
53 | #define REG_DPI_H_TIMING 0x1F4 |
54 | #define REG_DPI_V_TIMING 0x1F8 |
55 | |
56 | /* MMU control registers */ |
57 | #define REG_MMU_EN 0x800 |
58 | #define REG_MMU_VPN_RANGE 0x80C |
59 | #define REG_MMU_PPN1 0x83C |
60 | #define REG_MMU_RANGE1 0x840 |
61 | #define REG_MMU_PPN2 0x844 |
62 | #define REG_MMU_RANGE2 0x848 |
63 | |
64 | /* Global control bits */ |
65 | #define BIT_DPU_RUN BIT(0) |
66 | #define BIT_DPU_STOP BIT(1) |
67 | #define BIT_DPU_REG_UPDATE BIT(2) |
68 | #define BIT_DPU_IF_EDPI BIT(0) |
69 | |
70 | /* Layer control bits */ |
71 | #define BIT_DPU_LAY_EN BIT(0) |
72 | #define BIT_DPU_LAY_LAYER_ALPHA (0x01 << 2) |
73 | #define BIT_DPU_LAY_COMBO_ALPHA (0x02 << 2) |
74 | #define BIT_DPU_LAY_FORMAT_YUV422_2PLANE (0x00 << 4) |
75 | #define BIT_DPU_LAY_FORMAT_YUV420_2PLANE (0x01 << 4) |
76 | #define BIT_DPU_LAY_FORMAT_YUV420_3PLANE (0x02 << 4) |
77 | #define BIT_DPU_LAY_FORMAT_ARGB8888 (0x03 << 4) |
78 | #define BIT_DPU_LAY_FORMAT_RGB565 (0x04 << 4) |
79 | #define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3 (0x00 << 8) |
80 | #define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0 (0x01 << 8) |
81 | #define BIT_DPU_LAY_NO_SWITCH (0x00 << 10) |
82 | #define BIT_DPU_LAY_RB_OR_UV_SWITCH (0x01 << 10) |
83 | #define BIT_DPU_LAY_MODE_BLEND_NORMAL (0x00 << 16) |
84 | #define BIT_DPU_LAY_MODE_BLEND_PREMULT (0x01 << 16) |
85 | #define BIT_DPU_LAY_ROTATION_0 (0x00 << 20) |
86 | #define BIT_DPU_LAY_ROTATION_90 (0x01 << 20) |
87 | #define BIT_DPU_LAY_ROTATION_180 (0x02 << 20) |
88 | #define BIT_DPU_LAY_ROTATION_270 (0x03 << 20) |
89 | #define BIT_DPU_LAY_ROTATION_0_M (0x04 << 20) |
90 | #define BIT_DPU_LAY_ROTATION_90_M (0x05 << 20) |
91 | #define BIT_DPU_LAY_ROTATION_180_M (0x06 << 20) |
92 | #define BIT_DPU_LAY_ROTATION_270_M (0x07 << 20) |
93 | |
94 | /* Interrupt control & status bits */ |
95 | #define BIT_DPU_INT_DONE BIT(0) |
96 | #define BIT_DPU_INT_TE BIT(1) |
97 | #define BIT_DPU_INT_ERR BIT(2) |
98 | #define BIT_DPU_INT_UPDATE_DONE BIT(4) |
99 | #define BIT_DPU_INT_VSYNC BIT(5) |
100 | |
101 | /* DPI control bits */ |
102 | #define BIT_DPU_EDPI_TE_EN BIT(8) |
103 | #define BIT_DPU_EDPI_FROM_EXTERNAL_PAD BIT(10) |
104 | #define BIT_DPU_DPI_HALT_EN BIT(16) |
105 | |
106 | static const u32 layer_fmts[] = { |
107 | DRM_FORMAT_XRGB8888, |
108 | DRM_FORMAT_XBGR8888, |
109 | DRM_FORMAT_ARGB8888, |
110 | DRM_FORMAT_ABGR8888, |
111 | DRM_FORMAT_RGBA8888, |
112 | DRM_FORMAT_BGRA8888, |
113 | DRM_FORMAT_RGBX8888, |
114 | DRM_FORMAT_RGB565, |
115 | DRM_FORMAT_BGR565, |
116 | DRM_FORMAT_NV12, |
117 | DRM_FORMAT_NV21, |
118 | DRM_FORMAT_NV16, |
119 | DRM_FORMAT_NV61, |
120 | DRM_FORMAT_YUV420, |
121 | DRM_FORMAT_YVU420, |
122 | }; |
123 | |
124 | struct sprd_plane { |
125 | struct drm_plane base; |
126 | }; |
127 | |
128 | static int dpu_wait_stop_done(struct sprd_dpu *dpu) |
129 | { |
130 | struct dpu_context *ctx = &dpu->ctx; |
131 | int rc; |
132 | |
133 | if (ctx->stopped) |
134 | return 0; |
135 | |
136 | rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop, |
137 | msecs_to_jiffies(500)); |
138 | ctx->evt_stop = false; |
139 | |
140 | ctx->stopped = true; |
141 | |
142 | if (!rc) { |
143 | drm_err(dpu->drm, "dpu wait for stop done time out!\n"); |
144 | return -ETIMEDOUT; |
145 | } |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static int dpu_wait_update_done(struct sprd_dpu *dpu) |
151 | { |
152 | struct dpu_context *ctx = &dpu->ctx; |
153 | int rc; |
154 | |
155 | ctx->evt_update = false; |
156 | |
157 | rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update, |
158 | msecs_to_jiffies(500)); |
159 | |
160 | if (!rc) { |
161 | drm_err(dpu->drm, "dpu wait for reg update done time out!\n"); |
162 | return -ETIMEDOUT; |
163 | } |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | static u32 drm_format_to_dpu(struct drm_framebuffer *fb) |
169 | { |
170 | u32 format = 0; |
171 | |
172 | switch (fb->format->format) { |
173 | case DRM_FORMAT_BGRA8888: |
174 | /* BGRA8888 -> ARGB8888 */ |
175 | format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0; |
176 | format |= BIT_DPU_LAY_FORMAT_ARGB8888; |
177 | break; |
178 | case DRM_FORMAT_RGBX8888: |
179 | case DRM_FORMAT_RGBA8888: |
180 | /* RGBA8888 -> ABGR8888 */ |
181 | format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0; |
182 | fallthrough; |
183 | case DRM_FORMAT_ABGR8888: |
184 | /* RB switch */ |
185 | format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; |
186 | fallthrough; |
187 | case DRM_FORMAT_ARGB8888: |
188 | format |= BIT_DPU_LAY_FORMAT_ARGB8888; |
189 | break; |
190 | case DRM_FORMAT_XBGR8888: |
191 | /* RB switch */ |
192 | format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; |
193 | fallthrough; |
194 | case DRM_FORMAT_XRGB8888: |
195 | format |= BIT_DPU_LAY_FORMAT_ARGB8888; |
196 | break; |
197 | case DRM_FORMAT_BGR565: |
198 | /* RB switch */ |
199 | format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; |
200 | fallthrough; |
201 | case DRM_FORMAT_RGB565: |
202 | format |= BIT_DPU_LAY_FORMAT_RGB565; |
203 | break; |
204 | case DRM_FORMAT_NV12: |
205 | /* 2-Lane: Yuv420 */ |
206 | format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE; |
207 | /* Y endian */ |
208 | format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; |
209 | /* UV endian */ |
210 | format |= BIT_DPU_LAY_NO_SWITCH; |
211 | break; |
212 | case DRM_FORMAT_NV21: |
213 | /* 2-Lane: Yuv420 */ |
214 | format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE; |
215 | /* Y endian */ |
216 | format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; |
217 | /* UV endian */ |
218 | format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; |
219 | break; |
220 | case DRM_FORMAT_NV16: |
221 | /* 2-Lane: Yuv422 */ |
222 | format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE; |
223 | /* Y endian */ |
224 | format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0; |
225 | /* UV endian */ |
226 | format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; |
227 | break; |
228 | case DRM_FORMAT_NV61: |
229 | /* 2-Lane: Yuv422 */ |
230 | format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE; |
231 | /* Y endian */ |
232 | format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; |
233 | /* UV endian */ |
234 | format |= BIT_DPU_LAY_NO_SWITCH; |
235 | break; |
236 | case DRM_FORMAT_YUV420: |
237 | format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE; |
238 | /* Y endian */ |
239 | format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; |
240 | /* UV endian */ |
241 | format |= BIT_DPU_LAY_NO_SWITCH; |
242 | break; |
243 | case DRM_FORMAT_YVU420: |
244 | format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE; |
245 | /* Y endian */ |
246 | format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; |
247 | /* UV endian */ |
248 | format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; |
249 | break; |
250 | default: |
251 | break; |
252 | } |
253 | |
254 | return format; |
255 | } |
256 | |
257 | static u32 drm_rotation_to_dpu(struct drm_plane_state *state) |
258 | { |
259 | u32 rotation = 0; |
260 | |
261 | switch (state->rotation) { |
262 | default: |
263 | case DRM_MODE_ROTATE_0: |
264 | rotation = BIT_DPU_LAY_ROTATION_0; |
265 | break; |
266 | case DRM_MODE_ROTATE_90: |
267 | rotation = BIT_DPU_LAY_ROTATION_90; |
268 | break; |
269 | case DRM_MODE_ROTATE_180: |
270 | rotation = BIT_DPU_LAY_ROTATION_180; |
271 | break; |
272 | case DRM_MODE_ROTATE_270: |
273 | rotation = BIT_DPU_LAY_ROTATION_270; |
274 | break; |
275 | case DRM_MODE_REFLECT_Y: |
276 | rotation = BIT_DPU_LAY_ROTATION_180_M; |
277 | break; |
278 | case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90): |
279 | rotation = BIT_DPU_LAY_ROTATION_90_M; |
280 | break; |
281 | case DRM_MODE_REFLECT_X: |
282 | rotation = BIT_DPU_LAY_ROTATION_0_M; |
283 | break; |
284 | case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90): |
285 | rotation = BIT_DPU_LAY_ROTATION_270_M; |
286 | break; |
287 | } |
288 | |
289 | return rotation; |
290 | } |
291 | |
292 | static u32 drm_blend_to_dpu(struct drm_plane_state *state) |
293 | { |
294 | u32 blend = 0; |
295 | |
296 | switch (state->pixel_blend_mode) { |
297 | case DRM_MODE_BLEND_COVERAGE: |
298 | /* alpha mode select - combo alpha */ |
299 | blend |= BIT_DPU_LAY_COMBO_ALPHA; |
300 | /* Normal mode */ |
301 | blend |= BIT_DPU_LAY_MODE_BLEND_NORMAL; |
302 | break; |
303 | case DRM_MODE_BLEND_PREMULTI: |
304 | /* alpha mode select - combo alpha */ |
305 | blend |= BIT_DPU_LAY_COMBO_ALPHA; |
306 | /* Pre-mult mode */ |
307 | blend |= BIT_DPU_LAY_MODE_BLEND_PREMULT; |
308 | break; |
309 | case DRM_MODE_BLEND_PIXEL_NONE: |
310 | default: |
311 | /* don't do blending, maybe RGBX */ |
312 | /* alpha mode select - layer alpha */ |
313 | blend |= BIT_DPU_LAY_LAYER_ALPHA; |
314 | break; |
315 | } |
316 | |
317 | return blend; |
318 | } |
319 | |
320 | static void sprd_dpu_layer(struct sprd_dpu *dpu, struct drm_plane_state *state) |
321 | { |
322 | struct dpu_context *ctx = &dpu->ctx; |
323 | struct drm_gem_dma_object *dma_obj; |
324 | struct drm_framebuffer *fb = state->fb; |
325 | u32 addr, size, offset, pitch, blend, format, rotation; |
326 | u32 src_x = state->src_x >> 16; |
327 | u32 src_y = state->src_y >> 16; |
328 | u32 src_w = state->src_w >> 16; |
329 | u32 src_h = state->src_h >> 16; |
330 | u32 dst_x = state->crtc_x; |
331 | u32 dst_y = state->crtc_y; |
332 | u32 alpha = state->alpha; |
333 | u32 index = state->zpos; |
334 | int i; |
335 | |
336 | offset = (dst_x & 0xffff) | (dst_y << 16); |
337 | size = (src_w & 0xffff) | (src_h << 16); |
338 | |
339 | for (i = 0; i < fb->format->num_planes; i++) { |
340 | dma_obj = drm_fb_dma_get_gem_obj(fb, plane: i); |
341 | addr = dma_obj->dma_addr + fb->offsets[i]; |
342 | |
343 | if (i == 0) |
344 | layer_reg_wr(ctx, REG_LAY_BASE_ADDR0, cfg_bits: addr, index); |
345 | else if (i == 1) |
346 | layer_reg_wr(ctx, REG_LAY_BASE_ADDR1, cfg_bits: addr, index); |
347 | else |
348 | layer_reg_wr(ctx, REG_LAY_BASE_ADDR2, cfg_bits: addr, index); |
349 | } |
350 | |
351 | if (fb->format->num_planes == 3) { |
352 | /* UV pitch is 1/2 of Y pitch */ |
353 | pitch = (fb->pitches[0] / fb->format->cpp[0]) | |
354 | (fb->pitches[0] / fb->format->cpp[0] << 15); |
355 | } else { |
356 | pitch = fb->pitches[0] / fb->format->cpp[0]; |
357 | } |
358 | |
359 | layer_reg_wr(ctx, REG_LAY_POS, cfg_bits: offset, index); |
360 | layer_reg_wr(ctx, REG_LAY_SIZE, cfg_bits: size, index); |
361 | layer_reg_wr(ctx, REG_LAY_CROP_START, |
362 | cfg_bits: src_y << 16 | src_x, index); |
363 | layer_reg_wr(ctx, REG_LAY_ALPHA, cfg_bits: alpha, index); |
364 | layer_reg_wr(ctx, REG_LAY_PITCH, cfg_bits: pitch, index); |
365 | |
366 | format = drm_format_to_dpu(fb); |
367 | blend = drm_blend_to_dpu(state); |
368 | rotation = drm_rotation_to_dpu(state); |
369 | |
370 | layer_reg_wr(ctx, REG_LAY_CTRL, BIT_DPU_LAY_EN | |
371 | format | |
372 | blend | |
373 | rotation, |
374 | index); |
375 | } |
376 | |
377 | static void sprd_dpu_flip(struct sprd_dpu *dpu) |
378 | { |
379 | struct dpu_context *ctx = &dpu->ctx; |
380 | |
381 | /* |
382 | * Make sure the dpu is in stop status. DPU has no shadow |
383 | * registers in EDPI mode. So the config registers can only be |
384 | * updated in the rising edge of DPU_RUN bit. |
385 | */ |
386 | if (ctx->if_type == SPRD_DPU_IF_EDPI) |
387 | dpu_wait_stop_done(dpu); |
388 | |
389 | /* update trigger and wait */ |
390 | if (ctx->if_type == SPRD_DPU_IF_DPI) { |
391 | if (!ctx->stopped) { |
392 | dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_REG_UPDATE); |
393 | dpu_wait_update_done(dpu); |
394 | } |
395 | |
396 | dpu_reg_set(ctx, REG_DPU_INT_EN, BIT_DPU_INT_ERR); |
397 | } else if (ctx->if_type == SPRD_DPU_IF_EDPI) { |
398 | dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN); |
399 | |
400 | ctx->stopped = false; |
401 | } |
402 | } |
403 | |
404 | static void sprd_dpu_init(struct sprd_dpu *dpu) |
405 | { |
406 | struct dpu_context *ctx = &dpu->ctx; |
407 | u32 int_mask = 0; |
408 | |
409 | writel(val: 0x00, addr: ctx->base + REG_BG_COLOR); |
410 | writel(val: 0x00, addr: ctx->base + REG_MMU_EN); |
411 | writel(val: 0x00, addr: ctx->base + REG_MMU_PPN1); |
412 | writel(val: 0xffff, addr: ctx->base + REG_MMU_RANGE1); |
413 | writel(val: 0x00, addr: ctx->base + REG_MMU_PPN2); |
414 | writel(val: 0xffff, addr: ctx->base + REG_MMU_RANGE2); |
415 | writel(val: 0x1ffff, addr: ctx->base + REG_MMU_VPN_RANGE); |
416 | |
417 | if (ctx->if_type == SPRD_DPU_IF_DPI) { |
418 | /* use dpi as interface */ |
419 | dpu_reg_clr(ctx, REG_DPU_CFG0, BIT_DPU_IF_EDPI); |
420 | /* disable Halt function for SPRD DSI */ |
421 | dpu_reg_clr(ctx, REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN); |
422 | /* select te from external pad */ |
423 | dpu_reg_set(ctx, REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD); |
424 | |
425 | /* enable dpu update done INT */ |
426 | int_mask |= BIT_DPU_INT_UPDATE_DONE; |
427 | /* enable dpu done INT */ |
428 | int_mask |= BIT_DPU_INT_DONE; |
429 | /* enable dpu dpi vsync */ |
430 | int_mask |= BIT_DPU_INT_VSYNC; |
431 | /* enable dpu TE INT */ |
432 | int_mask |= BIT_DPU_INT_TE; |
433 | /* enable underflow err INT */ |
434 | int_mask |= BIT_DPU_INT_ERR; |
435 | } else if (ctx->if_type == SPRD_DPU_IF_EDPI) { |
436 | /* use edpi as interface */ |
437 | dpu_reg_set(ctx, REG_DPU_CFG0, BIT_DPU_IF_EDPI); |
438 | /* use external te */ |
439 | dpu_reg_set(ctx, REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD); |
440 | /* enable te */ |
441 | dpu_reg_set(ctx, REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN); |
442 | |
443 | /* enable stop done INT */ |
444 | int_mask |= BIT_DPU_INT_DONE; |
445 | /* enable TE INT */ |
446 | int_mask |= BIT_DPU_INT_TE; |
447 | } |
448 | |
449 | writel(val: int_mask, addr: ctx->base + REG_DPU_INT_EN); |
450 | } |
451 | |
452 | static void sprd_dpu_fini(struct sprd_dpu *dpu) |
453 | { |
454 | struct dpu_context *ctx = &dpu->ctx; |
455 | |
456 | writel(val: 0x00, addr: ctx->base + REG_DPU_INT_EN); |
457 | writel(val: 0xff, addr: ctx->base + REG_DPU_INT_CLR); |
458 | } |
459 | |
460 | static void sprd_dpi_init(struct sprd_dpu *dpu) |
461 | { |
462 | struct dpu_context *ctx = &dpu->ctx; |
463 | u32 reg_val; |
464 | u32 size; |
465 | |
466 | size = (ctx->vm.vactive << 16) | ctx->vm.hactive; |
467 | writel(val: size, addr: ctx->base + REG_PANEL_SIZE); |
468 | writel(val: size, addr: ctx->base + REG_BLEND_SIZE); |
469 | |
470 | if (ctx->if_type == SPRD_DPU_IF_DPI) { |
471 | /* set dpi timing */ |
472 | reg_val = ctx->vm.hsync_len << 0 | |
473 | ctx->vm.hback_porch << 8 | |
474 | ctx->vm.hfront_porch << 20; |
475 | writel(val: reg_val, addr: ctx->base + REG_DPI_H_TIMING); |
476 | |
477 | reg_val = ctx->vm.vsync_len << 0 | |
478 | ctx->vm.vback_porch << 8 | |
479 | ctx->vm.vfront_porch << 20; |
480 | writel(val: reg_val, addr: ctx->base + REG_DPI_V_TIMING); |
481 | } |
482 | } |
483 | |
484 | void sprd_dpu_run(struct sprd_dpu *dpu) |
485 | { |
486 | struct dpu_context *ctx = &dpu->ctx; |
487 | |
488 | dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN); |
489 | |
490 | ctx->stopped = false; |
491 | } |
492 | |
493 | void sprd_dpu_stop(struct sprd_dpu *dpu) |
494 | { |
495 | struct dpu_context *ctx = &dpu->ctx; |
496 | |
497 | if (ctx->if_type == SPRD_DPU_IF_DPI) |
498 | dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_STOP); |
499 | |
500 | dpu_wait_stop_done(dpu); |
501 | } |
502 | |
503 | static int sprd_plane_atomic_check(struct drm_plane *plane, |
504 | struct drm_atomic_state *state) |
505 | { |
506 | struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, |
507 | plane); |
508 | struct drm_crtc_state *crtc_state; |
509 | u32 fmt; |
510 | |
511 | if (!plane_state->fb || !plane_state->crtc) |
512 | return 0; |
513 | |
514 | fmt = drm_format_to_dpu(fb: plane_state->fb); |
515 | if (!fmt) |
516 | return -EINVAL; |
517 | |
518 | crtc_state = drm_atomic_get_crtc_state(state: plane_state->state, crtc: plane_state->crtc); |
519 | if (IS_ERR(ptr: crtc_state)) |
520 | return PTR_ERR(ptr: crtc_state); |
521 | |
522 | return drm_atomic_helper_check_plane_state(plane_state, crtc_state, |
523 | DRM_PLANE_NO_SCALING, |
524 | DRM_PLANE_NO_SCALING, |
525 | can_position: true, can_update_disabled: true); |
526 | } |
527 | |
528 | static void sprd_plane_atomic_update(struct drm_plane *drm_plane, |
529 | struct drm_atomic_state *state) |
530 | { |
531 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
532 | plane: drm_plane); |
533 | struct sprd_dpu *dpu = to_sprd_crtc(crtc: new_state->crtc); |
534 | |
535 | /* start configure dpu layers */ |
536 | sprd_dpu_layer(dpu, state: new_state); |
537 | } |
538 | |
539 | static void sprd_plane_atomic_disable(struct drm_plane *drm_plane, |
540 | struct drm_atomic_state *state) |
541 | { |
542 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, |
543 | plane: drm_plane); |
544 | struct sprd_dpu *dpu = to_sprd_crtc(crtc: old_state->crtc); |
545 | |
546 | layer_reg_wr(ctx: &dpu->ctx, REG_LAY_CTRL, cfg_bits: 0x00, index: old_state->zpos); |
547 | } |
548 | |
549 | static void sprd_plane_create_properties(struct sprd_plane *plane, int index) |
550 | { |
551 | unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) | |
552 | BIT(DRM_MODE_BLEND_PREMULTI) | |
553 | BIT(DRM_MODE_BLEND_COVERAGE); |
554 | |
555 | /* create rotation property */ |
556 | drm_plane_create_rotation_property(plane: &plane->base, |
557 | DRM_MODE_ROTATE_0, |
558 | DRM_MODE_ROTATE_MASK | |
559 | DRM_MODE_REFLECT_MASK); |
560 | |
561 | /* create alpha property */ |
562 | drm_plane_create_alpha_property(plane: &plane->base); |
563 | |
564 | /* create blend mode property */ |
565 | drm_plane_create_blend_mode_property(plane: &plane->base, supported_modes); |
566 | |
567 | /* create zpos property */ |
568 | drm_plane_create_zpos_immutable_property(plane: &plane->base, zpos: index); |
569 | } |
570 | |
571 | static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = { |
572 | .atomic_check = sprd_plane_atomic_check, |
573 | .atomic_update = sprd_plane_atomic_update, |
574 | .atomic_disable = sprd_plane_atomic_disable, |
575 | }; |
576 | |
577 | static const struct drm_plane_funcs sprd_plane_funcs = { |
578 | .update_plane = drm_atomic_helper_update_plane, |
579 | .disable_plane = drm_atomic_helper_disable_plane, |
580 | .destroy = drm_plane_cleanup, |
581 | .reset = drm_atomic_helper_plane_reset, |
582 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
583 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
584 | }; |
585 | |
586 | static struct sprd_plane *sprd_planes_init(struct drm_device *drm) |
587 | { |
588 | struct sprd_plane *plane, *primary; |
589 | enum drm_plane_type plane_type; |
590 | int i; |
591 | |
592 | for (i = 0; i < 6; i++) { |
593 | plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY : |
594 | DRM_PLANE_TYPE_OVERLAY; |
595 | |
596 | plane = drmm_universal_plane_alloc(drm, struct sprd_plane, base, |
597 | 1, &sprd_plane_funcs, |
598 | layer_fmts, ARRAY_SIZE(layer_fmts), |
599 | NULL, plane_type, NULL); |
600 | if (IS_ERR(ptr: plane)) { |
601 | drm_err(drm, "failed to init drm plane: %d\n", i); |
602 | return plane; |
603 | } |
604 | |
605 | drm_plane_helper_add(plane: &plane->base, funcs: &sprd_plane_helper_funcs); |
606 | |
607 | sprd_plane_create_properties(plane, index: i); |
608 | |
609 | if (i == 0) |
610 | primary = plane; |
611 | } |
612 | |
613 | return primary; |
614 | } |
615 | |
616 | static void sprd_crtc_mode_set_nofb(struct drm_crtc *crtc) |
617 | { |
618 | struct sprd_dpu *dpu = to_sprd_crtc(crtc); |
619 | struct drm_display_mode *mode = &crtc->state->adjusted_mode; |
620 | struct drm_encoder *encoder; |
621 | struct sprd_dsi *dsi; |
622 | |
623 | drm_display_mode_to_videomode(dmode: mode, vm: &dpu->ctx.vm); |
624 | |
625 | drm_for_each_encoder_mask(encoder, crtc->dev, |
626 | crtc->state->encoder_mask) { |
627 | dsi = encoder_to_dsi(encoder); |
628 | |
629 | if (dsi->slave->mode_flags & MIPI_DSI_MODE_VIDEO) |
630 | dpu->ctx.if_type = SPRD_DPU_IF_DPI; |
631 | else |
632 | dpu->ctx.if_type = SPRD_DPU_IF_EDPI; |
633 | } |
634 | |
635 | sprd_dpi_init(dpu); |
636 | } |
637 | |
638 | static void sprd_crtc_atomic_enable(struct drm_crtc *crtc, |
639 | struct drm_atomic_state *state) |
640 | { |
641 | struct sprd_dpu *dpu = to_sprd_crtc(crtc); |
642 | |
643 | sprd_dpu_init(dpu); |
644 | |
645 | drm_crtc_vblank_on(crtc: &dpu->base); |
646 | } |
647 | |
648 | static void sprd_crtc_atomic_disable(struct drm_crtc *crtc, |
649 | struct drm_atomic_state *state) |
650 | { |
651 | struct sprd_dpu *dpu = to_sprd_crtc(crtc); |
652 | struct drm_device *drm = dpu->base.dev; |
653 | |
654 | drm_crtc_vblank_off(crtc: &dpu->base); |
655 | |
656 | sprd_dpu_fini(dpu); |
657 | |
658 | spin_lock_irq(lock: &drm->event_lock); |
659 | if (crtc->state->event) { |
660 | drm_crtc_send_vblank_event(crtc, e: crtc->state->event); |
661 | crtc->state->event = NULL; |
662 | } |
663 | spin_unlock_irq(lock: &drm->event_lock); |
664 | } |
665 | |
666 | static void sprd_crtc_atomic_flush(struct drm_crtc *crtc, |
667 | struct drm_atomic_state *state) |
668 | |
669 | { |
670 | struct sprd_dpu *dpu = to_sprd_crtc(crtc); |
671 | struct drm_device *drm = dpu->base.dev; |
672 | |
673 | sprd_dpu_flip(dpu); |
674 | |
675 | spin_lock_irq(lock: &drm->event_lock); |
676 | if (crtc->state->event) { |
677 | drm_crtc_send_vblank_event(crtc, e: crtc->state->event); |
678 | crtc->state->event = NULL; |
679 | } |
680 | spin_unlock_irq(lock: &drm->event_lock); |
681 | } |
682 | |
683 | static int sprd_crtc_enable_vblank(struct drm_crtc *crtc) |
684 | { |
685 | struct sprd_dpu *dpu = to_sprd_crtc(crtc); |
686 | |
687 | dpu_reg_set(ctx: &dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC); |
688 | |
689 | return 0; |
690 | } |
691 | |
692 | static void sprd_crtc_disable_vblank(struct drm_crtc *crtc) |
693 | { |
694 | struct sprd_dpu *dpu = to_sprd_crtc(crtc); |
695 | |
696 | dpu_reg_clr(ctx: &dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC); |
697 | } |
698 | |
699 | static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = { |
700 | .mode_set_nofb = sprd_crtc_mode_set_nofb, |
701 | .atomic_flush = sprd_crtc_atomic_flush, |
702 | .atomic_enable = sprd_crtc_atomic_enable, |
703 | .atomic_disable = sprd_crtc_atomic_disable, |
704 | }; |
705 | |
706 | static const struct drm_crtc_funcs sprd_crtc_funcs = { |
707 | .destroy = drm_crtc_cleanup, |
708 | .set_config = drm_atomic_helper_set_config, |
709 | .page_flip = drm_atomic_helper_page_flip, |
710 | .reset = drm_atomic_helper_crtc_reset, |
711 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
712 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
713 | .enable_vblank = sprd_crtc_enable_vblank, |
714 | .disable_vblank = sprd_crtc_disable_vblank, |
715 | }; |
716 | |
717 | static struct sprd_dpu *sprd_crtc_init(struct drm_device *drm, |
718 | struct drm_plane *primary, struct device *dev) |
719 | { |
720 | struct device_node *port; |
721 | struct sprd_dpu *dpu; |
722 | |
723 | dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base, |
724 | primary, NULL, |
725 | &sprd_crtc_funcs, NULL); |
726 | if (IS_ERR(ptr: dpu)) { |
727 | drm_err(drm, "failed to init crtc\n"); |
728 | return dpu; |
729 | } |
730 | drm_crtc_helper_add(crtc: &dpu->base, funcs: &sprd_crtc_helper_funcs); |
731 | |
732 | /* |
733 | * set crtc port so that drm_of_find_possible_crtcs call works |
734 | */ |
735 | port = of_graph_get_port_by_id(node: dev->of_node, id: 0); |
736 | if (!port) { |
737 | drm_err(drm, "failed to found crtc output port for %s\n", |
738 | dev->of_node->full_name); |
739 | return ERR_PTR(error: -EINVAL); |
740 | } |
741 | dpu->base.port = port; |
742 | of_node_put(node: port); |
743 | |
744 | return dpu; |
745 | } |
746 | |
747 | static irqreturn_t sprd_dpu_isr(int irq, void *data) |
748 | { |
749 | struct sprd_dpu *dpu = data; |
750 | struct dpu_context *ctx = &dpu->ctx; |
751 | u32 reg_val, int_mask = 0; |
752 | |
753 | reg_val = readl(addr: ctx->base + REG_DPU_INT_STS); |
754 | |
755 | /* disable err interrupt */ |
756 | if (reg_val & BIT_DPU_INT_ERR) { |
757 | int_mask |= BIT_DPU_INT_ERR; |
758 | drm_warn(dpu->drm, "Warning: dpu underflow!\n"); |
759 | } |
760 | |
761 | /* dpu update done isr */ |
762 | if (reg_val & BIT_DPU_INT_UPDATE_DONE) { |
763 | ctx->evt_update = true; |
764 | wake_up_interruptible_all(&ctx->wait_queue); |
765 | } |
766 | |
767 | /* dpu stop done isr */ |
768 | if (reg_val & BIT_DPU_INT_DONE) { |
769 | ctx->evt_stop = true; |
770 | wake_up_interruptible_all(&ctx->wait_queue); |
771 | } |
772 | |
773 | if (reg_val & BIT_DPU_INT_VSYNC) |
774 | drm_crtc_handle_vblank(crtc: &dpu->base); |
775 | |
776 | writel(val: reg_val, addr: ctx->base + REG_DPU_INT_CLR); |
777 | dpu_reg_clr(ctx, REG_DPU_INT_EN, clr_bits: int_mask); |
778 | |
779 | return IRQ_HANDLED; |
780 | } |
781 | |
782 | static int sprd_dpu_context_init(struct sprd_dpu *dpu, |
783 | struct device *dev) |
784 | { |
785 | struct platform_device *pdev = to_platform_device(dev); |
786 | struct dpu_context *ctx = &dpu->ctx; |
787 | struct resource *res; |
788 | int ret; |
789 | |
790 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
791 | if (!res) { |
792 | dev_err(dev, "failed to get I/O resource\n"); |
793 | return -EINVAL; |
794 | } |
795 | |
796 | ctx->base = devm_ioremap(dev, offset: res->start, size: resource_size(res)); |
797 | if (!ctx->base) { |
798 | dev_err(dev, "failed to map dpu registers\n"); |
799 | return -EFAULT; |
800 | } |
801 | |
802 | ctx->irq = platform_get_irq(pdev, 0); |
803 | if (ctx->irq < 0) |
804 | return ctx->irq; |
805 | |
806 | /* disable and clear interrupts before register dpu IRQ. */ |
807 | writel(val: 0x00, addr: ctx->base + REG_DPU_INT_EN); |
808 | writel(val: 0xff, addr: ctx->base + REG_DPU_INT_CLR); |
809 | |
810 | ret = devm_request_irq(dev, irq: ctx->irq, handler: sprd_dpu_isr, |
811 | IRQF_TRIGGER_NONE, devname: "DPU", dev_id: dpu); |
812 | if (ret) { |
813 | dev_err(dev, "failed to register dpu irq handler\n"); |
814 | return ret; |
815 | } |
816 | |
817 | init_waitqueue_head(&ctx->wait_queue); |
818 | |
819 | return 0; |
820 | } |
821 | |
822 | static int sprd_dpu_bind(struct device *dev, struct device *master, void *data) |
823 | { |
824 | struct drm_device *drm = data; |
825 | struct sprd_dpu *dpu; |
826 | struct sprd_plane *plane; |
827 | int ret; |
828 | |
829 | plane = sprd_planes_init(drm); |
830 | if (IS_ERR(ptr: plane)) |
831 | return PTR_ERR(ptr: plane); |
832 | |
833 | dpu = sprd_crtc_init(drm, primary: &plane->base, dev); |
834 | if (IS_ERR(ptr: dpu)) |
835 | return PTR_ERR(ptr: dpu); |
836 | |
837 | dpu->drm = drm; |
838 | dev_set_drvdata(dev, data: dpu); |
839 | |
840 | ret = sprd_dpu_context_init(dpu, dev); |
841 | if (ret) |
842 | return ret; |
843 | |
844 | return 0; |
845 | } |
846 | |
847 | static const struct component_ops dpu_component_ops = { |
848 | .bind = sprd_dpu_bind, |
849 | }; |
850 | |
851 | static const struct of_device_id dpu_match_table[] = { |
852 | { .compatible = "sprd,sharkl3-dpu"}, |
853 | { /* sentinel */ }, |
854 | }; |
855 | MODULE_DEVICE_TABLE(of, dpu_match_table); |
856 | |
857 | static int sprd_dpu_probe(struct platform_device *pdev) |
858 | { |
859 | return component_add(&pdev->dev, &dpu_component_ops); |
860 | } |
861 | |
862 | static void sprd_dpu_remove(struct platform_device *pdev) |
863 | { |
864 | component_del(&pdev->dev, &dpu_component_ops); |
865 | } |
866 | |
867 | struct platform_driver sprd_dpu_driver = { |
868 | .probe = sprd_dpu_probe, |
869 | .remove_new = sprd_dpu_remove, |
870 | .driver = { |
871 | .name = "sprd-dpu-drv", |
872 | .of_match_table = dpu_match_table, |
873 | }, |
874 | }; |
875 | |
876 | MODULE_AUTHOR("Leon He <leon.he@unisoc.com>"); |
877 | MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>"); |
878 | MODULE_DESCRIPTION("Unisoc Display Controller Driver"); |
879 | MODULE_LICENSE("GPL v2"); |
880 |
Definitions
- layer_fmts
- sprd_plane
- dpu_wait_stop_done
- dpu_wait_update_done
- drm_format_to_dpu
- drm_rotation_to_dpu
- drm_blend_to_dpu
- sprd_dpu_layer
- sprd_dpu_flip
- sprd_dpu_init
- sprd_dpu_fini
- sprd_dpi_init
- sprd_dpu_run
- sprd_dpu_stop
- sprd_plane_atomic_check
- sprd_plane_atomic_update
- sprd_plane_atomic_disable
- sprd_plane_create_properties
- sprd_plane_helper_funcs
- sprd_plane_funcs
- sprd_planes_init
- sprd_crtc_mode_set_nofb
- sprd_crtc_atomic_enable
- sprd_crtc_atomic_disable
- sprd_crtc_atomic_flush
- sprd_crtc_enable_vblank
- sprd_crtc_disable_vblank
- sprd_crtc_helper_funcs
- sprd_crtc_funcs
- sprd_crtc_init
- sprd_dpu_isr
- sprd_dpu_context_init
- sprd_dpu_bind
- dpu_component_ops
- dpu_match_table
- sprd_dpu_probe
- sprd_dpu_remove
Improve your Profiling and Debugging skills
Find out more