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.12 375 lines 9.3 kB view raw
1/* 2 * rcar_du_drv.c -- R-Car Display Unit DRM driver 3 * 4 * Copyright (C) 2013-2015 Renesas Electronics Corporation 5 * 6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 */ 13 14#include <linux/clk.h> 15#include <linux/io.h> 16#include <linux/mm.h> 17#include <linux/module.h> 18#include <linux/of_device.h> 19#include <linux/platform_device.h> 20#include <linux/pm.h> 21#include <linux/slab.h> 22#include <linux/wait.h> 23 24#include <drm/drmP.h> 25#include <drm/drm_crtc_helper.h> 26#include <drm/drm_fb_cma_helper.h> 27#include <drm/drm_gem_cma_helper.h> 28 29#include "rcar_du_drv.h" 30#include "rcar_du_kms.h" 31#include "rcar_du_regs.h" 32 33/* ----------------------------------------------------------------------------- 34 * Device Information 35 */ 36 37static const struct rcar_du_device_info rcar_du_r8a7779_info = { 38 .gen = 2, 39 .features = 0, 40 .num_crtcs = 2, 41 .routes = { 42 /* R8A7779 has two RGB outputs and one (currently unsupported) 43 * TCON output. 44 */ 45 [RCAR_DU_OUTPUT_DPAD0] = { 46 .possible_crtcs = BIT(0), 47 .port = 0, 48 }, 49 [RCAR_DU_OUTPUT_DPAD1] = { 50 .possible_crtcs = BIT(1) | BIT(0), 51 .port = 1, 52 }, 53 }, 54 .num_lvds = 0, 55}; 56 57static const struct rcar_du_device_info rcar_du_r8a7790_info = { 58 .gen = 2, 59 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 60 | RCAR_DU_FEATURE_EXT_CTRL_REGS, 61 .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES, 62 .num_crtcs = 3, 63 .routes = { 64 /* R8A7790 has one RGB output, two LVDS outputs and one 65 * (currently unsupported) TCON output. 66 */ 67 [RCAR_DU_OUTPUT_DPAD0] = { 68 .possible_crtcs = BIT(2) | BIT(1) | BIT(0), 69 .port = 0, 70 }, 71 [RCAR_DU_OUTPUT_LVDS0] = { 72 .possible_crtcs = BIT(0), 73 .port = 1, 74 }, 75 [RCAR_DU_OUTPUT_LVDS1] = { 76 .possible_crtcs = BIT(2) | BIT(1), 77 .port = 2, 78 }, 79 }, 80 .num_lvds = 2, 81}; 82 83/* M2-W (r8a7791) and M2-N (r8a7793) are identical */ 84static const struct rcar_du_device_info rcar_du_r8a7791_info = { 85 .gen = 2, 86 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 87 | RCAR_DU_FEATURE_EXT_CTRL_REGS, 88 .num_crtcs = 2, 89 .routes = { 90 /* R8A779[13] has one RGB output, one LVDS output and one 91 * (currently unsupported) TCON output. 92 */ 93 [RCAR_DU_OUTPUT_DPAD0] = { 94 .possible_crtcs = BIT(1) | BIT(0), 95 .port = 0, 96 }, 97 [RCAR_DU_OUTPUT_LVDS0] = { 98 .possible_crtcs = BIT(0), 99 .port = 1, 100 }, 101 }, 102 .num_lvds = 1, 103}; 104 105static const struct rcar_du_device_info rcar_du_r8a7792_info = { 106 .gen = 2, 107 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 108 | RCAR_DU_FEATURE_EXT_CTRL_REGS, 109 .num_crtcs = 2, 110 .routes = { 111 /* R8A7792 has two RGB outputs. */ 112 [RCAR_DU_OUTPUT_DPAD0] = { 113 .possible_crtcs = BIT(0), 114 .port = 0, 115 }, 116 [RCAR_DU_OUTPUT_DPAD1] = { 117 .possible_crtcs = BIT(1), 118 .port = 1, 119 }, 120 }, 121 .num_lvds = 0, 122}; 123 124static const struct rcar_du_device_info rcar_du_r8a7794_info = { 125 .gen = 2, 126 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 127 | RCAR_DU_FEATURE_EXT_CTRL_REGS, 128 .num_crtcs = 2, 129 .routes = { 130 /* R8A7794 has two RGB outputs and one (currently unsupported) 131 * TCON output. 132 */ 133 [RCAR_DU_OUTPUT_DPAD0] = { 134 .possible_crtcs = BIT(0), 135 .port = 0, 136 }, 137 [RCAR_DU_OUTPUT_DPAD1] = { 138 .possible_crtcs = BIT(1), 139 .port = 1, 140 }, 141 }, 142 .num_lvds = 0, 143}; 144 145static const struct rcar_du_device_info rcar_du_r8a7795_info = { 146 .gen = 3, 147 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 148 | RCAR_DU_FEATURE_EXT_CTRL_REGS 149 | RCAR_DU_FEATURE_VSP1_SOURCE, 150 .num_crtcs = 4, 151 .routes = { 152 /* R8A7795 has one RGB output, two HDMI outputs and one 153 * LVDS output. 154 */ 155 [RCAR_DU_OUTPUT_DPAD0] = { 156 .possible_crtcs = BIT(3), 157 .port = 0, 158 }, 159 [RCAR_DU_OUTPUT_HDMI0] = { 160 .possible_crtcs = BIT(1), 161 .port = 1, 162 }, 163 [RCAR_DU_OUTPUT_HDMI1] = { 164 .possible_crtcs = BIT(2), 165 .port = 2, 166 }, 167 [RCAR_DU_OUTPUT_LVDS0] = { 168 .possible_crtcs = BIT(0), 169 .port = 3, 170 }, 171 }, 172 .num_lvds = 1, 173 .dpll_ch = BIT(1) | BIT(2), 174}; 175 176static const struct rcar_du_device_info rcar_du_r8a7796_info = { 177 .gen = 3, 178 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 179 | RCAR_DU_FEATURE_EXT_CTRL_REGS 180 | RCAR_DU_FEATURE_VSP1_SOURCE, 181 .num_crtcs = 3, 182 .routes = { 183 /* R8A7796 has one RGB output, one LVDS output and one 184 * (currently unsupported) HDMI output. 185 */ 186 [RCAR_DU_OUTPUT_DPAD0] = { 187 .possible_crtcs = BIT(2), 188 .port = 0, 189 }, 190 [RCAR_DU_OUTPUT_LVDS0] = { 191 .possible_crtcs = BIT(0), 192 .port = 2, 193 }, 194 }, 195 .num_lvds = 1, 196}; 197 198static const struct of_device_id rcar_du_of_table[] = { 199 { .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info }, 200 { .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info }, 201 { .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info }, 202 { .compatible = "renesas,du-r8a7792", .data = &rcar_du_r8a7792_info }, 203 { .compatible = "renesas,du-r8a7793", .data = &rcar_du_r8a7791_info }, 204 { .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info }, 205 { .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info }, 206 { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info }, 207 { } 208}; 209 210MODULE_DEVICE_TABLE(of, rcar_du_of_table); 211 212/* ----------------------------------------------------------------------------- 213 * DRM operations 214 */ 215 216static void rcar_du_lastclose(struct drm_device *dev) 217{ 218 struct rcar_du_device *rcdu = dev->dev_private; 219 220 drm_fbdev_cma_restore_mode(rcdu->fbdev); 221} 222 223DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops); 224 225static struct drm_driver rcar_du_driver = { 226 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME 227 | DRIVER_ATOMIC, 228 .lastclose = rcar_du_lastclose, 229 .gem_free_object_unlocked = drm_gem_cma_free_object, 230 .gem_vm_ops = &drm_gem_cma_vm_ops, 231 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 232 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 233 .gem_prime_import = drm_gem_prime_import, 234 .gem_prime_export = drm_gem_prime_export, 235 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 236 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 237 .gem_prime_vmap = drm_gem_cma_prime_vmap, 238 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 239 .gem_prime_mmap = drm_gem_cma_prime_mmap, 240 .dumb_create = rcar_du_dumb_create, 241 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 242 .dumb_destroy = drm_gem_dumb_destroy, 243 .fops = &rcar_du_fops, 244 .name = "rcar-du", 245 .desc = "Renesas R-Car Display Unit", 246 .date = "20130110", 247 .major = 1, 248 .minor = 0, 249}; 250 251/* ----------------------------------------------------------------------------- 252 * Power management 253 */ 254 255#ifdef CONFIG_PM_SLEEP 256static int rcar_du_pm_suspend(struct device *dev) 257{ 258 struct rcar_du_device *rcdu = dev_get_drvdata(dev); 259 260 drm_kms_helper_poll_disable(rcdu->ddev); 261 /* TODO Suspend the CRTC */ 262 263 return 0; 264} 265 266static int rcar_du_pm_resume(struct device *dev) 267{ 268 struct rcar_du_device *rcdu = dev_get_drvdata(dev); 269 270 /* TODO Resume the CRTC */ 271 272 drm_kms_helper_poll_enable(rcdu->ddev); 273 return 0; 274} 275#endif 276 277static const struct dev_pm_ops rcar_du_pm_ops = { 278 SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume) 279}; 280 281/* ----------------------------------------------------------------------------- 282 * Platform driver 283 */ 284 285static int rcar_du_remove(struct platform_device *pdev) 286{ 287 struct rcar_du_device *rcdu = platform_get_drvdata(pdev); 288 struct drm_device *ddev = rcdu->ddev; 289 290 drm_dev_unregister(ddev); 291 292 if (rcdu->fbdev) 293 drm_fbdev_cma_fini(rcdu->fbdev); 294 295 drm_kms_helper_poll_fini(ddev); 296 drm_mode_config_cleanup(ddev); 297 298 drm_dev_unref(ddev); 299 300 return 0; 301} 302 303static int rcar_du_probe(struct platform_device *pdev) 304{ 305 struct rcar_du_device *rcdu; 306 struct drm_device *ddev; 307 struct resource *mem; 308 int ret; 309 310 /* Allocate and initialize the R-Car device structure. */ 311 rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL); 312 if (rcdu == NULL) 313 return -ENOMEM; 314 315 rcdu->dev = &pdev->dev; 316 rcdu->info = of_device_get_match_data(rcdu->dev); 317 318 platform_set_drvdata(pdev, rcdu); 319 320 /* I/O resources */ 321 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 322 rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); 323 if (IS_ERR(rcdu->mmio)) 324 return PTR_ERR(rcdu->mmio); 325 326 /* DRM/KMS objects */ 327 ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev); 328 if (IS_ERR(ddev)) 329 return PTR_ERR(ddev); 330 331 rcdu->ddev = ddev; 332 ddev->dev_private = rcdu; 333 334 ret = rcar_du_modeset_init(rcdu); 335 if (ret < 0) { 336 if (ret != -EPROBE_DEFER) 337 dev_err(&pdev->dev, 338 "failed to initialize DRM/KMS (%d)\n", ret); 339 goto error; 340 } 341 342 ddev->irq_enabled = 1; 343 344 /* Register the DRM device with the core and the connectors with 345 * sysfs. 346 */ 347 ret = drm_dev_register(ddev, 0); 348 if (ret) 349 goto error; 350 351 DRM_INFO("Device %s probed\n", dev_name(&pdev->dev)); 352 353 return 0; 354 355error: 356 rcar_du_remove(pdev); 357 358 return ret; 359} 360 361static struct platform_driver rcar_du_platform_driver = { 362 .probe = rcar_du_probe, 363 .remove = rcar_du_remove, 364 .driver = { 365 .name = "rcar-du", 366 .pm = &rcar_du_pm_ops, 367 .of_match_table = rcar_du_of_table, 368 }, 369}; 370 371module_platform_driver(rcar_du_platform_driver); 372 373MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 374MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver"); 375MODULE_LICENSE("GPL");