| .. | .. |
|---|
| 1 | | -#include <linux/amba/clcd-regs.h> |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 2 | + |
|---|
| 3 | +/* |
|---|
| 4 | + * Versatile family (ARM reference designs) handling for the PL11x. |
|---|
| 5 | + * This is based on code and know-how in the previous frame buffer |
|---|
| 6 | + * driver in drivers/video/fbdev/amba-clcd.c: |
|---|
| 7 | + * Copyright (C) 2001 ARM Limited, by David A Rusling |
|---|
| 8 | + * Updated to 2.5 by Deep Blue Solutions Ltd. |
|---|
| 9 | + * Major contributions and discoveries by Russell King. |
|---|
| 10 | + */ |
|---|
| 11 | + |
|---|
| 12 | +#include <linux/bitops.h> |
|---|
| 2 | 13 | #include <linux/device.h> |
|---|
| 14 | +#include <linux/mfd/syscon.h> |
|---|
| 15 | +#include <linux/module.h> |
|---|
| 3 | 16 | #include <linux/of.h> |
|---|
| 4 | 17 | #include <linux/of_platform.h> |
|---|
| 5 | 18 | #include <linux/regmap.h> |
|---|
| 6 | | -#include <linux/mfd/syscon.h> |
|---|
| 7 | | -#include <linux/bitops.h> |
|---|
| 8 | | -#include <linux/module.h> |
|---|
| 9 | | -#include <drm/drmP.h> |
|---|
| 19 | +#include <linux/vexpress.h> |
|---|
| 20 | + |
|---|
| 10 | 21 | #include "pl111_versatile.h" |
|---|
| 11 | | -#include "pl111_vexpress.h" |
|---|
| 12 | 22 | #include "pl111_drm.h" |
|---|
| 13 | 23 | |
|---|
| 14 | 24 | static struct regmap *versatile_syscon_map; |
|---|
| .. | .. |
|---|
| 17 | 27 | * We detect the different syscon types from the compatible strings. |
|---|
| 18 | 28 | */ |
|---|
| 19 | 29 | enum versatile_clcd { |
|---|
| 30 | + INTEGRATOR_IMPD1, |
|---|
| 20 | 31 | INTEGRATOR_CLCD_CM, |
|---|
| 21 | 32 | VERSATILE_CLCD, |
|---|
| 22 | 33 | REALVIEW_CLCD_EB, |
|---|
| .. | .. |
|---|
| 59 | 70 | { |
|---|
| 60 | 71 | .compatible = "arm,vexpress-muxfpga", |
|---|
| 61 | 72 | .data = (void *)VEXPRESS_CLCD_V2M, |
|---|
| 73 | + }, |
|---|
| 74 | + {}, |
|---|
| 75 | +}; |
|---|
| 76 | + |
|---|
| 77 | +static const struct of_device_id impd1_clcd_of_match[] = { |
|---|
| 78 | + { |
|---|
| 79 | + .compatible = "arm,im-pd1-syscon", |
|---|
| 80 | + .data = (void *)INTEGRATOR_IMPD1, |
|---|
| 62 | 81 | }, |
|---|
| 63 | 82 | {}, |
|---|
| 64 | 83 | }; |
|---|
| .. | .. |
|---|
| 121 | 140 | INTEGRATOR_HDR_CTRL_OFFSET, |
|---|
| 122 | 141 | INTEGRATOR_CLCD_MASK, |
|---|
| 123 | 142 | val); |
|---|
| 143 | +} |
|---|
| 144 | + |
|---|
| 145 | +#define IMPD1_CTRL_OFFSET 0x18 |
|---|
| 146 | +#define IMPD1_CTRL_DISP_LCD (0 << 0) |
|---|
| 147 | +#define IMPD1_CTRL_DISP_VGA (1 << 0) |
|---|
| 148 | +#define IMPD1_CTRL_DISP_LCD1 (2 << 0) |
|---|
| 149 | +#define IMPD1_CTRL_DISP_ENABLE (1 << 2) |
|---|
| 150 | +#define IMPD1_CTRL_DISP_MASK (7 << 0) |
|---|
| 151 | + |
|---|
| 152 | +static void pl111_impd1_enable(struct drm_device *drm, u32 format) |
|---|
| 153 | +{ |
|---|
| 154 | + u32 val; |
|---|
| 155 | + |
|---|
| 156 | + dev_info(drm->dev, "enable IM-PD1 CLCD connectors\n"); |
|---|
| 157 | + val = IMPD1_CTRL_DISP_VGA | IMPD1_CTRL_DISP_ENABLE; |
|---|
| 158 | + |
|---|
| 159 | + regmap_update_bits(versatile_syscon_map, |
|---|
| 160 | + IMPD1_CTRL_OFFSET, |
|---|
| 161 | + IMPD1_CTRL_DISP_MASK, |
|---|
| 162 | + val); |
|---|
| 163 | +} |
|---|
| 164 | + |
|---|
| 165 | +static void pl111_impd1_disable(struct drm_device *drm) |
|---|
| 166 | +{ |
|---|
| 167 | + dev_info(drm->dev, "disable IM-PD1 CLCD connectors\n"); |
|---|
| 168 | + |
|---|
| 169 | + regmap_update_bits(versatile_syscon_map, |
|---|
| 170 | + IMPD1_CTRL_OFFSET, |
|---|
| 171 | + IMPD1_CTRL_DISP_MASK, |
|---|
| 172 | + 0); |
|---|
| 124 | 173 | } |
|---|
| 125 | 174 | |
|---|
| 126 | 175 | /* |
|---|
| .. | .. |
|---|
| 269 | 318 | }; |
|---|
| 270 | 319 | |
|---|
| 271 | 320 | /* |
|---|
| 321 | + * The IM-PD1 variant is a PL110 with a bunch of broken, or not |
|---|
| 322 | + * yet implemented features |
|---|
| 323 | + */ |
|---|
| 324 | +static const struct pl111_variant_data pl110_impd1 = { |
|---|
| 325 | + .name = "PL110 IM-PD1", |
|---|
| 326 | + .is_pl110 = true, |
|---|
| 327 | + .broken_clockdivider = true, |
|---|
| 328 | + .broken_vblank = true, |
|---|
| 329 | + .formats = pl110_integrator_pixel_formats, |
|---|
| 330 | + .nformats = ARRAY_SIZE(pl110_integrator_pixel_formats), |
|---|
| 331 | + .fb_bpp = 16, |
|---|
| 332 | +}; |
|---|
| 333 | + |
|---|
| 334 | +/* |
|---|
| 272 | 335 | * This is the in-between PL110 variant found in the ARM Versatile, |
|---|
| 273 | 336 | * supporting RGB565/BGR565 |
|---|
| 274 | 337 | */ |
|---|
| .. | .. |
|---|
| 306 | 369 | .broken_clockdivider = true, |
|---|
| 307 | 370 | }; |
|---|
| 308 | 371 | |
|---|
| 372 | +#define VEXPRESS_FPGAMUX_MOTHERBOARD 0x00 |
|---|
| 373 | +#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_1 0x01 |
|---|
| 374 | +#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_2 0x02 |
|---|
| 375 | + |
|---|
| 376 | +static int pl111_vexpress_clcd_init(struct device *dev, struct device_node *np, |
|---|
| 377 | + struct pl111_drm_dev_private *priv) |
|---|
| 378 | +{ |
|---|
| 379 | + struct platform_device *pdev; |
|---|
| 380 | + struct device_node *root; |
|---|
| 381 | + struct device_node *child; |
|---|
| 382 | + struct device_node *ct_clcd = NULL; |
|---|
| 383 | + struct regmap *map; |
|---|
| 384 | + bool has_coretile_clcd = false; |
|---|
| 385 | + bool has_coretile_hdlcd = false; |
|---|
| 386 | + bool mux_motherboard = true; |
|---|
| 387 | + u32 val; |
|---|
| 388 | + int ret; |
|---|
| 389 | + |
|---|
| 390 | + if (!IS_ENABLED(CONFIG_VEXPRESS_CONFIG)) |
|---|
| 391 | + return -ENODEV; |
|---|
| 392 | + |
|---|
| 393 | + /* |
|---|
| 394 | + * Check if we have a CLCD or HDLCD on the core tile by checking if a |
|---|
| 395 | + * CLCD or HDLCD is available in the root of the device tree. |
|---|
| 396 | + */ |
|---|
| 397 | + root = of_find_node_by_path("/"); |
|---|
| 398 | + if (!root) |
|---|
| 399 | + return -EINVAL; |
|---|
| 400 | + |
|---|
| 401 | + for_each_available_child_of_node(root, child) { |
|---|
| 402 | + if (of_device_is_compatible(child, "arm,pl111")) { |
|---|
| 403 | + has_coretile_clcd = true; |
|---|
| 404 | + ct_clcd = child; |
|---|
| 405 | + of_node_put(child); |
|---|
| 406 | + break; |
|---|
| 407 | + } |
|---|
| 408 | + if (of_device_is_compatible(child, "arm,hdlcd")) { |
|---|
| 409 | + has_coretile_hdlcd = true; |
|---|
| 410 | + of_node_put(child); |
|---|
| 411 | + break; |
|---|
| 412 | + } |
|---|
| 413 | + } |
|---|
| 414 | + |
|---|
| 415 | + of_node_put(root); |
|---|
| 416 | + |
|---|
| 417 | + /* |
|---|
| 418 | + * If there is a coretile HDLCD and it has a driver, |
|---|
| 419 | + * do not mux the CLCD on the motherboard to the DVI. |
|---|
| 420 | + */ |
|---|
| 421 | + if (has_coretile_hdlcd && IS_ENABLED(CONFIG_DRM_HDLCD)) |
|---|
| 422 | + mux_motherboard = false; |
|---|
| 423 | + |
|---|
| 424 | + /* |
|---|
| 425 | + * On the Vexpress CA9 we let the CLCD on the coretile |
|---|
| 426 | + * take precedence, so also in this case do not mux the |
|---|
| 427 | + * motherboard to the DVI. |
|---|
| 428 | + */ |
|---|
| 429 | + if (has_coretile_clcd) |
|---|
| 430 | + mux_motherboard = false; |
|---|
| 431 | + |
|---|
| 432 | + if (mux_motherboard) { |
|---|
| 433 | + dev_info(dev, "DVI muxed to motherboard CLCD\n"); |
|---|
| 434 | + val = VEXPRESS_FPGAMUX_MOTHERBOARD; |
|---|
| 435 | + } else if (ct_clcd == dev->of_node) { |
|---|
| 436 | + dev_info(dev, |
|---|
| 437 | + "DVI muxed to daughterboard 1 (core tile) CLCD\n"); |
|---|
| 438 | + val = VEXPRESS_FPGAMUX_DAUGHTERBOARD_1; |
|---|
| 439 | + } else { |
|---|
| 440 | + dev_info(dev, "core tile graphics present\n"); |
|---|
| 441 | + dev_info(dev, "this device will be deactivated\n"); |
|---|
| 442 | + return -ENODEV; |
|---|
| 443 | + } |
|---|
| 444 | + |
|---|
| 445 | + /* Call into deep Vexpress configuration API */ |
|---|
| 446 | + pdev = of_find_device_by_node(np); |
|---|
| 447 | + if (!pdev) { |
|---|
| 448 | + dev_err(dev, "can't find the sysreg device, deferring\n"); |
|---|
| 449 | + return -EPROBE_DEFER; |
|---|
| 450 | + } |
|---|
| 451 | + |
|---|
| 452 | + map = devm_regmap_init_vexpress_config(&pdev->dev); |
|---|
| 453 | + if (IS_ERR(map)) { |
|---|
| 454 | + platform_device_put(pdev); |
|---|
| 455 | + return PTR_ERR(map); |
|---|
| 456 | + } |
|---|
| 457 | + |
|---|
| 458 | + ret = regmap_write(map, 0, val); |
|---|
| 459 | + platform_device_put(pdev); |
|---|
| 460 | + if (ret) { |
|---|
| 461 | + dev_err(dev, "error setting DVI muxmode\n"); |
|---|
| 462 | + return -ENODEV; |
|---|
| 463 | + } |
|---|
| 464 | + |
|---|
| 465 | + priv->variant = &pl111_vexpress; |
|---|
| 466 | + dev_info(dev, "initializing Versatile Express PL111\n"); |
|---|
| 467 | + |
|---|
| 468 | + return 0; |
|---|
| 469 | +} |
|---|
| 470 | + |
|---|
| 309 | 471 | int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv) |
|---|
| 310 | 472 | { |
|---|
| 311 | 473 | const struct of_device_id *clcd_id; |
|---|
| 312 | 474 | enum versatile_clcd versatile_clcd_type; |
|---|
| 313 | 475 | struct device_node *np; |
|---|
| 314 | 476 | struct regmap *map; |
|---|
| 315 | | - int ret; |
|---|
| 316 | 477 | |
|---|
| 317 | 478 | np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match, |
|---|
| 318 | 479 | &clcd_id); |
|---|
| .. | .. |
|---|
| 320 | 481 | /* Non-ARM reference designs, just bail out */ |
|---|
| 321 | 482 | return 0; |
|---|
| 322 | 483 | } |
|---|
| 484 | + |
|---|
| 323 | 485 | versatile_clcd_type = (enum versatile_clcd)clcd_id->data; |
|---|
| 324 | 486 | |
|---|
| 325 | 487 | /* Versatile Express special handling */ |
|---|
| 326 | 488 | if (versatile_clcd_type == VEXPRESS_CLCD_V2M) { |
|---|
| 327 | | - struct platform_device *pdev; |
|---|
| 328 | | - |
|---|
| 329 | | - /* Registers a driver for the muxfpga */ |
|---|
| 330 | | - ret = vexpress_muxfpga_init(); |
|---|
| 331 | | - if (ret) { |
|---|
| 332 | | - dev_err(dev, "unable to initialize muxfpga driver\n"); |
|---|
| 333 | | - of_node_put(np); |
|---|
| 334 | | - return ret; |
|---|
| 335 | | - } |
|---|
| 336 | | - |
|---|
| 337 | | - /* Call into deep Vexpress configuration API */ |
|---|
| 338 | | - pdev = of_find_device_by_node(np); |
|---|
| 339 | | - if (!pdev) { |
|---|
| 340 | | - dev_err(dev, "can't find the sysreg device, deferring\n"); |
|---|
| 341 | | - of_node_put(np); |
|---|
| 342 | | - return -EPROBE_DEFER; |
|---|
| 343 | | - } |
|---|
| 344 | | - map = dev_get_drvdata(&pdev->dev); |
|---|
| 345 | | - if (!map) { |
|---|
| 346 | | - dev_err(dev, "sysreg has not yet probed\n"); |
|---|
| 347 | | - platform_device_put(pdev); |
|---|
| 348 | | - of_node_put(np); |
|---|
| 349 | | - return -EPROBE_DEFER; |
|---|
| 350 | | - } |
|---|
| 351 | | - } else { |
|---|
| 352 | | - map = syscon_node_to_regmap(np); |
|---|
| 489 | + int ret = pl111_vexpress_clcd_init(dev, np, priv); |
|---|
| 490 | + of_node_put(np); |
|---|
| 491 | + if (ret) |
|---|
| 492 | + dev_err(dev, "Versatile Express init failed - %d", ret); |
|---|
| 493 | + return ret; |
|---|
| 353 | 494 | } |
|---|
| 354 | | - of_node_put(np); |
|---|
| 355 | 495 | |
|---|
| 496 | + /* |
|---|
| 497 | + * On the Integrator, check if we should use the IM-PD1 instead, |
|---|
| 498 | + * if we find it, it will take precedence. This is on the Integrator/AP |
|---|
| 499 | + * which only has this option for PL110 graphics. |
|---|
| 500 | + */ |
|---|
| 501 | + if (versatile_clcd_type == INTEGRATOR_CLCD_CM) { |
|---|
| 502 | + np = of_find_matching_node_and_match(NULL, impd1_clcd_of_match, |
|---|
| 503 | + &clcd_id); |
|---|
| 504 | + if (np) |
|---|
| 505 | + versatile_clcd_type = (enum versatile_clcd)clcd_id->data; |
|---|
| 506 | + } |
|---|
| 507 | + |
|---|
| 508 | + map = syscon_node_to_regmap(np); |
|---|
| 509 | + of_node_put(np); |
|---|
| 356 | 510 | if (IS_ERR(map)) { |
|---|
| 357 | 511 | dev_err(dev, "no Versatile syscon regmap\n"); |
|---|
| 358 | 512 | return PTR_ERR(map); |
|---|
| .. | .. |
|---|
| 364 | 518 | priv->variant = &pl110_integrator; |
|---|
| 365 | 519 | priv->variant_display_enable = pl111_integrator_enable; |
|---|
| 366 | 520 | dev_info(dev, "set up callbacks for Integrator PL110\n"); |
|---|
| 521 | + break; |
|---|
| 522 | + case INTEGRATOR_IMPD1: |
|---|
| 523 | + versatile_syscon_map = map; |
|---|
| 524 | + priv->variant = &pl110_impd1; |
|---|
| 525 | + priv->variant_display_enable = pl111_impd1_enable; |
|---|
| 526 | + priv->variant_display_disable = pl111_impd1_disable; |
|---|
| 527 | + dev_info(dev, "set up callbacks for IM-PD1 PL110\n"); |
|---|
| 367 | 528 | break; |
|---|
| 368 | 529 | case VERSATILE_CLCD: |
|---|
| 369 | 530 | versatile_syscon_map = map; |
|---|
| .. | .. |
|---|
| 390 | 551 | priv->variant_display_enable = pl111_realview_clcd_enable; |
|---|
| 391 | 552 | priv->variant_display_disable = pl111_realview_clcd_disable; |
|---|
| 392 | 553 | dev_info(dev, "set up callbacks for RealView PL111\n"); |
|---|
| 393 | | - break; |
|---|
| 394 | | - case VEXPRESS_CLCD_V2M: |
|---|
| 395 | | - priv->variant = &pl111_vexpress; |
|---|
| 396 | | - dev_info(dev, "initializing Versatile Express PL111\n"); |
|---|
| 397 | | - ret = pl111_vexpress_clcd_init(dev, priv, map); |
|---|
| 398 | | - if (ret) |
|---|
| 399 | | - return ret; |
|---|
| 400 | 554 | break; |
|---|
| 401 | 555 | default: |
|---|
| 402 | 556 | dev_info(dev, "unknown Versatile system controller\n"); |
|---|