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