.. | .. |
---|
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"); |
---|