1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2016 BayLibre, SAS 4 * Author: Neil Armstrong <narmstrong@baylibre.com> 5 * Copyright (C) 2014 Endless Mobile 6 * 7 * Written by: 8 * Jasper St. Pierre <jstpierre@mecheye.net> 9 */ 10 11 #include <linux/aperture.h> 12 #include <linux/component.h> 13 #include <linux/module.h> 14 #include <linux/of_graph.h> 15 #include <linux/sys_soc.h> 16 #include <linux/platform_device.h> 17 #include <linux/soc/amlogic/meson-canvas.h> 18 19 #include <drm/drm_atomic_helper.h> 20 #include <drm/drm_client_setup.h> 21 #include <drm/drm_drv.h> 22 #include <drm/drm_fbdev_dma.h> 23 #include <drm/drm_gem_dma_helper.h> 24 #include <drm/drm_gem_framebuffer_helper.h> 25 #include <drm/drm_modeset_helper_vtables.h> 26 #include <drm/drm_module.h> 27 #include <drm/drm_probe_helper.h> 28 #include <drm/drm_vblank.h> 29 30 #include "meson_crtc.h" 31 #include "meson_drv.h" 32 #include "meson_overlay.h" 33 #include "meson_plane.h" 34 #include "meson_osd_afbcd.h" 35 #include "meson_registers.h" 36 #include "meson_encoder_cvbs.h" 37 #include "meson_encoder_hdmi.h" 38 #include "meson_encoder_dsi.h" 39 #include "meson_viu.h" 40 #include "meson_vpp.h" 41 #include "meson_rdma.h" 42 43 #define DRIVER_NAME "meson" 44 #define DRIVER_DESC "Amlogic Meson DRM driver" 45 46 /** 47 * DOC: Video Processing Unit 48 * 49 * VPU Handles the Global Video Processing, it includes management of the 50 * clocks gates, blocks reset lines and power domains. 51 * 52 * What is missing : 53 * 54 * - Full reset of entire video processing HW blocks 55 * - Scaling and setup of the VPU clock 56 * - Bus clock gates 57 * - Powering up video processing HW blocks 58 * - Powering Up HDMI controller and PHY 59 */ 60 61 static const struct drm_mode_config_funcs meson_mode_config_funcs = { 62 .atomic_check = drm_atomic_helper_check, 63 .atomic_commit = drm_atomic_helper_commit, 64 .fb_create = drm_gem_fb_create, 65 }; 66 67 static const struct drm_mode_config_helper_funcs meson_mode_config_helpers = { 68 .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 69 }; 70 71 static irqreturn_t meson_irq(int irq, void *arg) 72 { 73 struct drm_device *dev = arg; 74 struct meson_drm *priv = dev->dev_private; 75 76 (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG)); 77 78 meson_crtc_irq(priv); 79 80 return IRQ_HANDLED; 81 } 82 83 static int meson_dumb_create(struct drm_file *file, struct drm_device *dev, 84 struct drm_mode_create_dumb *args) 85 { 86 /* 87 * We need 64bytes aligned stride, and PAGE aligned size 88 */ 89 args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64); 90 args->size = PAGE_ALIGN(args->pitch * args->height); 91 92 return drm_gem_dma_dumb_create_internal(file, dev, args); 93 } 94 95 DEFINE_DRM_GEM_DMA_FOPS(fops); 96 97 static const struct drm_driver meson_driver = { 98 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 99 100 /* DMA Ops */ 101 DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create), 102 DRM_FBDEV_DMA_DRIVER_OPS, 103 104 /* Misc */ 105 .fops = &fops, 106 .name = DRIVER_NAME, 107 .desc = DRIVER_DESC, 108 .date = "20161109", 109 .major = 1, 110 .minor = 0, 111 }; 112 113 static bool meson_vpu_has_available_connectors(struct device *dev) 114 { 115 struct device_node *ep, *remote; 116 117 /* Parses each endpoint and check if remote exists */ 118 for_each_endpoint_of_node(dev->of_node, ep) { 119 /* If the endpoint node exists, consider it enabled */ 120 remote = of_graph_get_remote_port(ep); 121 if (remote) { 122 of_node_put(remote); 123 of_node_put(ep); 124 return true; 125 } 126 } 127 128 return false; 129 } 130 131 static const struct regmap_config meson_regmap_config = { 132 .reg_bits = 32, 133 .val_bits = 32, 134 .reg_stride = 4, 135 .max_register = 0x1000, 136 }; 137 138 static void meson_vpu_init(struct meson_drm *priv) 139 { 140 u32 value; 141 142 /* 143 * Slave dc0 and dc5 connected to master port 1. 144 * By default other slaves are connected to master port 0. 145 */ 146 value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) | 147 VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1); 148 writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C1)); 149 150 /* Slave dc0 connected to master port 1 */ 151 value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1); 152 writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C2)); 153 154 /* Slave dc4 and dc7 connected to master port 1 */ 155 value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) | 156 VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1); 157 writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L2C1)); 158 159 /* Slave dc1 connected to master port 1 */ 160 value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1); 161 writel_relaxed(value, priv->io_base + _REG(VPU_WRARB_MODE_L2C1)); 162 } 163 164 struct meson_drm_soc_attr { 165 struct meson_drm_soc_limits limits; 166 const struct soc_device_attribute *attrs; 167 }; 168 169 static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = { 170 /* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */ 171 { 172 .limits = { 173 .max_hdmi_phy_freq = 1650000, 174 }, 175 .attrs = (const struct soc_device_attribute []) { 176 { .soc_id = "GXL (S805*)", }, 177 { /* sentinel */ } 178 } 179 }, 180 }; 181 182 static int meson_drv_bind_master(struct device *dev, bool has_components) 183 { 184 struct platform_device *pdev = to_platform_device(dev); 185 const struct meson_drm_match_data *match; 186 struct meson_drm *priv; 187 struct drm_device *drm; 188 struct resource *res; 189 void __iomem *regs; 190 int ret, i; 191 192 /* Checks if an output connector is available */ 193 if (!meson_vpu_has_available_connectors(dev)) { 194 dev_err(dev, "No output connector available\n"); 195 return -ENODEV; 196 } 197 198 match = of_device_get_match_data(dev); 199 if (!match) 200 return -ENODEV; 201 202 drm = drm_dev_alloc(&meson_driver, dev); 203 if (IS_ERR(drm)) 204 return PTR_ERR(drm); 205 206 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 207 if (!priv) { 208 ret = -ENOMEM; 209 goto free_drm; 210 } 211 drm->dev_private = priv; 212 priv->drm = drm; 213 priv->dev = dev; 214 priv->compat = match->compat; 215 priv->afbcd.ops = match->afbcd_ops; 216 217 regs = devm_platform_ioremap_resource_byname(pdev, "vpu"); 218 if (IS_ERR(regs)) { 219 ret = PTR_ERR(regs); 220 goto free_drm; 221 } 222 223 priv->io_base = regs; 224 225 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); 226 if (!res) { 227 ret = -EINVAL; 228 goto free_drm; 229 } 230 /* Simply ioremap since it may be a shared register zone */ 231 regs = devm_ioremap(dev, res->start, resource_size(res)); 232 if (!regs) { 233 ret = -EADDRNOTAVAIL; 234 goto free_drm; 235 } 236 237 priv->hhi = devm_regmap_init_mmio(dev, regs, 238 &meson_regmap_config); 239 if (IS_ERR(priv->hhi)) { 240 dev_err(&pdev->dev, "Couldn't create the HHI regmap\n"); 241 ret = PTR_ERR(priv->hhi); 242 goto free_drm; 243 } 244 245 priv->canvas = meson_canvas_get(dev); 246 if (IS_ERR(priv->canvas)) { 247 ret = PTR_ERR(priv->canvas); 248 goto free_drm; 249 } 250 251 ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); 252 if (ret) 253 goto free_drm; 254 ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0); 255 if (ret) 256 goto free_canvas_osd1; 257 ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1); 258 if (ret) 259 goto free_canvas_vd1_0; 260 ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2); 261 if (ret) 262 goto free_canvas_vd1_1; 263 264 priv->vsync_irq = platform_get_irq(pdev, 0); 265 266 ret = drm_vblank_init(drm, 1); 267 if (ret) 268 goto free_canvas_vd1_2; 269 270 /* Assign limits per soc revision/package */ 271 for (i = 0 ; i < ARRAY_SIZE(meson_drm_soc_attrs) ; ++i) { 272 if (soc_device_match(meson_drm_soc_attrs[i].attrs)) { 273 priv->limits = &meson_drm_soc_attrs[i].limits; 274 break; 275 } 276 } 277 278 /* 279 * Remove early framebuffers (ie. simplefb). The framebuffer can be 280 * located anywhere in RAM 281 */ 282 ret = aperture_remove_all_conflicting_devices(meson_driver.name); 283 if (ret) 284 goto free_canvas_vd1_2; 285 286 ret = drmm_mode_config_init(drm); 287 if (ret) 288 goto free_canvas_vd1_2; 289 drm->mode_config.max_width = 3840; 290 drm->mode_config.max_height = 2160; 291 drm->mode_config.funcs = &meson_mode_config_funcs; 292 drm->mode_config.helper_private = &meson_mode_config_helpers; 293 294 /* Hardware Initialization */ 295 296 meson_vpu_init(priv); 297 meson_venc_init(priv); 298 meson_vpp_init(priv); 299 meson_viu_init(priv); 300 if (priv->afbcd.ops) { 301 ret = priv->afbcd.ops->init(priv); 302 if (ret) 303 goto free_canvas_vd1_2; 304 } 305 306 /* Encoder Initialization */ 307 308 ret = meson_encoder_cvbs_probe(priv); 309 if (ret) 310 goto exit_afbcd; 311 312 if (has_components) { 313 ret = component_bind_all(dev, drm); 314 if (ret) { 315 dev_err(drm->dev, "Couldn't bind all components\n"); 316 /* Do not try to unbind */ 317 has_components = false; 318 goto exit_afbcd; 319 } 320 } 321 322 ret = meson_encoder_hdmi_probe(priv); 323 if (ret) 324 goto exit_afbcd; 325 326 if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { 327 ret = meson_encoder_dsi_probe(priv); 328 if (ret) 329 goto exit_afbcd; 330 } 331 332 ret = meson_plane_create(priv); 333 if (ret) 334 goto exit_afbcd; 335 336 ret = meson_overlay_create(priv); 337 if (ret) 338 goto exit_afbcd; 339 340 ret = meson_crtc_create(priv); 341 if (ret) 342 goto exit_afbcd; 343 344 ret = request_irq(priv->vsync_irq, meson_irq, 0, drm->driver->name, drm); 345 if (ret) 346 goto exit_afbcd; 347 348 drm_mode_config_reset(drm); 349 350 drm_kms_helper_poll_init(drm); 351 352 platform_set_drvdata(pdev, priv); 353 354 ret = drm_dev_register(drm, 0); 355 if (ret) 356 goto uninstall_irq; 357 358 drm_client_setup(drm, NULL); 359 360 return 0; 361 362 uninstall_irq: 363 free_irq(priv->vsync_irq, drm); 364 exit_afbcd: 365 if (priv->afbcd.ops) 366 priv->afbcd.ops->exit(priv); 367 free_canvas_vd1_2: 368 meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2); 369 free_canvas_vd1_1: 370 meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); 371 free_canvas_vd1_0: 372 meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 373 free_canvas_osd1: 374 meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 375 free_drm: 376 drm_dev_put(drm); 377 378 meson_encoder_dsi_remove(priv); 379 meson_encoder_hdmi_remove(priv); 380 meson_encoder_cvbs_remove(priv); 381 382 if (has_components) 383 component_unbind_all(dev, drm); 384 385 return ret; 386 } 387 388 static int meson_drv_bind(struct device *dev) 389 { 390 return meson_drv_bind_master(dev, true); 391 } 392 393 static void meson_drv_unbind(struct device *dev) 394 { 395 struct meson_drm *priv = dev_get_drvdata(dev); 396 struct drm_device *drm = priv->drm; 397 398 if (priv->canvas) { 399 meson_canvas_free(priv->canvas, priv->canvas_id_osd1); 400 meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); 401 meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); 402 meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2); 403 } 404 405 drm_dev_unregister(drm); 406 drm_kms_helper_poll_fini(drm); 407 drm_atomic_helper_shutdown(drm); 408 free_irq(priv->vsync_irq, drm); 409 drm_dev_put(drm); 410 411 meson_encoder_dsi_remove(priv); 412 meson_encoder_hdmi_remove(priv); 413 meson_encoder_cvbs_remove(priv); 414 415 component_unbind_all(dev, drm); 416 417 if (priv->afbcd.ops) 418 priv->afbcd.ops->exit(priv); 419 } 420 421 static const struct component_master_ops meson_drv_master_ops = { 422 .bind = meson_drv_bind, 423 .unbind = meson_drv_unbind, 424 }; 425 426 static int __maybe_unused meson_drv_pm_suspend(struct device *dev) 427 { 428 struct meson_drm *priv = dev_get_drvdata(dev); 429 430 if (!priv) 431 return 0; 432 433 return drm_mode_config_helper_suspend(priv->drm); 434 } 435 436 static int __maybe_unused meson_drv_pm_resume(struct device *dev) 437 { 438 struct meson_drm *priv = dev_get_drvdata(dev); 439 440 if (!priv) 441 return 0; 442 443 meson_vpu_init(priv); 444 meson_venc_init(priv); 445 meson_vpp_init(priv); 446 meson_viu_init(priv); 447 if (priv->afbcd.ops) 448 priv->afbcd.ops->init(priv); 449 450 return drm_mode_config_helper_resume(priv->drm); 451 } 452 453 static void meson_drv_shutdown(struct platform_device *pdev) 454 { 455 struct meson_drm *priv = dev_get_drvdata(&pdev->dev); 456 457 if (!priv) 458 return; 459 460 drm_kms_helper_poll_fini(priv->drm); 461 drm_atomic_helper_shutdown(priv->drm); 462 } 463 464 /* 465 * Only devices to use as components 466 * TOFIX: get rid of components when we can finally 467 * get meson_dx_hdmi to stop using the meson_drm 468 * private structure for HHI registers. 469 */ 470 static const struct of_device_id components_dev_match[] = { 471 { .compatible = "amlogic,meson-gxbb-dw-hdmi" }, 472 { .compatible = "amlogic,meson-gxl-dw-hdmi" }, 473 { .compatible = "amlogic,meson-gxm-dw-hdmi" }, 474 { .compatible = "amlogic,meson-g12a-dw-hdmi" }, 475 {} 476 }; 477 478 static int meson_drv_probe(struct platform_device *pdev) 479 { 480 struct component_match *match = NULL; 481 struct device_node *np = pdev->dev.of_node; 482 struct device_node *ep, *remote; 483 int count = 0; 484 485 for_each_endpoint_of_node(np, ep) { 486 remote = of_graph_get_remote_port_parent(ep); 487 if (!remote || !of_device_is_available(remote)) { 488 of_node_put(remote); 489 continue; 490 } 491 492 if (of_match_node(components_dev_match, remote)) { 493 component_match_add(&pdev->dev, &match, component_compare_of, remote); 494 495 dev_dbg(&pdev->dev, "parent %pOF remote match add %pOF parent %s\n", 496 np, remote, dev_name(&pdev->dev)); 497 } 498 499 of_node_put(remote); 500 501 ++count; 502 } 503 504 if (count && !match) 505 return meson_drv_bind_master(&pdev->dev, false); 506 507 /* If some endpoints were found, initialize the nodes */ 508 if (count) { 509 dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count); 510 511 return component_master_add_with_match(&pdev->dev, 512 &meson_drv_master_ops, 513 match); 514 } 515 516 /* If no output endpoints were available, simply bail out */ 517 return 0; 518 }; 519 520 static void meson_drv_remove(struct platform_device *pdev) 521 { 522 component_master_del(&pdev->dev, &meson_drv_master_ops); 523 } 524 525 static struct meson_drm_match_data meson_drm_gxbb_data = { 526 .compat = VPU_COMPATIBLE_GXBB, 527 }; 528 529 static struct meson_drm_match_data meson_drm_gxl_data = { 530 .compat = VPU_COMPATIBLE_GXL, 531 }; 532 533 static struct meson_drm_match_data meson_drm_gxm_data = { 534 .compat = VPU_COMPATIBLE_GXM, 535 .afbcd_ops = &meson_afbcd_gxm_ops, 536 }; 537 538 static struct meson_drm_match_data meson_drm_g12a_data = { 539 .compat = VPU_COMPATIBLE_G12A, 540 .afbcd_ops = &meson_afbcd_g12a_ops, 541 }; 542 543 static const struct of_device_id dt_match[] = { 544 { .compatible = "amlogic,meson-gxbb-vpu", 545 .data = (void *)&meson_drm_gxbb_data }, 546 { .compatible = "amlogic,meson-gxl-vpu", 547 .data = (void *)&meson_drm_gxl_data }, 548 { .compatible = "amlogic,meson-gxm-vpu", 549 .data = (void *)&meson_drm_gxm_data }, 550 { .compatible = "amlogic,meson-g12a-vpu", 551 .data = (void *)&meson_drm_g12a_data }, 552 {} 553 }; 554 MODULE_DEVICE_TABLE(of, dt_match); 555 556 static const struct dev_pm_ops meson_drv_pm_ops = { 557 SET_SYSTEM_SLEEP_PM_OPS(meson_drv_pm_suspend, meson_drv_pm_resume) 558 }; 559 560 static struct platform_driver meson_drm_platform_driver = { 561 .probe = meson_drv_probe, 562 .remove = meson_drv_remove, 563 .shutdown = meson_drv_shutdown, 564 .driver = { 565 .name = "meson-drm", 566 .of_match_table = dt_match, 567 .pm = &meson_drv_pm_ops, 568 }, 569 }; 570 571 drm_module_platform_driver(meson_drm_platform_driver); 572 573 MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>"); 574 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 575 MODULE_DESCRIPTION(DRIVER_DESC); 576 MODULE_LICENSE("GPL"); 577