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 27 static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, 28 int overlay, bool enable, unsigned int zpos, 29 unsigned int old_zpos) 30 { 31 u32 val, bld_base, ch_base; 32 33 bld_base = sun8i_blender_base(mixer); 34 ch_base = sun8i_channel_base(mixer, channel); 35 36 DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n", 37 enable ? "En" : "Dis", channel, overlay); 38 39 if (enable) 40 val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN; 41 else 42 val = 0; 43 44 regmap_update_bits(mixer->engine.regs, 45 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), 46 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val); 47 48 if (!enable || zpos != old_zpos) { 49 regmap_update_bits(mixer->engine.regs, 50 SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 51 SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), 52 0); 53 54 regmap_update_bits(mixer->engine.regs, 55 SUN8I_MIXER_BLEND_ROUTE(bld_base), 56 SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), 57 0); 58 } 59 60 if (enable) { 61 val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); 62 63 regmap_update_bits(mixer->engine.regs, 64 SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 65 val, val); 66 67 val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos); 68 69 regmap_update_bits(mixer->engine.regs, 70 SUN8I_MIXER_BLEND_ROUTE(bld_base), 71 SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos), 72 val); 73 } 74 } 75 76 static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel, 77 int overlay, struct drm_plane *plane) 78 { 79 u32 mask, val, ch_base; 80 81 ch_base = sun8i_channel_base(mixer, channel); 82 83 mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK | 84 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK; 85 86 val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(plane->state->alpha >> 8); 87 88 val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ? 89 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_PIXEL : 90 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_COMBINED; 91 92 regmap_update_bits(mixer->engine.regs, 93 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), 94 mask, val); 95 } 96 97 static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, 98 int overlay, struct drm_plane *plane, 99 unsigned int zpos) 100 { 101 struct drm_plane_state *state = plane->state; 102 u32 src_w, src_h, dst_w, dst_h; 103 u32 bld_base, ch_base; 104 u32 outsize, insize; 105 u32 hphase, vphase; 106 107 DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n", 108 channel, overlay); 109 110 bld_base = sun8i_blender_base(mixer); 111 ch_base = sun8i_channel_base(mixer, channel); 112 113 src_w = drm_rect_width(&state->src) >> 16; 114 src_h = drm_rect_height(&state->src) >> 16; 115 dst_w = drm_rect_width(&state->dst); 116 dst_h = drm_rect_height(&state->dst); 117 118 hphase = state->src.x1 & 0xffff; 119 vphase = state->src.y1 & 0xffff; 120 121 insize = SUN8I_MIXER_SIZE(src_w, src_h); 122 outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); 123 124 /* Set height and width */ 125 DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n", 126 state->src.x1 >> 16, state->src.y1 >> 16); 127 DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); 128 regmap_write(mixer->engine.regs, 129 SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, overlay), 130 insize); 131 regmap_write(mixer->engine.regs, 132 SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch_base), 133 insize); 134 135 if (insize != outsize || hphase || vphase) { 136 u32 hscale, vscale; 137 138 DRM_DEBUG_DRIVER("HW scaling is enabled\n"); 139 140 hscale = state->src_w / state->crtc_w; 141 vscale = state->src_h / state->crtc_h; 142 143 sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w, 144 dst_h, hscale, vscale, hphase, vphase); 145 sun8i_ui_scaler_enable(mixer, channel, true); 146 } else { 147 DRM_DEBUG_DRIVER("HW scaling is not needed\n"); 148 sun8i_ui_scaler_enable(mixer, channel, false); 149 } 150 151 /* Set base coordinates */ 152 DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", 153 state->dst.x1, state->dst.y1); 154 DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); 155 regmap_write(mixer->engine.regs, 156 SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), 157 SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); 158 regmap_write(mixer->engine.regs, 159 SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), 160 outsize); 161 162 return 0; 163 } 164 165 static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel, 166 int overlay, struct drm_plane *plane) 167 { 168 struct drm_plane_state *state = plane->state; 169 const struct drm_format_info *fmt; 170 u32 val, ch_base, hw_fmt; 171 int ret; 172 173 ch_base = sun8i_channel_base(mixer, channel); 174 175 fmt = state->fb->format; 176 ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt); 177 if (ret || fmt->is_yuv) { 178 DRM_DEBUG_DRIVER("Invalid format\n"); 179 return -EINVAL; 180 } 181 182 val = hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET; 183 regmap_update_bits(mixer->engine.regs, 184 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), 185 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val); 186 187 return 0; 188 } 189 190 static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel, 191 int overlay, struct drm_plane *plane) 192 { 193 struct drm_plane_state *state = plane->state; 194 struct drm_framebuffer *fb = state->fb; 195 struct drm_gem_dma_object *gem; 196 dma_addr_t dma_addr; 197 u32 ch_base; 198 int bpp; 199 200 ch_base = sun8i_channel_base(mixer, channel); 201 202 /* Get the physical address of the buffer in memory */ 203 gem = drm_fb_dma_get_gem_obj(fb, 0); 204 205 DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr); 206 207 /* Compute the start of the displayed memory */ 208 bpp = fb->format->cpp[0]; 209 dma_addr = gem->dma_addr + fb->offsets[0]; 210 211 /* Fixup framebuffer address for src coordinates */ 212 dma_addr += (state->src.x1 >> 16) * bpp; 213 dma_addr += (state->src.y1 >> 16) * fb->pitches[0]; 214 215 /* Set the line width */ 216 DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]); 217 regmap_write(mixer->engine.regs, 218 SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, overlay), 219 fb->pitches[0]); 220 221 DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &dma_addr); 222 223 regmap_write(mixer->engine.regs, 224 SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, overlay), 225 lower_32_bits(dma_addr)); 226 227 return 0; 228 } 229 230 static int sun8i_ui_layer_atomic_check(struct drm_plane *plane, 231 struct drm_atomic_state *state) 232 { 233 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 234 plane); 235 struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); 236 struct drm_crtc *crtc = new_plane_state->crtc; 237 struct drm_crtc_state *crtc_state; 238 int min_scale, max_scale; 239 240 if (!crtc) 241 return 0; 242 243 crtc_state = drm_atomic_get_existing_crtc_state(state, 244 crtc); 245 if (WARN_ON(!crtc_state)) 246 return -EINVAL; 247 248 min_scale = DRM_PLANE_NO_SCALING; 249 max_scale = DRM_PLANE_NO_SCALING; 250 251 if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { 252 min_scale = SUN8I_UI_SCALER_SCALE_MIN; 253 max_scale = SUN8I_UI_SCALER_SCALE_MAX; 254 } 255 256 return drm_atomic_helper_check_plane_state(new_plane_state, 257 crtc_state, 258 min_scale, max_scale, 259 true, true); 260 } 261 262 static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane, 263 struct drm_atomic_state *state) 264 { 265 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 266 plane); 267 struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); 268 unsigned int old_zpos = old_state->normalized_zpos; 269 struct sun8i_mixer *mixer = layer->mixer; 270 271 sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false, 0, 272 old_zpos); 273 } 274 275 static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, 276 struct drm_atomic_state *state) 277 { 278 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 279 plane); 280 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 281 plane); 282 struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); 283 unsigned int zpos = new_state->normalized_zpos; 284 unsigned int old_zpos = old_state->normalized_zpos; 285 struct sun8i_mixer *mixer = layer->mixer; 286 287 if (!new_state->visible) { 288 sun8i_ui_layer_enable(mixer, layer->channel, 289 layer->overlay, false, 0, old_zpos); 290 return; 291 } 292 293 sun8i_ui_layer_update_coord(mixer, layer->channel, 294 layer->overlay, plane, zpos); 295 sun8i_ui_layer_update_alpha(mixer, layer->channel, 296 layer->overlay, plane); 297 sun8i_ui_layer_update_formats(mixer, layer->channel, 298 layer->overlay, plane); 299 sun8i_ui_layer_update_buffer(mixer, layer->channel, 300 layer->overlay, plane); 301 sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, 302 true, zpos, old_zpos); 303 } 304 305 static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = { 306 .atomic_check = sun8i_ui_layer_atomic_check, 307 .atomic_disable = sun8i_ui_layer_atomic_disable, 308 .atomic_update = sun8i_ui_layer_atomic_update, 309 }; 310 311 static const struct drm_plane_funcs sun8i_ui_layer_funcs = { 312 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 313 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 314 .destroy = drm_plane_cleanup, 315 .disable_plane = drm_atomic_helper_disable_plane, 316 .reset = drm_atomic_helper_plane_reset, 317 .update_plane = drm_atomic_helper_update_plane, 318 }; 319 320 static const u32 sun8i_ui_layer_formats[] = { 321 DRM_FORMAT_ABGR1555, 322 DRM_FORMAT_ABGR4444, 323 DRM_FORMAT_ABGR8888, 324 DRM_FORMAT_ARGB1555, 325 DRM_FORMAT_ARGB4444, 326 DRM_FORMAT_ARGB8888, 327 DRM_FORMAT_BGR565, 328 DRM_FORMAT_BGR888, 329 DRM_FORMAT_BGRA5551, 330 DRM_FORMAT_BGRA4444, 331 DRM_FORMAT_BGRA8888, 332 DRM_FORMAT_BGRX8888, 333 DRM_FORMAT_RGB565, 334 DRM_FORMAT_RGB888, 335 DRM_FORMAT_RGBA4444, 336 DRM_FORMAT_RGBA5551, 337 DRM_FORMAT_RGBA8888, 338 DRM_FORMAT_RGBX8888, 339 DRM_FORMAT_XBGR8888, 340 DRM_FORMAT_XRGB8888, 341 }; 342 343 static const uint64_t sun8i_layer_modifiers[] = { 344 DRM_FORMAT_MOD_LINEAR, 345 DRM_FORMAT_MOD_INVALID 346 }; 347 348 struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm, 349 struct sun8i_mixer *mixer, 350 int index) 351 { 352 enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; 353 int channel = mixer->cfg->vi_num + index; 354 struct sun8i_ui_layer *layer; 355 unsigned int plane_cnt; 356 int ret; 357 358 layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); 359 if (!layer) 360 return ERR_PTR(-ENOMEM); 361 362 if (index == 0) 363 type = DRM_PLANE_TYPE_PRIMARY; 364 365 /* possible crtcs are set later */ 366 ret = drm_universal_plane_init(drm, &layer->plane, 0, 367 &sun8i_ui_layer_funcs, 368 sun8i_ui_layer_formats, 369 ARRAY_SIZE(sun8i_ui_layer_formats), 370 sun8i_layer_modifiers, type, NULL); 371 if (ret) { 372 dev_err(drm->dev, "Couldn't initialize layer\n"); 373 return ERR_PTR(ret); 374 } 375 376 plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num; 377 378 ret = drm_plane_create_alpha_property(&layer->plane); 379 if (ret) { 380 dev_err(drm->dev, "Couldn't add alpha property\n"); 381 return ERR_PTR(ret); 382 } 383 384 ret = drm_plane_create_zpos_property(&layer->plane, channel, 385 0, plane_cnt - 1); 386 if (ret) { 387 dev_err(drm->dev, "Couldn't add zpos property\n"); 388 return ERR_PTR(ret); 389 } 390 391 drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs); 392 layer->mixer = mixer; 393 layer->channel = channel; 394 layer->overlay = 0; 395 396 return layer; 397 } 398