1 /* 2 * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of 7 * the License, or (at your option) any later version. 8 */ 9 10 #include <drm/drm_atomic.h> 11 #include <drm/drm_atomic_helper.h> 12 #include <drm/drm_crtc.h> 13 #include <drm/drm_fb_cma_helper.h> 14 #include <drm/drm_gem_cma_helper.h> 15 #include <drm/drm_gem_framebuffer_helper.h> 16 #include <drm/drm_plane_helper.h> 17 #include <drm/drm_probe_helper.h> 18 #include <drm/drmP.h> 19 20 #include "sun8i_vi_layer.h" 21 #include "sun8i_mixer.h" 22 #include "sun8i_vi_scaler.h" 23 24 static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, 25 int overlay, bool enable, unsigned int zpos, 26 unsigned int old_zpos) 27 { 28 u32 val, bld_base, ch_base; 29 30 bld_base = sun8i_blender_base(mixer); 31 ch_base = sun8i_channel_base(mixer, channel); 32 33 DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n", 34 enable ? "En" : "Dis", channel, overlay); 35 36 if (enable) 37 val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN; 38 else 39 val = 0; 40 41 regmap_update_bits(mixer->engine.regs, 42 SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), 43 SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); 44 45 if (!enable || zpos != old_zpos) { 46 regmap_update_bits(mixer->engine.regs, 47 SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 48 SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), 49 0); 50 51 regmap_update_bits(mixer->engine.regs, 52 SUN8I_MIXER_BLEND_ROUTE(bld_base), 53 SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), 54 0); 55 } 56 57 if (enable) { 58 val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); 59 60 regmap_update_bits(mixer->engine.regs, 61 SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 62 val, val); 63 64 val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos); 65 66 regmap_update_bits(mixer->engine.regs, 67 SUN8I_MIXER_BLEND_ROUTE(bld_base), 68 SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos), 69 val); 70 } 71 } 72 73 static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, 74 int overlay, struct drm_plane *plane, 75 unsigned int zpos) 76 { 77 struct drm_plane_state *state = plane->state; 78 const struct drm_format_info *format = state->fb->format; 79 u32 src_w, src_h, dst_w, dst_h; 80 u32 bld_base, ch_base; 81 u32 outsize, insize; 82 u32 hphase, vphase; 83 bool subsampled; 84 85 DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n", 86 channel, overlay); 87 88 bld_base = sun8i_blender_base(mixer); 89 ch_base = sun8i_channel_base(mixer, channel); 90 91 src_w = drm_rect_width(&state->src) >> 16; 92 src_h = drm_rect_height(&state->src) >> 16; 93 dst_w = drm_rect_width(&state->dst); 94 dst_h = drm_rect_height(&state->dst); 95 96 hphase = state->src.x1 & 0xffff; 97 vphase = state->src.y1 & 0xffff; 98 99 /* make coordinates dividable by subsampling factor */ 100 if (format->hsub > 1) { 101 int mask, remainder; 102 103 mask = format->hsub - 1; 104 remainder = (state->src.x1 >> 16) & mask; 105 src_w = (src_w + remainder) & ~mask; 106 hphase += remainder << 16; 107 } 108 109 if (format->vsub > 1) { 110 int mask, remainder; 111 112 mask = format->vsub - 1; 113 remainder = (state->src.y1 >> 16) & mask; 114 src_h = (src_h + remainder) & ~mask; 115 vphase += remainder << 16; 116 } 117 118 insize = SUN8I_MIXER_SIZE(src_w, src_h); 119 outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); 120 121 /* Set height and width */ 122 DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n", 123 (state->src.x1 >> 16) & ~(format->hsub - 1), 124 (state->src.y1 >> 16) & ~(format->vsub - 1)); 125 DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); 126 regmap_write(mixer->engine.regs, 127 SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay), 128 insize); 129 regmap_write(mixer->engine.regs, 130 SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base), 131 insize); 132 133 /* 134 * Scaler must be enabled for subsampled formats, so it scales 135 * chroma to same size as luma. 136 */ 137 subsampled = format->hsub > 1 || format->vsub > 1; 138 139 if (insize != outsize || subsampled || hphase || vphase) { 140 u32 hscale, vscale; 141 142 DRM_DEBUG_DRIVER("HW scaling is enabled\n"); 143 144 hscale = state->src_w / state->crtc_w; 145 vscale = state->src_h / state->crtc_h; 146 147 sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w, 148 dst_h, hscale, vscale, hphase, vphase, 149 format); 150 sun8i_vi_scaler_enable(mixer, channel, true); 151 } else { 152 DRM_DEBUG_DRIVER("HW scaling is not needed\n"); 153 sun8i_vi_scaler_enable(mixer, channel, false); 154 } 155 156 /* Set base coordinates */ 157 DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", 158 state->dst.x1, state->dst.y1); 159 DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); 160 regmap_write(mixer->engine.regs, 161 SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), 162 SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); 163 regmap_write(mixer->engine.regs, 164 SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), 165 outsize); 166 167 return 0; 168 } 169 170 static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, 171 int overlay, struct drm_plane *plane) 172 { 173 struct drm_plane_state *state = plane->state; 174 const struct de2_fmt_info *fmt_info; 175 u32 val, ch_base; 176 177 ch_base = sun8i_channel_base(mixer, channel); 178 179 fmt_info = sun8i_mixer_format_info(state->fb->format->format); 180 if (!fmt_info) { 181 DRM_DEBUG_DRIVER("Invalid format\n"); 182 return -EINVAL; 183 } 184 185 val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET; 186 regmap_update_bits(mixer->engine.regs, 187 SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), 188 SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val); 189 190 if (fmt_info->csc != SUN8I_CSC_MODE_OFF) { 191 sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_info->csc); 192 sun8i_csc_enable_ccsc(mixer, channel, true); 193 } else { 194 sun8i_csc_enable_ccsc(mixer, channel, false); 195 } 196 197 if (fmt_info->rgb) 198 val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE; 199 else 200 val = 0; 201 202 regmap_update_bits(mixer->engine.regs, 203 SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), 204 SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val); 205 206 /* It seems that YUV formats use global alpha setting. */ 207 if (mixer->cfg->is_de3) 208 regmap_update_bits(mixer->engine.regs, 209 SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, 210 overlay), 211 SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK, 212 SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(0xff)); 213 214 return 0; 215 } 216 217 static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, 218 int overlay, struct drm_plane *plane) 219 { 220 struct drm_plane_state *state = plane->state; 221 struct drm_framebuffer *fb = state->fb; 222 const struct drm_format_info *format = fb->format; 223 struct drm_gem_cma_object *gem; 224 u32 dx, dy, src_x, src_y; 225 dma_addr_t paddr; 226 u32 ch_base; 227 int i; 228 229 ch_base = sun8i_channel_base(mixer, channel); 230 231 /* Adjust x and y to be dividable by subsampling factor */ 232 src_x = (state->src.x1 >> 16) & ~(format->hsub - 1); 233 src_y = (state->src.y1 >> 16) & ~(format->vsub - 1); 234 235 for (i = 0; i < format->num_planes; i++) { 236 /* Get the physical address of the buffer in memory */ 237 gem = drm_fb_cma_get_gem_obj(fb, i); 238 239 DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); 240 241 /* Compute the start of the displayed memory */ 242 paddr = gem->paddr + fb->offsets[i]; 243 244 dx = src_x; 245 dy = src_y; 246 247 if (i > 0) { 248 dx /= format->hsub; 249 dy /= format->vsub; 250 } 251 252 /* Fixup framebuffer address for src coordinates */ 253 paddr += dx * format->cpp[i]; 254 paddr += dy * fb->pitches[i]; 255 256 /* Set the line width */ 257 DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n", 258 i + 1, fb->pitches[i]); 259 regmap_write(mixer->engine.regs, 260 SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base, 261 overlay, i), 262 fb->pitches[i]); 263 264 DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n", 265 i + 1, &paddr); 266 267 regmap_write(mixer->engine.regs, 268 SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base, 269 overlay, i), 270 lower_32_bits(paddr)); 271 } 272 273 return 0; 274 } 275 276 static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, 277 struct drm_plane_state *state) 278 { 279 struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 280 struct drm_crtc *crtc = state->crtc; 281 struct drm_crtc_state *crtc_state; 282 int min_scale, max_scale; 283 284 if (!crtc) 285 return 0; 286 287 crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); 288 if (WARN_ON(!crtc_state)) 289 return -EINVAL; 290 291 min_scale = DRM_PLANE_HELPER_NO_SCALING; 292 max_scale = DRM_PLANE_HELPER_NO_SCALING; 293 294 if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { 295 min_scale = SUN8I_VI_SCALER_SCALE_MIN; 296 max_scale = SUN8I_VI_SCALER_SCALE_MAX; 297 } 298 299 return drm_atomic_helper_check_plane_state(state, crtc_state, 300 min_scale, max_scale, 301 true, true); 302 } 303 304 static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, 305 struct drm_plane_state *old_state) 306 { 307 struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 308 unsigned int old_zpos = old_state->normalized_zpos; 309 struct sun8i_mixer *mixer = layer->mixer; 310 311 sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0, 312 old_zpos); 313 } 314 315 static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, 316 struct drm_plane_state *old_state) 317 { 318 struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 319 unsigned int zpos = plane->state->normalized_zpos; 320 unsigned int old_zpos = old_state->normalized_zpos; 321 struct sun8i_mixer *mixer = layer->mixer; 322 323 if (!plane->state->visible) { 324 sun8i_vi_layer_enable(mixer, layer->channel, 325 layer->overlay, false, 0, old_zpos); 326 return; 327 } 328 329 sun8i_vi_layer_update_coord(mixer, layer->channel, 330 layer->overlay, plane, zpos); 331 sun8i_vi_layer_update_formats(mixer, layer->channel, 332 layer->overlay, plane); 333 sun8i_vi_layer_update_buffer(mixer, layer->channel, 334 layer->overlay, plane); 335 sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, 336 true, zpos, old_zpos); 337 } 338 339 static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { 340 .prepare_fb = drm_gem_fb_prepare_fb, 341 .atomic_check = sun8i_vi_layer_atomic_check, 342 .atomic_disable = sun8i_vi_layer_atomic_disable, 343 .atomic_update = sun8i_vi_layer_atomic_update, 344 }; 345 346 static const struct drm_plane_funcs sun8i_vi_layer_funcs = { 347 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 348 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 349 .destroy = drm_plane_cleanup, 350 .disable_plane = drm_atomic_helper_disable_plane, 351 .reset = drm_atomic_helper_plane_reset, 352 .update_plane = drm_atomic_helper_update_plane, 353 }; 354 355 /* 356 * While all RGB formats are supported, VI planes don't support 357 * alpha blending, so there is no point having formats with alpha 358 * channel if their opaque analog exist. 359 */ 360 static const u32 sun8i_vi_layer_formats[] = { 361 DRM_FORMAT_ABGR1555, 362 DRM_FORMAT_ABGR4444, 363 DRM_FORMAT_ARGB1555, 364 DRM_FORMAT_ARGB4444, 365 DRM_FORMAT_BGR565, 366 DRM_FORMAT_BGR888, 367 DRM_FORMAT_BGRA5551, 368 DRM_FORMAT_BGRA4444, 369 DRM_FORMAT_BGRX8888, 370 DRM_FORMAT_RGB565, 371 DRM_FORMAT_RGB888, 372 DRM_FORMAT_RGBA4444, 373 DRM_FORMAT_RGBA5551, 374 DRM_FORMAT_RGBX8888, 375 DRM_FORMAT_XBGR8888, 376 DRM_FORMAT_XRGB8888, 377 378 DRM_FORMAT_NV16, 379 DRM_FORMAT_NV12, 380 DRM_FORMAT_NV21, 381 DRM_FORMAT_NV61, 382 DRM_FORMAT_UYVY, 383 DRM_FORMAT_VYUY, 384 DRM_FORMAT_YUYV, 385 DRM_FORMAT_YVYU, 386 DRM_FORMAT_YUV411, 387 DRM_FORMAT_YUV420, 388 DRM_FORMAT_YUV422, 389 DRM_FORMAT_YUV444, 390 DRM_FORMAT_YVU411, 391 DRM_FORMAT_YVU420, 392 DRM_FORMAT_YVU422, 393 DRM_FORMAT_YVU444, 394 }; 395 396 struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, 397 struct sun8i_mixer *mixer, 398 int index) 399 { 400 struct sun8i_vi_layer *layer; 401 unsigned int plane_cnt; 402 int ret; 403 404 layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); 405 if (!layer) 406 return ERR_PTR(-ENOMEM); 407 408 /* possible crtcs are set later */ 409 ret = drm_universal_plane_init(drm, &layer->plane, 0, 410 &sun8i_vi_layer_funcs, 411 sun8i_vi_layer_formats, 412 ARRAY_SIZE(sun8i_vi_layer_formats), 413 NULL, DRM_PLANE_TYPE_OVERLAY, NULL); 414 if (ret) { 415 dev_err(drm->dev, "Couldn't initialize layer\n"); 416 return ERR_PTR(ret); 417 } 418 419 plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num; 420 421 ret = drm_plane_create_zpos_property(&layer->plane, index, 422 0, plane_cnt - 1); 423 if (ret) { 424 dev_err(drm->dev, "Couldn't add zpos property\n"); 425 return ERR_PTR(ret); 426 } 427 428 drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs); 429 layer->mixer = mixer; 430 layer->channel = index; 431 layer->overlay = 0; 432 433 return layer; 434 } 435