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_XRGB1555, 27 DRM_FORMAT_XBGR1555, 28 DRM_FORMAT_ARGB8888, 29 DRM_FORMAT_XRGB8888, 30 DRM_FORMAT_ABGR8888, 31 DRM_FORMAT_XBGR8888, 32 DRM_FORMAT_YUYV, 33 DRM_FORMAT_YVYU, 34 DRM_FORMAT_YUV420, 35 DRM_FORMAT_YVU420, 36}; 37 38int ipu_plane_irq(struct ipu_plane *ipu_plane) 39{ 40 return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch, 41 IPU_IRQ_EOF); 42} 43 44static int calc_vref(struct drm_display_mode *mode) 45{ 46 unsigned long htotal, vtotal; 47 48 htotal = mode->htotal; 49 vtotal = mode->vtotal; 50 51 if (!htotal || !vtotal) 52 return 60; 53 54 return DIV_ROUND_UP(mode->clock * 1000, vtotal * htotal); 55} 56 57static inline int calc_bandwidth(int width, int height, unsigned int vref) 58{ 59 return width * height * vref; 60} 61 62int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb, 63 int x, int y) 64{ 65 struct drm_gem_cma_object *cma_obj; 66 unsigned long eba; 67 int active; 68 69 cma_obj = drm_fb_cma_get_gem_obj(fb, 0); 70 if (!cma_obj) { 71 DRM_DEBUG_KMS("entry is null.\n"); 72 return -EFAULT; 73 } 74 75 dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d", 76 &cma_obj->paddr, x, y); 77 78 eba = cma_obj->paddr + fb->offsets[0] + 79 fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x; 80 81 if (ipu_plane->enabled) { 82 active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch); 83 ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba); 84 ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active); 85 } else { 86 ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba); 87 ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba); 88 } 89 90 /* cache offsets for subsequent pageflips */ 91 ipu_plane->x = x; 92 ipu_plane->y = y; 93 94 return 0; 95} 96 97int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc, 98 struct drm_display_mode *mode, 99 struct drm_framebuffer *fb, int crtc_x, int crtc_y, 100 unsigned int crtc_w, unsigned int crtc_h, 101 uint32_t src_x, uint32_t src_y, 102 uint32_t src_w, uint32_t src_h, bool interlaced) 103{ 104 struct device *dev = ipu_plane->base.dev->dev; 105 int ret; 106 107 /* no scaling */ 108 if (src_w != crtc_w || src_h != crtc_h) 109 return -EINVAL; 110 111 /* clip to crtc bounds */ 112 if (crtc_x < 0) { 113 if (-crtc_x > crtc_w) 114 return -EINVAL; 115 src_x += -crtc_x; 116 src_w -= -crtc_x; 117 crtc_w -= -crtc_x; 118 crtc_x = 0; 119 } 120 if (crtc_y < 0) { 121 if (-crtc_y > crtc_h) 122 return -EINVAL; 123 src_y += -crtc_y; 124 src_h -= -crtc_y; 125 crtc_h -= -crtc_y; 126 crtc_y = 0; 127 } 128 if (crtc_x + crtc_w > mode->hdisplay) { 129 if (crtc_x > mode->hdisplay) 130 return -EINVAL; 131 crtc_w = mode->hdisplay - crtc_x; 132 src_w = crtc_w; 133 } 134 if (crtc_y + crtc_h > mode->vdisplay) { 135 if (crtc_y > mode->vdisplay) 136 return -EINVAL; 137 crtc_h = mode->vdisplay - crtc_y; 138 src_h = crtc_h; 139 } 140 /* full plane minimum width is 13 pixels */ 141 if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG)) 142 return -EINVAL; 143 if (crtc_h < 2) 144 return -EINVAL; 145 146 /* 147 * since we cannot touch active IDMAC channels, we do not support 148 * resizing the enabled plane or changing its format 149 */ 150 if (ipu_plane->enabled) { 151 if (src_w != ipu_plane->w || src_h != ipu_plane->h || 152 fb->pixel_format != ipu_plane->base.fb->pixel_format) 153 return -EINVAL; 154 155 return ipu_plane_set_base(ipu_plane, fb, src_x, src_y); 156 } 157 158 switch (ipu_plane->dp_flow) { 159 case IPU_DP_FLOW_SYNC_BG: 160 ret = ipu_dp_setup_channel(ipu_plane->dp, 161 IPUV3_COLORSPACE_RGB, 162 IPUV3_COLORSPACE_RGB); 163 if (ret) { 164 dev_err(dev, 165 "initializing display processor failed with %d\n", 166 ret); 167 return ret; 168 } 169 ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true); 170 break; 171 case IPU_DP_FLOW_SYNC_FG: 172 ipu_dp_setup_channel(ipu_plane->dp, 173 ipu_drm_fourcc_to_colorspace(fb->pixel_format), 174 IPUV3_COLORSPACE_UNKNOWN); 175 ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y); 176 /* Enable local alpha on partial plane */ 177 switch (fb->pixel_format) { 178 case DRM_FORMAT_ARGB8888: 179 case DRM_FORMAT_ABGR8888: 180 ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false); 181 break; 182 default: 183 break; 184 } 185 } 186 187 ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w); 188 if (ret) { 189 dev_err(dev, "initializing dmfc channel failed with %d\n", ret); 190 return ret; 191 } 192 193 ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc, 194 calc_bandwidth(crtc_w, crtc_h, 195 calc_vref(mode)), 64); 196 if (ret) { 197 dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret); 198 return ret; 199 } 200 201 ipu_cpmem_zero(ipu_plane->ipu_ch); 202 ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h); 203 ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format); 204 if (ret < 0) { 205 dev_err(dev, "unsupported pixel format 0x%08x\n", 206 fb->pixel_format); 207 return ret; 208 } 209 ipu_cpmem_set_high_priority(ipu_plane->ipu_ch); 210 ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1); 211 ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]); 212 213 ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y); 214 if (ret < 0) 215 return ret; 216 if (interlaced) 217 ipu_cpmem_interlaced_scan(ipu_plane->ipu_ch, fb->pitches[0]); 218 219 ipu_plane->w = src_w; 220 ipu_plane->h = src_h; 221 222 return 0; 223} 224 225void ipu_plane_put_resources(struct ipu_plane *ipu_plane) 226{ 227 if (!IS_ERR_OR_NULL(ipu_plane->dp)) 228 ipu_dp_put(ipu_plane->dp); 229 if (!IS_ERR_OR_NULL(ipu_plane->dmfc)) 230 ipu_dmfc_put(ipu_plane->dmfc); 231 if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch)) 232 ipu_idmac_put(ipu_plane->ipu_ch); 233} 234 235int ipu_plane_get_resources(struct ipu_plane *ipu_plane) 236{ 237 int ret; 238 239 ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma); 240 if (IS_ERR(ipu_plane->ipu_ch)) { 241 ret = PTR_ERR(ipu_plane->ipu_ch); 242 DRM_ERROR("failed to get idmac channel: %d\n", ret); 243 return ret; 244 } 245 246 ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma); 247 if (IS_ERR(ipu_plane->dmfc)) { 248 ret = PTR_ERR(ipu_plane->dmfc); 249 DRM_ERROR("failed to get dmfc: ret %d\n", ret); 250 goto err_out; 251 } 252 253 if (ipu_plane->dp_flow >= 0) { 254 ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow); 255 if (IS_ERR(ipu_plane->dp)) { 256 ret = PTR_ERR(ipu_plane->dp); 257 DRM_ERROR("failed to get dp flow: %d\n", ret); 258 goto err_out; 259 } 260 } 261 262 return 0; 263err_out: 264 ipu_plane_put_resources(ipu_plane); 265 266 return ret; 267} 268 269void ipu_plane_enable(struct ipu_plane *ipu_plane) 270{ 271 if (ipu_plane->dp) 272 ipu_dp_enable(ipu_plane->ipu); 273 ipu_dmfc_enable_channel(ipu_plane->dmfc); 274 ipu_idmac_enable_channel(ipu_plane->ipu_ch); 275 if (ipu_plane->dp) 276 ipu_dp_enable_channel(ipu_plane->dp); 277 278 ipu_plane->enabled = true; 279} 280 281void ipu_plane_disable(struct ipu_plane *ipu_plane) 282{ 283 ipu_plane->enabled = false; 284 285 ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50); 286 287 if (ipu_plane->dp) 288 ipu_dp_disable_channel(ipu_plane->dp); 289 ipu_idmac_disable_channel(ipu_plane->ipu_ch); 290 ipu_dmfc_disable_channel(ipu_plane->dmfc); 291 if (ipu_plane->dp) 292 ipu_dp_disable(ipu_plane->ipu); 293} 294 295/* 296 * drm_plane API 297 */ 298 299static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, 300 struct drm_framebuffer *fb, int crtc_x, int crtc_y, 301 unsigned int crtc_w, unsigned int crtc_h, 302 uint32_t src_x, uint32_t src_y, 303 uint32_t src_w, uint32_t src_h) 304{ 305 struct ipu_plane *ipu_plane = to_ipu_plane(plane); 306 int ret = 0; 307 308 DRM_DEBUG_KMS("plane - %p\n", plane); 309 310 if (!ipu_plane->enabled) 311 ret = ipu_plane_get_resources(ipu_plane); 312 if (ret < 0) 313 return ret; 314 315 ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb, 316 crtc_x, crtc_y, crtc_w, crtc_h, 317 src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16, 318 false); 319 if (ret < 0) { 320 ipu_plane_put_resources(ipu_plane); 321 return ret; 322 } 323 324 if (crtc != plane->crtc) 325 dev_info(plane->dev->dev, "crtc change: %p -> %p\n", 326 plane->crtc, crtc); 327 plane->crtc = crtc; 328 329 if (!ipu_plane->enabled) 330 ipu_plane_enable(ipu_plane); 331 332 return 0; 333} 334 335static int ipu_disable_plane(struct drm_plane *plane) 336{ 337 struct ipu_plane *ipu_plane = to_ipu_plane(plane); 338 339 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); 340 341 if (ipu_plane->enabled) 342 ipu_plane_disable(ipu_plane); 343 344 ipu_plane_put_resources(ipu_plane); 345 346 return 0; 347} 348 349static void ipu_plane_destroy(struct drm_plane *plane) 350{ 351 struct ipu_plane *ipu_plane = to_ipu_plane(plane); 352 353 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); 354 355 ipu_disable_plane(plane); 356 drm_plane_cleanup(plane); 357 kfree(ipu_plane); 358} 359 360static struct drm_plane_funcs ipu_plane_funcs = { 361 .update_plane = ipu_update_plane, 362 .disable_plane = ipu_disable_plane, 363 .destroy = ipu_plane_destroy, 364}; 365 366struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, 367 int dma, int dp, unsigned int possible_crtcs, 368 bool priv) 369{ 370 struct ipu_plane *ipu_plane; 371 int ret; 372 373 DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n", 374 dma, dp, possible_crtcs); 375 376 ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL); 377 if (!ipu_plane) { 378 DRM_ERROR("failed to allocate plane\n"); 379 return ERR_PTR(-ENOMEM); 380 } 381 382 ipu_plane->ipu = ipu; 383 ipu_plane->dma = dma; 384 ipu_plane->dp_flow = dp; 385 386 ret = drm_plane_init(dev, &ipu_plane->base, possible_crtcs, 387 &ipu_plane_funcs, ipu_plane_formats, 388 ARRAY_SIZE(ipu_plane_formats), 389 priv); 390 if (ret) { 391 DRM_ERROR("failed to initialize plane\n"); 392 kfree(ipu_plane); 393 return ERR_PTR(ret); 394 } 395 396 return ipu_plane; 397} 398