root/drivers/gpu/drm/panel/panel-arm-versatile.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. to_versatile_panel
  2. versatile_panel_disable
  3. versatile_panel_enable
  4. versatile_panel_get_modes
  5. versatile_panel_probe

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Panel driver for the ARM Versatile family reference designs from
   4  * ARM Limited.
   5  *
   6  * Author:
   7  * Linus Walleij <linus.wallei@linaro.org>
   8  *
   9  * On the Versatile AB, these panels come mounted on daughterboards
  10  * named "IB1" or "IB2" (Interface Board 1 & 2 respectively.) They
  11  * are documented in ARM DUI 0225D Appendix C and D. These daughter
  12  * boards support TFT display panels.
  13  *
  14  * - The IB1 is a passive board where the display connector defines a
  15  *   few wires for encoding the display type for autodetection,
  16  *   suitable display settings can then be looked up from this setting.
  17  *   The magic bits can be read out from the system controller.
  18  *
  19  * - The IB2 is a more complex board intended for GSM phone development
  20  *   with some logic and a control register, which needs to be accessed
  21  *   and the board display needs to be turned on explicitly.
  22  *
  23  * On the Versatile PB, a special CLCD adaptor board is available
  24  * supporting the same displays as the Versatile AB, plus one more
  25  * Epson QCIF display.
  26  *
  27  */
  28 
  29 #include <linux/bitops.h>
  30 #include <linux/init.h>
  31 #include <linux/kernel.h>
  32 #include <linux/mfd/syscon.h>
  33 #include <linux/mod_devicetable.h>
  34 #include <linux/module.h>
  35 #include <linux/platform_device.h>
  36 #include <linux/regmap.h>
  37 
  38 #include <video/of_videomode.h>
  39 #include <video/videomode.h>
  40 
  41 #include <drm/drm_modes.h>
  42 #include <drm/drm_panel.h>
  43 
  44 /*
  45  * This configuration register in the Versatile and RealView
  46  * family is uniformly present but appears more and more
  47  * unutilized starting with the RealView series.
  48  */
  49 #define SYS_CLCD                        0x50
  50 
  51 /* The Versatile can detect the connected panel type */
  52 #define SYS_CLCD_CLCDID_MASK            (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12))
  53 #define SYS_CLCD_ID_SANYO_3_8           (0x00 << 8)
  54 #define SYS_CLCD_ID_SHARP_8_4           (0x01 << 8)
  55 #define SYS_CLCD_ID_EPSON_2_2           (0x02 << 8)
  56 #define SYS_CLCD_ID_SANYO_2_5           (0x07 << 8)
  57 #define SYS_CLCD_ID_VGA                 (0x1f << 8)
  58 
  59 /* IB2 control register for the Versatile daughterboard */
  60 #define IB2_CTRL                        0x00
  61 #define IB2_CTRL_LCD_SD                 BIT(1) /* 1 = shut down LCD */
  62 #define IB2_CTRL_LCD_BL_ON              BIT(0)
  63 #define IB2_CTRL_LCD_MASK               (BIT(0)|BIT(1))
  64 
  65 /**
  66  * struct versatile_panel_type - lookup struct for the supported panels
  67  */
  68 struct versatile_panel_type {
  69         /**
  70          * @name: the name of this panel
  71          */
  72         const char *name;
  73         /**
  74          * @magic: the magic value from the detection register
  75          */
  76         u32 magic;
  77         /**
  78          * @mode: the DRM display mode for this panel
  79          */
  80         struct drm_display_mode mode;
  81         /**
  82          * @bus_flags: the DRM bus flags for this panel e.g. inverted clock
  83          */
  84         u32 bus_flags;
  85         /**
  86          * @width_mm: the panel width in mm
  87          */
  88         u32 width_mm;
  89         /**
  90          * @height_mm: the panel height in mm
  91          */
  92         u32 height_mm;
  93         /**
  94          * @ib2: the panel may be connected on an IB2 daughterboard
  95          */
  96         bool ib2;
  97 };
  98 
  99 /**
 100  * struct versatile_panel - state container for the Versatile panels
 101  */
 102 struct versatile_panel {
 103         /**
 104          * @dev: the container device
 105          */
 106         struct device *dev;
 107         /**
 108          * @panel: the DRM panel instance for this device
 109          */
 110         struct drm_panel panel;
 111         /**
 112          * @panel_type: the Versatile panel type as detected
 113          */
 114         const struct versatile_panel_type *panel_type;
 115         /**
 116          * @map: map to the parent syscon where the main register reside
 117          */
 118         struct regmap *map;
 119         /**
 120          * @ib2_map: map to the IB2 syscon, if applicable
 121          */
 122         struct regmap *ib2_map;
 123 };
 124 
 125 static const struct versatile_panel_type versatile_panels[] = {
 126         /*
 127          * Sanyo TM38QV67A02A - 3.8 inch QVGA (320x240) Color TFT
 128          * found on the Versatile AB IB1 connector or the Versatile
 129          * PB adaptor board connector.
 130          */
 131         {
 132                 .name = "Sanyo TM38QV67A02A",
 133                 .magic = SYS_CLCD_ID_SANYO_3_8,
 134                 .width_mm = 79,
 135                 .height_mm = 54,
 136                 .mode = {
 137                         .clock = 10000,
 138                         .hdisplay = 320,
 139                         .hsync_start = 320 + 6,
 140                         .hsync_end = 320 + 6 + 6,
 141                         .htotal = 320 + 6 + 6 + 6,
 142                         .vdisplay = 240,
 143                         .vsync_start = 240 + 5,
 144                         .vsync_end = 240 + 5 + 6,
 145                         .vtotal = 240 + 5 + 6 + 5,
 146                         .vrefresh = 116,
 147                         .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
 148                 },
 149         },
 150         /*
 151          * Sharp LQ084V1DG21 640x480 VGA Color TFT module
 152          * found on the Versatile AB IB1 connector or the Versatile
 153          * PB adaptor board connector.
 154          */
 155         {
 156                 .name = "Sharp LQ084V1DG21",
 157                 .magic = SYS_CLCD_ID_SHARP_8_4,
 158                 .width_mm = 171,
 159                 .height_mm = 130,
 160                 .mode = {
 161                         .clock = 25000,
 162                         .hdisplay = 640,
 163                         .hsync_start = 640 + 24,
 164                         .hsync_end = 640 + 24 + 96,
 165                         .htotal = 640 + 24 + 96 + 24,
 166                         .vdisplay = 480,
 167                         .vsync_start = 480 + 11,
 168                         .vsync_end = 480 + 11 + 2,
 169                         .vtotal = 480 + 11 + 2 + 32,
 170                         .vrefresh = 60,
 171                         .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
 172                 },
 173         },
 174         /*
 175          * Epson L2F50113T00 - 2.2 inch QCIF 176x220 Color TFT
 176          * found on the Versatile PB adaptor board connector.
 177          */
 178         {
 179                 .name = "Epson L2F50113T00",
 180                 .magic = SYS_CLCD_ID_EPSON_2_2,
 181                 .width_mm = 34,
 182                 .height_mm = 45,
 183                 .mode = {
 184                         .clock = 62500,
 185                         .hdisplay = 176,
 186                         .hsync_start = 176 + 2,
 187                         .hsync_end = 176 + 2 + 3,
 188                         .htotal = 176 + 2 + 3 + 3,
 189                         .vdisplay = 220,
 190                         .vsync_start = 220 + 0,
 191                         .vsync_end = 220 + 0 + 2,
 192                         .vtotal = 220 + 0 + 2 + 1,
 193                         .vrefresh = 390,
 194                         .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
 195                 },
 196                 .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
 197         },
 198         /*
 199          * Sanyo ALR252RGT 240x320 portrait display found on the
 200          * Versatile AB IB2 daughterboard for GSM prototyping.
 201          */
 202         {
 203                 .name = "Sanyo ALR252RGT",
 204                 .magic = SYS_CLCD_ID_SANYO_2_5,
 205                 .width_mm = 37,
 206                 .height_mm = 50,
 207                 .mode = {
 208                         .clock = 5400,
 209                         .hdisplay = 240,
 210                         .hsync_start = 240 + 10,
 211                         .hsync_end = 240 + 10 + 10,
 212                         .htotal = 240 + 10 + 10 + 20,
 213                         .vdisplay = 320,
 214                         .vsync_start = 320 + 2,
 215                         .vsync_end = 320 + 2 + 2,
 216                         .vtotal = 320 + 2 + 2 + 2,
 217                         .vrefresh = 116,
 218                         .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
 219                 },
 220                 .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
 221                 .ib2 = true,
 222         },
 223 };
 224 
 225 static inline struct versatile_panel *
 226 to_versatile_panel(struct drm_panel *panel)
 227 {
 228         return container_of(panel, struct versatile_panel, panel);
 229 }
 230 
 231 static int versatile_panel_disable(struct drm_panel *panel)
 232 {
 233         struct versatile_panel *vpanel = to_versatile_panel(panel);
 234 
 235         /* If we're on an IB2 daughterboard, turn off display */
 236         if (vpanel->ib2_map) {
 237                 dev_dbg(vpanel->dev, "disable IB2 display\n");
 238                 regmap_update_bits(vpanel->ib2_map,
 239                                    IB2_CTRL,
 240                                    IB2_CTRL_LCD_MASK,
 241                                    IB2_CTRL_LCD_SD);
 242         }
 243 
 244         return 0;
 245 }
 246 
 247 static int versatile_panel_enable(struct drm_panel *panel)
 248 {
 249         struct versatile_panel *vpanel = to_versatile_panel(panel);
 250 
 251         /* If we're on an IB2 daughterboard, turn on display */
 252         if (vpanel->ib2_map) {
 253                 dev_dbg(vpanel->dev, "enable IB2 display\n");
 254                 regmap_update_bits(vpanel->ib2_map,
 255                                    IB2_CTRL,
 256                                    IB2_CTRL_LCD_MASK,
 257                                    IB2_CTRL_LCD_BL_ON);
 258         }
 259 
 260         return 0;
 261 }
 262 
 263 static int versatile_panel_get_modes(struct drm_panel *panel)
 264 {
 265         struct drm_connector *connector = panel->connector;
 266         struct versatile_panel *vpanel = to_versatile_panel(panel);
 267         struct drm_display_mode *mode;
 268 
 269         connector->display_info.width_mm = vpanel->panel_type->width_mm;
 270         connector->display_info.height_mm = vpanel->panel_type->height_mm;
 271         connector->display_info.bus_flags = vpanel->panel_type->bus_flags;
 272 
 273         mode = drm_mode_duplicate(panel->drm, &vpanel->panel_type->mode);
 274         drm_mode_set_name(mode);
 275         mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 276 
 277         mode->width_mm = vpanel->panel_type->width_mm;
 278         mode->height_mm = vpanel->panel_type->height_mm;
 279         drm_mode_probed_add(connector, mode);
 280 
 281         return 1;
 282 }
 283 
 284 static const struct drm_panel_funcs versatile_panel_drm_funcs = {
 285         .disable = versatile_panel_disable,
 286         .enable = versatile_panel_enable,
 287         .get_modes = versatile_panel_get_modes,
 288 };
 289 
 290 static int versatile_panel_probe(struct platform_device *pdev)
 291 {
 292         struct device *dev = &pdev->dev;
 293         struct versatile_panel *vpanel;
 294         struct device *parent;
 295         struct regmap *map;
 296         int ret;
 297         u32 val;
 298         int i;
 299 
 300         parent = dev->parent;
 301         if (!parent) {
 302                 dev_err(dev, "no parent for versatile panel\n");
 303                 return -ENODEV;
 304         }
 305         map = syscon_node_to_regmap(parent->of_node);
 306         if (IS_ERR(map)) {
 307                 dev_err(dev, "no regmap for versatile panel parent\n");
 308                 return PTR_ERR(map);
 309         }
 310 
 311         vpanel = devm_kzalloc(dev, sizeof(*vpanel), GFP_KERNEL);
 312         if (!vpanel)
 313                 return -ENOMEM;
 314 
 315         ret = regmap_read(map, SYS_CLCD, &val);
 316         if (ret) {
 317                 dev_err(dev, "cannot access syscon regs\n");
 318                 return ret;
 319         }
 320 
 321         val &= SYS_CLCD_CLCDID_MASK;
 322 
 323         for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) {
 324                 const struct versatile_panel_type *pt;
 325 
 326                 pt = &versatile_panels[i];
 327                 if (pt->magic == val) {
 328                         vpanel->panel_type = pt;
 329                         break;
 330                 }
 331         }
 332 
 333         /* No panel detected or VGA, let's leave this show */
 334         if (i == ARRAY_SIZE(versatile_panels)) {
 335                 dev_info(dev, "no panel detected\n");
 336                 return -ENODEV;
 337         }
 338 
 339         dev_info(dev, "detected: %s\n", vpanel->panel_type->name);
 340         vpanel->dev = dev;
 341         vpanel->map = map;
 342 
 343         /* Check if the panel is mounted on an IB2 daughterboard */
 344         if (vpanel->panel_type->ib2) {
 345                 vpanel->ib2_map = syscon_regmap_lookup_by_compatible(
 346                         "arm,versatile-ib2-syscon");
 347                 if (IS_ERR(vpanel->ib2_map))
 348                         vpanel->ib2_map = NULL;
 349                 else
 350                         dev_info(dev, "panel mounted on IB2 daughterboard\n");
 351         }
 352 
 353         drm_panel_init(&vpanel->panel);
 354         vpanel->panel.dev = dev;
 355         vpanel->panel.funcs = &versatile_panel_drm_funcs;
 356 
 357         return drm_panel_add(&vpanel->panel);
 358 }
 359 
 360 static const struct of_device_id versatile_panel_match[] = {
 361         { .compatible = "arm,versatile-tft-panel", },
 362         {},
 363 };
 364 MODULE_DEVICE_TABLE(of, versatile_panel_match);
 365 
 366 static struct platform_driver versatile_panel_driver = {
 367         .probe          = versatile_panel_probe,
 368         .driver         = {
 369                 .name   = "versatile-tft-panel",
 370                 .of_match_table = versatile_panel_match,
 371         },
 372 };
 373 module_platform_driver(versatile_panel_driver);
 374 
 375 MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
 376 MODULE_DESCRIPTION("ARM Versatile panel driver");
 377 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */