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