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.9-rc5 472 lines 12 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#include <linux/component.h> 16#include <linux/module.h> 17#include <linux/export.h> 18#include <linux/device.h> 19#include <linux/platform_device.h> 20#include <drm/drmP.h> 21#include <drm/drm_atomic.h> 22#include <drm/drm_atomic_helper.h> 23#include <drm/drm_crtc_helper.h> 24#include <linux/clk.h> 25#include <linux/errno.h> 26#include <drm/drm_gem_cma_helper.h> 27#include <drm/drm_fb_cma_helper.h> 28 29#include <video/imx-ipu-v3.h> 30#include "imx-drm.h" 31#include "ipuv3-plane.h" 32 33#define DRIVER_DESC "i.MX IPUv3 Graphics" 34 35struct ipu_crtc { 36 struct device *dev; 37 struct drm_crtc base; 38 struct imx_drm_crtc *imx_crtc; 39 40 /* plane[0] is the full plane, plane[1] is the partial plane */ 41 struct ipu_plane *plane[2]; 42 43 struct ipu_dc *dc; 44 struct ipu_di *di; 45 int irq; 46}; 47 48static inline struct ipu_crtc *to_ipu_crtc(struct drm_crtc *crtc) 49{ 50 return container_of(crtc, struct ipu_crtc, base); 51} 52 53static void ipu_crtc_enable(struct drm_crtc *crtc) 54{ 55 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 56 struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); 57 58 ipu_dc_enable(ipu); 59 ipu_dc_enable_channel(ipu_crtc->dc); 60 ipu_di_enable(ipu_crtc->di); 61} 62 63static void ipu_crtc_atomic_disable(struct drm_crtc *crtc, 64 struct drm_crtc_state *old_crtc_state) 65{ 66 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 67 struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); 68 69 ipu_dc_disable_channel(ipu_crtc->dc); 70 ipu_di_disable(ipu_crtc->di); 71 /* 72 * Planes must be disabled before DC clock is removed, as otherwise the 73 * attached IDMACs will be left in undefined state, possibly hanging 74 * the IPU or even system. 75 */ 76 drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); 77 ipu_dc_disable(ipu); 78 79 spin_lock_irq(&crtc->dev->event_lock); 80 if (crtc->state->event) { 81 drm_crtc_send_vblank_event(crtc, crtc->state->event); 82 crtc->state->event = NULL; 83 } 84 spin_unlock_irq(&crtc->dev->event_lock); 85 86 drm_crtc_vblank_off(crtc); 87} 88 89static void imx_drm_crtc_reset(struct drm_crtc *crtc) 90{ 91 struct imx_crtc_state *state; 92 93 if (crtc->state) { 94 if (crtc->state->mode_blob) 95 drm_property_unreference_blob(crtc->state->mode_blob); 96 97 state = to_imx_crtc_state(crtc->state); 98 memset(state, 0, sizeof(*state)); 99 } else { 100 state = kzalloc(sizeof(*state), GFP_KERNEL); 101 if (!state) 102 return; 103 crtc->state = &state->base; 104 } 105 106 state->base.crtc = crtc; 107} 108 109static struct drm_crtc_state *imx_drm_crtc_duplicate_state(struct drm_crtc *crtc) 110{ 111 struct imx_crtc_state *state; 112 113 state = kzalloc(sizeof(*state), GFP_KERNEL); 114 if (!state) 115 return NULL; 116 117 __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); 118 119 WARN_ON(state->base.crtc != crtc); 120 state->base.crtc = crtc; 121 122 return &state->base; 123} 124 125static void imx_drm_crtc_destroy_state(struct drm_crtc *crtc, 126 struct drm_crtc_state *state) 127{ 128 __drm_atomic_helper_crtc_destroy_state(state); 129 kfree(to_imx_crtc_state(state)); 130} 131 132static void imx_drm_crtc_destroy(struct drm_crtc *crtc) 133{ 134 imx_drm_remove_crtc(to_ipu_crtc(crtc)->imx_crtc); 135} 136 137static const struct drm_crtc_funcs ipu_crtc_funcs = { 138 .set_config = drm_atomic_helper_set_config, 139 .destroy = imx_drm_crtc_destroy, 140 .page_flip = drm_atomic_helper_page_flip, 141 .reset = imx_drm_crtc_reset, 142 .atomic_duplicate_state = imx_drm_crtc_duplicate_state, 143 .atomic_destroy_state = imx_drm_crtc_destroy_state, 144}; 145 146static irqreturn_t ipu_irq_handler(int irq, void *dev_id) 147{ 148 struct ipu_crtc *ipu_crtc = dev_id; 149 150 drm_crtc_handle_vblank(&ipu_crtc->base); 151 152 return IRQ_HANDLED; 153} 154 155static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc, 156 const struct drm_display_mode *mode, 157 struct drm_display_mode *adjusted_mode) 158{ 159 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 160 struct videomode vm; 161 int ret; 162 163 drm_display_mode_to_videomode(adjusted_mode, &vm); 164 165 ret = ipu_di_adjust_videomode(ipu_crtc->di, &vm); 166 if (ret) 167 return false; 168 169 if ((vm.vsync_len == 0) || (vm.hsync_len == 0)) 170 return false; 171 172 drm_display_mode_from_videomode(&vm, adjusted_mode); 173 174 return true; 175} 176 177static int ipu_crtc_atomic_check(struct drm_crtc *crtc, 178 struct drm_crtc_state *state) 179{ 180 u32 primary_plane_mask = 1 << drm_plane_index(crtc->primary); 181 182 if (state->active && (primary_plane_mask & state->plane_mask) == 0) 183 return -EINVAL; 184 185 return 0; 186} 187 188static void ipu_crtc_atomic_begin(struct drm_crtc *crtc, 189 struct drm_crtc_state *old_crtc_state) 190{ 191 drm_crtc_vblank_on(crtc); 192 193 spin_lock_irq(&crtc->dev->event_lock); 194 if (crtc->state->event) { 195 WARN_ON(drm_crtc_vblank_get(crtc)); 196 drm_crtc_arm_vblank_event(crtc, crtc->state->event); 197 crtc->state->event = NULL; 198 } 199 spin_unlock_irq(&crtc->dev->event_lock); 200} 201 202static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc) 203{ 204 struct drm_device *dev = crtc->dev; 205 struct drm_encoder *encoder; 206 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 207 struct drm_display_mode *mode = &crtc->state->adjusted_mode; 208 struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc->state); 209 struct ipu_di_signal_cfg sig_cfg = {}; 210 unsigned long encoder_types = 0; 211 212 dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, 213 mode->hdisplay); 214 dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__, 215 mode->vdisplay); 216 217 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 218 if (encoder->crtc == crtc) 219 encoder_types |= BIT(encoder->encoder_type); 220 } 221 222 dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n", 223 __func__, encoder_types); 224 225 /* 226 * If we have DAC or LDB, then we need the IPU DI clock to be 227 * the same as the LDB DI clock. For TVDAC, derive the IPU DI 228 * clock from 27 MHz TVE_DI clock, but allow to divide it. 229 */ 230 if (encoder_types & (BIT(DRM_MODE_ENCODER_DAC) | 231 BIT(DRM_MODE_ENCODER_LVDS))) 232 sig_cfg.clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT; 233 else if (encoder_types & BIT(DRM_MODE_ENCODER_TVDAC)) 234 sig_cfg.clkflags = IPU_DI_CLKMODE_EXT; 235 else 236 sig_cfg.clkflags = 0; 237 238 sig_cfg.enable_pol = !(imx_crtc_state->bus_flags & DRM_BUS_FLAG_DE_LOW); 239 /* Default to driving pixel data on negative clock edges */ 240 sig_cfg.clk_pol = !!(imx_crtc_state->bus_flags & 241 DRM_BUS_FLAG_PIXDATA_POSEDGE); 242 sig_cfg.bus_format = imx_crtc_state->bus_format; 243 sig_cfg.v_to_h_sync = 0; 244 sig_cfg.hsync_pin = imx_crtc_state->di_hsync_pin; 245 sig_cfg.vsync_pin = imx_crtc_state->di_vsync_pin; 246 247 drm_display_mode_to_videomode(mode, &sig_cfg.mode); 248 249 ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, 250 mode->flags & DRM_MODE_FLAG_INTERLACE, 251 imx_crtc_state->bus_format, mode->hdisplay); 252 ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg); 253} 254 255static const struct drm_crtc_helper_funcs ipu_helper_funcs = { 256 .mode_fixup = ipu_crtc_mode_fixup, 257 .mode_set_nofb = ipu_crtc_mode_set_nofb, 258 .atomic_check = ipu_crtc_atomic_check, 259 .atomic_begin = ipu_crtc_atomic_begin, 260 .atomic_disable = ipu_crtc_atomic_disable, 261 .enable = ipu_crtc_enable, 262}; 263 264static int ipu_enable_vblank(struct drm_crtc *crtc) 265{ 266 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 267 268 enable_irq(ipu_crtc->irq); 269 270 return 0; 271} 272 273static void ipu_disable_vblank(struct drm_crtc *crtc) 274{ 275 struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 276 277 disable_irq_nosync(ipu_crtc->irq); 278} 279 280static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = { 281 .enable_vblank = ipu_enable_vblank, 282 .disable_vblank = ipu_disable_vblank, 283 .crtc_funcs = &ipu_crtc_funcs, 284 .crtc_helper_funcs = &ipu_helper_funcs, 285}; 286 287static void ipu_put_resources(struct ipu_crtc *ipu_crtc) 288{ 289 if (!IS_ERR_OR_NULL(ipu_crtc->dc)) 290 ipu_dc_put(ipu_crtc->dc); 291 if (!IS_ERR_OR_NULL(ipu_crtc->di)) 292 ipu_di_put(ipu_crtc->di); 293} 294 295static int ipu_get_resources(struct ipu_crtc *ipu_crtc, 296 struct ipu_client_platformdata *pdata) 297{ 298 struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); 299 int ret; 300 301 ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc); 302 if (IS_ERR(ipu_crtc->dc)) { 303 ret = PTR_ERR(ipu_crtc->dc); 304 goto err_out; 305 } 306 307 ipu_crtc->di = ipu_di_get(ipu, pdata->di); 308 if (IS_ERR(ipu_crtc->di)) { 309 ret = PTR_ERR(ipu_crtc->di); 310 goto err_out; 311 } 312 313 return 0; 314err_out: 315 ipu_put_resources(ipu_crtc); 316 317 return ret; 318} 319 320static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, 321 struct ipu_client_platformdata *pdata, struct drm_device *drm) 322{ 323 struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); 324 int dp = -EINVAL; 325 int ret; 326 327 ret = ipu_get_resources(ipu_crtc, pdata); 328 if (ret) { 329 dev_err(ipu_crtc->dev, "getting resources failed with %d.\n", 330 ret); 331 return ret; 332 } 333 334 if (pdata->dp >= 0) 335 dp = IPU_DP_FLOW_SYNC_BG; 336 ipu_crtc->plane[0] = ipu_plane_init(drm, ipu, pdata->dma[0], dp, 0, 337 DRM_PLANE_TYPE_PRIMARY); 338 if (IS_ERR(ipu_crtc->plane[0])) { 339 ret = PTR_ERR(ipu_crtc->plane[0]); 340 goto err_put_resources; 341 } 342 343 ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc, 344 &ipu_crtc->plane[0]->base, &ipu_crtc_helper_funcs, 345 pdata->of_node); 346 if (ret) { 347 dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret); 348 goto err_put_resources; 349 } 350 351 ret = ipu_plane_get_resources(ipu_crtc->plane[0]); 352 if (ret) { 353 dev_err(ipu_crtc->dev, "getting plane 0 resources failed with %d.\n", 354 ret); 355 goto err_remove_crtc; 356 } 357 358 /* If this crtc is using the DP, add an overlay plane */ 359 if (pdata->dp >= 0 && pdata->dma[1] > 0) { 360 ipu_crtc->plane[1] = ipu_plane_init(drm, ipu, pdata->dma[1], 361 IPU_DP_FLOW_SYNC_FG, 362 drm_crtc_mask(&ipu_crtc->base), 363 DRM_PLANE_TYPE_OVERLAY); 364 if (IS_ERR(ipu_crtc->plane[1])) { 365 ipu_crtc->plane[1] = NULL; 366 } else { 367 ret = ipu_plane_get_resources(ipu_crtc->plane[1]); 368 if (ret) { 369 dev_err(ipu_crtc->dev, "getting plane 1 " 370 "resources failed with %d.\n", ret); 371 goto err_put_plane0_res; 372 } 373 } 374 } 375 376 ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]); 377 ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0, 378 "imx_drm", ipu_crtc); 379 if (ret < 0) { 380 dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret); 381 goto err_put_plane1_res; 382 } 383 /* Only enable IRQ when we actually need it to trigger work. */ 384 disable_irq(ipu_crtc->irq); 385 386 return 0; 387 388err_put_plane1_res: 389 if (ipu_crtc->plane[1]) 390 ipu_plane_put_resources(ipu_crtc->plane[1]); 391err_put_plane0_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_bind(struct device *dev, struct device *master, void *data) 402{ 403 struct ipu_client_platformdata *pdata = dev->platform_data; 404 struct drm_device *drm = data; 405 struct ipu_crtc *ipu_crtc; 406 int ret; 407 408 ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL); 409 if (!ipu_crtc) 410 return -ENOMEM; 411 412 ipu_crtc->dev = dev; 413 414 ret = ipu_crtc_init(ipu_crtc, pdata, drm); 415 if (ret) 416 return ret; 417 418 dev_set_drvdata(dev, ipu_crtc); 419 420 return 0; 421} 422 423static void ipu_drm_unbind(struct device *dev, struct device *master, 424 void *data) 425{ 426 struct ipu_crtc *ipu_crtc = dev_get_drvdata(dev); 427 428 ipu_put_resources(ipu_crtc); 429 if (ipu_crtc->plane[1]) 430 ipu_plane_put_resources(ipu_crtc->plane[1]); 431 ipu_plane_put_resources(ipu_crtc->plane[0]); 432} 433 434static const struct component_ops ipu_crtc_ops = { 435 .bind = ipu_drm_bind, 436 .unbind = ipu_drm_unbind, 437}; 438 439static int ipu_drm_probe(struct platform_device *pdev) 440{ 441 struct device *dev = &pdev->dev; 442 int ret; 443 444 if (!dev->platform_data) 445 return -EINVAL; 446 447 ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); 448 if (ret) 449 return ret; 450 451 return component_add(dev, &ipu_crtc_ops); 452} 453 454static int ipu_drm_remove(struct platform_device *pdev) 455{ 456 component_del(&pdev->dev, &ipu_crtc_ops); 457 return 0; 458} 459 460static struct platform_driver ipu_drm_driver = { 461 .driver = { 462 .name = "imx-ipuv3-crtc", 463 }, 464 .probe = ipu_drm_probe, 465 .remove = ipu_drm_remove, 466}; 467module_platform_driver(ipu_drm_driver); 468 469MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 470MODULE_DESCRIPTION(DRIVER_DESC); 471MODULE_LICENSE("GPL"); 472MODULE_ALIAS("platform:imx-ipuv3-crtc");