1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Hisilicon Hi6220 SoC ADE(Advanced Display Engine)'s crtc&plane driver |
4 | * |
5 | * Copyright (c) 2016 Linaro Limited. |
6 | * Copyright (c) 2014-2016 HiSilicon Limited. |
7 | * |
8 | * Author: |
9 | * Xinliang Liu <z.liuxinliang@hisilicon.com> |
10 | * Xinliang Liu <xinliang.liu@linaro.org> |
11 | * Xinwei Kong <kong.kongxinwei@hisilicon.com> |
12 | */ |
13 | |
14 | #include <linux/bitops.h> |
15 | #include <linux/clk.h> |
16 | #include <linux/mfd/syscon.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/regmap.h> |
19 | #include <linux/reset.h> |
20 | |
21 | #include <video/display_timing.h> |
22 | |
23 | #include <drm/drm_atomic.h> |
24 | #include <drm/drm_atomic_helper.h> |
25 | #include <drm/drm_crtc.h> |
26 | #include <drm/drm_drv.h> |
27 | #include <drm/drm_fb_dma_helper.h> |
28 | #include <drm/drm_fourcc.h> |
29 | #include <drm/drm_framebuffer.h> |
30 | #include <drm/drm_gem_dma_helper.h> |
31 | #include <drm/drm_probe_helper.h> |
32 | #include <drm/drm_vblank.h> |
33 | #include <drm/drm_gem_framebuffer_helper.h> |
34 | |
35 | #include "kirin_drm_drv.h" |
36 | #include "kirin_ade_reg.h" |
37 | |
38 | #define OUT_OVLY ADE_OVLY2 /* output overlay compositor */ |
39 | #define ADE_DEBUG 1 |
40 | |
41 | |
42 | struct ade_hw_ctx { |
43 | void __iomem *base; |
44 | struct regmap *noc_regmap; |
45 | struct clk *ade_core_clk; |
46 | struct clk *media_noc_clk; |
47 | struct clk *ade_pix_clk; |
48 | struct reset_control *reset; |
49 | bool power_on; |
50 | int irq; |
51 | |
52 | struct drm_crtc *crtc; |
53 | }; |
54 | |
55 | static const struct kirin_format ade_formats[] = { |
56 | /* 16bpp RGB: */ |
57 | { DRM_FORMAT_RGB565, ADE_RGB_565 }, |
58 | { DRM_FORMAT_BGR565, ADE_BGR_565 }, |
59 | /* 24bpp RGB: */ |
60 | { DRM_FORMAT_RGB888, ADE_RGB_888 }, |
61 | { DRM_FORMAT_BGR888, ADE_BGR_888 }, |
62 | /* 32bpp [A]RGB: */ |
63 | { DRM_FORMAT_XRGB8888, ADE_XRGB_8888 }, |
64 | { DRM_FORMAT_XBGR8888, ADE_XBGR_8888 }, |
65 | { DRM_FORMAT_RGBA8888, ADE_RGBA_8888 }, |
66 | { DRM_FORMAT_BGRA8888, ADE_BGRA_8888 }, |
67 | { DRM_FORMAT_ARGB8888, ADE_ARGB_8888 }, |
68 | { DRM_FORMAT_ABGR8888, ADE_ABGR_8888 }, |
69 | }; |
70 | |
71 | static const u32 channel_formats[] = { |
72 | /* channel 1,2,3,4 */ |
73 | DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888, |
74 | DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, |
75 | DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888, |
76 | DRM_FORMAT_ABGR8888 |
77 | }; |
78 | |
79 | /* convert from fourcc format to ade format */ |
80 | static u32 ade_get_format(u32 pixel_format) |
81 | { |
82 | int i; |
83 | |
84 | for (i = 0; i < ARRAY_SIZE(ade_formats); i++) |
85 | if (ade_formats[i].pixel_format == pixel_format) |
86 | return ade_formats[i].hw_format; |
87 | |
88 | /* not found */ |
89 | DRM_ERROR("Not found pixel format!!fourcc_format= %d\n" , |
90 | pixel_format); |
91 | return ADE_FORMAT_UNSUPPORT; |
92 | } |
93 | |
94 | static void ade_update_reload_bit(void __iomem *base, u32 bit_num, u32 val) |
95 | { |
96 | u32 bit_ofst, reg_num; |
97 | |
98 | bit_ofst = bit_num % 32; |
99 | reg_num = bit_num / 32; |
100 | |
101 | ade_update_bits(addr: base + ADE_RELOAD_DIS(reg_num), bit_start: bit_ofst, |
102 | MASK(1), val: !!val); |
103 | } |
104 | |
105 | static u32 ade_read_reload_bit(void __iomem *base, u32 bit_num) |
106 | { |
107 | u32 tmp, bit_ofst, reg_num; |
108 | |
109 | bit_ofst = bit_num % 32; |
110 | reg_num = bit_num / 32; |
111 | |
112 | tmp = readl(addr: base + ADE_RELOAD_DIS(reg_num)); |
113 | return !!(BIT(bit_ofst) & tmp); |
114 | } |
115 | |
116 | static void ade_init(struct ade_hw_ctx *ctx) |
117 | { |
118 | void __iomem *base = ctx->base; |
119 | |
120 | /* enable clk gate */ |
121 | ade_update_bits(addr: base + ADE_CTRL1, AUTO_CLK_GATE_EN_OFST, |
122 | AUTO_CLK_GATE_EN, ADE_ENABLE); |
123 | /* clear overlay */ |
124 | writel(val: 0, addr: base + ADE_OVLY1_TRANS_CFG); |
125 | writel(val: 0, addr: base + ADE_OVLY_CTL); |
126 | writel(val: 0, addr: base + ADE_OVLYX_CTL(OUT_OVLY)); |
127 | /* clear reset and reload regs */ |
128 | writel(MASK(32), addr: base + ADE_SOFT_RST_SEL(0)); |
129 | writel(MASK(32), addr: base + ADE_SOFT_RST_SEL(1)); |
130 | writel(MASK(32), addr: base + ADE_RELOAD_DIS(0)); |
131 | writel(MASK(32), addr: base + ADE_RELOAD_DIS(1)); |
132 | /* |
133 | * for video mode, all the ade registers should |
134 | * become effective at frame end. |
135 | */ |
136 | ade_update_bits(addr: base + ADE_CTRL, FRM_END_START_OFST, |
137 | FRM_END_START_MASK, val: REG_EFFECTIVE_IN_ADEEN_FRMEND); |
138 | } |
139 | |
140 | static bool ade_crtc_mode_fixup(struct drm_crtc *crtc, |
141 | const struct drm_display_mode *mode, |
142 | struct drm_display_mode *adjusted_mode) |
143 | { |
144 | struct kirin_crtc *kcrtc = to_kirin_crtc(crtc); |
145 | struct ade_hw_ctx *ctx = kcrtc->hw_ctx; |
146 | |
147 | adjusted_mode->clock = |
148 | clk_round_rate(clk: ctx->ade_pix_clk, rate: mode->clock * 1000) / 1000; |
149 | return true; |
150 | } |
151 | |
152 | |
153 | static void ade_set_pix_clk(struct ade_hw_ctx *ctx, |
154 | struct drm_display_mode *mode, |
155 | struct drm_display_mode *adj_mode) |
156 | { |
157 | u32 clk_Hz = mode->clock * 1000; |
158 | int ret; |
159 | |
160 | /* |
161 | * Success should be guaranteed in mode_valid call back, |
162 | * so failure shouldn't happen here |
163 | */ |
164 | ret = clk_set_rate(clk: ctx->ade_pix_clk, rate: clk_Hz); |
165 | if (ret) |
166 | DRM_ERROR("failed to set pixel clk %dHz (%d)\n" , clk_Hz, ret); |
167 | adj_mode->clock = clk_get_rate(clk: ctx->ade_pix_clk) / 1000; |
168 | } |
169 | |
170 | static void ade_ldi_set_mode(struct ade_hw_ctx *ctx, |
171 | struct drm_display_mode *mode, |
172 | struct drm_display_mode *adj_mode) |
173 | { |
174 | void __iomem *base = ctx->base; |
175 | u32 width = mode->hdisplay; |
176 | u32 height = mode->vdisplay; |
177 | u32 hfp, hbp, hsw, vfp, vbp, vsw; |
178 | u32 plr_flags; |
179 | |
180 | plr_flags = (mode->flags & DRM_MODE_FLAG_NVSYNC) ? FLAG_NVSYNC : 0; |
181 | plr_flags |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? FLAG_NHSYNC : 0; |
182 | hfp = mode->hsync_start - mode->hdisplay; |
183 | hbp = mode->htotal - mode->hsync_end; |
184 | hsw = mode->hsync_end - mode->hsync_start; |
185 | vfp = mode->vsync_start - mode->vdisplay; |
186 | vbp = mode->vtotal - mode->vsync_end; |
187 | vsw = mode->vsync_end - mode->vsync_start; |
188 | if (vsw > 15) { |
189 | DRM_DEBUG_DRIVER("vsw exceeded 15\n" ); |
190 | vsw = 15; |
191 | } |
192 | |
193 | writel(val: (hbp << HBP_OFST) | hfp, addr: base + LDI_HRZ_CTRL0); |
194 | /* the configured value is actual value - 1 */ |
195 | writel(val: hsw - 1, addr: base + LDI_HRZ_CTRL1); |
196 | writel(val: (vbp << VBP_OFST) | vfp, addr: base + LDI_VRT_CTRL0); |
197 | /* the configured value is actual value - 1 */ |
198 | writel(val: vsw - 1, addr: base + LDI_VRT_CTRL1); |
199 | /* the configured value is actual value - 1 */ |
200 | writel(val: ((height - 1) << VSIZE_OFST) | (width - 1), |
201 | addr: base + LDI_DSP_SIZE); |
202 | writel(val: plr_flags, addr: base + LDI_PLR_CTRL); |
203 | |
204 | /* set overlay compositor output size */ |
205 | writel(val: ((width - 1) << OUTPUT_XSIZE_OFST) | (height - 1), |
206 | addr: base + ADE_OVLY_OUTPUT_SIZE(OUT_OVLY)); |
207 | |
208 | /* ctran6 setting */ |
209 | writel(CTRAN_BYPASS_ON, addr: base + ADE_CTRAN_DIS(ADE_CTRAN6)); |
210 | /* the configured value is actual value - 1 */ |
211 | writel(val: width * height - 1, addr: base + ADE_CTRAN_IMAGE_SIZE(ADE_CTRAN6)); |
212 | ade_update_reload_bit(base, CTRAN_OFST + ADE_CTRAN6, val: 0); |
213 | |
214 | ade_set_pix_clk(ctx, mode, adj_mode); |
215 | |
216 | DRM_DEBUG_DRIVER("set mode: %dx%d\n" , width, height); |
217 | } |
218 | |
219 | static int ade_power_up(struct ade_hw_ctx *ctx) |
220 | { |
221 | int ret; |
222 | |
223 | ret = clk_prepare_enable(clk: ctx->media_noc_clk); |
224 | if (ret) { |
225 | DRM_ERROR("failed to enable media_noc_clk (%d)\n" , ret); |
226 | return ret; |
227 | } |
228 | |
229 | ret = reset_control_deassert(rstc: ctx->reset); |
230 | if (ret) { |
231 | DRM_ERROR("failed to deassert reset\n" ); |
232 | return ret; |
233 | } |
234 | |
235 | ret = clk_prepare_enable(clk: ctx->ade_core_clk); |
236 | if (ret) { |
237 | DRM_ERROR("failed to enable ade_core_clk (%d)\n" , ret); |
238 | return ret; |
239 | } |
240 | |
241 | ade_init(ctx); |
242 | ctx->power_on = true; |
243 | return 0; |
244 | } |
245 | |
246 | static void ade_power_down(struct ade_hw_ctx *ctx) |
247 | { |
248 | void __iomem *base = ctx->base; |
249 | |
250 | writel(ADE_DISABLE, addr: base + LDI_CTRL); |
251 | /* dsi pixel off */ |
252 | writel(val: DSI_PCLK_OFF, addr: base + LDI_HDMI_DSI_GT); |
253 | |
254 | clk_disable_unprepare(clk: ctx->ade_core_clk); |
255 | reset_control_assert(rstc: ctx->reset); |
256 | clk_disable_unprepare(clk: ctx->media_noc_clk); |
257 | ctx->power_on = false; |
258 | } |
259 | |
260 | static void ade_set_medianoc_qos(struct ade_hw_ctx *ctx) |
261 | { |
262 | struct regmap *map = ctx->noc_regmap; |
263 | |
264 | regmap_update_bits(map, ADE0_QOSGENERATOR_MODE, |
265 | QOSGENERATOR_MODE_MASK, val: BYPASS_MODE); |
266 | regmap_update_bits(map, ADE0_QOSGENERATOR_EXTCONTROL, |
267 | SOCKET_QOS_EN, SOCKET_QOS_EN); |
268 | |
269 | regmap_update_bits(map, ADE1_QOSGENERATOR_MODE, |
270 | QOSGENERATOR_MODE_MASK, val: BYPASS_MODE); |
271 | regmap_update_bits(map, ADE1_QOSGENERATOR_EXTCONTROL, |
272 | SOCKET_QOS_EN, SOCKET_QOS_EN); |
273 | } |
274 | |
275 | static int ade_crtc_enable_vblank(struct drm_crtc *crtc) |
276 | { |
277 | struct kirin_crtc *kcrtc = to_kirin_crtc(crtc); |
278 | struct ade_hw_ctx *ctx = kcrtc->hw_ctx; |
279 | void __iomem *base = ctx->base; |
280 | |
281 | if (!ctx->power_on) |
282 | (void)ade_power_up(ctx); |
283 | |
284 | ade_update_bits(addr: base + LDI_INT_EN, FRAME_END_INT_EN_OFST, |
285 | MASK(1), val: 1); |
286 | |
287 | return 0; |
288 | } |
289 | |
290 | static void ade_crtc_disable_vblank(struct drm_crtc *crtc) |
291 | { |
292 | struct kirin_crtc *kcrtc = to_kirin_crtc(crtc); |
293 | struct ade_hw_ctx *ctx = kcrtc->hw_ctx; |
294 | void __iomem *base = ctx->base; |
295 | |
296 | if (!ctx->power_on) { |
297 | DRM_ERROR("power is down! vblank disable fail\n" ); |
298 | return; |
299 | } |
300 | |
301 | ade_update_bits(addr: base + LDI_INT_EN, FRAME_END_INT_EN_OFST, |
302 | MASK(1), val: 0); |
303 | } |
304 | |
305 | static irqreturn_t ade_irq_handler(int irq, void *data) |
306 | { |
307 | struct ade_hw_ctx *ctx = data; |
308 | struct drm_crtc *crtc = ctx->crtc; |
309 | void __iomem *base = ctx->base; |
310 | u32 status; |
311 | |
312 | status = readl(addr: base + LDI_MSK_INT); |
313 | DRM_DEBUG_VBL("LDI IRQ: status=0x%X\n" , status); |
314 | |
315 | /* vblank irq */ |
316 | if (status & BIT(FRAME_END_INT_EN_OFST)) { |
317 | ade_update_bits(addr: base + LDI_INT_CLR, FRAME_END_INT_EN_OFST, |
318 | MASK(1), val: 1); |
319 | drm_crtc_handle_vblank(crtc); |
320 | } |
321 | |
322 | return IRQ_HANDLED; |
323 | } |
324 | |
325 | static void ade_display_enable(struct ade_hw_ctx *ctx) |
326 | { |
327 | void __iomem *base = ctx->base; |
328 | u32 out_fmt = LDI_OUT_RGB_888; |
329 | |
330 | /* enable output overlay compositor */ |
331 | writel(ADE_ENABLE, addr: base + ADE_OVLYX_CTL(OUT_OVLY)); |
332 | ade_update_reload_bit(base, OVLY_OFST + OUT_OVLY, val: 0); |
333 | |
334 | /* display source setting */ |
335 | writel(val: DISP_SRC_OVLY2, addr: base + ADE_DISP_SRC_CFG); |
336 | |
337 | /* enable ade */ |
338 | writel(ADE_ENABLE, addr: base + ADE_EN); |
339 | /* enable ldi */ |
340 | writel(val: NORMAL_MODE, addr: base + LDI_WORK_MODE); |
341 | writel(val: (out_fmt << BPP_OFST) | DATA_GATE_EN | LDI_EN, |
342 | addr: base + LDI_CTRL); |
343 | /* dsi pixel on */ |
344 | writel(val: DSI_PCLK_ON, addr: base + LDI_HDMI_DSI_GT); |
345 | } |
346 | |
347 | #if ADE_DEBUG |
348 | static void ade_rdma_dump_regs(void __iomem *base, u32 ch) |
349 | { |
350 | u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en; |
351 | u32 val; |
352 | |
353 | reg_ctrl = RD_CH_CTRL(ch); |
354 | reg_addr = RD_CH_ADDR(ch); |
355 | reg_size = RD_CH_SIZE(ch); |
356 | reg_stride = RD_CH_STRIDE(ch); |
357 | reg_space = RD_CH_SPACE(ch); |
358 | reg_en = RD_CH_EN(ch); |
359 | |
360 | val = ade_read_reload_bit(base, RDMA_OFST + ch); |
361 | DRM_DEBUG_DRIVER("[rdma%d]: reload(%d)\n" , ch + 1, val); |
362 | val = readl(addr: base + reg_ctrl); |
363 | DRM_DEBUG_DRIVER("[rdma%d]: reg_ctrl(0x%08x)\n" , ch + 1, val); |
364 | val = readl(addr: base + reg_addr); |
365 | DRM_DEBUG_DRIVER("[rdma%d]: reg_addr(0x%08x)\n" , ch + 1, val); |
366 | val = readl(addr: base + reg_size); |
367 | DRM_DEBUG_DRIVER("[rdma%d]: reg_size(0x%08x)\n" , ch + 1, val); |
368 | val = readl(addr: base + reg_stride); |
369 | DRM_DEBUG_DRIVER("[rdma%d]: reg_stride(0x%08x)\n" , ch + 1, val); |
370 | val = readl(addr: base + reg_space); |
371 | DRM_DEBUG_DRIVER("[rdma%d]: reg_space(0x%08x)\n" , ch + 1, val); |
372 | val = readl(addr: base + reg_en); |
373 | DRM_DEBUG_DRIVER("[rdma%d]: reg_en(0x%08x)\n" , ch + 1, val); |
374 | } |
375 | |
376 | static void ade_clip_dump_regs(void __iomem *base, u32 ch) |
377 | { |
378 | u32 val; |
379 | |
380 | val = ade_read_reload_bit(base, CLIP_OFST + ch); |
381 | DRM_DEBUG_DRIVER("[clip%d]: reload(%d)\n" , ch + 1, val); |
382 | val = readl(addr: base + ADE_CLIP_DISABLE(ch)); |
383 | DRM_DEBUG_DRIVER("[clip%d]: reg_clip_disable(0x%08x)\n" , ch + 1, val); |
384 | val = readl(addr: base + ADE_CLIP_SIZE0(ch)); |
385 | DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size0(0x%08x)\n" , ch + 1, val); |
386 | val = readl(addr: base + ADE_CLIP_SIZE1(ch)); |
387 | DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size1(0x%08x)\n" , ch + 1, val); |
388 | } |
389 | |
390 | static void ade_compositor_routing_dump_regs(void __iomem *base, u32 ch) |
391 | { |
392 | u8 ovly_ch = 0; /* TODO: Only primary plane now */ |
393 | u32 val; |
394 | |
395 | val = readl(addr: base + ADE_OVLY_CH_XY0(ovly_ch)); |
396 | DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy0(0x%08x)\n" , ovly_ch, val); |
397 | val = readl(addr: base + ADE_OVLY_CH_XY1(ovly_ch)); |
398 | DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy1(0x%08x)\n" , ovly_ch, val); |
399 | val = readl(addr: base + ADE_OVLY_CH_CTL(ovly_ch)); |
400 | DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_ctl(0x%08x)\n" , ovly_ch, val); |
401 | } |
402 | |
403 | static void ade_dump_overlay_compositor_regs(void __iomem *base, u32 comp) |
404 | { |
405 | u32 val; |
406 | |
407 | val = ade_read_reload_bit(base, OVLY_OFST + comp); |
408 | DRM_DEBUG_DRIVER("[overlay%d]: reload(%d)\n" , comp + 1, val); |
409 | writel(ADE_ENABLE, addr: base + ADE_OVLYX_CTL(comp)); |
410 | DRM_DEBUG_DRIVER("[overlay%d]: reg_ctl(0x%08x)\n" , comp + 1, val); |
411 | val = readl(addr: base + ADE_OVLY_CTL); |
412 | DRM_DEBUG_DRIVER("ovly_ctl(0x%08x)\n" , val); |
413 | } |
414 | |
415 | static void ade_dump_regs(void __iomem *base) |
416 | { |
417 | u32 i; |
418 | |
419 | /* dump channel regs */ |
420 | for (i = 0; i < ADE_CH_NUM; i++) { |
421 | /* dump rdma regs */ |
422 | ade_rdma_dump_regs(base, ch: i); |
423 | |
424 | /* dump clip regs */ |
425 | ade_clip_dump_regs(base, ch: i); |
426 | |
427 | /* dump compositor routing regs */ |
428 | ade_compositor_routing_dump_regs(base, ch: i); |
429 | } |
430 | |
431 | /* dump overlay compositor regs */ |
432 | ade_dump_overlay_compositor_regs(base, OUT_OVLY); |
433 | } |
434 | #else |
435 | static void ade_dump_regs(void __iomem *base) { } |
436 | #endif |
437 | |
438 | static void ade_crtc_atomic_enable(struct drm_crtc *crtc, |
439 | struct drm_atomic_state *state) |
440 | { |
441 | struct kirin_crtc *kcrtc = to_kirin_crtc(crtc); |
442 | struct ade_hw_ctx *ctx = kcrtc->hw_ctx; |
443 | int ret; |
444 | |
445 | if (kcrtc->enable) |
446 | return; |
447 | |
448 | if (!ctx->power_on) { |
449 | ret = ade_power_up(ctx); |
450 | if (ret) |
451 | return; |
452 | } |
453 | |
454 | ade_set_medianoc_qos(ctx); |
455 | ade_display_enable(ctx); |
456 | ade_dump_regs(base: ctx->base); |
457 | drm_crtc_vblank_on(crtc); |
458 | kcrtc->enable = true; |
459 | } |
460 | |
461 | static void ade_crtc_atomic_disable(struct drm_crtc *crtc, |
462 | struct drm_atomic_state *state) |
463 | { |
464 | struct kirin_crtc *kcrtc = to_kirin_crtc(crtc); |
465 | struct ade_hw_ctx *ctx = kcrtc->hw_ctx; |
466 | |
467 | if (!kcrtc->enable) |
468 | return; |
469 | |
470 | drm_crtc_vblank_off(crtc); |
471 | ade_power_down(ctx); |
472 | kcrtc->enable = false; |
473 | } |
474 | |
475 | static void ade_crtc_mode_set_nofb(struct drm_crtc *crtc) |
476 | { |
477 | struct kirin_crtc *kcrtc = to_kirin_crtc(crtc); |
478 | struct ade_hw_ctx *ctx = kcrtc->hw_ctx; |
479 | struct drm_display_mode *mode = &crtc->state->mode; |
480 | struct drm_display_mode *adj_mode = &crtc->state->adjusted_mode; |
481 | |
482 | if (!ctx->power_on) |
483 | (void)ade_power_up(ctx); |
484 | ade_ldi_set_mode(ctx, mode, adj_mode); |
485 | } |
486 | |
487 | static void ade_crtc_atomic_begin(struct drm_crtc *crtc, |
488 | struct drm_atomic_state *state) |
489 | { |
490 | struct kirin_crtc *kcrtc = to_kirin_crtc(crtc); |
491 | struct ade_hw_ctx *ctx = kcrtc->hw_ctx; |
492 | struct drm_display_mode *mode = &crtc->state->mode; |
493 | struct drm_display_mode *adj_mode = &crtc->state->adjusted_mode; |
494 | |
495 | if (!ctx->power_on) |
496 | (void)ade_power_up(ctx); |
497 | ade_ldi_set_mode(ctx, mode, adj_mode); |
498 | } |
499 | |
500 | static void ade_crtc_atomic_flush(struct drm_crtc *crtc, |
501 | struct drm_atomic_state *state) |
502 | |
503 | { |
504 | struct kirin_crtc *kcrtc = to_kirin_crtc(crtc); |
505 | struct ade_hw_ctx *ctx = kcrtc->hw_ctx; |
506 | struct drm_pending_vblank_event *event = crtc->state->event; |
507 | void __iomem *base = ctx->base; |
508 | |
509 | /* only crtc is enabled regs take effect */ |
510 | if (kcrtc->enable) { |
511 | ade_dump_regs(base); |
512 | /* flush ade registers */ |
513 | writel(ADE_ENABLE, addr: base + ADE_EN); |
514 | } |
515 | |
516 | if (event) { |
517 | crtc->state->event = NULL; |
518 | |
519 | spin_lock_irq(lock: &crtc->dev->event_lock); |
520 | if (drm_crtc_vblank_get(crtc) == 0) |
521 | drm_crtc_arm_vblank_event(crtc, e: event); |
522 | else |
523 | drm_crtc_send_vblank_event(crtc, e: event); |
524 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
525 | } |
526 | } |
527 | |
528 | static const struct drm_crtc_helper_funcs ade_crtc_helper_funcs = { |
529 | .mode_fixup = ade_crtc_mode_fixup, |
530 | .mode_set_nofb = ade_crtc_mode_set_nofb, |
531 | .atomic_begin = ade_crtc_atomic_begin, |
532 | .atomic_flush = ade_crtc_atomic_flush, |
533 | .atomic_enable = ade_crtc_atomic_enable, |
534 | .atomic_disable = ade_crtc_atomic_disable, |
535 | }; |
536 | |
537 | static const struct drm_crtc_funcs ade_crtc_funcs = { |
538 | .destroy = drm_crtc_cleanup, |
539 | .set_config = drm_atomic_helper_set_config, |
540 | .page_flip = drm_atomic_helper_page_flip, |
541 | .reset = drm_atomic_helper_crtc_reset, |
542 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
543 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
544 | .enable_vblank = ade_crtc_enable_vblank, |
545 | .disable_vblank = ade_crtc_disable_vblank, |
546 | }; |
547 | |
548 | static void ade_rdma_set(void __iomem *base, struct drm_framebuffer *fb, |
549 | u32 ch, u32 y, u32 in_h, u32 fmt) |
550 | { |
551 | struct drm_gem_dma_object *obj = drm_fb_dma_get_gem_obj(fb, plane: 0); |
552 | u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en; |
553 | u32 stride = fb->pitches[0]; |
554 | u32 addr = (u32) obj->dma_addr + y * stride; |
555 | |
556 | DRM_DEBUG_DRIVER("rdma%d: (y=%d, height=%d), stride=%d, paddr=0x%x\n" , |
557 | ch + 1, y, in_h, stride, (u32) obj->dma_addr); |
558 | DRM_DEBUG_DRIVER("addr=0x%x, fb:%dx%d, pixel_format=%d(%p4cc)\n" , |
559 | addr, fb->width, fb->height, fmt, |
560 | &fb->format->format); |
561 | |
562 | /* get reg offset */ |
563 | reg_ctrl = RD_CH_CTRL(ch); |
564 | reg_addr = RD_CH_ADDR(ch); |
565 | reg_size = RD_CH_SIZE(ch); |
566 | reg_stride = RD_CH_STRIDE(ch); |
567 | reg_space = RD_CH_SPACE(ch); |
568 | reg_en = RD_CH_EN(ch); |
569 | |
570 | /* |
571 | * TODO: set rotation |
572 | */ |
573 | writel(val: (fmt << 16) & 0x1f0000, addr: base + reg_ctrl); |
574 | writel(val: addr, addr: base + reg_addr); |
575 | writel(val: (in_h << 16) | stride, addr: base + reg_size); |
576 | writel(val: stride, addr: base + reg_stride); |
577 | writel(val: in_h * stride, addr: base + reg_space); |
578 | writel(ADE_ENABLE, addr: base + reg_en); |
579 | ade_update_reload_bit(base, RDMA_OFST + ch, val: 0); |
580 | } |
581 | |
582 | static void ade_rdma_disable(void __iomem *base, u32 ch) |
583 | { |
584 | u32 reg_en; |
585 | |
586 | /* get reg offset */ |
587 | reg_en = RD_CH_EN(ch); |
588 | writel(val: 0, addr: base + reg_en); |
589 | ade_update_reload_bit(base, RDMA_OFST + ch, val: 1); |
590 | } |
591 | |
592 | static void ade_clip_set(void __iomem *base, u32 ch, u32 fb_w, u32 x, |
593 | u32 in_w, u32 in_h) |
594 | { |
595 | u32 disable_val; |
596 | u32 clip_left; |
597 | u32 clip_right; |
598 | |
599 | /* |
600 | * clip width, no need to clip height |
601 | */ |
602 | if (fb_w == in_w) { /* bypass */ |
603 | disable_val = 1; |
604 | clip_left = 0; |
605 | clip_right = 0; |
606 | } else { |
607 | disable_val = 0; |
608 | clip_left = x; |
609 | clip_right = fb_w - (x + in_w) - 1; |
610 | } |
611 | |
612 | DRM_DEBUG_DRIVER("clip%d: clip_left=%d, clip_right=%d\n" , |
613 | ch + 1, clip_left, clip_right); |
614 | |
615 | writel(val: disable_val, addr: base + ADE_CLIP_DISABLE(ch)); |
616 | writel(val: (fb_w - 1) << 16 | (in_h - 1), addr: base + ADE_CLIP_SIZE0(ch)); |
617 | writel(val: clip_left << 16 | clip_right, addr: base + ADE_CLIP_SIZE1(ch)); |
618 | ade_update_reload_bit(base, CLIP_OFST + ch, val: 0); |
619 | } |
620 | |
621 | static void ade_clip_disable(void __iomem *base, u32 ch) |
622 | { |
623 | writel(val: 1, addr: base + ADE_CLIP_DISABLE(ch)); |
624 | ade_update_reload_bit(base, CLIP_OFST + ch, val: 1); |
625 | } |
626 | |
627 | static bool has_Alpha_channel(int format) |
628 | { |
629 | switch (format) { |
630 | case ADE_ARGB_8888: |
631 | case ADE_ABGR_8888: |
632 | case ADE_RGBA_8888: |
633 | case ADE_BGRA_8888: |
634 | return true; |
635 | default: |
636 | return false; |
637 | } |
638 | } |
639 | |
640 | static void ade_get_blending_params(u32 fmt, u8 glb_alpha, u8 *alp_mode, |
641 | u8 *alp_sel, u8 *under_alp_sel) |
642 | { |
643 | bool has_alpha = has_Alpha_channel(format: fmt); |
644 | |
645 | /* |
646 | * get alp_mode |
647 | */ |
648 | if (has_alpha && glb_alpha < 255) |
649 | *alp_mode = ADE_ALP_PIXEL_AND_GLB; |
650 | else if (has_alpha) |
651 | *alp_mode = ADE_ALP_PIXEL; |
652 | else |
653 | *alp_mode = ADE_ALP_GLOBAL; |
654 | |
655 | /* |
656 | * get alp sel |
657 | */ |
658 | *alp_sel = ADE_ALP_MUL_COEFF_3; /* 1 */ |
659 | *under_alp_sel = ADE_ALP_MUL_COEFF_2; /* 0 */ |
660 | } |
661 | |
662 | static void ade_compositor_routing_set(void __iomem *base, u8 ch, |
663 | u32 x0, u32 y0, |
664 | u32 in_w, u32 in_h, u32 fmt) |
665 | { |
666 | u8 ovly_ch = 0; /* TODO: This is the zpos, only one plane now */ |
667 | u8 glb_alpha = 255; |
668 | u32 x1 = x0 + in_w - 1; |
669 | u32 y1 = y0 + in_h - 1; |
670 | u32 val; |
671 | u8 alp_sel; |
672 | u8 under_alp_sel; |
673 | u8 alp_mode; |
674 | |
675 | ade_get_blending_params(fmt, glb_alpha, alp_mode: &alp_mode, alp_sel: &alp_sel, |
676 | under_alp_sel: &under_alp_sel); |
677 | |
678 | /* overlay routing setting |
679 | */ |
680 | writel(val: x0 << 16 | y0, addr: base + ADE_OVLY_CH_XY0(ovly_ch)); |
681 | writel(val: x1 << 16 | y1, addr: base + ADE_OVLY_CH_XY1(ovly_ch)); |
682 | val = (ch + 1) << CH_SEL_OFST | BIT(CH_EN_OFST) | |
683 | alp_sel << CH_ALP_SEL_OFST | |
684 | under_alp_sel << CH_UNDER_ALP_SEL_OFST | |
685 | glb_alpha << CH_ALP_GBL_OFST | |
686 | alp_mode << CH_ALP_MODE_OFST; |
687 | writel(val, addr: base + ADE_OVLY_CH_CTL(ovly_ch)); |
688 | /* connect this plane/channel to overlay2 compositor */ |
689 | ade_update_bits(addr: base + ADE_OVLY_CTL, CH_OVLY_SEL_OFST(ovly_ch), |
690 | CH_OVLY_SEL_MASK, CH_OVLY_SEL_VAL(OUT_OVLY)); |
691 | } |
692 | |
693 | static void ade_compositor_routing_disable(void __iomem *base, u32 ch) |
694 | { |
695 | u8 ovly_ch = 0; /* TODO: Only primary plane now */ |
696 | |
697 | /* disable this plane/channel */ |
698 | ade_update_bits(addr: base + ADE_OVLY_CH_CTL(ovly_ch), CH_EN_OFST, |
699 | MASK(1), val: 0); |
700 | /* dis-connect this plane/channel of overlay2 compositor */ |
701 | ade_update_bits(addr: base + ADE_OVLY_CTL, CH_OVLY_SEL_OFST(ovly_ch), |
702 | CH_OVLY_SEL_MASK, val: 0); |
703 | } |
704 | |
705 | /* |
706 | * Typicaly, a channel looks like: DMA-->clip-->scale-->ctrans-->compositor |
707 | */ |
708 | static void ade_update_channel(struct kirin_plane *kplane, |
709 | struct drm_framebuffer *fb, int crtc_x, |
710 | int crtc_y, unsigned int crtc_w, |
711 | unsigned int crtc_h, u32 src_x, |
712 | u32 src_y, u32 src_w, u32 src_h) |
713 | { |
714 | struct ade_hw_ctx *ctx = kplane->hw_ctx; |
715 | void __iomem *base = ctx->base; |
716 | u32 fmt = ade_get_format(pixel_format: fb->format->format); |
717 | u32 ch = kplane->ch; |
718 | u32 in_w; |
719 | u32 in_h; |
720 | |
721 | DRM_DEBUG_DRIVER("channel%d: src:(%d, %d)-%dx%d, crtc:(%d, %d)-%dx%d" , |
722 | ch + 1, src_x, src_y, src_w, src_h, |
723 | crtc_x, crtc_y, crtc_w, crtc_h); |
724 | |
725 | /* 1) DMA setting */ |
726 | in_w = src_w; |
727 | in_h = src_h; |
728 | ade_rdma_set(base, fb, ch, y: src_y, in_h, fmt); |
729 | |
730 | /* 2) clip setting */ |
731 | ade_clip_set(base, ch, fb_w: fb->width, x: src_x, in_w, in_h); |
732 | |
733 | /* 3) TODO: scale setting for overlay planes */ |
734 | |
735 | /* 4) TODO: ctran/csc setting for overlay planes */ |
736 | |
737 | /* 5) compositor routing setting */ |
738 | ade_compositor_routing_set(base, ch, x0: crtc_x, y0: crtc_y, in_w, in_h, fmt); |
739 | } |
740 | |
741 | static void ade_disable_channel(struct kirin_plane *kplane) |
742 | { |
743 | struct ade_hw_ctx *ctx = kplane->hw_ctx; |
744 | void __iomem *base = ctx->base; |
745 | u32 ch = kplane->ch; |
746 | |
747 | DRM_DEBUG_DRIVER("disable channel%d\n" , ch + 1); |
748 | |
749 | /* disable read DMA */ |
750 | ade_rdma_disable(base, ch); |
751 | |
752 | /* disable clip */ |
753 | ade_clip_disable(base, ch); |
754 | |
755 | /* disable compositor routing */ |
756 | ade_compositor_routing_disable(base, ch); |
757 | } |
758 | |
759 | static int ade_plane_atomic_check(struct drm_plane *plane, |
760 | struct drm_atomic_state *state) |
761 | { |
762 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, |
763 | plane); |
764 | struct drm_framebuffer *fb = new_plane_state->fb; |
765 | struct drm_crtc *crtc = new_plane_state->crtc; |
766 | struct drm_crtc_state *crtc_state; |
767 | u32 src_x = new_plane_state->src_x >> 16; |
768 | u32 src_y = new_plane_state->src_y >> 16; |
769 | u32 src_w = new_plane_state->src_w >> 16; |
770 | u32 src_h = new_plane_state->src_h >> 16; |
771 | int crtc_x = new_plane_state->crtc_x; |
772 | int crtc_y = new_plane_state->crtc_y; |
773 | u32 crtc_w = new_plane_state->crtc_w; |
774 | u32 crtc_h = new_plane_state->crtc_h; |
775 | u32 fmt; |
776 | |
777 | if (!crtc || !fb) |
778 | return 0; |
779 | |
780 | fmt = ade_get_format(pixel_format: fb->format->format); |
781 | if (fmt == ADE_FORMAT_UNSUPPORT) |
782 | return -EINVAL; |
783 | |
784 | crtc_state = drm_atomic_get_crtc_state(state, crtc); |
785 | if (IS_ERR(ptr: crtc_state)) |
786 | return PTR_ERR(ptr: crtc_state); |
787 | |
788 | if (src_w != crtc_w || src_h != crtc_h) { |
789 | return -EINVAL; |
790 | } |
791 | |
792 | if (src_x + src_w > fb->width || |
793 | src_y + src_h > fb->height) |
794 | return -EINVAL; |
795 | |
796 | if (crtc_x < 0 || crtc_y < 0) |
797 | return -EINVAL; |
798 | |
799 | if (crtc_x + crtc_w > crtc_state->adjusted_mode.hdisplay || |
800 | crtc_y + crtc_h > crtc_state->adjusted_mode.vdisplay) |
801 | return -EINVAL; |
802 | |
803 | return 0; |
804 | } |
805 | |
806 | static void ade_plane_atomic_update(struct drm_plane *plane, |
807 | struct drm_atomic_state *state) |
808 | { |
809 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
810 | plane); |
811 | struct kirin_plane *kplane = to_kirin_plane(plane); |
812 | |
813 | ade_update_channel(kplane, fb: new_state->fb, crtc_x: new_state->crtc_x, |
814 | crtc_y: new_state->crtc_y, |
815 | crtc_w: new_state->crtc_w, crtc_h: new_state->crtc_h, |
816 | src_x: new_state->src_x >> 16, src_y: new_state->src_y >> 16, |
817 | src_w: new_state->src_w >> 16, src_h: new_state->src_h >> 16); |
818 | } |
819 | |
820 | static void ade_plane_atomic_disable(struct drm_plane *plane, |
821 | struct drm_atomic_state *state) |
822 | { |
823 | struct kirin_plane *kplane = to_kirin_plane(plane); |
824 | |
825 | ade_disable_channel(kplane); |
826 | } |
827 | |
828 | static const struct drm_plane_helper_funcs ade_plane_helper_funcs = { |
829 | .atomic_check = ade_plane_atomic_check, |
830 | .atomic_update = ade_plane_atomic_update, |
831 | .atomic_disable = ade_plane_atomic_disable, |
832 | }; |
833 | |
834 | static struct drm_plane_funcs ade_plane_funcs = { |
835 | .update_plane = drm_atomic_helper_update_plane, |
836 | .disable_plane = drm_atomic_helper_disable_plane, |
837 | .destroy = drm_plane_cleanup, |
838 | .reset = drm_atomic_helper_plane_reset, |
839 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
840 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
841 | }; |
842 | |
843 | static void *ade_hw_ctx_alloc(struct platform_device *pdev, |
844 | struct drm_crtc *crtc) |
845 | { |
846 | struct resource *res; |
847 | struct device *dev = &pdev->dev; |
848 | struct device_node *np = pdev->dev.of_node; |
849 | struct ade_hw_ctx *ctx = NULL; |
850 | int ret; |
851 | |
852 | ctx = devm_kzalloc(dev, size: sizeof(*ctx), GFP_KERNEL); |
853 | if (!ctx) { |
854 | DRM_ERROR("failed to alloc ade_hw_ctx\n" ); |
855 | return ERR_PTR(error: -ENOMEM); |
856 | } |
857 | |
858 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
859 | ctx->base = devm_ioremap_resource(dev, res); |
860 | if (IS_ERR(ptr: ctx->base)) { |
861 | DRM_ERROR("failed to remap ade io base\n" ); |
862 | return ERR_PTR(error: -EIO); |
863 | } |
864 | |
865 | ctx->reset = devm_reset_control_get(dev, NULL); |
866 | if (IS_ERR(ptr: ctx->reset)) |
867 | return ERR_PTR(error: -ENODEV); |
868 | |
869 | ctx->noc_regmap = |
870 | syscon_regmap_lookup_by_phandle(np, property: "hisilicon,noc-syscon" ); |
871 | if (IS_ERR(ptr: ctx->noc_regmap)) { |
872 | DRM_ERROR("failed to get noc regmap\n" ); |
873 | return ERR_PTR(error: -ENODEV); |
874 | } |
875 | |
876 | ctx->irq = platform_get_irq(pdev, 0); |
877 | if (ctx->irq < 0) { |
878 | DRM_ERROR("failed to get irq\n" ); |
879 | return ERR_PTR(error: -ENODEV); |
880 | } |
881 | |
882 | ctx->ade_core_clk = devm_clk_get(dev, id: "clk_ade_core" ); |
883 | if (IS_ERR(ptr: ctx->ade_core_clk)) { |
884 | DRM_ERROR("failed to parse clk ADE_CORE\n" ); |
885 | return ERR_PTR(error: -ENODEV); |
886 | } |
887 | |
888 | ctx->media_noc_clk = devm_clk_get(dev, id: "clk_codec_jpeg" ); |
889 | if (IS_ERR(ptr: ctx->media_noc_clk)) { |
890 | DRM_ERROR("failed to parse clk CODEC_JPEG\n" ); |
891 | return ERR_PTR(error: -ENODEV); |
892 | } |
893 | |
894 | ctx->ade_pix_clk = devm_clk_get(dev, id: "clk_ade_pix" ); |
895 | if (IS_ERR(ptr: ctx->ade_pix_clk)) { |
896 | DRM_ERROR("failed to parse clk ADE_PIX\n" ); |
897 | return ERR_PTR(error: -ENODEV); |
898 | } |
899 | |
900 | /* vblank irq init */ |
901 | ret = devm_request_irq(dev, irq: ctx->irq, handler: ade_irq_handler, |
902 | IRQF_SHARED, devname: dev->driver->name, dev_id: ctx); |
903 | if (ret) |
904 | return ERR_PTR(error: -EIO); |
905 | |
906 | ctx->crtc = crtc; |
907 | |
908 | return ctx; |
909 | } |
910 | |
911 | static void ade_hw_ctx_cleanup(void *hw_ctx) |
912 | { |
913 | } |
914 | |
915 | static const struct drm_mode_config_funcs ade_mode_config_funcs = { |
916 | .fb_create = drm_gem_fb_create, |
917 | .atomic_check = drm_atomic_helper_check, |
918 | .atomic_commit = drm_atomic_helper_commit, |
919 | |
920 | }; |
921 | |
922 | DEFINE_DRM_GEM_DMA_FOPS(ade_fops); |
923 | |
924 | static const struct drm_driver ade_driver = { |
925 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, |
926 | .fops = &ade_fops, |
927 | DRM_GEM_DMA_DRIVER_OPS, |
928 | .name = "kirin" , |
929 | .desc = "Hisilicon Kirin620 SoC DRM Driver" , |
930 | .date = "20150718" , |
931 | .major = 1, |
932 | .minor = 0, |
933 | }; |
934 | |
935 | struct kirin_drm_data ade_driver_data = { |
936 | .num_planes = ADE_CH_NUM, |
937 | .prim_plane = ADE_CH1, |
938 | .channel_formats = channel_formats, |
939 | .channel_formats_cnt = ARRAY_SIZE(channel_formats), |
940 | .config_max_width = 2048, |
941 | .config_max_height = 2048, |
942 | .driver = &ade_driver, |
943 | .crtc_helper_funcs = &ade_crtc_helper_funcs, |
944 | .crtc_funcs = &ade_crtc_funcs, |
945 | .plane_helper_funcs = &ade_plane_helper_funcs, |
946 | .plane_funcs = &ade_plane_funcs, |
947 | .mode_config_funcs = &ade_mode_config_funcs, |
948 | |
949 | .alloc_hw_ctx = ade_hw_ctx_alloc, |
950 | .cleanup_hw_ctx = ade_hw_ctx_cleanup, |
951 | }; |
952 | |