1 // SPDX-License-Identifier: GPL-2.0-only 2 /* drivers/video/backlight/vgg2432a4.c 3 * 4 * VGG2432A4 (ILI9320) LCD controller driver. 5 * 6 * Copyright 2007 Simtec Electronics 7 * http://armlinux.simtec.co.uk/ 8 * Ben Dooks <ben@simtec.co.uk> 9 */ 10 11 #include <linux/delay.h> 12 #include <linux/err.h> 13 #include <linux/init.h> 14 #include <linux/lcd.h> 15 #include <linux/module.h> 16 17 #include <linux/spi/spi.h> 18 19 #include <video/ili9320.h> 20 21 #include "ili9320.h" 22 23 /* Device initialisation sequences */ 24 25 static const struct ili9320_reg vgg_init1[] = { 26 { 27 .address = ILI9320_POWER1, 28 .value = ILI9320_POWER1_AP(0) | ILI9320_POWER1_BT(0), 29 }, { 30 .address = ILI9320_POWER2, 31 .value = (ILI9320_POWER2_VC(7) | 32 ILI9320_POWER2_DC0(0) | ILI9320_POWER2_DC1(0)), 33 }, { 34 .address = ILI9320_POWER3, 35 .value = ILI9320_POWER3_VRH(0), 36 }, { 37 .address = ILI9320_POWER4, 38 .value = ILI9320_POWER4_VREOUT(0), 39 }, 40 }; 41 42 static const struct ili9320_reg vgg_init2[] = { 43 { 44 .address = ILI9320_POWER1, 45 .value = (ILI9320_POWER1_AP(3) | ILI9320_POWER1_APE | 46 ILI9320_POWER1_BT(7) | ILI9320_POWER1_SAP), 47 }, { 48 .address = ILI9320_POWER2, 49 .value = ILI9320_POWER2_VC(7) | ILI9320_POWER2_DC0(3), 50 } 51 }; 52 53 static const struct ili9320_reg vgg_gamma[] = { 54 { 55 .address = ILI9320_GAMMA1, 56 .value = 0x0000, 57 }, { 58 .address = ILI9320_GAMMA2, 59 .value = 0x0505, 60 }, { 61 .address = ILI9320_GAMMA3, 62 .value = 0x0004, 63 }, { 64 .address = ILI9320_GAMMA4, 65 .value = 0x0006, 66 }, { 67 .address = ILI9320_GAMMA5, 68 .value = 0x0707, 69 }, { 70 .address = ILI9320_GAMMA6, 71 .value = 0x0105, 72 }, { 73 .address = ILI9320_GAMMA7, 74 .value = 0x0002, 75 }, { 76 .address = ILI9320_GAMMA8, 77 .value = 0x0707, 78 }, { 79 .address = ILI9320_GAMMA9, 80 .value = 0x0704, 81 }, { 82 .address = ILI9320_GAMMA10, 83 .value = 0x807, 84 } 85 86 }; 87 88 static const struct ili9320_reg vgg_init0[] = { 89 [0] = { 90 /* set direction and scan mode gate */ 91 .address = ILI9320_DRIVER, 92 .value = ILI9320_DRIVER_SS, 93 }, { 94 .address = ILI9320_DRIVEWAVE, 95 .value = (ILI9320_DRIVEWAVE_MUSTSET | 96 ILI9320_DRIVEWAVE_EOR | ILI9320_DRIVEWAVE_BC), 97 }, { 98 .address = ILI9320_ENTRYMODE, 99 .value = ILI9320_ENTRYMODE_ID(3) | ILI9320_ENTRYMODE_BGR, 100 }, { 101 .address = ILI9320_RESIZING, 102 .value = 0x0, 103 }, 104 }; 105 106 107 static int vgg2432a4_lcd_init(struct ili9320 *lcd, 108 struct ili9320_platdata *cfg) 109 { 110 unsigned int addr; 111 int ret; 112 113 /* Set VCore before anything else (VGG243237-6UFLWA) */ 114 ret = ili9320_write(lcd, 0x00e5, 0x8000); 115 if (ret) 116 goto err_initial; 117 118 /* Start the oscillator up before we can do anything else. */ 119 ret = ili9320_write(lcd, ILI9320_OSCILATION, ILI9320_OSCILATION_OSC); 120 if (ret) 121 goto err_initial; 122 123 /* must wait at-lesat 10ms after starting */ 124 mdelay(15); 125 126 ret = ili9320_write_regs(lcd, vgg_init0, ARRAY_SIZE(vgg_init0)); 127 if (ret != 0) 128 goto err_initial; 129 130 ili9320_write(lcd, ILI9320_DISPLAY2, cfg->display2); 131 ili9320_write(lcd, ILI9320_DISPLAY3, cfg->display3); 132 ili9320_write(lcd, ILI9320_DISPLAY4, cfg->display4); 133 134 ili9320_write(lcd, ILI9320_RGB_IF1, cfg->rgb_if1); 135 ili9320_write(lcd, ILI9320_FRAMEMAKER, 0x0); 136 ili9320_write(lcd, ILI9320_RGB_IF2, cfg->rgb_if2); 137 138 ret = ili9320_write_regs(lcd, vgg_init1, ARRAY_SIZE(vgg_init1)); 139 if (ret != 0) 140 goto err_vgg; 141 142 mdelay(300); 143 144 ret = ili9320_write_regs(lcd, vgg_init2, ARRAY_SIZE(vgg_init2)); 145 if (ret != 0) 146 goto err_vgg2; 147 148 mdelay(100); 149 150 ili9320_write(lcd, ILI9320_POWER3, 0x13c); 151 152 mdelay(100); 153 154 ili9320_write(lcd, ILI9320_POWER4, 0x1c00); 155 ili9320_write(lcd, ILI9320_POWER7, 0x000e); 156 157 mdelay(100); 158 159 ili9320_write(lcd, ILI9320_GRAM_HORIZ_ADDR, 0x00); 160 ili9320_write(lcd, ILI9320_GRAM_VERT_ADD, 0x00); 161 162 ret = ili9320_write_regs(lcd, vgg_gamma, ARRAY_SIZE(vgg_gamma)); 163 if (ret != 0) 164 goto err_vgg3; 165 166 ili9320_write(lcd, ILI9320_HORIZ_START, 0x0); 167 ili9320_write(lcd, ILI9320_HORIZ_END, cfg->hsize - 1); 168 ili9320_write(lcd, ILI9320_VERT_START, 0x0); 169 ili9320_write(lcd, ILI9320_VERT_END, cfg->vsize - 1); 170 171 ili9320_write(lcd, ILI9320_DRIVER2, 172 ILI9320_DRIVER2_NL(((cfg->vsize - 240) / 8) + 0x1D)); 173 174 ili9320_write(lcd, ILI9320_BASE_IMAGE, 0x1); 175 ili9320_write(lcd, ILI9320_VERT_SCROLL, 0x00); 176 177 for (addr = ILI9320_PARTIAL1_POSITION; addr <= ILI9320_PARTIAL2_END; 178 addr++) { 179 ili9320_write(lcd, addr, 0x0); 180 } 181 182 ili9320_write(lcd, ILI9320_INTERFACE1, 0x10); 183 ili9320_write(lcd, ILI9320_INTERFACE2, cfg->interface2); 184 ili9320_write(lcd, ILI9320_INTERFACE3, cfg->interface3); 185 ili9320_write(lcd, ILI9320_INTERFACE4, cfg->interface4); 186 ili9320_write(lcd, ILI9320_INTERFACE5, cfg->interface5); 187 ili9320_write(lcd, ILI9320_INTERFACE6, cfg->interface6); 188 189 lcd->display1 = (ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_DTE | 190 ILI9320_DISPLAY1_GON | ILI9320_DISPLAY1_BASEE | 191 0x40); 192 193 ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1); 194 195 return 0; 196 197 err_vgg3: 198 err_vgg2: 199 err_vgg: 200 err_initial: 201 return ret; 202 } 203 204 #ifdef CONFIG_PM_SLEEP 205 static int vgg2432a4_suspend(struct device *dev) 206 { 207 return ili9320_suspend(dev_get_drvdata(dev)); 208 } 209 static int vgg2432a4_resume(struct device *dev) 210 { 211 return ili9320_resume(dev_get_drvdata(dev)); 212 } 213 #endif 214 215 static struct ili9320_client vgg2432a4_client = { 216 .name = "VGG2432A4", 217 .init = vgg2432a4_lcd_init, 218 }; 219 220 /* Device probe */ 221 222 static int vgg2432a4_probe(struct spi_device *spi) 223 { 224 int ret; 225 226 ret = ili9320_probe_spi(spi, &vgg2432a4_client); 227 if (ret != 0) { 228 dev_err(&spi->dev, "failed to initialise ili9320\n"); 229 return ret; 230 } 231 232 return 0; 233 } 234 235 static void vgg2432a4_remove(struct spi_device *spi) 236 { 237 ili9320_remove(spi_get_drvdata(spi)); 238 } 239 240 static void vgg2432a4_shutdown(struct spi_device *spi) 241 { 242 ili9320_shutdown(spi_get_drvdata(spi)); 243 } 244 245 static SIMPLE_DEV_PM_OPS(vgg2432a4_pm_ops, vgg2432a4_suspend, vgg2432a4_resume); 246 247 static struct spi_driver vgg2432a4_driver = { 248 .driver = { 249 .name = "VGG2432A4", 250 .pm = &vgg2432a4_pm_ops, 251 }, 252 .probe = vgg2432a4_probe, 253 .remove = vgg2432a4_remove, 254 .shutdown = vgg2432a4_shutdown, 255 }; 256 257 module_spi_driver(vgg2432a4_driver); 258 259 MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); 260 MODULE_DESCRIPTION("VGG2432A4 LCD Driver"); 261 MODULE_LICENSE("GPL v2"); 262 MODULE_ALIAS("spi:VGG2432A4"); 263