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