1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) Icenowy Zheng <icenowy@aosc.io> 4 * 5 * Based on sun4i_layer.h, which is: 6 * Copyright (C) 2015 Free Electrons 7 * Copyright (C) 2015 NextThing Co 8 * 9 * Maxime Ripard <maxime.ripard@free-electrons.com> 10 */ 11 12 #include <drm/drm_atomic.h> 13 #include <drm/drm_atomic_helper.h> 14 #include <drm/drm_blend.h> 15 #include <drm/drm_crtc.h> 16 #include <drm/drm_fb_dma_helper.h> 17 #include <drm/drm_fourcc.h> 18 #include <drm/drm_framebuffer.h> 19 #include <drm/drm_gem_atomic_helper.h> 20 #include <drm/drm_gem_dma_helper.h> 21 #include <drm/drm_probe_helper.h> 22 23 #include "sun8i_mixer.h" 24 #include "sun8i_ui_layer.h" 25 #include "sun8i_ui_scaler.h" 26 #include "sun8i_vi_scaler.h" 27 28 static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel, 29 int overlay, struct drm_plane *plane) 30 { 31 u32 mask, val, ch_base; 32 33 ch_base = sun8i_channel_base(mixer, channel); 34 35 mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK | 36 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK; 37 38 val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(plane->state->alpha >> 8); 39 40 val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ? 41 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_PIXEL : 42 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_COMBINED; 43 44 regmap_update_bits(mixer->engine.regs, 45 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), 46 mask, val); 47 } 48 49 static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, 50 int overlay, struct drm_plane *plane, 51 unsigned int zpos) 52 { 53 struct drm_plane_state *state = plane->state; 54 u32 src_w, src_h, dst_w, dst_h; 55 struct regmap *bld_regs; 56 u32 bld_base, ch_base; 57 u32 outsize, insize; 58 u32 hphase, vphase; 59 60 DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n", 61 channel, overlay); 62 63 bld_base = sun8i_blender_base(mixer); 64 bld_regs = sun8i_blender_regmap(mixer); 65 ch_base = sun8i_channel_base(mixer, channel); 66 67 src_w = drm_rect_width(&state->src) >> 16; 68 src_h = drm_rect_height(&state->src) >> 16; 69 dst_w = drm_rect_width(&state->dst); 70 dst_h = drm_rect_height(&state->dst); 71 72 hphase = state->src.x1 & 0xffff; 73 vphase = state->src.y1 & 0xffff; 74 75 insize = SUN8I_MIXER_SIZE(src_w, src_h); 76 outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); 77 78 /* Set height and width */ 79 DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n", 80 state->src.x1 >> 16, state->src.y1 >> 16); 81 DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); 82 regmap_write(mixer->engine.regs, 83 SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, overlay), 84 insize); 85 regmap_write(mixer->engine.regs, 86 SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch_base), 87 insize); 88 89 if (insize != outsize || hphase || vphase) { 90 u32 hscale, vscale; 91 92 DRM_DEBUG_DRIVER("HW scaling is enabled\n"); 93 94 hscale = state->src_w / state->crtc_w; 95 vscale = state->src_h / state->crtc_h; 96 97 if (mixer->cfg->de_type == SUN8I_MIXER_DE33) { 98 sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, 99 dst_w, dst_h, hscale, vscale, 100 hphase, vphase, 101 state->fb->format); 102 sun8i_vi_scaler_enable(mixer, channel, true); 103 } else { 104 sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, 105 dst_w, dst_h, hscale, vscale, 106 hphase, vphase); 107 sun8i_ui_scaler_enable(mixer, channel, true); 108 } 109 } else { 110 DRM_DEBUG_DRIVER("HW scaling is not needed\n"); 111 if (mixer->cfg->de_type == SUN8I_MIXER_DE33) 112 sun8i_vi_scaler_enable(mixer, channel, false); 113 else 114 sun8i_ui_scaler_enable(mixer, channel, false); 115 } 116 117 /* Set base coordinates */ 118 DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", 119 state->dst.x1, state->dst.y1); 120 DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); 121 regmap_write(bld_regs, 122 SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), 123 SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); 124 regmap_write(bld_regs, 125 SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), 126 outsize); 127 128 return 0; 129 } 130 131 static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel, 132 int overlay, struct drm_plane *plane) 133 { 134 struct drm_plane_state *state = plane->state; 135 const struct drm_format_info *fmt; 136 u32 val, ch_base, hw_fmt; 137 int ret; 138 139 ch_base = sun8i_channel_base(mixer, channel); 140 141 fmt = state->fb->format; 142 ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt); 143 if (ret || fmt->is_yuv) { 144 DRM_DEBUG_DRIVER("Invalid format\n"); 145 return -EINVAL; 146 } 147 148 val = hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET; 149 regmap_update_bits(mixer->engine.regs, 150 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), 151 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val); 152 153 return 0; 154 } 155 156 static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel, 157 int overlay, struct drm_plane *plane) 158 { 159 struct drm_plane_state *state = plane->state; 160 struct drm_framebuffer *fb = state->fb; 161 struct drm_gem_dma_object *gem; 162 dma_addr_t dma_addr; 163 u32 ch_base; 164 int bpp; 165 166 ch_base = sun8i_channel_base(mixer, channel); 167 168 /* Get the physical address of the buffer in memory */ 169 gem = drm_fb_dma_get_gem_obj(fb, 0); 170 171 DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr); 172 173 /* Compute the start of the displayed memory */ 174 bpp = fb->format->cpp[0]; 175 dma_addr = gem->dma_addr + fb->offsets[0]; 176 177 /* Fixup framebuffer address for src coordinates */ 178 dma_addr += (state->src.x1 >> 16) * bpp; 179 dma_addr += (state->src.y1 >> 16) * fb->pitches[0]; 180 181 /* Set the line width */ 182 DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]); 183 regmap_write(mixer->engine.regs, 184 SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, overlay), 185 fb->pitches[0]); 186 187 DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &dma_addr); 188 189 regmap_write(mixer->engine.regs, 190 SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, overlay), 191 lower_32_bits(dma_addr)); 192 193 return 0; 194 } 195 196 static int sun8i_ui_layer_atomic_check(struct drm_plane *plane, 197 struct drm_atomic_state *state) 198 { 199 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 200 plane); 201 struct sun8i_layer *layer = plane_to_sun8i_layer(plane); 202 struct drm_crtc *crtc = new_plane_state->crtc; 203 struct drm_crtc_state *crtc_state; 204 int min_scale, max_scale; 205 206 if (!crtc) 207 return 0; 208 209 crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 210 if (WARN_ON(!crtc_state)) 211 return -EINVAL; 212 213 min_scale = DRM_PLANE_NO_SCALING; 214 max_scale = DRM_PLANE_NO_SCALING; 215 216 if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { 217 min_scale = SUN8I_UI_SCALER_SCALE_MIN; 218 max_scale = SUN8I_UI_SCALER_SCALE_MAX; 219 } 220 221 return drm_atomic_helper_check_plane_state(new_plane_state, 222 crtc_state, 223 min_scale, max_scale, 224 true, true); 225 } 226 227 228 static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, 229 struct drm_atomic_state *state) 230 { 231 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 232 plane); 233 struct sun8i_layer *layer = plane_to_sun8i_layer(plane); 234 unsigned int zpos = new_state->normalized_zpos; 235 struct sun8i_mixer *mixer = layer->mixer; 236 237 if (!new_state->crtc || !new_state->visible) 238 return; 239 240 sun8i_ui_layer_update_coord(mixer, layer->channel, 241 layer->overlay, plane, zpos); 242 sun8i_ui_layer_update_alpha(mixer, layer->channel, 243 layer->overlay, plane); 244 sun8i_ui_layer_update_formats(mixer, layer->channel, 245 layer->overlay, plane); 246 sun8i_ui_layer_update_buffer(mixer, layer->channel, 247 layer->overlay, plane); 248 } 249 250 static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = { 251 .atomic_check = sun8i_ui_layer_atomic_check, 252 .atomic_update = sun8i_ui_layer_atomic_update, 253 }; 254 255 static const struct drm_plane_funcs sun8i_ui_layer_funcs = { 256 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 257 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 258 .destroy = drm_plane_cleanup, 259 .disable_plane = drm_atomic_helper_disable_plane, 260 .reset = drm_atomic_helper_plane_reset, 261 .update_plane = drm_atomic_helper_update_plane, 262 }; 263 264 static const u32 sun8i_ui_layer_formats[] = { 265 DRM_FORMAT_ABGR1555, 266 DRM_FORMAT_ABGR4444, 267 DRM_FORMAT_ABGR8888, 268 DRM_FORMAT_ARGB1555, 269 DRM_FORMAT_ARGB4444, 270 DRM_FORMAT_ARGB8888, 271 DRM_FORMAT_BGR565, 272 DRM_FORMAT_BGR888, 273 DRM_FORMAT_BGRA5551, 274 DRM_FORMAT_BGRA4444, 275 DRM_FORMAT_BGRA8888, 276 DRM_FORMAT_BGRX8888, 277 DRM_FORMAT_RGB565, 278 DRM_FORMAT_RGB888, 279 DRM_FORMAT_RGBA4444, 280 DRM_FORMAT_RGBA5551, 281 DRM_FORMAT_RGBA8888, 282 DRM_FORMAT_RGBX8888, 283 DRM_FORMAT_XBGR8888, 284 DRM_FORMAT_XRGB8888, 285 }; 286 287 static const uint64_t sun8i_layer_modifiers[] = { 288 DRM_FORMAT_MOD_LINEAR, 289 DRM_FORMAT_MOD_INVALID 290 }; 291 292 struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm, 293 struct sun8i_mixer *mixer, 294 int index) 295 { 296 enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; 297 int channel = mixer->cfg->vi_num + index; 298 struct sun8i_layer *layer; 299 unsigned int plane_cnt; 300 int ret; 301 302 layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); 303 if (!layer) 304 return ERR_PTR(-ENOMEM); 305 306 if (index == 0) 307 type = DRM_PLANE_TYPE_PRIMARY; 308 309 /* possible crtcs are set later */ 310 ret = drm_universal_plane_init(drm, &layer->plane, 0, 311 &sun8i_ui_layer_funcs, 312 sun8i_ui_layer_formats, 313 ARRAY_SIZE(sun8i_ui_layer_formats), 314 sun8i_layer_modifiers, type, NULL); 315 if (ret) { 316 dev_err(drm->dev, "Couldn't initialize layer\n"); 317 return ERR_PTR(ret); 318 } 319 320 plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num; 321 322 ret = drm_plane_create_alpha_property(&layer->plane); 323 if (ret) { 324 dev_err(drm->dev, "Couldn't add alpha property\n"); 325 return ERR_PTR(ret); 326 } 327 328 ret = drm_plane_create_zpos_property(&layer->plane, channel, 329 0, plane_cnt - 1); 330 if (ret) { 331 dev_err(drm->dev, "Couldn't add zpos property\n"); 332 return ERR_PTR(ret); 333 } 334 335 drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs); 336 layer->mixer = mixer; 337 layer->type = SUN8I_LAYER_TYPE_UI; 338 layer->channel = channel; 339 layer->overlay = 0; 340 341 return layer; 342 } 343