pl111_versatile.c (15026B)
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> 13#include <linux/device.h> 14#include <linux/mfd/syscon.h> 15#include <linux/module.h> 16#include <linux/of.h> 17#include <linux/of_platform.h> 18#include <linux/regmap.h> 19#include <linux/vexpress.h> 20 21#include "pl111_versatile.h" 22#include "pl111_drm.h" 23 24static struct regmap *versatile_syscon_map; 25 26/* 27 * We detect the different syscon types from the compatible strings. 28 */ 29enum versatile_clcd { 30 INTEGRATOR_IMPD1, 31 INTEGRATOR_CLCD_CM, 32 VERSATILE_CLCD, 33 REALVIEW_CLCD_EB, 34 REALVIEW_CLCD_PB1176, 35 REALVIEW_CLCD_PB11MP, 36 REALVIEW_CLCD_PBA8, 37 REALVIEW_CLCD_PBX, 38 VEXPRESS_CLCD_V2M, 39}; 40 41static const struct of_device_id versatile_clcd_of_match[] = { 42 { 43 .compatible = "arm,core-module-integrator", 44 .data = (void *)INTEGRATOR_CLCD_CM, 45 }, 46 { 47 .compatible = "arm,versatile-sysreg", 48 .data = (void *)VERSATILE_CLCD, 49 }, 50 { 51 .compatible = "arm,realview-eb-syscon", 52 .data = (void *)REALVIEW_CLCD_EB, 53 }, 54 { 55 .compatible = "arm,realview-pb1176-syscon", 56 .data = (void *)REALVIEW_CLCD_PB1176, 57 }, 58 { 59 .compatible = "arm,realview-pb11mp-syscon", 60 .data = (void *)REALVIEW_CLCD_PB11MP, 61 }, 62 { 63 .compatible = "arm,realview-pba8-syscon", 64 .data = (void *)REALVIEW_CLCD_PBA8, 65 }, 66 { 67 .compatible = "arm,realview-pbx-syscon", 68 .data = (void *)REALVIEW_CLCD_PBX, 69 }, 70 { 71 .compatible = "arm,vexpress-muxfpga", 72 .data = (void *)VEXPRESS_CLCD_V2M, 73 }, 74 {}, 75}; 76 77static const struct of_device_id impd1_clcd_of_match[] = { 78 { 79 .compatible = "arm,im-pd1-syscon", 80 .data = (void *)INTEGRATOR_IMPD1, 81 }, 82 {}, 83}; 84 85/* 86 * Core module CLCD control on the Integrator/CP, bits 87 * 8 thru 19 of the CM_CONTROL register controls a bunch 88 * of CLCD settings. 89 */ 90#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C 91#define INTEGRATOR_CLCD_LCDBIASEN BIT(8) 92#define INTEGRATOR_CLCD_LCDBIASUP BIT(9) 93#define INTEGRATOR_CLCD_LCDBIASDN BIT(10) 94/* Bits 11,12,13 controls the LCD or VGA bridge type */ 95#define INTEGRATOR_CLCD_LCDMUX_LCD24 BIT(11) 96#define INTEGRATOR_CLCD_LCDMUX_SHARP (BIT(11)|BIT(12)) 97#define INTEGRATOR_CLCD_LCDMUX_VGA555 BIT(13) 98#define INTEGRATOR_CLCD_LCDMUX_VGA24 (BIT(11)|BIT(12)|BIT(13)) 99#define INTEGRATOR_CLCD_LCD0_EN BIT(14) 100#define INTEGRATOR_CLCD_LCD1_EN BIT(15) 101/* R/L flip on Sharp */ 102#define INTEGRATOR_CLCD_LCD_STATIC1 BIT(16) 103/* U/D flip on Sharp */ 104#define INTEGRATOR_CLCD_LCD_STATIC2 BIT(17) 105/* No connection on Sharp */ 106#define INTEGRATOR_CLCD_LCD_STATIC BIT(18) 107/* 0 = 24bit VGA, 1 = 18bit VGA */ 108#define INTEGRATOR_CLCD_LCD_N24BITEN BIT(19) 109 110#define INTEGRATOR_CLCD_MASK GENMASK(19, 8) 111 112static void pl111_integrator_enable(struct drm_device *drm, u32 format) 113{ 114 u32 val; 115 116 dev_info(drm->dev, "enable Integrator CLCD connectors\n"); 117 118 /* FIXME: really needed? */ 119 val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 | 120 INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN; 121 122 switch (format) { 123 case DRM_FORMAT_XBGR8888: 124 case DRM_FORMAT_XRGB8888: 125 /* 24bit formats */ 126 val |= INTEGRATOR_CLCD_LCDMUX_VGA24; 127 break; 128 case DRM_FORMAT_XBGR1555: 129 case DRM_FORMAT_XRGB1555: 130 /* Pseudocolor, RGB555, BGR555 */ 131 val |= INTEGRATOR_CLCD_LCDMUX_VGA555; 132 break; 133 default: 134 dev_err(drm->dev, "unhandled format on Integrator 0x%08x\n", 135 format); 136 break; 137 } 138 139 regmap_update_bits(versatile_syscon_map, 140 INTEGRATOR_HDR_CTRL_OFFSET, 141 INTEGRATOR_CLCD_MASK, 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 152static 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 165static 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); 173} 174 175/* 176 * This configuration register in the Versatile and RealView 177 * family is uniformly present but appears more and more 178 * unutilized starting with the RealView series. 179 */ 180#define SYS_CLCD 0x50 181#define SYS_CLCD_MODE_MASK (BIT(0)|BIT(1)) 182#define SYS_CLCD_MODE_888 0 183#define SYS_CLCD_MODE_5551 BIT(0) 184#define SYS_CLCD_MODE_565_R_LSB BIT(1) 185#define SYS_CLCD_MODE_565_B_LSB (BIT(0)|BIT(1)) 186#define SYS_CLCD_CONNECTOR_MASK (BIT(2)|BIT(3)|BIT(4)|BIT(5)) 187#define SYS_CLCD_NLCDIOON BIT(2) 188#define SYS_CLCD_VDDPOSSWITCH BIT(3) 189#define SYS_CLCD_PWR3V5SWITCH BIT(4) 190#define SYS_CLCD_VDDNEGSWITCH BIT(5) 191 192static void pl111_versatile_disable(struct drm_device *drm) 193{ 194 dev_info(drm->dev, "disable Versatile CLCD connectors\n"); 195 regmap_update_bits(versatile_syscon_map, 196 SYS_CLCD, 197 SYS_CLCD_CONNECTOR_MASK, 198 0); 199} 200 201static void pl111_versatile_enable(struct drm_device *drm, u32 format) 202{ 203 u32 val = 0; 204 205 dev_info(drm->dev, "enable Versatile CLCD connectors\n"); 206 207 switch (format) { 208 case DRM_FORMAT_ABGR8888: 209 case DRM_FORMAT_XBGR8888: 210 case DRM_FORMAT_ARGB8888: 211 case DRM_FORMAT_XRGB8888: 212 val |= SYS_CLCD_MODE_888; 213 break; 214 case DRM_FORMAT_BGR565: 215 val |= SYS_CLCD_MODE_565_R_LSB; 216 break; 217 case DRM_FORMAT_RGB565: 218 val |= SYS_CLCD_MODE_565_B_LSB; 219 break; 220 case DRM_FORMAT_ABGR1555: 221 case DRM_FORMAT_XBGR1555: 222 case DRM_FORMAT_ARGB1555: 223 case DRM_FORMAT_XRGB1555: 224 val |= SYS_CLCD_MODE_5551; 225 break; 226 default: 227 dev_err(drm->dev, "unhandled format on Versatile 0x%08x\n", 228 format); 229 break; 230 } 231 232 /* Set up the MUX */ 233 regmap_update_bits(versatile_syscon_map, 234 SYS_CLCD, 235 SYS_CLCD_MODE_MASK, 236 val); 237 238 /* Then enable the display */ 239 regmap_update_bits(versatile_syscon_map, 240 SYS_CLCD, 241 SYS_CLCD_CONNECTOR_MASK, 242 SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH); 243} 244 245static void pl111_realview_clcd_disable(struct drm_device *drm) 246{ 247 dev_info(drm->dev, "disable RealView CLCD connectors\n"); 248 regmap_update_bits(versatile_syscon_map, 249 SYS_CLCD, 250 SYS_CLCD_CONNECTOR_MASK, 251 0); 252} 253 254static void pl111_realview_clcd_enable(struct drm_device *drm, u32 format) 255{ 256 dev_info(drm->dev, "enable RealView CLCD connectors\n"); 257 regmap_update_bits(versatile_syscon_map, 258 SYS_CLCD, 259 SYS_CLCD_CONNECTOR_MASK, 260 SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH); 261} 262 263/* PL110 pixel formats for Integrator, vanilla PL110 */ 264static const u32 pl110_integrator_pixel_formats[] = { 265 DRM_FORMAT_ABGR8888, 266 DRM_FORMAT_XBGR8888, 267 DRM_FORMAT_ARGB8888, 268 DRM_FORMAT_XRGB8888, 269 DRM_FORMAT_ABGR1555, 270 DRM_FORMAT_XBGR1555, 271 DRM_FORMAT_ARGB1555, 272 DRM_FORMAT_XRGB1555, 273}; 274 275/* Extended PL110 pixel formats for Integrator and Versatile */ 276static const u32 pl110_versatile_pixel_formats[] = { 277 DRM_FORMAT_ABGR8888, 278 DRM_FORMAT_XBGR8888, 279 DRM_FORMAT_ARGB8888, 280 DRM_FORMAT_XRGB8888, 281 DRM_FORMAT_BGR565, /* Uses external PLD */ 282 DRM_FORMAT_RGB565, /* Uses external PLD */ 283 DRM_FORMAT_ABGR1555, 284 DRM_FORMAT_XBGR1555, 285 DRM_FORMAT_ARGB1555, 286 DRM_FORMAT_XRGB1555, 287}; 288 289static const u32 pl111_realview_pixel_formats[] = { 290 DRM_FORMAT_ABGR8888, 291 DRM_FORMAT_XBGR8888, 292 DRM_FORMAT_ARGB8888, 293 DRM_FORMAT_XRGB8888, 294 DRM_FORMAT_BGR565, 295 DRM_FORMAT_RGB565, 296 DRM_FORMAT_ABGR1555, 297 DRM_FORMAT_XBGR1555, 298 DRM_FORMAT_ARGB1555, 299 DRM_FORMAT_XRGB1555, 300 DRM_FORMAT_ABGR4444, 301 DRM_FORMAT_XBGR4444, 302 DRM_FORMAT_ARGB4444, 303 DRM_FORMAT_XRGB4444, 304}; 305 306/* 307 * The Integrator variant is a PL110 with a bunch of broken, or not 308 * yet implemented features 309 */ 310static const struct pl111_variant_data pl110_integrator = { 311 .name = "PL110 Integrator", 312 .is_pl110 = true, 313 .broken_clockdivider = true, 314 .broken_vblank = true, 315 .formats = pl110_integrator_pixel_formats, 316 .nformats = ARRAY_SIZE(pl110_integrator_pixel_formats), 317 .fb_bpp = 16, 318}; 319 320/* 321 * The IM-PD1 variant is a PL110 with a bunch of broken, or not 322 * yet implemented features 323 */ 324static 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/* 335 * This is the in-between PL110 variant found in the ARM Versatile, 336 * supporting RGB565/BGR565 337 */ 338static const struct pl111_variant_data pl110_versatile = { 339 .name = "PL110 Versatile", 340 .is_pl110 = true, 341 .external_bgr = true, 342 .formats = pl110_versatile_pixel_formats, 343 .nformats = ARRAY_SIZE(pl110_versatile_pixel_formats), 344 .fb_bpp = 16, 345}; 346 347/* 348 * RealView PL111 variant, the only real difference from the vanilla 349 * PL111 is that we select 16bpp framebuffer by default to be able 350 * to get 1024x768 without saturating the memory bus. 351 */ 352static const struct pl111_variant_data pl111_realview = { 353 .name = "PL111 RealView", 354 .formats = pl111_realview_pixel_formats, 355 .nformats = ARRAY_SIZE(pl111_realview_pixel_formats), 356 .fb_bpp = 16, 357}; 358 359/* 360 * Versatile Express PL111 variant, again we just push the maximum 361 * BPP to 16 to be able to get 1024x768 without saturating the memory 362 * bus. The clockdivider also seems broken on the Versatile Express. 363 */ 364static const struct pl111_variant_data pl111_vexpress = { 365 .name = "PL111 Versatile Express", 366 .formats = pl111_realview_pixel_formats, 367 .nformats = ARRAY_SIZE(pl111_realview_pixel_formats), 368 .fb_bpp = 16, 369 .broken_clockdivider = true, 370}; 371 372#define VEXPRESS_FPGAMUX_MOTHERBOARD 0x00 373#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_1 0x01 374#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_2 0x02 375 376static 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 break; 406 } 407 if (of_device_is_compatible(child, "arm,hdlcd")) { 408 has_coretile_hdlcd = true; 409 of_node_put(child); 410 break; 411 } 412 } 413 414 of_node_put(root); 415 416 /* 417 * If there is a coretile HDLCD and it has a driver, 418 * do not mux the CLCD on the motherboard to the DVI. 419 */ 420 if (has_coretile_hdlcd && IS_ENABLED(CONFIG_DRM_HDLCD)) 421 mux_motherboard = false; 422 423 /* 424 * On the Vexpress CA9 we let the CLCD on the coretile 425 * take precedence, so also in this case do not mux the 426 * motherboard to the DVI. 427 */ 428 if (has_coretile_clcd) 429 mux_motherboard = false; 430 431 if (mux_motherboard) { 432 dev_info(dev, "DVI muxed to motherboard CLCD\n"); 433 val = VEXPRESS_FPGAMUX_MOTHERBOARD; 434 } else if (ct_clcd == dev->of_node) { 435 dev_info(dev, 436 "DVI muxed to daughterboard 1 (core tile) CLCD\n"); 437 val = VEXPRESS_FPGAMUX_DAUGHTERBOARD_1; 438 } else { 439 dev_info(dev, "core tile graphics present\n"); 440 dev_info(dev, "this device will be deactivated\n"); 441 return -ENODEV; 442 } 443 444 /* Call into deep Vexpress configuration API */ 445 pdev = of_find_device_by_node(np); 446 if (!pdev) { 447 dev_err(dev, "can't find the sysreg device, deferring\n"); 448 return -EPROBE_DEFER; 449 } 450 451 map = devm_regmap_init_vexpress_config(&pdev->dev); 452 if (IS_ERR(map)) { 453 platform_device_put(pdev); 454 return PTR_ERR(map); 455 } 456 457 ret = regmap_write(map, 0, val); 458 platform_device_put(pdev); 459 if (ret) { 460 dev_err(dev, "error setting DVI muxmode\n"); 461 return -ENODEV; 462 } 463 464 priv->variant = &pl111_vexpress; 465 dev_info(dev, "initializing Versatile Express PL111\n"); 466 467 return 0; 468} 469 470int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv) 471{ 472 const struct of_device_id *clcd_id; 473 enum versatile_clcd versatile_clcd_type; 474 struct device_node *np; 475 struct regmap *map; 476 477 np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match, 478 &clcd_id); 479 if (!np) { 480 /* Non-ARM reference designs, just bail out */ 481 return 0; 482 } 483 484 versatile_clcd_type = (enum versatile_clcd)clcd_id->data; 485 486 /* Versatile Express special handling */ 487 if (versatile_clcd_type == VEXPRESS_CLCD_V2M) { 488 int ret = pl111_vexpress_clcd_init(dev, np, priv); 489 of_node_put(np); 490 if (ret) 491 dev_err(dev, "Versatile Express init failed - %d", ret); 492 return ret; 493 } 494 495 /* 496 * On the Integrator, check if we should use the IM-PD1 instead, 497 * if we find it, it will take precedence. This is on the Integrator/AP 498 * which only has this option for PL110 graphics. 499 */ 500 if (versatile_clcd_type == INTEGRATOR_CLCD_CM) { 501 np = of_find_matching_node_and_match(NULL, impd1_clcd_of_match, 502 &clcd_id); 503 if (np) 504 versatile_clcd_type = (enum versatile_clcd)clcd_id->data; 505 } 506 507 map = syscon_node_to_regmap(np); 508 of_node_put(np); 509 if (IS_ERR(map)) { 510 dev_err(dev, "no Versatile syscon regmap\n"); 511 return PTR_ERR(map); 512 } 513 514 switch (versatile_clcd_type) { 515 case INTEGRATOR_CLCD_CM: 516 versatile_syscon_map = map; 517 priv->variant = &pl110_integrator; 518 priv->variant_display_enable = pl111_integrator_enable; 519 dev_info(dev, "set up callbacks for Integrator PL110\n"); 520 break; 521 case INTEGRATOR_IMPD1: 522 versatile_syscon_map = map; 523 priv->variant = &pl110_impd1; 524 priv->variant_display_enable = pl111_impd1_enable; 525 priv->variant_display_disable = pl111_impd1_disable; 526 dev_info(dev, "set up callbacks for IM-PD1 PL110\n"); 527 break; 528 case VERSATILE_CLCD: 529 versatile_syscon_map = map; 530 /* This can do RGB565 with external PLD */ 531 priv->variant = &pl110_versatile; 532 priv->variant_display_enable = pl111_versatile_enable; 533 priv->variant_display_disable = pl111_versatile_disable; 534 /* 535 * The Versatile has a variant halfway between PL110 536 * and PL111 where these two registers have already been 537 * swapped. 538 */ 539 priv->ienb = CLCD_PL111_IENB; 540 priv->ctrl = CLCD_PL111_CNTL; 541 dev_info(dev, "set up callbacks for Versatile PL110\n"); 542 break; 543 case REALVIEW_CLCD_EB: 544 case REALVIEW_CLCD_PB1176: 545 case REALVIEW_CLCD_PB11MP: 546 case REALVIEW_CLCD_PBA8: 547 case REALVIEW_CLCD_PBX: 548 versatile_syscon_map = map; 549 priv->variant = &pl111_realview; 550 priv->variant_display_enable = pl111_realview_clcd_enable; 551 priv->variant_display_disable = pl111_realview_clcd_disable; 552 dev_info(dev, "set up callbacks for RealView PL111\n"); 553 break; 554 default: 555 dev_info(dev, "unknown Versatile system controller\n"); 556 break; 557 } 558 559 return 0; 560} 561EXPORT_SYMBOL_GPL(pl111_versatile_init);