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