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