Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.6-rc5 501 lines 13 kB view raw
1/* 2 * i.MX IPUv3 DP Overlay Planes 3 * 4 * Copyright (C) 2013 Philipp Zabel, Pengutronix 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 2 9 * of the License, or (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16#include <drm/drmP.h> 17#include <drm/drm_fb_cma_helper.h> 18#include <drm/drm_gem_cma_helper.h> 19 20#include "video/imx-ipu-v3.h" 21#include "ipuv3-plane.h" 22 23#define to_ipu_plane(x) container_of(x, struct ipu_plane, base) 24 25static const uint32_t ipu_plane_formats[] = { 26 DRM_FORMAT_ARGB1555, 27 DRM_FORMAT_XRGB1555, 28 DRM_FORMAT_ABGR1555, 29 DRM_FORMAT_XBGR1555, 30 DRM_FORMAT_RGBA5551, 31 DRM_FORMAT_BGRA5551, 32 DRM_FORMAT_ARGB4444, 33 DRM_FORMAT_ARGB8888, 34 DRM_FORMAT_XRGB8888, 35 DRM_FORMAT_ABGR8888, 36 DRM_FORMAT_XBGR8888, 37 DRM_FORMAT_RGBA8888, 38 DRM_FORMAT_RGBX8888, 39 DRM_FORMAT_BGRA8888, 40 DRM_FORMAT_BGRA8888, 41 DRM_FORMAT_YUYV, 42 DRM_FORMAT_YVYU, 43 DRM_FORMAT_YUV420, 44 DRM_FORMAT_YVU420, 45 DRM_FORMAT_RGB565, 46}; 47 48int ipu_plane_irq(struct ipu_plane *ipu_plane) 49{ 50 return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch, 51 IPU_IRQ_EOF); 52} 53 54static int calc_vref(struct drm_display_mode *mode) 55{ 56 unsigned long htotal, vtotal; 57 58 htotal = mode->htotal; 59 vtotal = mode->vtotal; 60 61 if (!htotal || !vtotal) 62 return 60; 63 64 return DIV_ROUND_UP(mode->clock * 1000, vtotal * htotal); 65} 66 67static inline int calc_bandwidth(int width, int height, unsigned int vref) 68{ 69 return width * height * vref; 70} 71 72int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb, 73 int x, int y) 74{ 75 struct drm_gem_cma_object *cma_obj[3]; 76 unsigned long eba, ubo, vbo; 77 int active, i; 78 79 for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { 80 cma_obj[i] = drm_fb_cma_get_gem_obj(fb, i); 81 if (!cma_obj[i]) { 82 DRM_DEBUG_KMS("plane %d entry is null.\n", i); 83 return -EFAULT; 84 } 85 } 86 87 eba = cma_obj[0]->paddr + fb->offsets[0] + 88 fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x; 89 90 if (eba & 0x7) { 91 DRM_DEBUG_KMS("base address must be a multiple of 8.\n"); 92 return -EINVAL; 93 } 94 95 if (fb->pitches[0] < 1 || fb->pitches[0] > 16384) { 96 DRM_DEBUG_KMS("pitches out of range.\n"); 97 return -EINVAL; 98 } 99 100 if (ipu_plane->enabled && fb->pitches[0] != ipu_plane->stride[0]) { 101 DRM_DEBUG_KMS("pitches must not change while plane is enabled.\n"); 102 return -EINVAL; 103 } 104 105 ipu_plane->stride[0] = fb->pitches[0]; 106 107 switch (fb->pixel_format) { 108 case DRM_FORMAT_YUV420: 109 case DRM_FORMAT_YVU420: 110 /* 111 * Multiplanar formats have to meet the following restrictions: 112 * - The (up to) three plane addresses are EBA, EBA+UBO, EBA+VBO 113 * - EBA, UBO and VBO are a multiple of 8 114 * - UBO and VBO are unsigned and not larger than 0xfffff8 115 * - Only EBA may be changed while scanout is active 116 * - The strides of U and V planes must be identical. 117 */ 118 ubo = cma_obj[1]->paddr + fb->offsets[1] + 119 fb->pitches[1] * y / 2 + x / 2 - eba; 120 vbo = cma_obj[2]->paddr + fb->offsets[2] + 121 fb->pitches[2] * y / 2 + x / 2 - eba; 122 123 if ((ubo & 0x7) || (vbo & 0x7)) { 124 DRM_DEBUG_KMS("U/V buffer offsets must be a multiple of 8.\n"); 125 return -EINVAL; 126 } 127 128 if ((ubo > 0xfffff8) || (vbo > 0xfffff8)) { 129 DRM_DEBUG_KMS("U/V buffer offsets must be positive and not larger than 0xfffff8.\n"); 130 return -EINVAL; 131 } 132 133 if (ipu_plane->enabled && ((ipu_plane->u_offset != ubo) || 134 (ipu_plane->v_offset != vbo))) { 135 DRM_DEBUG_KMS("U/V buffer offsets must not change while plane is enabled.\n"); 136 return -EINVAL; 137 } 138 139 if (fb->pitches[1] != fb->pitches[2]) { 140 DRM_DEBUG_KMS("U/V pitches must be identical.\n"); 141 return -EINVAL; 142 } 143 144 if (fb->pitches[1] < 1 || fb->pitches[1] > 16384) { 145 DRM_DEBUG_KMS("U/V pitches out of range.\n"); 146 return -EINVAL; 147 } 148 149 if (ipu_plane->enabled && 150 (ipu_plane->stride[1] != fb->pitches[1])) { 151 DRM_DEBUG_KMS("U/V pitches must not change while plane is enabled.\n"); 152 return -EINVAL; 153 } 154 155 ipu_plane->u_offset = ubo; 156 ipu_plane->v_offset = vbo; 157 ipu_plane->stride[1] = fb->pitches[1]; 158 159 dev_dbg(ipu_plane->base.dev->dev, 160 "phys = %pad %pad %pad, x = %d, y = %d", 161 &cma_obj[0]->paddr, &cma_obj[1]->paddr, 162 &cma_obj[2]->paddr, x, y); 163 break; 164 default: 165 dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d", 166 &cma_obj[0]->paddr, x, y); 167 break; 168 } 169 170 if (ipu_plane->enabled) { 171 active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch); 172 ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba); 173 ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active); 174 } else { 175 ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba); 176 ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba); 177 } 178 179 /* cache offsets for subsequent pageflips */ 180 ipu_plane->x = x; 181 ipu_plane->y = y; 182 183 return 0; 184} 185 186int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc, 187 struct drm_display_mode *mode, 188 struct drm_framebuffer *fb, int crtc_x, int crtc_y, 189 unsigned int crtc_w, unsigned int crtc_h, 190 uint32_t src_x, uint32_t src_y, 191 uint32_t src_w, uint32_t src_h, bool interlaced) 192{ 193 struct device *dev = ipu_plane->base.dev->dev; 194 int ret; 195 196 /* no scaling */ 197 if (src_w != crtc_w || src_h != crtc_h) 198 return -EINVAL; 199 200 /* clip to crtc bounds */ 201 if (crtc_x < 0) { 202 if (-crtc_x > crtc_w) 203 return -EINVAL; 204 src_x += -crtc_x; 205 src_w -= -crtc_x; 206 crtc_w -= -crtc_x; 207 crtc_x = 0; 208 } 209 if (crtc_y < 0) { 210 if (-crtc_y > crtc_h) 211 return -EINVAL; 212 src_y += -crtc_y; 213 src_h -= -crtc_y; 214 crtc_h -= -crtc_y; 215 crtc_y = 0; 216 } 217 if (crtc_x + crtc_w > mode->hdisplay) { 218 if (crtc_x > mode->hdisplay) 219 return -EINVAL; 220 crtc_w = mode->hdisplay - crtc_x; 221 src_w = crtc_w; 222 } 223 if (crtc_y + crtc_h > mode->vdisplay) { 224 if (crtc_y > mode->vdisplay) 225 return -EINVAL; 226 crtc_h = mode->vdisplay - crtc_y; 227 src_h = crtc_h; 228 } 229 /* full plane minimum width is 13 pixels */ 230 if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG)) 231 return -EINVAL; 232 if (crtc_h < 2) 233 return -EINVAL; 234 235 /* 236 * since we cannot touch active IDMAC channels, we do not support 237 * resizing the enabled plane or changing its format 238 */ 239 if (ipu_plane->enabled) { 240 if (src_w != ipu_plane->w || src_h != ipu_plane->h || 241 fb->pixel_format != ipu_plane->base.fb->pixel_format) 242 return -EINVAL; 243 244 return ipu_plane_set_base(ipu_plane, fb, src_x, src_y); 245 } 246 247 switch (ipu_plane->dp_flow) { 248 case IPU_DP_FLOW_SYNC_BG: 249 ret = ipu_dp_setup_channel(ipu_plane->dp, 250 IPUV3_COLORSPACE_RGB, 251 IPUV3_COLORSPACE_RGB); 252 if (ret) { 253 dev_err(dev, 254 "initializing display processor failed with %d\n", 255 ret); 256 return ret; 257 } 258 ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true); 259 break; 260 case IPU_DP_FLOW_SYNC_FG: 261 ipu_dp_setup_channel(ipu_plane->dp, 262 ipu_drm_fourcc_to_colorspace(fb->pixel_format), 263 IPUV3_COLORSPACE_UNKNOWN); 264 ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y); 265 /* Enable local alpha on partial plane */ 266 switch (fb->pixel_format) { 267 case DRM_FORMAT_ARGB1555: 268 case DRM_FORMAT_ABGR1555: 269 case DRM_FORMAT_RGBA5551: 270 case DRM_FORMAT_BGRA5551: 271 case DRM_FORMAT_ARGB4444: 272 case DRM_FORMAT_ARGB8888: 273 case DRM_FORMAT_ABGR8888: 274 case DRM_FORMAT_RGBA8888: 275 case DRM_FORMAT_BGRA8888: 276 ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false); 277 break; 278 default: 279 break; 280 } 281 } 282 283 ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc, 284 calc_bandwidth(crtc_w, crtc_h, 285 calc_vref(mode)), 64); 286 if (ret) { 287 dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret); 288 return ret; 289 } 290 291 ipu_dmfc_config_wait4eot(ipu_plane->dmfc, crtc_w); 292 293 ipu_cpmem_zero(ipu_plane->ipu_ch); 294 ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h); 295 ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format); 296 if (ret < 0) { 297 dev_err(dev, "unsupported pixel format 0x%08x\n", 298 fb->pixel_format); 299 return ret; 300 } 301 ipu_cpmem_set_high_priority(ipu_plane->ipu_ch); 302 ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1); 303 ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]); 304 305 ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y); 306 if (ret < 0) 307 return ret; 308 if (interlaced) 309 ipu_cpmem_interlaced_scan(ipu_plane->ipu_ch, fb->pitches[0]); 310 311 if (fb->pixel_format == DRM_FORMAT_YUV420) { 312 ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch, 313 ipu_plane->stride[1], 314 ipu_plane->u_offset, 315 ipu_plane->v_offset); 316 } else if (fb->pixel_format == DRM_FORMAT_YVU420) { 317 ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch, 318 ipu_plane->stride[1], 319 ipu_plane->v_offset, 320 ipu_plane->u_offset); 321 } 322 323 ipu_plane->w = src_w; 324 ipu_plane->h = src_h; 325 326 return 0; 327} 328 329void ipu_plane_put_resources(struct ipu_plane *ipu_plane) 330{ 331 if (!IS_ERR_OR_NULL(ipu_plane->dp)) 332 ipu_dp_put(ipu_plane->dp); 333 if (!IS_ERR_OR_NULL(ipu_plane->dmfc)) 334 ipu_dmfc_put(ipu_plane->dmfc); 335 if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch)) 336 ipu_idmac_put(ipu_plane->ipu_ch); 337} 338 339int ipu_plane_get_resources(struct ipu_plane *ipu_plane) 340{ 341 int ret; 342 343 ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma); 344 if (IS_ERR(ipu_plane->ipu_ch)) { 345 ret = PTR_ERR(ipu_plane->ipu_ch); 346 DRM_ERROR("failed to get idmac channel: %d\n", ret); 347 return ret; 348 } 349 350 ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma); 351 if (IS_ERR(ipu_plane->dmfc)) { 352 ret = PTR_ERR(ipu_plane->dmfc); 353 DRM_ERROR("failed to get dmfc: ret %d\n", ret); 354 goto err_out; 355 } 356 357 if (ipu_plane->dp_flow >= 0) { 358 ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow); 359 if (IS_ERR(ipu_plane->dp)) { 360 ret = PTR_ERR(ipu_plane->dp); 361 DRM_ERROR("failed to get dp flow: %d\n", ret); 362 goto err_out; 363 } 364 } 365 366 return 0; 367err_out: 368 ipu_plane_put_resources(ipu_plane); 369 370 return ret; 371} 372 373void ipu_plane_enable(struct ipu_plane *ipu_plane) 374{ 375 if (ipu_plane->dp) 376 ipu_dp_enable(ipu_plane->ipu); 377 ipu_dmfc_enable_channel(ipu_plane->dmfc); 378 ipu_idmac_enable_channel(ipu_plane->ipu_ch); 379 if (ipu_plane->dp) 380 ipu_dp_enable_channel(ipu_plane->dp); 381 382 ipu_plane->enabled = true; 383} 384 385void ipu_plane_disable(struct ipu_plane *ipu_plane) 386{ 387 ipu_plane->enabled = false; 388 389 ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50); 390 391 if (ipu_plane->dp) 392 ipu_dp_disable_channel(ipu_plane->dp); 393 ipu_idmac_disable_channel(ipu_plane->ipu_ch); 394 ipu_dmfc_disable_channel(ipu_plane->dmfc); 395 if (ipu_plane->dp) 396 ipu_dp_disable(ipu_plane->ipu); 397} 398 399/* 400 * drm_plane API 401 */ 402 403static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, 404 struct drm_framebuffer *fb, int crtc_x, int crtc_y, 405 unsigned int crtc_w, unsigned int crtc_h, 406 uint32_t src_x, uint32_t src_y, 407 uint32_t src_w, uint32_t src_h) 408{ 409 struct ipu_plane *ipu_plane = to_ipu_plane(plane); 410 int ret = 0; 411 412 DRM_DEBUG_KMS("plane - %p\n", plane); 413 414 if (!ipu_plane->enabled) 415 ret = ipu_plane_get_resources(ipu_plane); 416 if (ret < 0) 417 return ret; 418 419 ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb, 420 crtc_x, crtc_y, crtc_w, crtc_h, 421 src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16, 422 false); 423 if (ret < 0) { 424 ipu_plane_put_resources(ipu_plane); 425 return ret; 426 } 427 428 if (crtc != plane->crtc) 429 dev_dbg(plane->dev->dev, "crtc change: %p -> %p\n", 430 plane->crtc, crtc); 431 plane->crtc = crtc; 432 433 if (!ipu_plane->enabled) 434 ipu_plane_enable(ipu_plane); 435 436 return 0; 437} 438 439static int ipu_disable_plane(struct drm_plane *plane) 440{ 441 struct ipu_plane *ipu_plane = to_ipu_plane(plane); 442 443 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); 444 445 if (ipu_plane->enabled) 446 ipu_plane_disable(ipu_plane); 447 448 ipu_plane_put_resources(ipu_plane); 449 450 return 0; 451} 452 453static void ipu_plane_destroy(struct drm_plane *plane) 454{ 455 struct ipu_plane *ipu_plane = to_ipu_plane(plane); 456 457 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); 458 459 ipu_disable_plane(plane); 460 drm_plane_cleanup(plane); 461 kfree(ipu_plane); 462} 463 464static struct drm_plane_funcs ipu_plane_funcs = { 465 .update_plane = ipu_update_plane, 466 .disable_plane = ipu_disable_plane, 467 .destroy = ipu_plane_destroy, 468}; 469 470struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, 471 int dma, int dp, unsigned int possible_crtcs, 472 enum drm_plane_type type) 473{ 474 struct ipu_plane *ipu_plane; 475 int ret; 476 477 DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n", 478 dma, dp, possible_crtcs); 479 480 ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL); 481 if (!ipu_plane) { 482 DRM_ERROR("failed to allocate plane\n"); 483 return ERR_PTR(-ENOMEM); 484 } 485 486 ipu_plane->ipu = ipu; 487 ipu_plane->dma = dma; 488 ipu_plane->dp_flow = dp; 489 490 ret = drm_universal_plane_init(dev, &ipu_plane->base, possible_crtcs, 491 &ipu_plane_funcs, ipu_plane_formats, 492 ARRAY_SIZE(ipu_plane_formats), type, 493 NULL); 494 if (ret) { 495 DRM_ERROR("failed to initialize plane\n"); 496 kfree(ipu_plane); 497 return ERR_PTR(ret); 498 } 499 500 return ipu_plane; 501}