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-rc5 453 lines 11 kB view raw
1/* 2 * i.MX IPUv3 Graphics driver 3 * 4 * Copyright (C) 2011 Sascha Hauer, 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 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 18 * MA 02110-1301, USA. 19 */ 20#include <linux/module.h> 21#include <linux/export.h> 22#include <linux/device.h> 23#include <linux/platform_device.h> 24#include <drm/drmP.h> 25#include <drm/drm_crtc_helper.h> 26#include <linux/fb.h> 27#include <linux/clk.h> 28#include <linux/errno.h> 29#include <drm/drm_gem_cma_helper.h> 30#include <drm/drm_fb_cma_helper.h> 31 32#include "ipu-v3/imx-ipu-v3.h" 33#include "imx-drm.h" 34#include "ipuv3-plane.h" 35 36#define DRIVER_DESC "i.MX IPUv3 Graphics" 37 38struct ipu_crtc { 39 struct device *dev; 40 struct drm_crtc base; 41 struct imx_drm_crtc *imx_crtc; 42 43 /* plane[0] is the full plane, plane[1] is the partial plane */ 44 struct ipu_plane *plane[2]; 45 46 struct ipu_dc *dc; 47 struct ipu_di *di; 48 int enabled; 49 struct drm_pending_vblank_event *page_flip_event; 50 struct drm_framebuffer *newfb; 51 int irq; 52 u32 interface_pix_fmt; 53 unsigned long di_clkflags; 54 int di_hsync_pin; 55 int di_vsync_pin; 56}; 57 58#define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base) 59 60static void ipu_fb_enable(struct ipu_crtc *ipu_crtc) 61{ 62 if (ipu_crtc->enabled) 63 return; 64 65 ipu_di_enable(ipu_crtc->di); 66 ipu_dc_enable_channel(ipu_crtc->dc); 67 ipu_plane_enable(ipu_crtc->plane[0]); 68 69 ipu_crtc->enabled = 1; 70} 71 72static void ipu_fb_disable(struct ipu_crtc *ipu_crtc) 73{ 74 if (!ipu_crtc->enabled) 75 return; 76 77 ipu_plane_disable(ipu_crtc->plane[0]); 78 ipu_dc_disable_channel(ipu_crtc->dc); 79 ipu_di_disable(ipu_crtc->di); 80 81 ipu_crtc->enabled = 0; 82} 83 84static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode) 85{ 86 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 87 88 dev_dbg(ipu_crtc->dev, "%s mode: %d\n", __func__, mode); 89 90 switch (mode) { 91 case DRM_MODE_DPMS_ON: 92 ipu_fb_enable(ipu_crtc); 93 break; 94 case DRM_MODE_DPMS_STANDBY: 95 case DRM_MODE_DPMS_SUSPEND: 96 case DRM_MODE_DPMS_OFF: 97 ipu_fb_disable(ipu_crtc); 98 break; 99 } 100} 101 102static int ipu_page_flip(struct drm_crtc *crtc, 103 struct drm_framebuffer *fb, 104 struct drm_pending_vblank_event *event, 105 uint32_t page_flip_flags) 106{ 107 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 108 int ret; 109 110 if (ipu_crtc->newfb) 111 return -EBUSY; 112 113 ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc); 114 if (ret) { 115 dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n"); 116 list_del(&event->base.link); 117 118 return ret; 119 } 120 121 ipu_crtc->newfb = fb; 122 ipu_crtc->page_flip_event = event; 123 crtc->fb = fb; 124 125 return 0; 126} 127 128static const struct drm_crtc_funcs ipu_crtc_funcs = { 129 .set_config = drm_crtc_helper_set_config, 130 .destroy = drm_crtc_cleanup, 131 .page_flip = ipu_page_flip, 132}; 133 134static int ipu_crtc_mode_set(struct drm_crtc *crtc, 135 struct drm_display_mode *orig_mode, 136 struct drm_display_mode *mode, 137 int x, int y, 138 struct drm_framebuffer *old_fb) 139{ 140 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 141 int ret; 142 struct ipu_di_signal_cfg sig_cfg = {}; 143 u32 out_pixel_fmt; 144 145 dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, 146 mode->hdisplay); 147 dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__, 148 mode->vdisplay); 149 150 out_pixel_fmt = ipu_crtc->interface_pix_fmt; 151 152 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 153 sig_cfg.interlaced = 1; 154 if (mode->flags & DRM_MODE_FLAG_PHSYNC) 155 sig_cfg.Hsync_pol = 1; 156 if (mode->flags & DRM_MODE_FLAG_PVSYNC) 157 sig_cfg.Vsync_pol = 1; 158 159 sig_cfg.enable_pol = 1; 160 sig_cfg.clk_pol = 1; 161 sig_cfg.width = mode->hdisplay; 162 sig_cfg.height = mode->vdisplay; 163 sig_cfg.pixel_fmt = out_pixel_fmt; 164 sig_cfg.h_start_width = mode->htotal - mode->hsync_end; 165 sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start; 166 sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay; 167 168 sig_cfg.v_start_width = mode->vtotal - mode->vsync_end; 169 sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start; 170 sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay; 171 sig_cfg.pixelclock = mode->clock * 1000; 172 sig_cfg.clkflags = ipu_crtc->di_clkflags; 173 174 sig_cfg.v_to_h_sync = 0; 175 176 sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin; 177 sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin; 178 179 ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced, 180 out_pixel_fmt, mode->hdisplay); 181 if (ret) { 182 dev_err(ipu_crtc->dev, 183 "initializing display controller failed with %d\n", 184 ret); 185 return ret; 186 } 187 188 ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg); 189 if (ret) { 190 dev_err(ipu_crtc->dev, 191 "initializing panel failed with %d\n", ret); 192 return ret; 193 } 194 195 return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode, crtc->fb, 196 0, 0, mode->hdisplay, mode->vdisplay, 197 x, y, mode->hdisplay, mode->vdisplay); 198} 199 200static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc) 201{ 202 unsigned long flags; 203 struct drm_device *drm = ipu_crtc->base.dev; 204 205 spin_lock_irqsave(&drm->event_lock, flags); 206 if (ipu_crtc->page_flip_event) 207 drm_send_vblank_event(drm, -1, ipu_crtc->page_flip_event); 208 ipu_crtc->page_flip_event = NULL; 209 imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); 210 spin_unlock_irqrestore(&drm->event_lock, flags); 211} 212 213static irqreturn_t ipu_irq_handler(int irq, void *dev_id) 214{ 215 struct ipu_crtc *ipu_crtc = dev_id; 216 217 imx_drm_handle_vblank(ipu_crtc->imx_crtc); 218 219 if (ipu_crtc->newfb) { 220 ipu_crtc->newfb = NULL; 221 ipu_plane_set_base(ipu_crtc->plane[0], ipu_crtc->base.fb, 0, 0); 222 ipu_crtc_handle_pageflip(ipu_crtc); 223 } 224 225 return IRQ_HANDLED; 226} 227 228static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc, 229 const struct drm_display_mode *mode, 230 struct drm_display_mode *adjusted_mode) 231{ 232 return true; 233} 234 235static void ipu_crtc_prepare(struct drm_crtc *crtc) 236{ 237 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 238 239 ipu_fb_disable(ipu_crtc); 240} 241 242static void ipu_crtc_commit(struct drm_crtc *crtc) 243{ 244 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 245 246 ipu_fb_enable(ipu_crtc); 247} 248 249static struct drm_crtc_helper_funcs ipu_helper_funcs = { 250 .dpms = ipu_crtc_dpms, 251 .mode_fixup = ipu_crtc_mode_fixup, 252 .mode_set = ipu_crtc_mode_set, 253 .prepare = ipu_crtc_prepare, 254 .commit = ipu_crtc_commit, 255}; 256 257static int ipu_enable_vblank(struct drm_crtc *crtc) 258{ 259 return 0; 260} 261 262static void ipu_disable_vblank(struct drm_crtc *crtc) 263{ 264 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 265 266 ipu_crtc->page_flip_event = NULL; 267 ipu_crtc->newfb = NULL; 268} 269 270static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, 271 u32 pixfmt, int hsync_pin, int vsync_pin) 272{ 273 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 274 275 ipu_crtc->interface_pix_fmt = pixfmt; 276 ipu_crtc->di_hsync_pin = hsync_pin; 277 ipu_crtc->di_vsync_pin = vsync_pin; 278 279 switch (encoder_type) { 280 case DRM_MODE_ENCODER_DAC: 281 case DRM_MODE_ENCODER_TVDAC: 282 case DRM_MODE_ENCODER_LVDS: 283 ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC | 284 IPU_DI_CLKMODE_EXT; 285 break; 286 case DRM_MODE_ENCODER_NONE: 287 ipu_crtc->di_clkflags = 0; 288 break; 289 } 290 291 return 0; 292} 293 294static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = { 295 .enable_vblank = ipu_enable_vblank, 296 .disable_vblank = ipu_disable_vblank, 297 .set_interface_pix_fmt = ipu_set_interface_pix_fmt, 298 .crtc_funcs = &ipu_crtc_funcs, 299 .crtc_helper_funcs = &ipu_helper_funcs, 300}; 301 302static void ipu_put_resources(struct ipu_crtc *ipu_crtc) 303{ 304 if (!IS_ERR_OR_NULL(ipu_crtc->dc)) 305 ipu_dc_put(ipu_crtc->dc); 306 if (!IS_ERR_OR_NULL(ipu_crtc->di)) 307 ipu_di_put(ipu_crtc->di); 308} 309 310static int ipu_get_resources(struct ipu_crtc *ipu_crtc, 311 struct ipu_client_platformdata *pdata) 312{ 313 struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); 314 int ret; 315 316 ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc); 317 if (IS_ERR(ipu_crtc->dc)) { 318 ret = PTR_ERR(ipu_crtc->dc); 319 goto err_out; 320 } 321 322 ipu_crtc->di = ipu_di_get(ipu, pdata->di); 323 if (IS_ERR(ipu_crtc->di)) { 324 ret = PTR_ERR(ipu_crtc->di); 325 goto err_out; 326 } 327 328 return 0; 329err_out: 330 ipu_put_resources(ipu_crtc); 331 332 return ret; 333} 334 335static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, 336 struct ipu_client_platformdata *pdata) 337{ 338 struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); 339 int dp = -EINVAL; 340 int ret; 341 int id; 342 343 ret = ipu_get_resources(ipu_crtc, pdata); 344 if (ret) { 345 dev_err(ipu_crtc->dev, "getting resources failed with %d.\n", 346 ret); 347 return ret; 348 } 349 350 ret = imx_drm_add_crtc(&ipu_crtc->base, 351 &ipu_crtc->imx_crtc, 352 &ipu_crtc_helper_funcs, THIS_MODULE, 353 ipu_crtc->dev->parent->of_node, pdata->di); 354 if (ret) { 355 dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret); 356 goto err_put_resources; 357 } 358 359 if (pdata->dp >= 0) 360 dp = IPU_DP_FLOW_SYNC_BG; 361 id = imx_drm_crtc_id(ipu_crtc->imx_crtc); 362 ipu_crtc->plane[0] = ipu_plane_init(ipu_crtc->base.dev, ipu, 363 pdata->dma[0], dp, BIT(id), true); 364 ret = ipu_plane_get_resources(ipu_crtc->plane[0]); 365 if (ret) { 366 dev_err(ipu_crtc->dev, "getting plane 0 resources failed with %d.\n", 367 ret); 368 goto err_remove_crtc; 369 } 370 371 /* If this crtc is using the DP, add an overlay plane */ 372 if (pdata->dp >= 0 && pdata->dma[1] > 0) { 373 ipu_crtc->plane[1] = ipu_plane_init(ipu_crtc->base.dev, ipu, 374 pdata->dma[1], 375 IPU_DP_FLOW_SYNC_FG, 376 BIT(id), false); 377 if (IS_ERR(ipu_crtc->plane[1])) 378 ipu_crtc->plane[1] = NULL; 379 } 380 381 ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]); 382 ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0, 383 "imx_drm", ipu_crtc); 384 if (ret < 0) { 385 dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret); 386 goto err_put_plane_res; 387 } 388 389 return 0; 390 391err_put_plane_res: 392 ipu_plane_put_resources(ipu_crtc->plane[0]); 393err_remove_crtc: 394 imx_drm_remove_crtc(ipu_crtc->imx_crtc); 395err_put_resources: 396 ipu_put_resources(ipu_crtc); 397 398 return ret; 399} 400 401static int ipu_drm_probe(struct platform_device *pdev) 402{ 403 struct ipu_client_platformdata *pdata = pdev->dev.platform_data; 404 struct ipu_crtc *ipu_crtc; 405 int ret; 406 407 if (!pdata) 408 return -EINVAL; 409 410 ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 411 if (ret) 412 return ret; 413 414 ipu_crtc = devm_kzalloc(&pdev->dev, sizeof(*ipu_crtc), GFP_KERNEL); 415 if (!ipu_crtc) 416 return -ENOMEM; 417 418 ipu_crtc->dev = &pdev->dev; 419 420 ret = ipu_crtc_init(ipu_crtc, pdata); 421 if (ret) 422 return ret; 423 424 platform_set_drvdata(pdev, ipu_crtc); 425 426 return 0; 427} 428 429static int ipu_drm_remove(struct platform_device *pdev) 430{ 431 struct ipu_crtc *ipu_crtc = platform_get_drvdata(pdev); 432 433 imx_drm_remove_crtc(ipu_crtc->imx_crtc); 434 435 ipu_plane_put_resources(ipu_crtc->plane[0]); 436 ipu_put_resources(ipu_crtc); 437 438 return 0; 439} 440 441static struct platform_driver ipu_drm_driver = { 442 .driver = { 443 .name = "imx-ipuv3-crtc", 444 }, 445 .probe = ipu_drm_probe, 446 .remove = ipu_drm_remove, 447}; 448module_platform_driver(ipu_drm_driver); 449 450MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 451MODULE_DESCRIPTION(DRIVER_DESC); 452MODULE_LICENSE("GPL"); 453MODULE_ALIAS("platform:imx-ipuv3-crtc");