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