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