1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2016 BayLibre, SAS |
4 | * Author: Neil Armstrong <narmstrong@baylibre.com> |
5 | * Copyright (C) 2015 Amlogic, Inc. All rights reserved. |
6 | * Copyright (C) 2014 Endless Mobile |
7 | * |
8 | * Written by: |
9 | * Jasper St. Pierre <jstpierre@mecheye.net> |
10 | */ |
11 | |
12 | #include <linux/bitfield.h> |
13 | |
14 | #include <drm/drm_atomic.h> |
15 | #include <drm/drm_atomic_helper.h> |
16 | #include <drm/drm_blend.h> |
17 | #include <drm/drm_device.h> |
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 | |
24 | #include "meson_plane.h" |
25 | #include "meson_registers.h" |
26 | #include "meson_viu.h" |
27 | #include "meson_osd_afbcd.h" |
28 | |
29 | /* OSD_SCI_WH_M1 */ |
30 | #define SCI_WH_M1_W(w) FIELD_PREP(GENMASK(28, 16), w) |
31 | #define SCI_WH_M1_H(h) FIELD_PREP(GENMASK(12, 0), h) |
32 | |
33 | /* OSD_SCO_H_START_END */ |
34 | /* OSD_SCO_V_START_END */ |
35 | #define SCO_HV_START(start) FIELD_PREP(GENMASK(27, 16), start) |
36 | #define SCO_HV_END(end) FIELD_PREP(GENMASK(11, 0), end) |
37 | |
38 | /* OSD_SC_CTRL0 */ |
39 | #define SC_CTRL0_PATH_EN BIT(3) |
40 | #define SC_CTRL0_SEL_OSD1 BIT(2) |
41 | |
42 | /* OSD_VSC_CTRL0 */ |
43 | #define VSC_BANK_LEN(value) FIELD_PREP(GENMASK(2, 0), value) |
44 | #define VSC_TOP_INI_RCV_NUM(value) FIELD_PREP(GENMASK(6, 3), value) |
45 | #define VSC_TOP_RPT_L0_NUM(value) FIELD_PREP(GENMASK(9, 8), value) |
46 | #define VSC_BOT_INI_RCV_NUM(value) FIELD_PREP(GENMASK(14, 11), value) |
47 | #define VSC_BOT_RPT_L0_NUM(value) FIELD_PREP(GENMASK(17, 16), value) |
48 | #define VSC_PROG_INTERLACE BIT(23) |
49 | #define VSC_VERTICAL_SCALER_EN BIT(24) |
50 | |
51 | /* OSD_VSC_INI_PHASE */ |
52 | #define VSC_INI_PHASE_BOT(bottom) FIELD_PREP(GENMASK(31, 16), bottom) |
53 | #define VSC_INI_PHASE_TOP(top) FIELD_PREP(GENMASK(15, 0), top) |
54 | |
55 | /* OSD_HSC_CTRL0 */ |
56 | #define HSC_BANK_LENGTH(value) FIELD_PREP(GENMASK(2, 0), value) |
57 | #define HSC_INI_RCV_NUM0(value) FIELD_PREP(GENMASK(6, 3), value) |
58 | #define HSC_RPT_P0_NUM0(value) FIELD_PREP(GENMASK(9, 8), value) |
59 | #define HSC_HORIZ_SCALER_EN BIT(22) |
60 | |
61 | /* VPP_OSD_VSC_PHASE_STEP */ |
62 | /* VPP_OSD_HSC_PHASE_STEP */ |
63 | #define SC_PHASE_STEP(value) FIELD_PREP(GENMASK(27, 0), value) |
64 | |
65 | struct meson_plane { |
66 | struct drm_plane base; |
67 | struct meson_drm *priv; |
68 | bool enabled; |
69 | }; |
70 | #define to_meson_plane(x) container_of(x, struct meson_plane, base) |
71 | |
72 | #define FRAC_16_16(mult, div) (((mult) << 16) / (div)) |
73 | |
74 | static int meson_plane_atomic_check(struct drm_plane *plane, |
75 | struct drm_atomic_state *state) |
76 | { |
77 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, |
78 | plane); |
79 | struct drm_crtc_state *crtc_state; |
80 | |
81 | if (!new_plane_state->crtc) |
82 | return 0; |
83 | |
84 | crtc_state = drm_atomic_get_crtc_state(state, |
85 | crtc: new_plane_state->crtc); |
86 | if (IS_ERR(ptr: crtc_state)) |
87 | return PTR_ERR(ptr: crtc_state); |
88 | |
89 | /* |
90 | * Only allow : |
91 | * - Upscaling up to 5x, vertical and horizontal |
92 | * - Final coordinates must match crtc size |
93 | */ |
94 | return drm_atomic_helper_check_plane_state(plane_state: new_plane_state, |
95 | crtc_state, |
96 | FRAC_16_16(1, 5), |
97 | DRM_PLANE_NO_SCALING, |
98 | can_position: false, can_update_disabled: true); |
99 | } |
100 | |
101 | #define MESON_MOD_AFBC_VALID_BITS (AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | \ |
102 | AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | \ |
103 | AFBC_FORMAT_MOD_YTR | \ |
104 | AFBC_FORMAT_MOD_SPARSE | \ |
105 | AFBC_FORMAT_MOD_SPLIT) |
106 | |
107 | /* Takes a fixed 16.16 number and converts it to integer. */ |
108 | static inline int64_t fixed16_to_int(int64_t value) |
109 | { |
110 | return value >> 16; |
111 | } |
112 | |
113 | static u32 meson_g12a_afbcd_line_stride(struct meson_drm *priv) |
114 | { |
115 | u32 line_stride = 0; |
116 | |
117 | switch (priv->afbcd.format) { |
118 | case DRM_FORMAT_RGB565: |
119 | line_stride = ((priv->viu.osd1_width << 4) + 127) >> 7; |
120 | break; |
121 | case DRM_FORMAT_RGB888: |
122 | case DRM_FORMAT_XRGB8888: |
123 | case DRM_FORMAT_ARGB8888: |
124 | case DRM_FORMAT_XBGR8888: |
125 | case DRM_FORMAT_ABGR8888: |
126 | line_stride = ((priv->viu.osd1_width << 5) + 127) >> 7; |
127 | break; |
128 | } |
129 | |
130 | return ((line_stride + 1) >> 1) << 1; |
131 | } |
132 | |
133 | static void meson_plane_atomic_update(struct drm_plane *plane, |
134 | struct drm_atomic_state *state) |
135 | { |
136 | struct meson_plane *meson_plane = to_meson_plane(plane); |
137 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
138 | plane); |
139 | struct drm_rect dest = drm_plane_state_dest(state: new_state); |
140 | struct meson_drm *priv = meson_plane->priv; |
141 | struct drm_framebuffer *fb = new_state->fb; |
142 | struct drm_gem_dma_object *gem; |
143 | unsigned long flags; |
144 | int vsc_ini_rcv_num, vsc_ini_rpt_p0_num; |
145 | int vsc_bot_rcv_num, vsc_bot_rpt_p0_num; |
146 | int hsc_ini_rcv_num, hsc_ini_rpt_p0_num; |
147 | int hf_phase_step, vf_phase_step; |
148 | int src_w, src_h, dst_w, dst_h; |
149 | int bot_ini_phase; |
150 | int hf_bank_len; |
151 | int vf_bank_len; |
152 | u8 canvas_id_osd1; |
153 | |
154 | /* |
155 | * Update Coordinates |
156 | * Update Formats |
157 | * Update Buffer |
158 | * Enable Plane |
159 | */ |
160 | spin_lock_irqsave(&priv->drm->event_lock, flags); |
161 | |
162 | /* Check if AFBC decoder is required for this buffer */ |
163 | if ((meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_GXM) || |
164 | meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_G12A)) && |
165 | fb->modifier & DRM_FORMAT_MOD_ARM_AFBC(MESON_MOD_AFBC_VALID_BITS)) |
166 | priv->viu.osd1_afbcd = true; |
167 | else |
168 | priv->viu.osd1_afbcd = false; |
169 | |
170 | /* Enable OSD and BLK0, set max global alpha */ |
171 | priv->viu.osd1_ctrl_stat = OSD_ENABLE | |
172 | (0x100 << OSD_GLOBAL_ALPHA_SHIFT) | |
173 | OSD_BLK0_ENABLE; |
174 | |
175 | priv->viu.osd1_ctrl_stat2 = readl(addr: priv->io_base + |
176 | _REG(VIU_OSD1_CTRL_STAT2)); |
177 | |
178 | canvas_id_osd1 = priv->canvas_id_osd1; |
179 | |
180 | /* Set up BLK0 to point to the right canvas */ |
181 | priv->viu.osd1_blk0_cfg[0] = canvas_id_osd1 << OSD_CANVAS_SEL; |
182 | |
183 | if (priv->viu.osd1_afbcd) { |
184 | if (meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_G12A)) { |
185 | /* This is the internal decoding memory address */ |
186 | priv->viu.osd1_blk1_cfg4 = MESON_G12A_AFBCD_OUT_ADDR; |
187 | priv->viu.osd1_blk0_cfg[0] |= OSD_ENDIANNESS_BE; |
188 | priv->viu.osd1_ctrl_stat2 |= OSD_PENDING_STAT_CLEAN; |
189 | priv->viu.osd1_ctrl_stat |= VIU_OSD1_CFG_SYN_EN; |
190 | } |
191 | |
192 | if (meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_GXM)) { |
193 | priv->viu.osd1_blk0_cfg[0] |= OSD_ENDIANNESS_LE; |
194 | priv->viu.osd1_ctrl_stat2 |= OSD_DPATH_MALI_AFBCD; |
195 | } |
196 | } else { |
197 | priv->viu.osd1_blk0_cfg[0] |= OSD_ENDIANNESS_LE; |
198 | |
199 | if (meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_GXM)) |
200 | priv->viu.osd1_ctrl_stat2 &= ~OSD_DPATH_MALI_AFBCD; |
201 | } |
202 | |
203 | /* On GXBB, Use the old non-HDR RGB2YUV converter */ |
204 | if (meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_GXBB)) |
205 | priv->viu.osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB; |
206 | |
207 | if (priv->viu.osd1_afbcd && |
208 | meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_G12A)) { |
209 | priv->viu.osd1_blk0_cfg[0] |= OSD_MALI_SRC_EN | |
210 | priv->afbcd.ops->fmt_to_blk_mode(fb->modifier, |
211 | fb->format->format); |
212 | } else { |
213 | switch (fb->format->format) { |
214 | case DRM_FORMAT_XRGB8888: |
215 | case DRM_FORMAT_ARGB8888: |
216 | priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | |
217 | OSD_COLOR_MATRIX_32_ARGB; |
218 | break; |
219 | case DRM_FORMAT_XBGR8888: |
220 | case DRM_FORMAT_ABGR8888: |
221 | priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | |
222 | OSD_COLOR_MATRIX_32_ABGR; |
223 | break; |
224 | case DRM_FORMAT_RGB888: |
225 | priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_24 | |
226 | OSD_COLOR_MATRIX_24_RGB; |
227 | break; |
228 | case DRM_FORMAT_RGB565: |
229 | priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_16 | |
230 | OSD_COLOR_MATRIX_16_RGB565; |
231 | break; |
232 | } |
233 | } |
234 | |
235 | switch (fb->format->format) { |
236 | case DRM_FORMAT_XRGB8888: |
237 | case DRM_FORMAT_XBGR8888: |
238 | /* For XRGB, replace the pixel's alpha by 0xFF */ |
239 | priv->viu.osd1_ctrl_stat2 |= OSD_REPLACE_EN; |
240 | break; |
241 | case DRM_FORMAT_ARGB8888: |
242 | case DRM_FORMAT_ABGR8888: |
243 | /* For ARGB, use the pixel's alpha */ |
244 | priv->viu.osd1_ctrl_stat2 &= ~OSD_REPLACE_EN; |
245 | break; |
246 | } |
247 | |
248 | /* Default scaler parameters */ |
249 | vsc_bot_rcv_num = 0; |
250 | vsc_bot_rpt_p0_num = 0; |
251 | hf_bank_len = 4; |
252 | vf_bank_len = 4; |
253 | |
254 | if (new_state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { |
255 | vsc_bot_rcv_num = 6; |
256 | vsc_bot_rpt_p0_num = 2; |
257 | } |
258 | |
259 | hsc_ini_rcv_num = hf_bank_len; |
260 | vsc_ini_rcv_num = vf_bank_len; |
261 | hsc_ini_rpt_p0_num = (hf_bank_len / 2) - 1; |
262 | vsc_ini_rpt_p0_num = (vf_bank_len / 2) - 1; |
263 | |
264 | src_w = fixed16_to_int(value: new_state->src_w); |
265 | src_h = fixed16_to_int(value: new_state->src_h); |
266 | dst_w = new_state->crtc_w; |
267 | dst_h = new_state->crtc_h; |
268 | |
269 | /* |
270 | * When the output is interlaced, the OSD must switch between |
271 | * each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0 |
272 | * at each vsync. |
273 | * But the vertical scaler can provide such funtionnality if |
274 | * is configured for 2:1 scaling with interlace options enabled. |
275 | */ |
276 | if (new_state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { |
277 | dest.y1 /= 2; |
278 | dest.y2 /= 2; |
279 | dst_h /= 2; |
280 | } |
281 | |
282 | hf_phase_step = ((src_w << 18) / dst_w) << 6; |
283 | vf_phase_step = (src_h << 20) / dst_h; |
284 | |
285 | if (new_state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) |
286 | bot_ini_phase = ((vf_phase_step / 2) >> 4); |
287 | else |
288 | bot_ini_phase = 0; |
289 | |
290 | vf_phase_step = (vf_phase_step << 4); |
291 | |
292 | /* In interlaced mode, scaler is always active */ |
293 | if (src_h != dst_h || src_w != dst_w) { |
294 | priv->viu.osd_sc_i_wh_m1 = SCI_WH_M1_W(src_w - 1) | |
295 | SCI_WH_M1_H(src_h - 1); |
296 | priv->viu.osd_sc_o_h_start_end = SCO_HV_START(dest.x1) | |
297 | SCO_HV_END(dest.x2 - 1); |
298 | priv->viu.osd_sc_o_v_start_end = SCO_HV_START(dest.y1) | |
299 | SCO_HV_END(dest.y2 - 1); |
300 | /* Enable OSD Scaler */ |
301 | priv->viu.osd_sc_ctrl0 = SC_CTRL0_PATH_EN | SC_CTRL0_SEL_OSD1; |
302 | } else { |
303 | priv->viu.osd_sc_i_wh_m1 = 0; |
304 | priv->viu.osd_sc_o_h_start_end = 0; |
305 | priv->viu.osd_sc_o_v_start_end = 0; |
306 | priv->viu.osd_sc_ctrl0 = 0; |
307 | } |
308 | |
309 | /* In interlaced mode, vertical scaler is always active */ |
310 | if (src_h != dst_h) { |
311 | priv->viu.osd_sc_v_ctrl0 = |
312 | VSC_BANK_LEN(vf_bank_len) | |
313 | VSC_TOP_INI_RCV_NUM(vsc_ini_rcv_num) | |
314 | VSC_TOP_RPT_L0_NUM(vsc_ini_rpt_p0_num) | |
315 | VSC_VERTICAL_SCALER_EN; |
316 | |
317 | if (new_state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) |
318 | priv->viu.osd_sc_v_ctrl0 |= |
319 | VSC_BOT_INI_RCV_NUM(vsc_bot_rcv_num) | |
320 | VSC_BOT_RPT_L0_NUM(vsc_bot_rpt_p0_num) | |
321 | VSC_PROG_INTERLACE; |
322 | |
323 | priv->viu.osd_sc_v_phase_step = SC_PHASE_STEP(vf_phase_step); |
324 | priv->viu.osd_sc_v_ini_phase = VSC_INI_PHASE_BOT(bot_ini_phase); |
325 | } else { |
326 | priv->viu.osd_sc_v_ctrl0 = 0; |
327 | priv->viu.osd_sc_v_phase_step = 0; |
328 | priv->viu.osd_sc_v_ini_phase = 0; |
329 | } |
330 | |
331 | /* Horizontal scaler is only used if width does not match */ |
332 | if (src_w != dst_w) { |
333 | priv->viu.osd_sc_h_ctrl0 = |
334 | HSC_BANK_LENGTH(hf_bank_len) | |
335 | HSC_INI_RCV_NUM0(hsc_ini_rcv_num) | |
336 | HSC_RPT_P0_NUM0(hsc_ini_rpt_p0_num) | |
337 | HSC_HORIZ_SCALER_EN; |
338 | priv->viu.osd_sc_h_phase_step = SC_PHASE_STEP(hf_phase_step); |
339 | priv->viu.osd_sc_h_ini_phase = 0; |
340 | } else { |
341 | priv->viu.osd_sc_h_ctrl0 = 0; |
342 | priv->viu.osd_sc_h_phase_step = 0; |
343 | priv->viu.osd_sc_h_ini_phase = 0; |
344 | } |
345 | |
346 | /* |
347 | * The format of these registers is (x2 << 16 | x1), |
348 | * where x2 is exclusive. |
349 | * e.g. +30x1920 would be (1919 << 16) | 30 |
350 | */ |
351 | priv->viu.osd1_blk0_cfg[1] = |
352 | ((fixed16_to_int(value: new_state->src.x2) - 1) << 16) | |
353 | fixed16_to_int(value: new_state->src.x1); |
354 | priv->viu.osd1_blk0_cfg[2] = |
355 | ((fixed16_to_int(value: new_state->src.y2) - 1) << 16) | |
356 | fixed16_to_int(value: new_state->src.y1); |
357 | priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1; |
358 | priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1; |
359 | |
360 | if (meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_G12A)) { |
361 | priv->viu.osd_blend_din0_scope_h = ((dest.x2 - 1) << 16) | dest.x1; |
362 | priv->viu.osd_blend_din0_scope_v = ((dest.y2 - 1) << 16) | dest.y1; |
363 | priv->viu.osb_blend0_size = dst_h << 16 | dst_w; |
364 | priv->viu.osb_blend1_size = dst_h << 16 | dst_w; |
365 | } |
366 | |
367 | /* Update Canvas with buffer address */ |
368 | gem = drm_fb_dma_get_gem_obj(fb, plane: 0); |
369 | |
370 | priv->viu.osd1_addr = gem->dma_addr; |
371 | priv->viu.osd1_stride = fb->pitches[0]; |
372 | priv->viu.osd1_height = fb->height; |
373 | priv->viu.osd1_width = fb->width; |
374 | |
375 | if (priv->viu.osd1_afbcd) { |
376 | priv->afbcd.modifier = fb->modifier; |
377 | priv->afbcd.format = fb->format->format; |
378 | |
379 | /* Calculate decoder write stride */ |
380 | if (meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_G12A)) |
381 | priv->viu.osd1_blk2_cfg4 = |
382 | meson_g12a_afbcd_line_stride(priv); |
383 | } |
384 | |
385 | if (!meson_plane->enabled) { |
386 | /* Reset OSD1 before enabling it on GXL+ SoCs */ |
387 | if (meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_GXM) || |
388 | meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_GXL)) |
389 | meson_viu_osd1_reset(priv); |
390 | |
391 | meson_plane->enabled = true; |
392 | } |
393 | |
394 | priv->viu.osd1_enabled = true; |
395 | |
396 | spin_unlock_irqrestore(lock: &priv->drm->event_lock, flags); |
397 | } |
398 | |
399 | static void meson_plane_atomic_disable(struct drm_plane *plane, |
400 | struct drm_atomic_state *state) |
401 | { |
402 | struct meson_plane *meson_plane = to_meson_plane(plane); |
403 | struct meson_drm *priv = meson_plane->priv; |
404 | |
405 | if (priv->afbcd.ops) { |
406 | priv->afbcd.ops->reset(priv); |
407 | priv->afbcd.ops->disable(priv); |
408 | } |
409 | |
410 | /* Disable OSD1 */ |
411 | if (meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_G12A)) |
412 | writel_bits_relaxed(VIU_OSD1_POSTBLD_SRC_OSD1, 0, |
413 | priv->io_base + _REG(OSD1_BLEND_SRC_CTRL)); |
414 | else |
415 | writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0, |
416 | priv->io_base + _REG(VPP_MISC)); |
417 | |
418 | meson_plane->enabled = false; |
419 | priv->viu.osd1_enabled = false; |
420 | } |
421 | |
422 | static const struct drm_plane_helper_funcs meson_plane_helper_funcs = { |
423 | .atomic_check = meson_plane_atomic_check, |
424 | .atomic_disable = meson_plane_atomic_disable, |
425 | .atomic_update = meson_plane_atomic_update, |
426 | }; |
427 | |
428 | static bool meson_plane_format_mod_supported(struct drm_plane *plane, |
429 | u32 format, u64 modifier) |
430 | { |
431 | struct meson_plane *meson_plane = to_meson_plane(plane); |
432 | struct meson_drm *priv = meson_plane->priv; |
433 | int i; |
434 | |
435 | if (modifier == DRM_FORMAT_MOD_INVALID) |
436 | return false; |
437 | |
438 | if (modifier == DRM_FORMAT_MOD_LINEAR) |
439 | return true; |
440 | |
441 | if (!meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_GXM) && |
442 | !meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_G12A)) |
443 | return false; |
444 | |
445 | if (modifier & ~DRM_FORMAT_MOD_ARM_AFBC(MESON_MOD_AFBC_VALID_BITS)) |
446 | return false; |
447 | |
448 | for (i = 0 ; i < plane->modifier_count ; ++i) |
449 | if (plane->modifiers[i] == modifier) |
450 | break; |
451 | |
452 | if (i == plane->modifier_count) { |
453 | DRM_DEBUG_KMS("Unsupported modifier\n" ); |
454 | return false; |
455 | } |
456 | |
457 | if (priv->afbcd.ops && priv->afbcd.ops->supported_fmt) |
458 | return priv->afbcd.ops->supported_fmt(modifier, format); |
459 | |
460 | DRM_DEBUG_KMS("AFBC Unsupported\n" ); |
461 | return false; |
462 | } |
463 | |
464 | static const struct drm_plane_funcs meson_plane_funcs = { |
465 | .update_plane = drm_atomic_helper_update_plane, |
466 | .disable_plane = drm_atomic_helper_disable_plane, |
467 | .destroy = drm_plane_cleanup, |
468 | .reset = drm_atomic_helper_plane_reset, |
469 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
470 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
471 | .format_mod_supported = meson_plane_format_mod_supported, |
472 | }; |
473 | |
474 | static const uint32_t supported_drm_formats[] = { |
475 | DRM_FORMAT_ARGB8888, |
476 | DRM_FORMAT_ABGR8888, |
477 | DRM_FORMAT_XRGB8888, |
478 | DRM_FORMAT_XBGR8888, |
479 | DRM_FORMAT_RGB888, |
480 | DRM_FORMAT_RGB565, |
481 | }; |
482 | |
483 | static const uint64_t format_modifiers_afbc_gxm[] = { |
484 | DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | |
485 | AFBC_FORMAT_MOD_SPARSE | |
486 | AFBC_FORMAT_MOD_YTR), |
487 | /* SPLIT mandates SPARSE, RGB modes mandates YTR */ |
488 | DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | |
489 | AFBC_FORMAT_MOD_YTR | |
490 | AFBC_FORMAT_MOD_SPARSE | |
491 | AFBC_FORMAT_MOD_SPLIT), |
492 | DRM_FORMAT_MOD_LINEAR, |
493 | DRM_FORMAT_MOD_INVALID, |
494 | }; |
495 | |
496 | static const uint64_t format_modifiers_afbc_g12a[] = { |
497 | /* |
498 | * - TOFIX Support AFBC modifiers for YUV formats (16x16 + TILED) |
499 | * - SPLIT is mandatory for performances reasons when in 16x16 |
500 | * block size |
501 | * - 32x8 block size + SPLIT is mandatory with 4K frame size |
502 | * for performances reasons |
503 | */ |
504 | DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | |
505 | AFBC_FORMAT_MOD_SPARSE | |
506 | AFBC_FORMAT_MOD_SPLIT), |
507 | DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | |
508 | AFBC_FORMAT_MOD_YTR | |
509 | AFBC_FORMAT_MOD_SPARSE | |
510 | AFBC_FORMAT_MOD_SPLIT), |
511 | DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | |
512 | AFBC_FORMAT_MOD_SPARSE), |
513 | DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | |
514 | AFBC_FORMAT_MOD_YTR | |
515 | AFBC_FORMAT_MOD_SPARSE), |
516 | DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | |
517 | AFBC_FORMAT_MOD_SPARSE | |
518 | AFBC_FORMAT_MOD_SPLIT), |
519 | DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | |
520 | AFBC_FORMAT_MOD_YTR | |
521 | AFBC_FORMAT_MOD_SPARSE | |
522 | AFBC_FORMAT_MOD_SPLIT), |
523 | DRM_FORMAT_MOD_LINEAR, |
524 | DRM_FORMAT_MOD_INVALID, |
525 | }; |
526 | |
527 | static const uint64_t format_modifiers_default[] = { |
528 | DRM_FORMAT_MOD_LINEAR, |
529 | DRM_FORMAT_MOD_INVALID, |
530 | }; |
531 | |
532 | int meson_plane_create(struct meson_drm *priv) |
533 | { |
534 | struct meson_plane *meson_plane; |
535 | struct drm_plane *plane; |
536 | const uint64_t *format_modifiers = format_modifiers_default; |
537 | |
538 | meson_plane = devm_kzalloc(dev: priv->drm->dev, size: sizeof(*meson_plane), |
539 | GFP_KERNEL); |
540 | if (!meson_plane) |
541 | return -ENOMEM; |
542 | |
543 | meson_plane->priv = priv; |
544 | plane = &meson_plane->base; |
545 | |
546 | if (meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_GXM)) |
547 | format_modifiers = format_modifiers_afbc_gxm; |
548 | else if (meson_vpu_is_compatible(priv, family: VPU_COMPATIBLE_G12A)) |
549 | format_modifiers = format_modifiers_afbc_g12a; |
550 | |
551 | drm_universal_plane_init(dev: priv->drm, plane, possible_crtcs: 0xFF, |
552 | funcs: &meson_plane_funcs, |
553 | formats: supported_drm_formats, |
554 | ARRAY_SIZE(supported_drm_formats), |
555 | format_modifiers, |
556 | type: DRM_PLANE_TYPE_PRIMARY, name: "meson_primary_plane" ); |
557 | |
558 | drm_plane_helper_add(plane, funcs: &meson_plane_helper_funcs); |
559 | |
560 | /* For now, OSD Primary plane is always on the front */ |
561 | drm_plane_create_zpos_immutable_property(plane, zpos: 1); |
562 | |
563 | priv->primary_plane = plane; |
564 | |
565 | return 0; |
566 | } |
567 | |