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