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.14-rc7 441 lines 11 kB view raw
1/* 2 * Freescale i.MX drm 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 */ 16#include <linux/component.h> 17#include <linux/device.h> 18#include <linux/dma-buf.h> 19#include <linux/module.h> 20#include <linux/platform_device.h> 21#include <drm/drmP.h> 22#include <drm/drm_atomic.h> 23#include <drm/drm_atomic_helper.h> 24#include <drm/drm_fb_helper.h> 25#include <drm/drm_crtc_helper.h> 26#include <drm/drm_gem_cma_helper.h> 27#include <drm/drm_fb_cma_helper.h> 28#include <drm/drm_plane_helper.h> 29#include <drm/drm_of.h> 30#include <video/imx-ipu-v3.h> 31 32#include "imx-drm.h" 33#include "ipuv3-plane.h" 34 35#define MAX_CRTC 4 36 37struct imx_drm_device { 38 struct drm_device *drm; 39 unsigned int pipes; 40 struct drm_fbdev_cma *fbhelper; 41 struct drm_atomic_state *state; 42}; 43 44#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) 45static int legacyfb_depth = 16; 46module_param(legacyfb_depth, int, 0444); 47#endif 48 49static void imx_drm_driver_lastclose(struct drm_device *drm) 50{ 51 struct imx_drm_device *imxdrm = drm->dev_private; 52 53 drm_fbdev_cma_restore_mode(imxdrm->fbhelper); 54} 55 56DEFINE_DRM_GEM_CMA_FOPS(imx_drm_driver_fops); 57 58void imx_drm_connector_destroy(struct drm_connector *connector) 59{ 60 drm_connector_unregister(connector); 61 drm_connector_cleanup(connector); 62} 63EXPORT_SYMBOL_GPL(imx_drm_connector_destroy); 64 65void imx_drm_encoder_destroy(struct drm_encoder *encoder) 66{ 67 drm_encoder_cleanup(encoder); 68} 69EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy); 70 71static void imx_drm_output_poll_changed(struct drm_device *drm) 72{ 73 struct imx_drm_device *imxdrm = drm->dev_private; 74 75 drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); 76} 77 78static int imx_drm_atomic_check(struct drm_device *dev, 79 struct drm_atomic_state *state) 80{ 81 int ret; 82 83 ret = drm_atomic_helper_check_modeset(dev, state); 84 if (ret) 85 return ret; 86 87 ret = drm_atomic_helper_check_planes(dev, state); 88 if (ret) 89 return ret; 90 91 /* 92 * Check modeset again in case crtc_state->mode_changed is 93 * updated in plane's ->atomic_check callback. 94 */ 95 ret = drm_atomic_helper_check_modeset(dev, state); 96 if (ret) 97 return ret; 98 99 /* Assign PRG/PRE channels and check if all constrains are satisfied. */ 100 ret = ipu_planes_assign_pre(dev, state); 101 if (ret) 102 return ret; 103 104 return ret; 105} 106 107static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = { 108 .fb_create = drm_fb_cma_create, 109 .output_poll_changed = imx_drm_output_poll_changed, 110 .atomic_check = imx_drm_atomic_check, 111 .atomic_commit = drm_atomic_helper_commit, 112}; 113 114static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state) 115{ 116 struct drm_device *dev = state->dev; 117 struct drm_plane *plane; 118 struct drm_plane_state *old_plane_state, *new_plane_state; 119 bool plane_disabling = false; 120 int i; 121 122 drm_atomic_helper_commit_modeset_disables(dev, state); 123 124 drm_atomic_helper_commit_planes(dev, state, 125 DRM_PLANE_COMMIT_ACTIVE_ONLY | 126 DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET); 127 128 drm_atomic_helper_commit_modeset_enables(dev, state); 129 130 for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { 131 if (drm_atomic_plane_disabling(old_plane_state, new_plane_state)) 132 plane_disabling = true; 133 } 134 135 if (plane_disabling) { 136 drm_atomic_helper_wait_for_vblanks(dev, state); 137 138 for_each_old_plane_in_state(state, plane, old_plane_state, i) 139 ipu_plane_disable_deferred(plane); 140 141 } 142 143 drm_atomic_helper_commit_hw_done(state); 144} 145 146static const struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = { 147 .atomic_commit_tail = imx_drm_atomic_commit_tail, 148}; 149 150 151int imx_drm_encoder_parse_of(struct drm_device *drm, 152 struct drm_encoder *encoder, struct device_node *np) 153{ 154 uint32_t crtc_mask = drm_of_find_possible_crtcs(drm, np); 155 156 /* 157 * If we failed to find the CRTC(s) which this encoder is 158 * supposed to be connected to, it's because the CRTC has 159 * not been registered yet. Defer probing, and hope that 160 * the required CRTC is added later. 161 */ 162 if (crtc_mask == 0) 163 return -EPROBE_DEFER; 164 165 encoder->possible_crtcs = crtc_mask; 166 167 /* FIXME: this is the mask of outputs which can clone this output. */ 168 encoder->possible_clones = ~0; 169 170 return 0; 171} 172EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of); 173 174static const struct drm_ioctl_desc imx_drm_ioctls[] = { 175 /* none so far */ 176}; 177 178static struct drm_driver imx_drm_driver = { 179 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | 180 DRIVER_ATOMIC, 181 .lastclose = imx_drm_driver_lastclose, 182 .gem_free_object_unlocked = drm_gem_cma_free_object, 183 .gem_vm_ops = &drm_gem_cma_vm_ops, 184 .dumb_create = drm_gem_cma_dumb_create, 185 186 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 187 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 188 .gem_prime_import = drm_gem_prime_import, 189 .gem_prime_export = drm_gem_prime_export, 190 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 191 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 192 .gem_prime_vmap = drm_gem_cma_prime_vmap, 193 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 194 .gem_prime_mmap = drm_gem_cma_prime_mmap, 195 .ioctls = imx_drm_ioctls, 196 .num_ioctls = ARRAY_SIZE(imx_drm_ioctls), 197 .fops = &imx_drm_driver_fops, 198 .name = "imx-drm", 199 .desc = "i.MX DRM graphics", 200 .date = "20120507", 201 .major = 1, 202 .minor = 0, 203 .patchlevel = 0, 204}; 205 206static int compare_of(struct device *dev, void *data) 207{ 208 struct device_node *np = data; 209 210 /* Special case for DI, dev->of_node may not be set yet */ 211 if (strcmp(dev->driver->name, "imx-ipuv3-crtc") == 0) { 212 struct ipu_client_platformdata *pdata = dev->platform_data; 213 214 return pdata->of_node == np; 215 } 216 217 /* Special case for LDB, one device for two channels */ 218 if (of_node_cmp(np->name, "lvds-channel") == 0) { 219 np = of_get_parent(np); 220 of_node_put(np); 221 } 222 223 return dev->of_node == np; 224} 225 226static int imx_drm_bind(struct device *dev) 227{ 228 struct drm_device *drm; 229 struct imx_drm_device *imxdrm; 230 int ret; 231 232 drm = drm_dev_alloc(&imx_drm_driver, dev); 233 if (IS_ERR(drm)) 234 return PTR_ERR(drm); 235 236 imxdrm = devm_kzalloc(dev, sizeof(*imxdrm), GFP_KERNEL); 237 if (!imxdrm) { 238 ret = -ENOMEM; 239 goto err_unref; 240 } 241 242 imxdrm->drm = drm; 243 drm->dev_private = imxdrm; 244 245 /* 246 * enable drm irq mode. 247 * - with irq_enabled = true, we can use the vblank feature. 248 * 249 * P.S. note that we wouldn't use drm irq handler but 250 * just specific driver own one instead because 251 * drm framework supports only one irq handler and 252 * drivers can well take care of their interrupts 253 */ 254 drm->irq_enabled = true; 255 256 /* 257 * set max width and height as default value(4096x4096). 258 * this value would be used to check framebuffer size limitation 259 * at drm_mode_addfb(). 260 */ 261 drm->mode_config.min_width = 1; 262 drm->mode_config.min_height = 1; 263 drm->mode_config.max_width = 4096; 264 drm->mode_config.max_height = 4096; 265 drm->mode_config.funcs = &imx_drm_mode_config_funcs; 266 drm->mode_config.helper_private = &imx_drm_mode_config_helpers; 267 268 drm_mode_config_init(drm); 269 270 ret = drm_vblank_init(drm, MAX_CRTC); 271 if (ret) 272 goto err_kms; 273 274 dev_set_drvdata(dev, drm); 275 276 /* Now try and bind all our sub-components */ 277 ret = component_bind_all(dev, drm); 278 if (ret) 279 goto err_kms; 280 281 drm_mode_config_reset(drm); 282 283 /* 284 * All components are now initialised, so setup the fb helper. 285 * The fb helper takes copies of key hardware information, so the 286 * crtcs/connectors/encoders must not change after this point. 287 */ 288#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) 289 if (legacyfb_depth != 16 && legacyfb_depth != 32) { 290 dev_warn(dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); 291 legacyfb_depth = 16; 292 } 293 imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, MAX_CRTC); 294 if (IS_ERR(imxdrm->fbhelper)) { 295 ret = PTR_ERR(imxdrm->fbhelper); 296 imxdrm->fbhelper = NULL; 297 goto err_unbind; 298 } 299#endif 300 301 drm_kms_helper_poll_init(drm); 302 303 ret = drm_dev_register(drm, 0); 304 if (ret) 305 goto err_fbhelper; 306 307 return 0; 308 309err_fbhelper: 310 drm_kms_helper_poll_fini(drm); 311#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) 312 if (imxdrm->fbhelper) 313 drm_fbdev_cma_fini(imxdrm->fbhelper); 314err_unbind: 315#endif 316 component_unbind_all(drm->dev, drm); 317err_kms: 318 drm_mode_config_cleanup(drm); 319err_unref: 320 drm_dev_unref(drm); 321 322 return ret; 323} 324 325static void imx_drm_unbind(struct device *dev) 326{ 327 struct drm_device *drm = dev_get_drvdata(dev); 328 struct imx_drm_device *imxdrm = drm->dev_private; 329 330 drm_dev_unregister(drm); 331 332 drm_kms_helper_poll_fini(drm); 333 334 if (imxdrm->fbhelper) 335 drm_fbdev_cma_fini(imxdrm->fbhelper); 336 337 drm_mode_config_cleanup(drm); 338 339 component_unbind_all(drm->dev, drm); 340 dev_set_drvdata(dev, NULL); 341 342 drm_dev_unref(drm); 343} 344 345static const struct component_master_ops imx_drm_ops = { 346 .bind = imx_drm_bind, 347 .unbind = imx_drm_unbind, 348}; 349 350static int imx_drm_platform_probe(struct platform_device *pdev) 351{ 352 int ret = drm_of_component_probe(&pdev->dev, compare_of, &imx_drm_ops); 353 354 if (!ret) 355 ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 356 357 return ret; 358} 359 360static int imx_drm_platform_remove(struct platform_device *pdev) 361{ 362 component_master_del(&pdev->dev, &imx_drm_ops); 363 return 0; 364} 365 366#ifdef CONFIG_PM_SLEEP 367static int imx_drm_suspend(struct device *dev) 368{ 369 struct drm_device *drm_dev = dev_get_drvdata(dev); 370 struct imx_drm_device *imxdrm; 371 372 /* The drm_dev is NULL before .load hook is called */ 373 if (drm_dev == NULL) 374 return 0; 375 376 drm_kms_helper_poll_disable(drm_dev); 377 378 imxdrm = drm_dev->dev_private; 379 imxdrm->state = drm_atomic_helper_suspend(drm_dev); 380 if (IS_ERR(imxdrm->state)) { 381 drm_kms_helper_poll_enable(drm_dev); 382 return PTR_ERR(imxdrm->state); 383 } 384 385 return 0; 386} 387 388static int imx_drm_resume(struct device *dev) 389{ 390 struct drm_device *drm_dev = dev_get_drvdata(dev); 391 struct imx_drm_device *imx_drm; 392 393 if (drm_dev == NULL) 394 return 0; 395 396 imx_drm = drm_dev->dev_private; 397 drm_atomic_helper_resume(drm_dev, imx_drm->state); 398 drm_kms_helper_poll_enable(drm_dev); 399 400 return 0; 401} 402#endif 403 404static SIMPLE_DEV_PM_OPS(imx_drm_pm_ops, imx_drm_suspend, imx_drm_resume); 405 406static const struct of_device_id imx_drm_dt_ids[] = { 407 { .compatible = "fsl,imx-display-subsystem", }, 408 { /* sentinel */ }, 409}; 410MODULE_DEVICE_TABLE(of, imx_drm_dt_ids); 411 412static struct platform_driver imx_drm_pdrv = { 413 .probe = imx_drm_platform_probe, 414 .remove = imx_drm_platform_remove, 415 .driver = { 416 .name = "imx-drm", 417 .pm = &imx_drm_pm_ops, 418 .of_match_table = imx_drm_dt_ids, 419 }, 420}; 421 422static struct platform_driver * const drivers[] = { 423 &imx_drm_pdrv, 424 &ipu_drm_driver, 425}; 426 427static int __init imx_drm_init(void) 428{ 429 return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 430} 431module_init(imx_drm_init); 432 433static void __exit imx_drm_exit(void) 434{ 435 platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 436} 437module_exit(imx_drm_exit); 438 439MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 440MODULE_DESCRIPTION("i.MX drm driver core"); 441MODULE_LICENSE("GPL");