linux/drivers/gpu/drm/pl111/pl111_versatile.c
<<
>>
Prefs
   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);
 562