Russell King | 420c34e | 2011-01-18 20:08:06 +0000 | [diff] [blame] | 1 | #include <linux/device.h> |
| 2 | #include <linux/dma-mapping.h> |
| 3 | #include <linux/amba/bus.h> |
| 4 | #include <linux/amba/clcd.h> |
Linus Walleij | 11c32d7 | 2014-05-22 23:25:14 +0200 | [diff] [blame] | 5 | #include <linux/platform_data/video-clcd-versatile.h> |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 6 | #include <linux/of.h> |
| 7 | #include <linux/of_graph.h> |
| 8 | #include <linux/regmap.h> |
| 9 | #include <linux/mfd/syscon.h> |
| 10 | #include <linux/bitops.h> |
| 11 | #include "amba-clcd-versatile.h" |
Russell King | 420c34e | 2011-01-18 20:08:06 +0000 | [diff] [blame] | 12 | |
| 13 | static struct clcd_panel vga = { |
| 14 | .mode = { |
| 15 | .name = "VGA", |
| 16 | .refresh = 60, |
| 17 | .xres = 640, |
| 18 | .yres = 480, |
| 19 | .pixclock = 39721, |
| 20 | .left_margin = 40, |
| 21 | .right_margin = 24, |
| 22 | .upper_margin = 32, |
| 23 | .lower_margin = 11, |
| 24 | .hsync_len = 96, |
| 25 | .vsync_len = 2, |
| 26 | .sync = 0, |
| 27 | .vmode = FB_VMODE_NONINTERLACED, |
| 28 | }, |
| 29 | .width = -1, |
| 30 | .height = -1, |
| 31 | .tim2 = TIM2_BCD | TIM2_IPC, |
| 32 | .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), |
| 33 | .caps = CLCD_CAP_5551 | CLCD_CAP_565 | CLCD_CAP_888, |
| 34 | .bpp = 16, |
| 35 | }; |
| 36 | |
| 37 | static struct clcd_panel xvga = { |
| 38 | .mode = { |
| 39 | .name = "XVGA", |
| 40 | .refresh = 60, |
| 41 | .xres = 1024, |
| 42 | .yres = 768, |
| 43 | .pixclock = 15748, |
| 44 | .left_margin = 152, |
| 45 | .right_margin = 48, |
| 46 | .upper_margin = 23, |
| 47 | .lower_margin = 3, |
| 48 | .hsync_len = 104, |
| 49 | .vsync_len = 4, |
| 50 | .sync = 0, |
| 51 | .vmode = FB_VMODE_NONINTERLACED, |
| 52 | }, |
| 53 | .width = -1, |
| 54 | .height = -1, |
| 55 | .tim2 = TIM2_BCD | TIM2_IPC, |
| 56 | .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), |
| 57 | .caps = CLCD_CAP_5551 | CLCD_CAP_565 | CLCD_CAP_888, |
| 58 | .bpp = 16, |
| 59 | }; |
| 60 | |
| 61 | /* Sanyo TM38QV67A02A - 3.8 inch QVGA (320x240) Color TFT */ |
| 62 | static struct clcd_panel sanyo_tm38qv67a02a = { |
| 63 | .mode = { |
| 64 | .name = "Sanyo TM38QV67A02A", |
| 65 | .refresh = 116, |
| 66 | .xres = 320, |
| 67 | .yres = 240, |
| 68 | .pixclock = 100000, |
| 69 | .left_margin = 6, |
| 70 | .right_margin = 6, |
| 71 | .upper_margin = 5, |
| 72 | .lower_margin = 5, |
| 73 | .hsync_len = 6, |
| 74 | .vsync_len = 6, |
| 75 | .sync = 0, |
| 76 | .vmode = FB_VMODE_NONINTERLACED, |
| 77 | }, |
| 78 | .width = -1, |
| 79 | .height = -1, |
| 80 | .tim2 = TIM2_BCD, |
| 81 | .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), |
| 82 | .caps = CLCD_CAP_5551, |
| 83 | .bpp = 16, |
| 84 | }; |
| 85 | |
| 86 | static struct clcd_panel sanyo_2_5_in = { |
| 87 | .mode = { |
| 88 | .name = "Sanyo QVGA Portrait", |
| 89 | .refresh = 116, |
| 90 | .xres = 240, |
| 91 | .yres = 320, |
| 92 | .pixclock = 100000, |
| 93 | .left_margin = 20, |
| 94 | .right_margin = 10, |
| 95 | .upper_margin = 2, |
| 96 | .lower_margin = 2, |
| 97 | .hsync_len = 10, |
| 98 | .vsync_len = 2, |
| 99 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, |
| 100 | .vmode = FB_VMODE_NONINTERLACED, |
| 101 | }, |
| 102 | .width = -1, |
| 103 | .height = -1, |
| 104 | .tim2 = TIM2_IVS | TIM2_IHS | TIM2_IPC, |
| 105 | .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), |
| 106 | .caps = CLCD_CAP_5551, |
| 107 | .bpp = 16, |
| 108 | }; |
| 109 | |
| 110 | /* Epson L2F50113T00 - 2.2 inch 176x220 Color TFT */ |
| 111 | static struct clcd_panel epson_l2f50113t00 = { |
| 112 | .mode = { |
| 113 | .name = "Epson L2F50113T00", |
| 114 | .refresh = 390, |
| 115 | .xres = 176, |
| 116 | .yres = 220, |
| 117 | .pixclock = 62500, |
| 118 | .left_margin = 3, |
| 119 | .right_margin = 2, |
| 120 | .upper_margin = 1, |
| 121 | .lower_margin = 0, |
| 122 | .hsync_len = 3, |
| 123 | .vsync_len = 2, |
| 124 | .sync = 0, |
| 125 | .vmode = FB_VMODE_NONINTERLACED, |
| 126 | }, |
| 127 | .width = -1, |
| 128 | .height = -1, |
| 129 | .tim2 = TIM2_BCD | TIM2_IPC, |
| 130 | .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), |
| 131 | .caps = CLCD_CAP_5551, |
| 132 | .bpp = 16, |
| 133 | }; |
| 134 | |
| 135 | static struct clcd_panel *panels[] = { |
| 136 | &vga, |
| 137 | &xvga, |
| 138 | &sanyo_tm38qv67a02a, |
| 139 | &sanyo_2_5_in, |
| 140 | &epson_l2f50113t00, |
| 141 | }; |
| 142 | |
| 143 | struct clcd_panel *versatile_clcd_get_panel(const char *name) |
| 144 | { |
| 145 | int i; |
| 146 | |
| 147 | for (i = 0; i < ARRAY_SIZE(panels); i++) |
| 148 | if (strcmp(panels[i]->mode.name, name) == 0) |
| 149 | break; |
| 150 | |
| 151 | if (i < ARRAY_SIZE(panels)) |
| 152 | return panels[i]; |
| 153 | |
| 154 | pr_err("CLCD: couldn't get parameters for panel %s\n", name); |
| 155 | |
| 156 | return NULL; |
| 157 | } |
| 158 | |
| 159 | int versatile_clcd_setup_dma(struct clcd_fb *fb, unsigned long framesize) |
| 160 | { |
| 161 | dma_addr_t dma; |
| 162 | |
Luis R. Rodriguez | f6e4566 | 2016-01-22 18:34:22 -0800 | [diff] [blame] | 163 | fb->fb.screen_base = dma_alloc_wc(&fb->dev->dev, framesize, &dma, |
| 164 | GFP_KERNEL); |
Russell King | 420c34e | 2011-01-18 20:08:06 +0000 | [diff] [blame] | 165 | if (!fb->fb.screen_base) { |
| 166 | pr_err("CLCD: unable to map framebuffer\n"); |
| 167 | return -ENOMEM; |
| 168 | } |
| 169 | |
| 170 | fb->fb.fix.smem_start = dma; |
| 171 | fb->fb.fix.smem_len = framesize; |
| 172 | |
| 173 | return 0; |
| 174 | } |
| 175 | |
| 176 | int versatile_clcd_mmap_dma(struct clcd_fb *fb, struct vm_area_struct *vma) |
| 177 | { |
Luis R. Rodriguez | f6e4566 | 2016-01-22 18:34:22 -0800 | [diff] [blame] | 178 | return dma_mmap_wc(&fb->dev->dev, vma, fb->fb.screen_base, |
| 179 | fb->fb.fix.smem_start, fb->fb.fix.smem_len); |
Russell King | 420c34e | 2011-01-18 20:08:06 +0000 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | void versatile_clcd_remove_dma(struct clcd_fb *fb) |
| 183 | { |
Luis R. Rodriguez | f6e4566 | 2016-01-22 18:34:22 -0800 | [diff] [blame] | 184 | dma_free_wc(&fb->dev->dev, fb->fb.fix.smem_len, fb->fb.screen_base, |
| 185 | fb->fb.fix.smem_start); |
Russell King | 420c34e | 2011-01-18 20:08:06 +0000 | [diff] [blame] | 186 | } |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 187 | |
| 188 | #ifdef CONFIG_OF |
| 189 | |
| 190 | static struct regmap *versatile_syscon_map; |
| 191 | static struct regmap *versatile_ib2_map; |
| 192 | |
| 193 | /* |
| 194 | * We detect the different syscon types from the compatible strings. |
| 195 | */ |
| 196 | enum versatile_clcd { |
| 197 | INTEGRATOR_CLCD_CM, |
| 198 | VERSATILE_CLCD, |
| 199 | REALVIEW_CLCD_EB, |
| 200 | REALVIEW_CLCD_PB1176, |
| 201 | REALVIEW_CLCD_PB11MP, |
| 202 | REALVIEW_CLCD_PBA8, |
| 203 | REALVIEW_CLCD_PBX, |
| 204 | }; |
| 205 | |
| 206 | static const struct of_device_id versatile_clcd_of_match[] = { |
| 207 | { |
| 208 | .compatible = "arm,core-module-integrator", |
| 209 | .data = (void *)INTEGRATOR_CLCD_CM, |
| 210 | }, |
| 211 | { |
| 212 | .compatible = "arm,versatile-sysreg", |
| 213 | .data = (void *)VERSATILE_CLCD, |
| 214 | }, |
| 215 | { |
| 216 | .compatible = "arm,realview-eb-syscon", |
| 217 | .data = (void *)REALVIEW_CLCD_EB, |
| 218 | }, |
| 219 | { |
| 220 | .compatible = "arm,realview-pb1176-syscon", |
| 221 | .data = (void *)REALVIEW_CLCD_PB1176, |
| 222 | }, |
| 223 | { |
| 224 | .compatible = "arm,realview-pb11mp-syscon", |
| 225 | .data = (void *)REALVIEW_CLCD_PB11MP, |
| 226 | }, |
| 227 | { |
| 228 | .compatible = "arm,realview-pba8-syscon", |
| 229 | .data = (void *)REALVIEW_CLCD_PBA8, |
| 230 | }, |
| 231 | { |
| 232 | .compatible = "arm,realview-pbx-syscon", |
| 233 | .data = (void *)REALVIEW_CLCD_PBX, |
| 234 | }, |
| 235 | {}, |
| 236 | }; |
| 237 | |
| 238 | /* |
| 239 | * Core module CLCD control on the Integrator/CP, bits |
| 240 | * 8 thru 19 of the CM_CONTROL register controls a bunch |
| 241 | * of CLCD settings. |
| 242 | */ |
| 243 | #define INTEGRATOR_HDR_CTRL_OFFSET 0x0C |
| 244 | #define INTEGRATOR_CLCD_LCDBIASEN BIT(8) |
| 245 | #define INTEGRATOR_CLCD_LCDBIASUP BIT(9) |
| 246 | #define INTEGRATOR_CLCD_LCDBIASDN BIT(10) |
| 247 | /* Bits 11,12,13 controls the LCD type */ |
| 248 | #define INTEGRATOR_CLCD_LCDMUX_MASK (BIT(11)|BIT(12)|BIT(13)) |
| 249 | #define INTEGRATOR_CLCD_LCDMUX_LCD24 BIT(11) |
| 250 | #define INTEGRATOR_CLCD_LCDMUX_VGA565 BIT(12) |
| 251 | #define INTEGRATOR_CLCD_LCDMUX_SHARP (BIT(11)|BIT(12)) |
| 252 | #define INTEGRATOR_CLCD_LCDMUX_VGA555 BIT(13) |
| 253 | #define INTEGRATOR_CLCD_LCDMUX_VGA24 (BIT(11)|BIT(12)|BIT(13)) |
| 254 | #define INTEGRATOR_CLCD_LCD0_EN BIT(14) |
| 255 | #define INTEGRATOR_CLCD_LCD1_EN BIT(15) |
| 256 | /* R/L flip on Sharp */ |
| 257 | #define INTEGRATOR_CLCD_LCD_STATIC1 BIT(16) |
| 258 | /* U/D flip on Sharp */ |
| 259 | #define INTEGRATOR_CLCD_LCD_STATIC2 BIT(17) |
| 260 | /* No connection on Sharp */ |
| 261 | #define INTEGRATOR_CLCD_LCD_STATIC BIT(18) |
| 262 | /* 0 = 24bit VGA, 1 = 18bit VGA */ |
| 263 | #define INTEGRATOR_CLCD_LCD_N24BITEN BIT(19) |
| 264 | |
Linus Walleij | 9986908 | 2016-08-29 11:29:31 +0200 | [diff] [blame] | 265 | #define INTEGRATOR_CLCD_MASK (INTEGRATOR_CLCD_LCDBIASEN | \ |
| 266 | INTEGRATOR_CLCD_LCDBIASUP | \ |
| 267 | INTEGRATOR_CLCD_LCDBIASDN | \ |
| 268 | INTEGRATOR_CLCD_LCDMUX_MASK | \ |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 269 | INTEGRATOR_CLCD_LCD0_EN | \ |
| 270 | INTEGRATOR_CLCD_LCD1_EN | \ |
| 271 | INTEGRATOR_CLCD_LCD_STATIC1 | \ |
| 272 | INTEGRATOR_CLCD_LCD_STATIC2 | \ |
| 273 | INTEGRATOR_CLCD_LCD_STATIC | \ |
| 274 | INTEGRATOR_CLCD_LCD_N24BITEN) |
| 275 | |
| 276 | static void integrator_clcd_enable(struct clcd_fb *fb) |
| 277 | { |
| 278 | struct fb_var_screeninfo *var = &fb->fb.var; |
| 279 | u32 val; |
| 280 | |
| 281 | dev_info(&fb->dev->dev, "enable Integrator CLCD connectors\n"); |
| 282 | |
Linus Walleij | 9986908 | 2016-08-29 11:29:31 +0200 | [diff] [blame] | 283 | /* FIXME: really needed? */ |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 284 | val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 | |
| 285 | INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN; |
| 286 | if (var->bits_per_pixel <= 8 || |
| 287 | (var->bits_per_pixel == 16 && var->green.length == 5)) |
| 288 | /* Pseudocolor, RGB555, BGR555 */ |
| 289 | val |= INTEGRATOR_CLCD_LCDMUX_VGA555; |
| 290 | else if (fb->fb.var.bits_per_pixel <= 16) |
| 291 | /* truecolor RGB565 */ |
| 292 | val |= INTEGRATOR_CLCD_LCDMUX_VGA565; |
| 293 | else |
| 294 | val = 0; /* no idea for this, don't trust the docs */ |
| 295 | |
| 296 | regmap_update_bits(versatile_syscon_map, |
| 297 | INTEGRATOR_HDR_CTRL_OFFSET, |
Linus Walleij | 9986908 | 2016-08-29 11:29:31 +0200 | [diff] [blame] | 298 | INTEGRATOR_CLCD_MASK, |
| 299 | val); |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 300 | } |
| 301 | |
| 302 | /* |
| 303 | * This configuration register in the Versatile and RealView |
| 304 | * family is uniformly present but appears more and more |
| 305 | * unutilized starting with the RealView series. |
| 306 | */ |
| 307 | #define SYS_CLCD 0x50 |
| 308 | #define SYS_CLCD_MODE_MASK (BIT(0)|BIT(1)) |
| 309 | #define SYS_CLCD_MODE_888 0 |
| 310 | #define SYS_CLCD_MODE_5551 BIT(0) |
| 311 | #define SYS_CLCD_MODE_565_R_LSB BIT(1) |
| 312 | #define SYS_CLCD_MODE_565_B_LSB (BIT(0)|BIT(1)) |
| 313 | #define SYS_CLCD_CONNECTOR_MASK (BIT(2)|BIT(3)|BIT(4)|BIT(5)) |
| 314 | #define SYS_CLCD_NLCDIOON BIT(2) |
| 315 | #define SYS_CLCD_VDDPOSSWITCH BIT(3) |
| 316 | #define SYS_CLCD_PWR3V5SWITCH BIT(4) |
| 317 | #define SYS_CLCD_VDDNEGSWITCH BIT(5) |
| 318 | #define SYS_CLCD_TSNSS BIT(6) /* touchscreen enable */ |
| 319 | #define SYS_CLCD_SSPEXP BIT(7) /* SSP expansion enable */ |
| 320 | |
| 321 | /* The Versatile can detect the connected panel type */ |
| 322 | #define SYS_CLCD_CLCDID_MASK (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)) |
| 323 | #define SYS_CLCD_ID_SANYO_3_8 (0x00 << 8) |
| 324 | #define SYS_CLCD_ID_SHARP_8_4 (0x01 << 8) |
| 325 | #define SYS_CLCD_ID_EPSON_2_2 (0x02 << 8) |
| 326 | #define SYS_CLCD_ID_SANYO_2_5 (0x07 << 8) |
| 327 | #define SYS_CLCD_ID_VGA (0x1f << 8) |
| 328 | |
| 329 | #define SYS_CLCD_TSNDAV BIT(13) /* data ready from TS */ |
| 330 | |
| 331 | /* IB2 control register for the Versatile daughterboard */ |
| 332 | #define IB2_CTRL 0x00 |
| 333 | #define IB2_CTRL_LCD_SD BIT(1) /* 1 = shut down LCD */ |
| 334 | #define IB2_CTRL_LCD_BL_ON BIT(0) |
| 335 | #define IB2_CTRL_LCD_MASK (BIT(0)|BIT(1)) |
| 336 | |
| 337 | static void versatile_clcd_disable(struct clcd_fb *fb) |
| 338 | { |
| 339 | dev_info(&fb->dev->dev, "disable Versatile CLCD connectors\n"); |
| 340 | regmap_update_bits(versatile_syscon_map, |
| 341 | SYS_CLCD, |
| 342 | SYS_CLCD_CONNECTOR_MASK, |
| 343 | 0); |
| 344 | |
| 345 | /* If we're on an IB2 daughterboard, turn off display */ |
| 346 | if (versatile_ib2_map) { |
| 347 | dev_info(&fb->dev->dev, "disable IB2 display\n"); |
| 348 | regmap_update_bits(versatile_ib2_map, |
| 349 | IB2_CTRL, |
| 350 | IB2_CTRL_LCD_MASK, |
| 351 | IB2_CTRL_LCD_SD); |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | static void versatile_clcd_enable(struct clcd_fb *fb) |
| 356 | { |
| 357 | struct fb_var_screeninfo *var = &fb->fb.var; |
| 358 | u32 val = 0; |
| 359 | |
| 360 | dev_info(&fb->dev->dev, "enable Versatile CLCD connectors\n"); |
| 361 | switch (var->green.length) { |
| 362 | case 5: |
| 363 | val |= SYS_CLCD_MODE_5551; |
| 364 | break; |
| 365 | case 6: |
| 366 | if (var->red.offset == 0) |
| 367 | val |= SYS_CLCD_MODE_565_R_LSB; |
| 368 | else |
| 369 | val |= SYS_CLCD_MODE_565_B_LSB; |
| 370 | break; |
| 371 | case 8: |
| 372 | val |= SYS_CLCD_MODE_888; |
| 373 | break; |
| 374 | } |
| 375 | |
| 376 | /* Set up the MUX */ |
| 377 | regmap_update_bits(versatile_syscon_map, |
| 378 | SYS_CLCD, |
| 379 | SYS_CLCD_MODE_MASK, |
| 380 | val); |
| 381 | |
| 382 | /* Then enable the display */ |
| 383 | regmap_update_bits(versatile_syscon_map, |
| 384 | SYS_CLCD, |
| 385 | SYS_CLCD_CONNECTOR_MASK, |
| 386 | SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH); |
| 387 | |
| 388 | /* If we're on an IB2 daughterboard, turn on display */ |
| 389 | if (versatile_ib2_map) { |
| 390 | dev_info(&fb->dev->dev, "enable IB2 display\n"); |
| 391 | regmap_update_bits(versatile_ib2_map, |
| 392 | IB2_CTRL, |
| 393 | IB2_CTRL_LCD_MASK, |
| 394 | IB2_CTRL_LCD_BL_ON); |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | static void versatile_clcd_decode(struct clcd_fb *fb, struct clcd_regs *regs) |
| 399 | { |
| 400 | clcdfb_decode(fb, regs); |
| 401 | |
| 402 | /* Always clear BGR for RGB565: we do the routing externally */ |
| 403 | if (fb->fb.var.green.length == 6) |
| 404 | regs->cntl &= ~CNTL_BGR; |
| 405 | } |
| 406 | |
| 407 | static void realview_clcd_disable(struct clcd_fb *fb) |
| 408 | { |
| 409 | dev_info(&fb->dev->dev, "disable RealView CLCD connectors\n"); |
| 410 | regmap_update_bits(versatile_syscon_map, |
| 411 | SYS_CLCD, |
| 412 | SYS_CLCD_CONNECTOR_MASK, |
| 413 | 0); |
| 414 | } |
| 415 | |
| 416 | static void realview_clcd_enable(struct clcd_fb *fb) |
| 417 | { |
| 418 | dev_info(&fb->dev->dev, "enable RealView CLCD connectors\n"); |
| 419 | regmap_update_bits(versatile_syscon_map, |
| 420 | SYS_CLCD, |
| 421 | SYS_CLCD_CONNECTOR_MASK, |
| 422 | SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH); |
| 423 | } |
| 424 | |
| 425 | struct versatile_panel { |
| 426 | u32 id; |
| 427 | char *compatible; |
| 428 | bool ib2; |
| 429 | }; |
| 430 | |
| 431 | static const struct versatile_panel versatile_panels[] = { |
| 432 | { |
| 433 | .id = SYS_CLCD_ID_VGA, |
| 434 | .compatible = "VGA", |
| 435 | }, |
| 436 | { |
| 437 | .id = SYS_CLCD_ID_SANYO_3_8, |
| 438 | .compatible = "sanyo,tm38qv67a02a", |
| 439 | }, |
| 440 | { |
| 441 | .id = SYS_CLCD_ID_SHARP_8_4, |
| 442 | .compatible = "sharp,lq084v1dg21", |
| 443 | }, |
| 444 | { |
| 445 | .id = SYS_CLCD_ID_EPSON_2_2, |
| 446 | .compatible = "epson,l2f50113t00", |
| 447 | }, |
| 448 | { |
| 449 | .id = SYS_CLCD_ID_SANYO_2_5, |
| 450 | .compatible = "sanyo,alr252rgt", |
| 451 | .ib2 = true, |
| 452 | }, |
| 453 | }; |
| 454 | |
Vladimir Zapolskiy | be73679 | 2017-01-30 17:39:48 +0100 | [diff] [blame] | 455 | static void versatile_panel_probe(struct device *dev, struct device_node *panel) |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 456 | { |
| 457 | struct versatile_panel const *vpanel = NULL; |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 458 | u32 val; |
| 459 | int ret; |
| 460 | int i; |
| 461 | |
| 462 | /* |
| 463 | * The Versatile CLCD has a panel auto-detection mechanism. |
| 464 | * We use this and look for the compatible panel in the |
| 465 | * device tree. |
| 466 | */ |
| 467 | ret = regmap_read(versatile_syscon_map, SYS_CLCD, &val); |
| 468 | if (ret) { |
| 469 | dev_err(dev, "cannot read CLCD syscon register\n"); |
| 470 | return; |
| 471 | } |
| 472 | val &= SYS_CLCD_CLCDID_MASK; |
| 473 | |
| 474 | /* First find corresponding panel information */ |
| 475 | for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) { |
| 476 | vpanel = &versatile_panels[i]; |
| 477 | |
| 478 | if (val == vpanel->id) { |
| 479 | dev_err(dev, "autodetected panel \"%s\"\n", |
| 480 | vpanel->compatible); |
| 481 | break; |
| 482 | } |
| 483 | } |
| 484 | if (i == ARRAY_SIZE(versatile_panels)) { |
| 485 | dev_err(dev, "could not auto-detect panel\n"); |
| 486 | return; |
| 487 | } |
| 488 | |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 489 | if (!of_device_is_compatible(panel, vpanel->compatible)) |
| 490 | dev_err(dev, "panel in DT is not compatible with the " |
| 491 | "auto-detected panel, continuing anyway\n"); |
| 492 | |
| 493 | /* |
| 494 | * If we have a Sanyo 2.5" port |
| 495 | * that we're running on an IB2 and proceed to look for the |
| 496 | * IB2 syscon regmap. |
| 497 | */ |
| 498 | if (!vpanel->ib2) |
| 499 | return; |
| 500 | |
| 501 | versatile_ib2_map = syscon_regmap_lookup_by_compatible( |
| 502 | "arm,versatile-ib2-syscon"); |
| 503 | if (IS_ERR(versatile_ib2_map)) { |
| 504 | dev_err(dev, "could not locate IB2 control register\n"); |
| 505 | versatile_ib2_map = NULL; |
| 506 | return; |
| 507 | } |
| 508 | } |
| 509 | |
Vladimir Zapolskiy | be73679 | 2017-01-30 17:39:48 +0100 | [diff] [blame] | 510 | int versatile_clcd_init_panel(struct clcd_fb *fb, struct device_node *panel) |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 511 | { |
| 512 | const struct of_device_id *clcd_id; |
| 513 | enum versatile_clcd versatile_clcd_type; |
| 514 | struct device_node *np; |
| 515 | struct regmap *map; |
| 516 | struct device *dev = &fb->dev->dev; |
| 517 | |
| 518 | np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match, |
| 519 | &clcd_id); |
| 520 | if (!np) { |
Linus Walleij | cfbd950 | 2016-10-20 08:46:32 +0200 | [diff] [blame] | 521 | /* Vexpress does not have this */ |
| 522 | return 0; |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 523 | } |
| 524 | versatile_clcd_type = (enum versatile_clcd)clcd_id->data; |
| 525 | |
| 526 | map = syscon_node_to_regmap(np); |
Wei Yongjun | 763e636 | 2016-08-13 01:26:26 +0000 | [diff] [blame] | 527 | if (IS_ERR(map)) { |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 528 | dev_err(dev, "no Versatile syscon regmap\n"); |
Wei Yongjun | 763e636 | 2016-08-13 01:26:26 +0000 | [diff] [blame] | 529 | return PTR_ERR(map); |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 530 | } |
| 531 | |
| 532 | switch (versatile_clcd_type) { |
| 533 | case INTEGRATOR_CLCD_CM: |
| 534 | versatile_syscon_map = map; |
| 535 | fb->board->enable = integrator_clcd_enable; |
| 536 | /* Override the caps, we have only these */ |
| 537 | fb->board->caps = CLCD_CAP_5551 | CLCD_CAP_RGB565 | |
| 538 | CLCD_CAP_888; |
| 539 | dev_info(dev, "set up callbacks for Integrator PL110\n"); |
| 540 | break; |
| 541 | case VERSATILE_CLCD: |
| 542 | versatile_syscon_map = map; |
| 543 | fb->board->enable = versatile_clcd_enable; |
| 544 | fb->board->disable = versatile_clcd_disable; |
| 545 | fb->board->decode = versatile_clcd_decode; |
Vladimir Zapolskiy | be73679 | 2017-01-30 17:39:48 +0100 | [diff] [blame] | 546 | versatile_panel_probe(dev, panel); |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 547 | dev_info(dev, "set up callbacks for Versatile\n"); |
| 548 | break; |
| 549 | case REALVIEW_CLCD_EB: |
| 550 | case REALVIEW_CLCD_PB1176: |
| 551 | case REALVIEW_CLCD_PB11MP: |
| 552 | case REALVIEW_CLCD_PBA8: |
| 553 | case REALVIEW_CLCD_PBX: |
| 554 | versatile_syscon_map = map; |
| 555 | fb->board->enable = realview_clcd_enable; |
| 556 | fb->board->disable = realview_clcd_disable; |
| 557 | dev_info(dev, "set up callbacks for RealView PL111\n"); |
| 558 | break; |
| 559 | default: |
| 560 | dev_info(dev, "unknown Versatile system controller\n"); |
| 561 | break; |
| 562 | } |
| 563 | |
| 564 | return 0; |
| 565 | } |
Arnd Bergmann | ffe439d | 2016-08-26 17:34:29 +0200 | [diff] [blame] | 566 | EXPORT_SYMBOL_GPL(versatile_clcd_init_panel); |
Linus Walleij | 2534816 | 2016-06-16 11:36:18 +0200 | [diff] [blame] | 567 | #endif |