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_existing_crtc_state(state, 210 crtc); 211 if (WARN_ON(!crtc_state)) 212 return -EINVAL; 213 214 min_scale = DRM_PLANE_NO_SCALING; 215 max_scale = DRM_PLANE_NO_SCALING; 216 217 if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { 218 min_scale = SUN8I_UI_SCALER_SCALE_MIN; 219 max_scale = SUN8I_UI_SCALER_SCALE_MAX; 220 } 221 222 return drm_atomic_helper_check_plane_state(new_plane_state, 223 crtc_state, 224 min_scale, max_scale, 225 true, true); 226 } 227 228 229 static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, 230 struct drm_atomic_state *state) 231 { 232 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 233 plane); 234 struct sun8i_layer *layer = plane_to_sun8i_layer(plane); 235 unsigned int zpos = new_state->normalized_zpos; 236 struct sun8i_mixer *mixer = layer->mixer; 237 238 if (!new_state->crtc || !new_state->visible) 239 return; 240 241 sun8i_ui_layer_update_coord(mixer, layer->channel, 242 layer->overlay, plane, zpos); 243 sun8i_ui_layer_update_alpha(mixer, layer->channel, 244 layer->overlay, plane); 245 sun8i_ui_layer_update_formats(mixer, layer->channel, 246 layer->overlay, plane); 247 sun8i_ui_layer_update_buffer(mixer, layer->channel, 248 layer->overlay, plane); 249 } 250 251 static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = { 252 .atomic_check = sun8i_ui_layer_atomic_check, 253 .atomic_update = sun8i_ui_layer_atomic_update, 254 }; 255 256 static const struct drm_plane_funcs sun8i_ui_layer_funcs = { 257 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 258 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 259 .destroy = drm_plane_cleanup, 260 .disable_plane = drm_atomic_helper_disable_plane, 261 .reset = drm_atomic_helper_plane_reset, 262 .update_plane = drm_atomic_helper_update_plane, 263 }; 264 265 static const u32 sun8i_ui_layer_formats[] = { 266 DRM_FORMAT_ABGR1555, 267 DRM_FORMAT_ABGR4444, 268 DRM_FORMAT_ABGR8888, 269 DRM_FORMAT_ARGB1555, 270 DRM_FORMAT_ARGB4444, 271 DRM_FORMAT_ARGB8888, 272 DRM_FORMAT_BGR565, 273 DRM_FORMAT_BGR888, 274 DRM_FORMAT_BGRA5551, 275 DRM_FORMAT_BGRA4444, 276 DRM_FORMAT_BGRA8888, 277 DRM_FORMAT_BGRX8888, 278 DRM_FORMAT_RGB565, 279 DRM_FORMAT_RGB888, 280 DRM_FORMAT_RGBA4444, 281 DRM_FORMAT_RGBA5551, 282 DRM_FORMAT_RGBA8888, 283 DRM_FORMAT_RGBX8888, 284 DRM_FORMAT_XBGR8888, 285 DRM_FORMAT_XRGB8888, 286 }; 287 288 static const uint64_t sun8i_layer_modifiers[] = { 289 DRM_FORMAT_MOD_LINEAR, 290 DRM_FORMAT_MOD_INVALID 291 }; 292 293 struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm, 294 struct sun8i_mixer *mixer, 295 int index) 296 { 297 enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; 298 int channel = mixer->cfg->vi_num + index; 299 struct sun8i_layer *layer; 300 unsigned int plane_cnt; 301 int ret; 302 303 layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); 304 if (!layer) 305 return ERR_PTR(-ENOMEM); 306 307 if (index == 0) 308 type = DRM_PLANE_TYPE_PRIMARY; 309 310 /* possible crtcs are set later */ 311 ret = drm_universal_plane_init(drm, &layer->plane, 0, 312 &sun8i_ui_layer_funcs, 313 sun8i_ui_layer_formats, 314 ARRAY_SIZE(sun8i_ui_layer_formats), 315 sun8i_layer_modifiers, type, NULL); 316 if (ret) { 317 dev_err(drm->dev, "Couldn't initialize layer\n"); 318 return ERR_PTR(ret); 319 } 320 321 plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num; 322 323 ret = drm_plane_create_alpha_property(&layer->plane); 324 if (ret) { 325 dev_err(drm->dev, "Couldn't add alpha property\n"); 326 return ERR_PTR(ret); 327 } 328 329 ret = drm_plane_create_zpos_property(&layer->plane, channel, 330 0, plane_cnt - 1); 331 if (ret) { 332 dev_err(drm->dev, "Couldn't add zpos property\n"); 333 return ERR_PTR(ret); 334 } 335 336 drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs); 337 layer->mixer = mixer; 338 layer->type = SUN8I_LAYER_TYPE_UI; 339 layer->channel = channel; 340 layer->overlay = 0; 341 342 return layer; 343 } 344