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