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