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 of_node_put(child); 408 break; 409 } 410 if (of_device_is_compatible(child, "arm,hdlcd")) { 411 has_coretile_hdlcd = true; 412 of_node_put(child); 413 break; 414 } 415 } 416 417 of_node_put(root); 418 419 /* 420 * If there is a coretile HDLCD and it has a driver, 421 * do not mux the CLCD on the motherboard to the DVI. 422 */ 423 if (has_coretile_hdlcd && IS_ENABLED(CONFIG_DRM_HDLCD)) 424 mux_motherboard = false; 425 426 /* 427 * On the Vexpress CA9 we let the CLCD on the coretile 428 * take precedence, so also in this case do not mux the 429 * motherboard to the DVI. 430 */ 431 if (has_coretile_clcd) 432 mux_motherboard = false; 433 434 if (mux_motherboard) { 435 dev_info(dev, "DVI muxed to motherboard CLCD\n"); 436 val = VEXPRESS_FPGAMUX_MOTHERBOARD; 437 } else if (ct_clcd == dev->of_node) { 438 dev_info(dev, 439 "DVI muxed to daughterboard 1 (core tile) CLCD\n"); 440 val = VEXPRESS_FPGAMUX_DAUGHTERBOARD_1; 441 } else { 442 dev_info(dev, "core tile graphics present\n"); 443 dev_info(dev, "this device will be deactivated\n"); 444 return -ENODEV; 445 } 446 447 /* Call into deep Vexpress configuration API */ 448 pdev = of_find_device_by_node(np); 449 if (!pdev) { 450 dev_err(dev, "can't find the sysreg device, deferring\n"); 451 return -EPROBE_DEFER; 452 } 453 454 map = devm_regmap_init_vexpress_config(&pdev->dev); 455 if (IS_ERR(map)) { 456 platform_device_put(pdev); 457 return PTR_ERR(map); 458 } 459 460 ret = regmap_write(map, 0, val); 461 platform_device_put(pdev); 462 if (ret) { 463 dev_err(dev, "error setting DVI muxmode\n"); 464 return -ENODEV; 465 } 466 467 priv->variant = &pl111_vexpress; 468 dev_info(dev, "initializing Versatile Express PL111\n"); 469 470 return 0; 471 } 472 473 int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv) 474 { 475 const struct of_device_id *clcd_id; 476 enum versatile_clcd versatile_clcd_type; 477 struct device_node *np; 478 struct regmap *map; 479 480 np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match, 481 &clcd_id); 482 if (!np) { 483 /* Non-ARM reference designs, just bail out */ 484 return 0; 485 } 486 487 versatile_clcd_type = (enum versatile_clcd)clcd_id->data; 488 489 /* Versatile Express special handling */ 490 if (versatile_clcd_type == VEXPRESS_CLCD_V2M) { 491 int ret = pl111_vexpress_clcd_init(dev, np, priv); 492 of_node_put(np); 493 if (ret) 494 dev_err(dev, "Versatile Express init failed - %d", ret); 495 return ret; 496 } 497 498 /* 499 * On the Integrator, check if we should use the IM-PD1 instead, 500 * if we find it, it will take precedence. This is on the Integrator/AP 501 * which only has this option for PL110 graphics. 502 */ 503 if (versatile_clcd_type == INTEGRATOR_CLCD_CM) { 504 np = of_find_matching_node_and_match(NULL, impd1_clcd_of_match, 505 &clcd_id); 506 if (np) 507 versatile_clcd_type = (enum versatile_clcd)clcd_id->data; 508 } 509 510 map = syscon_node_to_regmap(np); 511 of_node_put(np); 512 if (IS_ERR(map)) { 513 dev_err(dev, "no Versatile syscon regmap\n"); 514 return PTR_ERR(map); 515 } 516 517 switch (versatile_clcd_type) { 518 case INTEGRATOR_CLCD_CM: 519 versatile_syscon_map = map; 520 priv->variant = &pl110_integrator; 521 priv->variant_display_enable = pl111_integrator_enable; 522 dev_info(dev, "set up callbacks for Integrator PL110\n"); 523 break; 524 case INTEGRATOR_IMPD1: 525 versatile_syscon_map = map; 526 priv->variant = &pl110_impd1; 527 priv->variant_display_enable = pl111_impd1_enable; 528 priv->variant_display_disable = pl111_impd1_disable; 529 dev_info(dev, "set up callbacks for IM-PD1 PL110\n"); 530 break; 531 case VERSATILE_CLCD: 532 versatile_syscon_map = map; 533 /* This can do RGB565 with external PLD */ 534 priv->variant = &pl110_versatile; 535 priv->variant_display_enable = pl111_versatile_enable; 536 priv->variant_display_disable = pl111_versatile_disable; 537 /* 538 * The Versatile has a variant halfway between PL110 539 * and PL111 where these two registers have already been 540 * swapped. 541 */ 542 priv->ienb = CLCD_PL111_IENB; 543 priv->ctrl = CLCD_PL111_CNTL; 544 dev_info(dev, "set up callbacks for Versatile PL110\n"); 545 break; 546 case REALVIEW_CLCD_EB: 547 case REALVIEW_CLCD_PB1176: 548 case REALVIEW_CLCD_PB11MP: 549 case REALVIEW_CLCD_PBA8: 550 case REALVIEW_CLCD_PBX: 551 versatile_syscon_map = map; 552 priv->variant = &pl111_realview; 553 priv->variant_display_enable = pl111_realview_clcd_enable; 554 priv->variant_display_disable = pl111_realview_clcd_disable; 555 dev_info(dev, "set up callbacks for RealView PL111\n"); 556 break; 557 default: 558 dev_info(dev, "unknown Versatile system controller\n"); 559 break; 560 } 561 562 return 0; 563 } 564 EXPORT_SYMBOL_GPL(pl111_versatile_init); 565