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