├── .gitignore ├── Kconfig ├── Makefile ├── README ├── fb_hx8340bn.c ├── fb_hx8347d.c ├── fb_ili9320.c ├── fb_ili9325.c ├── fb_ili9340.c ├── fb_ili9341.c ├── fb_pcd8544.c ├── fb_s6d1121.c ├── fb_ssd1289.c ├── fb_ssd1306.c ├── fb_ssd1331.c ├── fb_ssd1351.c ├── fb_st7735r.c ├── fb_watterott.c ├── fbtft-bus.c ├── fbtft-core.c ├── fbtft-io.c ├── fbtft-sysfs.c ├── fbtft.h ├── fbtft_device.c └── flexfb.c /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is copied from the Linux kernel sources 2 | # 3 | # NOTE! Don't add files that are generated in specific 4 | # subdirectories here. Add them in the ".gitignore" file 5 | # in that subdirectory instead. 6 | # 7 | # NOTE! Please use 'git ls-files -i --exclude-standard' 8 | # command after changing this file, to see if there are 9 | # any tracked files which get ignored after the change. 10 | # 11 | # Normal rules 12 | # 13 | .* 14 | *.o 15 | *.o.* 16 | *.a 17 | *.s 18 | *.ko 19 | *.so 20 | *.so.dbg 21 | *.mod.c 22 | *.i 23 | *.lst 24 | *.symtypes 25 | *.order 26 | modules.builtin 27 | *.elf 28 | *.bin 29 | *.gz 30 | *.bz2 31 | *.lzma 32 | *.xz 33 | *.lzo 34 | *.patch 35 | *.gcno 36 | 37 | # 38 | # Top-level generic files 39 | # 40 | /tags 41 | /TAGS 42 | /linux 43 | /vmlinux 44 | /vmlinuz 45 | /System.map 46 | /Module.markers 47 | /Module.symvers 48 | 49 | # 50 | # Debian directory (make deb-pkg) 51 | # 52 | /debian/ 53 | 54 | # 55 | # git files that we don't want to ignore even it they are dot-files 56 | # 57 | !.gitignore 58 | !.mailmap 59 | 60 | # 61 | # Generated include files 62 | # 63 | include/config 64 | include/linux/version.h 65 | include/generated 66 | arch/*/include/generated 67 | 68 | # stgit generated dirs 69 | patches-* 70 | 71 | # quilt's files 72 | patches 73 | series 74 | 75 | # cscope files 76 | cscope.* 77 | ncscope.* 78 | 79 | # gnu global files 80 | GPATH 81 | GRTAGS 82 | GSYMS 83 | GTAGS 84 | 85 | *.orig 86 | *~ 87 | \#*# 88 | -------------------------------------------------------------------------------- /Kconfig: -------------------------------------------------------------------------------- 1 | menuconfig FB_TFT 2 | tristate "Support for small TFT LCD display modules" 3 | depends on FB && SPI && GPIOLIB 4 | select FB_SYS_FILLRECT 5 | select FB_SYS_COPYAREA 6 | select FB_SYS_IMAGEBLIT 7 | select FB_SYS_FOPS 8 | select FB_DEFERRED_IO 9 | select FB_BACKLIGHT 10 | 11 | config FB_TFT_HX8340BN 12 | tristate "FB driver for the HX8340BN LCD Controller" 13 | depends on FB_TFT 14 | help 15 | Generic Framebuffer support for HX8340BN 16 | 17 | config FB_TFT_HX8347D 18 | tristate "FB driver for the HX8347D LCD Controller" 19 | depends on FB_TFT 20 | help 21 | Generic Framebuffer support for HX8347D 22 | 23 | config FB_TFT_ILI9320 24 | tristate "FB driver for the ILI9320 LCD Controller" 25 | depends on FB_TFT 26 | help 27 | Generic Framebuffer support for ILI9320 28 | 29 | config FB_TFT_ILI9325 30 | tristate "FB driver for the ILI9325 LCD Controller" 31 | depends on FB_TFT 32 | help 33 | Generic Framebuffer support for ILI9325 34 | 35 | config FB_TFT_S6D1121 36 | tristate "FB driver for the S6D1211 LCD Controller" 37 | depends on FB_TFT 38 | help 39 | Generic Framebuffer support for S6D1121 40 | 41 | config FB_TFT_ILI9340 42 | tristate "FB driver for the ILI9340 LCD Controller" 43 | depends on FB_TFT 44 | help 45 | Generic Framebuffer support for ILI9340 46 | 47 | config FB_TFT_ILI9341 48 | tristate "FB driver for the ILI9341 LCD Controller" 49 | depends on FB_TFT 50 | help 51 | Generic Framebuffer support for ILI9341 52 | 53 | config FB_TFT_PCD8544 54 | tristate "FB driver for the PCD8544 LCD Controller" 55 | depends on FB_TFT 56 | help 57 | Generic Framebuffer support for PCD8544 58 | 59 | config FB_TFT_SSD1289 60 | tristate "FB driver for the SSD1289 LCD Controller" 61 | depends on FB_TFT 62 | help 63 | Framebuffer support for SSD1289 64 | 65 | config FB_TFT_SSD1306 66 | tristate "FB driver for the SSD1306 OLED Controller" 67 | depends on FB_TFT 68 | help 69 | Framebuffer support for SSD1306 70 | 71 | config FB_TFT_SSD1331 72 | tristate "FB driver for the SSD1331 LCD Controller" 73 | depends on FB_TFT 74 | help 75 | Framebuffer support for SSD1331 76 | 77 | config FB_TFT_SSD1351 78 | tristate "FB driver for the SSD1351 LCD Controller" 79 | depends on FB_TFT 80 | help 81 | Framebuffer support for SSD1351 82 | 83 | config FB_TFT_ST7735R 84 | tristate "FB driver for the ST7735R LCD Controller" 85 | depends on FB_TFT 86 | help 87 | Generic Framebuffer support for ST7735R 88 | 89 | config FB_TFT_WATTEROTT 90 | tristate "FB driver for the WATTEROTT LCD Controller" 91 | depends on FB_TFT 92 | help 93 | Generic Framebuffer support for WATTEROTT 94 | 95 | config FB_FLEX 96 | tristate "Generic FB driver for TFT LCD displays" 97 | depends on FB_TFT 98 | help 99 | Generic Framebuffer support for TFT LCD displays. 100 | 101 | config FB_TFT_FBTFT_DEVICE 102 | tristate "Module to for adding FBTFT devices" 103 | depends on FB_TFT 104 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Core module 2 | obj-$(CONFIG_FB_TFT) += fbtft.o 3 | fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o 4 | 5 | # drivers 6 | obj-$(CONFIG_FB_TFT_HX8340BN) += fb_hx8340bn.o 7 | obj-$(CONFIG_FB_TFT_HX8347D) += fb_hx8347d.o 8 | obj-$(CONFIG_FB_TFT_ILI9320) += fb_ili9320.o 9 | obj-$(CONFIG_FB_TFT_ILI9325) += fb_ili9325.o 10 | obj-$(CONFIG_FB_TFT_ILI9340) += fb_ili9340.o 11 | obj-$(CONFIG_FB_TFT_ILI9341) += fb_ili9341.o 12 | obj-$(CONFIG_FB_TFT_PCD8544) += fb_pcd8544.o 13 | obj-$(CONFIG_FB_TFT_S6D1121) += fb_s6d1121.o 14 | obj-$(CONFIG_FB_TFT_SSD1289) += fb_ssd1289.o 15 | obj-$(CONFIG_FB_TFT_SSD1306) += fb_ssd1306.o 16 | obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o 17 | obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o 18 | obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o 19 | obj-$(CONFIG_FB_TFT_WATTEROTT) += fb_watterott.o 20 | obj-$(CONFIG_FB_FLEX) += flexfb.o 21 | 22 | # Device modules 23 | obj-$(CONFIG_FB_TFT_FBTFT_DEVICE) += fbtft_device.o 24 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | FBTFT 2 | ========= 3 | 4 | Linux Framebuffer drivers for small TFT LCD display modules. 5 | The module 'fbft' makes writing drivers for some of these displays very easy. 6 | 7 | Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution. 8 | 9 | INSTALLATION 10 | Download kernel sources 11 | 12 | cd drivers/video 13 | git clone https://github.com/notro/fbtft.git 14 | 15 | Add to drivers/video/Kconfig: source "drivers/video/fbtft/Kconfig" 16 | Add to drivers/video/Makefile: obj-y += fbtft/ 17 | 18 | Enable driver(s) in menuconfig and build the kernel 19 | 20 | 21 | See wiki for more information: https://github.com/notro/fbtft/wiki 22 | 23 | 24 | Source: https://github.com/notro/fbtft/ 25 | -------------------------------------------------------------------------------- /fb_hx8340bn.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the HX8340BN LCD Controller 3 | * 4 | * This display uses 9-bit SPI: Data/Command bit + 8 data bits 5 | * For platforms that doesn't support 9-bit, the driver is capable 6 | * of emulating this using 8-bit transfer. 7 | * This is done by transfering eight 9-bit words in 9 bytes. 8 | * 9 | * Copyright (C) 2013 Noralf Tronnes 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this program; if not, write to the Free Software 23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "fbtft.h" 34 | 35 | #define DRVNAME "fb_hx8340bn" 36 | #define WIDTH 176 37 | #define HEIGHT 220 38 | #define TXBUFLEN (4 * PAGE_SIZE) 39 | #define DEFAULT_GAMMA "1 3 0E 5 0 2 09 0 6 1 7 1 0 2 2\n" \ 40 | "3 3 17 8 4 7 05 7 6 0 3 1 6 0 0 " 41 | 42 | 43 | static bool emulate; 44 | module_param(emulate, bool, 0); 45 | MODULE_PARM_DESC(emulate, "Force emulation in 9-bit mode"); 46 | 47 | 48 | static int init_display(struct fbtft_par *par) 49 | { 50 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 51 | 52 | par->fbtftops.reset(par); 53 | 54 | /* BTL221722-276L startup sequence, from datasheet */ 55 | 56 | /* SETEXTCOM: Set extended command set (C1h) 57 | This command is used to set extended command set access enable. 58 | Enable: After command (C1h), must write: ffh,83h,40h */ 59 | write_reg(par, 0xC1, 0xFF, 0x83, 0x40); 60 | 61 | /* Sleep out 62 | This command turns off sleep mode. 63 | In this mode the DC/DC converter is enabled, Internal oscillator 64 | is started, and panel scanning is started. */ 65 | write_reg(par, 0x11); 66 | mdelay(150); 67 | 68 | /* Undoc'd register? */ 69 | write_reg(par, 0xCA, 0x70, 0x00, 0xD9); 70 | 71 | /* SETOSC: Set Internal Oscillator (B0h) 72 | This command is used to set internal oscillator related settings */ 73 | /* OSC_EN: Enable internal oscillator */ 74 | /* Internal oscillator frequency: 125% x 2.52MHz */ 75 | write_reg(par, 0xB0, 0x01, 0x11); 76 | 77 | /* Drive ability setting */ 78 | write_reg(par, 0xC9, 0x90, 0x49, 0x10, 0x28, 0x28, 0x10, 0x00, 0x06); 79 | mdelay(20); 80 | 81 | /* SETPWCTR5: Set Power Control 5(B5h) 82 | This command is used to set VCOM Low and VCOM High Voltage */ 83 | /* VCOMH 0110101 : 3.925 */ 84 | /* VCOML 0100000 : -1.700 */ 85 | /* 45h=69 VCOMH: "VMH" + 5d VCOML: "VMH" + 5d */ 86 | write_reg(par, 0xB5, 0x35, 0x20, 0x45); 87 | 88 | /* SETPWCTR4: Set Power Control 4(B4h) 89 | VRH[4:0]: Specify the VREG1 voltage adjusting. 90 | VREG1 voltage is for gamma voltage setting. 91 | BT[2:0]: Switch the output factor of step-up circuit 2 92 | for VGH and VGL voltage generation. */ 93 | write_reg(par, 0xB4, 0x33, 0x25, 0x4C); 94 | mdelay(10); 95 | 96 | /* Interface Pixel Format (3Ah) 97 | This command is used to define the format of RGB picture data, 98 | which is to be transfer via the system and RGB interface. */ 99 | /* RGB interface: 16 Bit/Pixel */ 100 | write_reg(par, 0x3A, 0x05); 101 | 102 | /* Display on (29h) 103 | This command is used to recover from DISPLAY OFF mode. 104 | Output from the Frame Memory is enabled. */ 105 | write_reg(par, 0x29); 106 | mdelay(10); 107 | 108 | return 0; 109 | } 110 | 111 | void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 112 | { 113 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 114 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 115 | 116 | write_reg(par, FBTFT_CASET, 0x00, xs, 0x00, xe); 117 | write_reg(par, FBTFT_RASET, 0x00, ys, 0x00, ye); 118 | write_reg(par, FBTFT_RAMWR); 119 | } 120 | 121 | static int set_var(struct fbtft_par *par) 122 | { 123 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 124 | 125 | /* MADCTL - Memory data access control */ 126 | /* RGB/BGR can be set with H/W pin SRGB and MADCTL BGR bit */ 127 | #define MY (1 << 7) 128 | #define MX (1 << 6) 129 | #define MV (1 << 5) 130 | switch (par->info->var.rotate) { 131 | case 0: 132 | write_reg(par, 0x36, (par->bgr << 3)); 133 | break; 134 | case 270: 135 | write_reg(par, 0x36, MX | MV | (par->bgr << 3)); 136 | break; 137 | case 180: 138 | write_reg(par, 0x36, MX | MY | (par->bgr << 3)); 139 | break; 140 | case 90: 141 | write_reg(par, 0x36, MY | MV | (par->bgr << 3)); 142 | break; 143 | } 144 | 145 | return 0; 146 | } 147 | 148 | /* 149 | Gamma Curve selection, GC (only GC0 can be customized): 150 | 0 = 2.2, 1 = 1.8, 2 = 2.5, 3 = 1.0 151 | Gamma string format: 152 | OP0 OP1 CP0 CP1 CP2 CP3 CP4 MP0 MP1 MP2 MP3 MP4 MP5 CGM0 CGM1 153 | ON0 ON1 CN0 CN1 CN2 CN3 CN4 MN0 MN1 MN2 MN3 MN4 MN5 XXXX GC 154 | */ 155 | #define CURVE(num, idx) curves[num*par->gamma.num_values + idx] 156 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 157 | { 158 | unsigned long mask[] = { 159 | 0b1111, 0b1111, 0b11111, 0b1111, 0b1111, 0b1111, 0b11111, 160 | 0b111, 0b111, 0b111, 0b111, 0b111, 0b111, 0b11, 0b11, 161 | 0b1111, 0b1111, 0b11111, 0b1111, 0b1111, 0b1111, 0b11111, 162 | 0b111, 0b111, 0b111, 0b111, 0b111, 0b111, 0b0, 0b0 }; 163 | int i, j; 164 | 165 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 166 | 167 | /* apply mask */ 168 | for (i = 0; i < par->gamma.num_curves; i++) 169 | for (j = 0; j < par->gamma.num_values; j++) 170 | CURVE(i, j) &= mask[i * par->gamma.num_values + j]; 171 | 172 | write_reg(par, 0x26, 1 << CURVE(1, 14)); /* Gamma Set (26h) */ 173 | 174 | if (CURVE(1, 14)) 175 | return 0; /* only GC0 can be customized */ 176 | 177 | write_reg(par, 0xC2, 178 | (CURVE(0, 8) << 4) | CURVE(0, 7), 179 | (CURVE(0, 10) << 4) | CURVE(0, 9), 180 | (CURVE(0, 12) << 4) | CURVE(0, 11), 181 | CURVE(0, 2), 182 | (CURVE(0, 4) << 4) | CURVE(0, 3), 183 | CURVE(0, 5), 184 | CURVE(0, 6), 185 | (CURVE(0, 1) << 4) | CURVE(0, 0), 186 | (CURVE(0, 14) << 2) | CURVE(0, 13)); 187 | 188 | write_reg(par, 0xC3, 189 | (CURVE(1, 8) << 4) | CURVE(1, 7), 190 | (CURVE(1, 10) << 4) | CURVE(1, 9), 191 | (CURVE(1, 12) << 4) | CURVE(1, 11), 192 | CURVE(1, 2), 193 | (CURVE(1, 4) << 4) | CURVE(1, 3), 194 | CURVE(1, 5), 195 | CURVE(1, 6), 196 | (CURVE(1, 1) << 4) | CURVE(1, 0)); 197 | 198 | mdelay(10); 199 | 200 | return 0; 201 | } 202 | #undef CURVE 203 | 204 | 205 | static struct fbtft_display display = { 206 | .regwidth = 8, 207 | .width = WIDTH, 208 | .height = HEIGHT, 209 | .txbuflen = TXBUFLEN, 210 | .gamma_num = 2, 211 | .gamma_len = 15, 212 | .gamma = DEFAULT_GAMMA, 213 | .fbtftops = { 214 | .init_display = init_display, 215 | .set_addr_win = set_addr_win, 216 | .set_var = set_var, 217 | .set_gamma = set_gamma, 218 | }, 219 | }; 220 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 221 | 222 | MODULE_ALIAS("spi:" DRVNAME); 223 | MODULE_ALIAS("platform:" DRVNAME); 224 | 225 | MODULE_DESCRIPTION("FB driver for the HX8340BN LCD Controller"); 226 | MODULE_AUTHOR("Noralf Tronnes"); 227 | MODULE_LICENSE("GPL"); 228 | -------------------------------------------------------------------------------- /fb_hx8347d.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the HX8347D LCD Controller 3 | * 4 | * Copyright (C) 2013 Christian Vogelgsang 5 | * 6 | * Based on driver code found here: https://github.com/watterott/r61505u-Adapter 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "fbtft.h" 29 | 30 | #define DRVNAME "fb_hx8347d" 31 | #define WIDTH 320 32 | #define HEIGHT 240 33 | #define DEFAULT_GAMMA "0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" \ 34 | "0 0 0 0 0 0 0 0 0 0 0 0 0 0" 35 | 36 | 37 | static int init_display(struct fbtft_par *par) 38 | { 39 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 40 | 41 | par->fbtftops.reset(par); 42 | 43 | /* driving ability */ 44 | write_reg(par, 0xEA, 0x00); 45 | write_reg(par, 0xEB, 0x20); 46 | write_reg(par, 0xEC, 0x0C); 47 | write_reg(par, 0xED, 0xC4); 48 | write_reg(par, 0xE8, 0x40); 49 | write_reg(par, 0xE9, 0x38); 50 | write_reg(par, 0xF1, 0x01); 51 | write_reg(par, 0xF2, 0x10); 52 | write_reg(par, 0x27, 0xA3); 53 | 54 | /* power voltage */ 55 | write_reg(par, 0x1B, 0x1B); 56 | write_reg(par, 0x1A, 0x01); 57 | write_reg(par, 0x24, 0x2F); 58 | write_reg(par, 0x25, 0x57); 59 | 60 | /* VCOM offset */ 61 | write_reg(par, 0x23, 0x8D); /* for flicker adjust */ 62 | 63 | /* power on */ 64 | write_reg(par, 0x18, 0x36); 65 | write_reg(par, 0x19, 0x01); /* start osc */ 66 | write_reg(par, 0x01, 0x00); /* wakeup */ 67 | write_reg(par, 0x1F, 0x88); 68 | mdelay(5); 69 | write_reg(par, 0x1F, 0x80); 70 | mdelay(5); 71 | write_reg(par, 0x1F, 0x90); 72 | mdelay(5); 73 | write_reg(par, 0x1F, 0xD0); 74 | mdelay(5); 75 | 76 | /* color selection */ 77 | write_reg(par, 0x17, 0x05); /* 65k */ 78 | 79 | /*panel characteristic */ 80 | write_reg(par, 0x36, 0x00); 81 | 82 | /*display on */ 83 | write_reg(par, 0x28, 0x38); 84 | mdelay(40); 85 | write_reg(par, 0x28, 0x3C); 86 | 87 | /* orientation */ 88 | write_reg(par, 0x16, 0x60 | (par->bgr << 3)); 89 | 90 | return 0; 91 | } 92 | 93 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 94 | { 95 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 96 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 97 | 98 | write_reg(par, 0x02, (xs >> 8) & 0xFF); 99 | write_reg(par, 0x03, xs & 0xFF); 100 | write_reg(par, 0x04, (xe >> 8) & 0xFF); 101 | write_reg(par, 0x05, xe & 0xFF); 102 | write_reg(par, 0x06, (ys >> 8) & 0xFF); 103 | write_reg(par, 0x07, ys & 0xFF); 104 | write_reg(par, 0x08, (ye >> 8) & 0xFF); 105 | write_reg(par, 0x09, ye & 0xFF); 106 | write_reg(par, 0x22); 107 | } 108 | 109 | /* 110 | Gamma string format: 111 | VRP0 VRP1 VRP2 VRP3 VRP4 VRP5 PRP0 PRP1 PKP0 PKP1 PKP2 PKP3 PKP4 CGM 112 | VRN0 VRN1 VRN2 VRN3 VRN4 VRN5 PRN0 PRN1 PKN0 PKN1 PKN2 PKN3 PKN4 CGM 113 | */ 114 | #define CURVE(num, idx) curves[num*par->gamma.num_values + idx] 115 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 116 | { 117 | unsigned long mask[] = { 118 | 0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 119 | 0b1111111, 0b1111111, 120 | 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 121 | 0b1111}; 122 | int i, j; 123 | int acc = 0; 124 | 125 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 126 | 127 | /* apply mask */ 128 | for (i = 0; i < par->gamma.num_curves; i++) 129 | for (j = 0; j < par->gamma.num_values; j++) { 130 | acc += CURVE(i, j); 131 | CURVE(i, j) &= mask[j]; 132 | } 133 | 134 | if (acc == 0) /* skip if all values are zero */ 135 | return 0; 136 | 137 | for (i = 0; i < par->gamma.num_curves; i++) { 138 | write_reg(par, 0x40 + (i * 0x10), CURVE(i, 0)); 139 | write_reg(par, 0x41 + (i * 0x10), CURVE(i, 1)); 140 | write_reg(par, 0x42 + (i * 0x10), CURVE(i, 2)); 141 | write_reg(par, 0x43 + (i * 0x10), CURVE(i, 3)); 142 | write_reg(par, 0x44 + (i * 0x10), CURVE(i, 4)); 143 | write_reg(par, 0x45 + (i * 0x10), CURVE(i, 5)); 144 | write_reg(par, 0x46 + (i * 0x10), CURVE(i, 6)); 145 | write_reg(par, 0x47 + (i * 0x10), CURVE(i, 7)); 146 | write_reg(par, 0x48 + (i * 0x10), CURVE(i, 8)); 147 | write_reg(par, 0x49 + (i * 0x10), CURVE(i, 9)); 148 | write_reg(par, 0x4A + (i * 0x10), CURVE(i, 10)); 149 | write_reg(par, 0x4B + (i * 0x10), CURVE(i, 11)); 150 | write_reg(par, 0x4C + (i * 0x10), CURVE(i, 12)); 151 | } 152 | write_reg(par, 0x5D, (CURVE(1, 0) << 4) | CURVE(0, 0)); 153 | 154 | return 0; 155 | } 156 | #undef CURVE 157 | 158 | 159 | static struct fbtft_display display = { 160 | .regwidth = 8, 161 | .width = WIDTH, 162 | .height = HEIGHT, 163 | .gamma_num = 2, 164 | .gamma_len = 14, 165 | .gamma = DEFAULT_GAMMA, 166 | .fbtftops = { 167 | .init_display = init_display, 168 | .set_addr_win = set_addr_win, 169 | .set_gamma = set_gamma, 170 | }, 171 | }; 172 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 173 | 174 | MODULE_ALIAS("spi:" DRVNAME); 175 | MODULE_ALIAS("platform:" DRVNAME); 176 | 177 | MODULE_DESCRIPTION("FB driver for the HX8347D LCD Controller"); 178 | MODULE_AUTHOR("Christian Vogelgsang"); 179 | MODULE_LICENSE("GPL"); 180 | -------------------------------------------------------------------------------- /fb_ili9320.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the ILI9320 LCD Controller 3 | * 4 | * Copyright (C) 2013 Noralf Tronnes 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "fbtft.h" 29 | 30 | #define DRVNAME "fb_ili9320" 31 | #define WIDTH 240 32 | #define HEIGHT 320 33 | #define DEFAULT_GAMMA "07 07 6 0 0 0 5 5 4 0\n" \ 34 | "07 08 4 7 5 1 2 0 7 7" 35 | 36 | 37 | static unsigned read_devicecode(struct fbtft_par *par) 38 | { 39 | int ret; 40 | u8 rxbuf[8] = {0, }; 41 | 42 | write_reg(par, 0x0000); 43 | ret = par->fbtftops.read(par, rxbuf, 4); 44 | return (rxbuf[2] << 8) | rxbuf[3]; 45 | } 46 | 47 | static int init_display(struct fbtft_par *par) 48 | { 49 | unsigned devcode; 50 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 51 | 52 | par->fbtftops.reset(par); 53 | 54 | devcode = read_devicecode(par); 55 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "Device code: 0x%04X\n", 56 | devcode); 57 | if ((devcode != 0x0000) && (devcode != 0x9320)) 58 | dev_warn(par->info->device, 59 | "Unrecognized Device code: 0x%04X (expected 0x9320)\n", 60 | devcode); 61 | 62 | /* Initialization sequence from ILI9320 Application Notes */ 63 | 64 | /* *********** Start Initial Sequence ********* */ 65 | write_reg(par, 0x00E5, 0x8000); /* Set the Vcore voltage and this setting is must. */ 66 | write_reg(par, 0x0000, 0x0001); /* Start internal OSC. */ 67 | write_reg(par, 0x0001, 0x0100); /* set SS and SM bit */ 68 | write_reg(par, 0x0002, 0x0700); /* set 1 line inversion */ 69 | write_reg(par, 0x0004, 0x0000); /* Resize register */ 70 | write_reg(par, 0x0008, 0x0202); /* set the back and front porch */ 71 | write_reg(par, 0x0009, 0x0000); /* set non-display area refresh cycle */ 72 | write_reg(par, 0x000A, 0x0000); /* FMARK function */ 73 | write_reg(par, 0x000C, 0x0000); /* RGB interface setting */ 74 | write_reg(par, 0x000D, 0x0000); /* Frame marker Position */ 75 | write_reg(par, 0x000F, 0x0000); /* RGB interface polarity */ 76 | 77 | /* ***********Power On sequence *************** */ 78 | write_reg(par, 0x0010, 0x0000); /* SAP, BT[3:0], AP, DSTB, SLP, STB */ 79 | write_reg(par, 0x0011, 0x0007); /* DC1[2:0], DC0[2:0], VC[2:0] */ 80 | write_reg(par, 0x0012, 0x0000); /* VREG1OUT voltage */ 81 | write_reg(par, 0x0013, 0x0000); /* VDV[4:0] for VCOM amplitude */ 82 | mdelay(200); /* Dis-charge capacitor power voltage */ 83 | write_reg(par, 0x0010, 0x17B0); /* SAP, BT[3:0], AP, DSTB, SLP, STB */ 84 | write_reg(par, 0x0011, 0x0031); /* R11h=0x0031 at VCI=3.3V DC1[2:0], DC0[2:0], VC[2:0] */ 85 | mdelay(50); 86 | write_reg(par, 0x0012, 0x0138); /* R12h=0x0138 at VCI=3.3V VREG1OUT voltage */ 87 | mdelay(50); 88 | write_reg(par, 0x0013, 0x1800); /* R13h=0x1800 at VCI=3.3V VDV[4:0] for VCOM amplitude */ 89 | write_reg(par, 0x0029, 0x0008); /* R29h=0x0008 at VCI=3.3V VCM[4:0] for VCOMH */ 90 | mdelay(50); 91 | write_reg(par, 0x0020, 0x0000); /* GRAM horizontal Address */ 92 | write_reg(par, 0x0021, 0x0000); /* GRAM Vertical Address */ 93 | 94 | /* ------------------ Set GRAM area --------------- */ 95 | write_reg(par, 0x0050, 0x0000); /* Horizontal GRAM Start Address */ 96 | write_reg(par, 0x0051, 0x00EF); /* Horizontal GRAM End Address */ 97 | write_reg(par, 0x0052, 0x0000); /* Vertical GRAM Start Address */ 98 | write_reg(par, 0x0053, 0x013F); /* Vertical GRAM Start Address */ 99 | write_reg(par, 0x0060, 0x2700); /* Gate Scan Line */ 100 | write_reg(par, 0x0061, 0x0001); /* NDL,VLE, REV */ 101 | write_reg(par, 0x006A, 0x0000); /* set scrolling line */ 102 | 103 | /* -------------- Partial Display Control --------- */ 104 | write_reg(par, 0x0080, 0x0000); 105 | write_reg(par, 0x0081, 0x0000); 106 | write_reg(par, 0x0082, 0x0000); 107 | write_reg(par, 0x0083, 0x0000); 108 | write_reg(par, 0x0084, 0x0000); 109 | write_reg(par, 0x0085, 0x0000); 110 | 111 | /* -------------- Panel Control ------------------- */ 112 | write_reg(par, 0x0090, 0x0010); 113 | write_reg(par, 0x0092, 0x0000); 114 | write_reg(par, 0x0093, 0x0003); 115 | write_reg(par, 0x0095, 0x0110); 116 | write_reg(par, 0x0097, 0x0000); 117 | write_reg(par, 0x0098, 0x0000); 118 | write_reg(par, 0x0007, 0x0173); /* 262K color and display ON */ 119 | 120 | return 0; 121 | } 122 | 123 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 124 | { 125 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 126 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 127 | 128 | switch (par->info->var.rotate) { 129 | /* R20h = Horizontal GRAM Start Address */ 130 | /* R21h = Vertical GRAM Start Address */ 131 | case 0: 132 | write_reg(par, 0x0020, xs); 133 | write_reg(par, 0x0021, ys); 134 | break; 135 | case 180: 136 | write_reg(par, 0x0020, WIDTH - 1 - xs); 137 | write_reg(par, 0x0021, HEIGHT - 1 - ys); 138 | break; 139 | case 270: 140 | write_reg(par, 0x0020, WIDTH - 1 - ys); 141 | write_reg(par, 0x0021, xs); 142 | break; 143 | case 90: 144 | write_reg(par, 0x0020, ys); 145 | write_reg(par, 0x0021, HEIGHT - 1 - xs); 146 | break; 147 | } 148 | write_reg(par, 0x0022); /* Write Data to GRAM */ 149 | } 150 | 151 | static int set_var(struct fbtft_par *par) 152 | { 153 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 154 | 155 | switch (par->info->var.rotate) { 156 | case 0: 157 | write_reg(par, 0x3, (par->bgr << 12) | 0x30); 158 | break; 159 | case 270: 160 | write_reg(par, 0x3, (par->bgr << 12) | 0x28); 161 | break; 162 | case 180: 163 | write_reg(par, 0x3, (par->bgr << 12) | 0x00); 164 | break; 165 | case 90: 166 | write_reg(par, 0x3, (par->bgr << 12) | 0x18); 167 | break; 168 | } 169 | return 0; 170 | } 171 | 172 | /* 173 | Gamma string format: 174 | VRP0 VRP1 RP0 RP1 KP0 KP1 KP2 KP3 KP4 KP5 175 | VRN0 VRN1 RN0 RN1 KN0 KN1 KN2 KN3 KN4 KN5 176 | */ 177 | #define CURVE(num, idx) curves[num*par->gamma.num_values + idx] 178 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 179 | { 180 | unsigned long mask[] = { 181 | 0b11111, 0b11111, 0b111, 0b111, 0b111, 182 | 0b111, 0b111, 0b111, 0b111, 0b111, 183 | 0b11111, 0b11111, 0b111, 0b111, 0b111, 184 | 0b111, 0b111, 0b111, 0b111, 0b111 }; 185 | int i, j; 186 | 187 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 188 | 189 | /* apply mask */ 190 | for (i = 0; i < 2; i++) 191 | for (j = 0; j < 10; j++) 192 | CURVE(i, j) &= mask[i*par->gamma.num_values + j]; 193 | 194 | write_reg(par, 0x0030, CURVE(0, 5) << 8 | CURVE(0, 4)); 195 | write_reg(par, 0x0031, CURVE(0, 7) << 8 | CURVE(0, 6)); 196 | write_reg(par, 0x0032, CURVE(0, 9) << 8 | CURVE(0, 8)); 197 | write_reg(par, 0x0035, CURVE(0, 3) << 8 | CURVE(0, 2)); 198 | write_reg(par, 0x0036, CURVE(0, 1) << 8 | CURVE(0, 0)); 199 | 200 | write_reg(par, 0x0037, CURVE(1, 5) << 8 | CURVE(1, 4)); 201 | write_reg(par, 0x0038, CURVE(1, 7) << 8 | CURVE(1, 6)); 202 | write_reg(par, 0x0039, CURVE(1, 9) << 8 | CURVE(1, 8)); 203 | write_reg(par, 0x003C, CURVE(1, 3) << 8 | CURVE(1, 2)); 204 | write_reg(par, 0x003D, CURVE(1, 1) << 8 | CURVE(1, 0)); 205 | 206 | return 0; 207 | } 208 | #undef CURVE 209 | 210 | 211 | static struct fbtft_display display = { 212 | .regwidth = 16, 213 | .width = WIDTH, 214 | .height = HEIGHT, 215 | .gamma_num = 2, 216 | .gamma_len = 10, 217 | .gamma = DEFAULT_GAMMA, 218 | .fbtftops = { 219 | .init_display = init_display, 220 | .set_addr_win = set_addr_win, 221 | .set_var = set_var, 222 | .set_gamma = set_gamma, 223 | }, 224 | }; 225 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 226 | 227 | MODULE_ALIAS("spi:" DRVNAME); 228 | MODULE_ALIAS("platform:" DRVNAME); 229 | 230 | MODULE_DESCRIPTION("FB driver for the ILI9320 LCD Controller"); 231 | MODULE_AUTHOR("Noralf Tronnes"); 232 | MODULE_LICENSE("GPL"); 233 | -------------------------------------------------------------------------------- /fb_ili9325.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the ILI9325 LCD Controller 3 | * 4 | * Copyright (C) 2013 Noralf Tronnes 5 | * 6 | * Based on ili9325.c by Jeroen Domburg 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "fbtft.h" 30 | 31 | #define DRVNAME "fb_ili9325" 32 | #define WIDTH 240 33 | #define HEIGHT 320 34 | #define BPP 16 35 | #define FPS 20 36 | #define DEFAULT_GAMMA "0F 00 7 2 0 0 6 5 4 1\n" \ 37 | "04 16 2 7 6 3 2 1 7 7" 38 | 39 | 40 | static unsigned bt = 6; /* VGL=Vci*4 , VGH=Vci*4 */ 41 | module_param(bt, uint, 0); 42 | MODULE_PARM_DESC(bt, "Sets the factor used in the step-up circuits"); 43 | 44 | static unsigned vc = 0b011; /* Vci1=Vci*0.80 */ 45 | module_param(vc, uint, 0); 46 | MODULE_PARM_DESC(vc, 47 | "Sets the ratio factor of Vci to generate the reference voltages Vci1"); 48 | 49 | static unsigned vrh = 0b1101; /* VREG1OUT=Vci*1.85 */ 50 | module_param(vrh, uint, 0); 51 | MODULE_PARM_DESC(vrh, 52 | "Set the amplifying rate (1.6 ~ 1.9) of Vci applied to output the VREG1OUT"); 53 | 54 | static unsigned vdv = 0b10010; /* VCOMH amplitude=VREG1OUT*0.98 */ 55 | module_param(vdv, uint, 0); 56 | MODULE_PARM_DESC(vdv, 57 | "Select the factor of VREG1OUT to set the amplitude of Vcom"); 58 | 59 | static unsigned vcm = 0b001010; /* VCOMH=VREG1OUT*0.735 */ 60 | module_param(vcm, uint, 0); 61 | MODULE_PARM_DESC(vcm, "Set the internal VcomH voltage"); 62 | 63 | 64 | /* 65 | Verify that this configuration is within the Voltage limits 66 | 67 | Display module configuration: Vcc = IOVcc = Vci = 3.3V 68 | 69 | Voltages 70 | ---------- 71 | Vci = 3.3 72 | Vci1 = Vci * 0.80 = 2.64 73 | DDVDH = Vci1 * 2 = 5.28 74 | VCL = -Vci1 = -2.64 75 | VREG1OUT = Vci * 1.85 = 4.88 76 | VCOMH = VREG1OUT * 0.735 = 3.59 77 | VCOM amplitude = VREG1OUT * 0.98 = 4.79 78 | VGH = Vci * 4 = 13.2 79 | VGL = -Vci * 4 = -13.2 80 | 81 | Limits 82 | -------- 83 | Power supplies 84 | 1.65 < IOVcc < 3.30 => 1.65 < 3.3 < 3.30 85 | 2.40 < Vcc < 3.30 => 2.40 < 3.3 < 3.30 86 | 2.50 < Vci < 3.30 => 2.50 < 3.3 < 3.30 87 | 88 | Source/VCOM power supply voltage 89 | 4.50 < DDVDH < 6.0 => 4.50 < 5.28 < 6.0 90 | -3.0 < VCL < -2.0 => -3.0 < -2.64 < -2.0 91 | VCI - VCL < 6.0 => 5.94 < 6.0 92 | 93 | Gate driver output voltage 94 | 10 < VGH < 20 => 10 < 13.2 < 20 95 | -15 < VGL < -5 => -15 < -13.2 < -5 96 | VGH - VGL < 32 => 26.4 < 32 97 | 98 | VCOM driver output voltage 99 | VCOMH - VCOML < 6.0 => 4.79 < 6.0 100 | */ 101 | 102 | static int init_display(struct fbtft_par *par) 103 | { 104 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 105 | 106 | par->fbtftops.reset(par); 107 | 108 | if (par->gpio.cs != -1) 109 | gpio_set_value(par->gpio.cs, 0); /* Activate chip */ 110 | 111 | bt &= 0b111; 112 | vc &= 0b111; 113 | vrh &= 0b1111; 114 | vdv &= 0b11111; 115 | vcm &= 0b111111; 116 | 117 | /* Initialization sequence from ILI9325 Application Notes */ 118 | 119 | /* ----------- Start Initial Sequence ----------- */ 120 | write_reg(par, 0x00E3, 0x3008); /* Set internal timing */ 121 | write_reg(par, 0x00E7, 0x0012); /* Set internal timing */ 122 | write_reg(par, 0x00EF, 0x1231); /* Set internal timing */ 123 | write_reg(par, 0x0001, 0x0100); /* set SS and SM bit */ 124 | write_reg(par, 0x0002, 0x0700); /* set 1 line inversion */ 125 | write_reg(par, 0x0004, 0x0000); /* Resize register */ 126 | write_reg(par, 0x0008, 0x0207); /* set the back porch and front porch */ 127 | write_reg(par, 0x0009, 0x0000); /* set non-display area refresh cycle */ 128 | write_reg(par, 0x000A, 0x0000); /* FMARK function */ 129 | write_reg(par, 0x000C, 0x0000); /* RGB interface setting */ 130 | write_reg(par, 0x000D, 0x0000); /* Frame marker Position */ 131 | write_reg(par, 0x000F, 0x0000); /* RGB interface polarity */ 132 | 133 | /* ----------- Power On sequence ----------- */ 134 | write_reg(par, 0x0010, 0x0000); /* SAP, BT[3:0], AP, DSTB, SLP, STB */ 135 | write_reg(par, 0x0011, 0x0007); /* DC1[2:0], DC0[2:0], VC[2:0] */ 136 | write_reg(par, 0x0012, 0x0000); /* VREG1OUT voltage */ 137 | write_reg(par, 0x0013, 0x0000); /* VDV[4:0] for VCOM amplitude */ 138 | mdelay(200); /* Dis-charge capacitor power voltage */ 139 | write_reg(par, 0x0010, /* SAP, BT[3:0], AP, DSTB, SLP, STB */ 140 | (1 << 12) | (bt << 8) | (1 << 7) | (0b001 << 4)); 141 | write_reg(par, 0x0011, 0x220 | vc); /* DC1[2:0], DC0[2:0], VC[2:0] */ 142 | mdelay(50); /* Delay 50ms */ 143 | write_reg(par, 0x0012, vrh); /* Internal reference voltage= Vci; */ 144 | mdelay(50); /* Delay 50ms */ 145 | write_reg(par, 0x0013, vdv << 8); /* Set VDV[4:0] for VCOM amplitude */ 146 | write_reg(par, 0x0029, vcm); /* Set VCM[5:0] for VCOMH */ 147 | write_reg(par, 0x002B, 0x000C); /* Set Frame Rate */ 148 | mdelay(50); /* Delay 50ms */ 149 | write_reg(par, 0x0020, 0x0000); /* GRAM horizontal Address */ 150 | write_reg(par, 0x0021, 0x0000); /* GRAM Vertical Address */ 151 | 152 | /*------------------ Set GRAM area --------------- */ 153 | write_reg(par, 0x0050, 0x0000); /* Horizontal GRAM Start Address */ 154 | write_reg(par, 0x0051, 0x00EF); /* Horizontal GRAM End Address */ 155 | write_reg(par, 0x0052, 0x0000); /* Vertical GRAM Start Address */ 156 | write_reg(par, 0x0053, 0x013F); /* Vertical GRAM Start Address */ 157 | write_reg(par, 0x0060, 0xA700); /* Gate Scan Line */ 158 | write_reg(par, 0x0061, 0x0001); /* NDL,VLE, REV */ 159 | write_reg(par, 0x006A, 0x0000); /* set scrolling line */ 160 | 161 | /*-------------- Partial Display Control --------- */ 162 | write_reg(par, 0x0080, 0x0000); 163 | write_reg(par, 0x0081, 0x0000); 164 | write_reg(par, 0x0082, 0x0000); 165 | write_reg(par, 0x0083, 0x0000); 166 | write_reg(par, 0x0084, 0x0000); 167 | write_reg(par, 0x0085, 0x0000); 168 | 169 | /*-------------- Panel Control ------------------- */ 170 | write_reg(par, 0x0090, 0x0010); 171 | write_reg(par, 0x0092, 0x0600); 172 | write_reg(par, 0x0007, 0x0133); /* 262K color and display ON */ 173 | 174 | return 0; 175 | } 176 | 177 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 178 | { 179 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 180 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 181 | switch (par->info->var.rotate) { 182 | /* R20h = Horizontal GRAM Start Address */ 183 | /* R21h = Vertical GRAM Start Address */ 184 | case 0: 185 | write_reg(par, 0x0020, xs); 186 | write_reg(par, 0x0021, ys); 187 | break; 188 | case 180: 189 | write_reg(par, 0x0020, WIDTH - 1 - xs); 190 | write_reg(par, 0x0021, HEIGHT - 1 - ys); 191 | break; 192 | case 270: 193 | write_reg(par, 0x0020, WIDTH - 1 - ys); 194 | write_reg(par, 0x0021, xs); 195 | break; 196 | case 90: 197 | write_reg(par, 0x0020, ys); 198 | write_reg(par, 0x0021, HEIGHT - 1 - xs); 199 | break; 200 | } 201 | write_reg(par, 0x0022); /* Write Data to GRAM */ 202 | } 203 | 204 | static int set_var(struct fbtft_par *par) 205 | { 206 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 207 | 208 | switch (par->info->var.rotate) { 209 | /* AM: GRAM update direction */ 210 | case 0: 211 | write_reg(par, 0x03, 0x0030 | (par->bgr << 12)); 212 | break; 213 | case 180: 214 | write_reg(par, 0x03, 0x0000 | (par->bgr << 12)); 215 | break; 216 | case 270: 217 | write_reg(par, 0x03, 0x0028 | (par->bgr << 12)); 218 | break; 219 | case 90: 220 | write_reg(par, 0x03, 0x0018 | (par->bgr << 12)); 221 | break; 222 | } 223 | 224 | return 0; 225 | } 226 | 227 | /* 228 | Gamma string format: 229 | VRP0 VRP1 RP0 RP1 KP0 KP1 KP2 KP3 KP4 KP5 230 | VRN0 VRN1 RN0 RN1 KN0 KN1 KN2 KN3 KN4 KN5 231 | */ 232 | #define CURVE(num, idx) curves[num*par->gamma.num_values + idx] 233 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 234 | { 235 | unsigned long mask[] = { 236 | 0b11111, 0b11111, 0b111, 0b111, 0b111, 237 | 0b111, 0b111, 0b111, 0b111, 0b111, 238 | 0b11111, 0b11111, 0b111, 0b111, 0b111, 239 | 0b111, 0b111, 0b111, 0b111, 0b111 }; 240 | int i, j; 241 | 242 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 243 | 244 | /* apply mask */ 245 | for (i = 0; i < 2; i++) 246 | for (j = 0; j < 10; j++) 247 | CURVE(i, j) &= mask[i*par->gamma.num_values + j]; 248 | 249 | write_reg(par, 0x0030, CURVE(0, 5) << 8 | CURVE(0, 4)); 250 | write_reg(par, 0x0031, CURVE(0, 7) << 8 | CURVE(0, 6)); 251 | write_reg(par, 0x0032, CURVE(0, 9) << 8 | CURVE(0, 8)); 252 | write_reg(par, 0x0035, CURVE(0, 3) << 8 | CURVE(0, 2)); 253 | write_reg(par, 0x0036, CURVE(0, 1) << 8 | CURVE(0, 0)); 254 | 255 | write_reg(par, 0x0037, CURVE(1, 5) << 8 | CURVE(1, 4)); 256 | write_reg(par, 0x0038, CURVE(1, 7) << 8 | CURVE(1, 6)); 257 | write_reg(par, 0x0039, CURVE(1, 9) << 8 | CURVE(1, 8)); 258 | write_reg(par, 0x003C, CURVE(1, 3) << 8 | CURVE(1, 2)); 259 | write_reg(par, 0x003D, CURVE(1, 1) << 8 | CURVE(1, 0)); 260 | 261 | return 0; 262 | } 263 | #undef CURVE 264 | 265 | 266 | static struct fbtft_display display = { 267 | .regwidth = 16, 268 | .width = WIDTH, 269 | .height = HEIGHT, 270 | .bpp = BPP, 271 | .fps = FPS, 272 | .gamma_num = 2, 273 | .gamma_len = 10, 274 | .gamma = DEFAULT_GAMMA, 275 | .fbtftops = { 276 | .init_display = init_display, 277 | .set_addr_win = set_addr_win, 278 | .set_var = set_var, 279 | .set_gamma = set_gamma, 280 | }, 281 | }; 282 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 283 | 284 | MODULE_ALIAS("spi:" DRVNAME); 285 | MODULE_ALIAS("platform:" DRVNAME); 286 | 287 | MODULE_DESCRIPTION("FB driver for the ILI9325 LCD Controller"); 288 | MODULE_AUTHOR("Noralf Tronnes"); 289 | MODULE_LICENSE("GPL"); 290 | -------------------------------------------------------------------------------- /fb_ili9340.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the ILI9340 LCD Controller 3 | * 4 | * Copyright (C) 2013 Noralf Tronnes 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "fbtft.h" 28 | 29 | #define DRVNAME "fb_ili9340" 30 | #define WIDTH 240 31 | #define HEIGHT 320 32 | 33 | 34 | /* Init sequence taken from: Arduino Library for the Adafruit 2.2" display */ 35 | static int init_display(struct fbtft_par *par) 36 | { 37 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 38 | 39 | par->fbtftops.reset(par); 40 | 41 | write_reg(par, 0xEF, 0x03, 0x80, 0x02); 42 | write_reg(par, 0xCF, 0x00 , 0XC1 , 0X30); 43 | write_reg(par, 0xED, 0x64 , 0x03 , 0X12 , 0X81); 44 | write_reg(par, 0xE8, 0x85 , 0x00 , 0x78); 45 | write_reg(par, 0xCB, 0x39 , 0x2C , 0x00 , 0x34 , 0x02); 46 | write_reg(par, 0xF7, 0x20); 47 | write_reg(par, 0xEA, 0x00 , 0x00); 48 | 49 | /* Power Control 1 */ 50 | write_reg(par, 0xC0, 0x23); 51 | 52 | /* Power Control 2 */ 53 | write_reg(par, 0xC1, 0x10); 54 | 55 | /* VCOM Control 1 */ 56 | write_reg(par, 0xC5, 0x3e, 0x28); 57 | 58 | /* VCOM Control 2 */ 59 | write_reg(par, 0xC7, 0x86); 60 | 61 | /* COLMOD: Pixel Format Set */ 62 | /* 16 bits/pixel */ 63 | write_reg(par, 0x3A, 0x55); 64 | 65 | /* Frame Rate Control */ 66 | /* Division ratio = fosc, Frame Rate = 79Hz */ 67 | write_reg(par, 0xB1, 0x00, 0x18); 68 | 69 | /* Display Function Control */ 70 | write_reg(par, 0xB6, 0x08, 0x82, 0x27); 71 | 72 | /* Gamma Function Disable */ 73 | write_reg(par, 0xF2, 0x00); 74 | 75 | /* Gamma curve selected */ 76 | write_reg(par, 0x26, 0x01); 77 | 78 | /* Positive Gamma Correction */ 79 | write_reg(par, 0xE0, 80 | 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 81 | 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00); 82 | 83 | /* Negative Gamma Correction */ 84 | write_reg(par, 0xE1, 85 | 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 86 | 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F); 87 | 88 | /* Sleep OUT */ 89 | write_reg(par, 0x11); 90 | 91 | mdelay(120); 92 | 93 | /* Display ON */ 94 | write_reg(par, 0x29); 95 | 96 | return 0; 97 | } 98 | 99 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 100 | { 101 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 102 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 103 | 104 | /* Column address */ 105 | write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF); 106 | 107 | /* Row adress */ 108 | write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF); 109 | 110 | /* Memory write */ 111 | write_reg(par, 0x2C); 112 | } 113 | 114 | #define ILI9340_MADCTL_MV 0x20 115 | #define ILI9340_MADCTL_MX 0x40 116 | #define ILI9340_MADCTL_MY 0x80 117 | static int set_var(struct fbtft_par *par) 118 | { 119 | u8 val; 120 | 121 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 122 | 123 | switch (par->info->var.rotate) { 124 | case 270: 125 | val = ILI9340_MADCTL_MV; 126 | break; 127 | case 180: 128 | val = ILI9340_MADCTL_MY; 129 | break; 130 | case 90: 131 | val = ILI9340_MADCTL_MV | ILI9340_MADCTL_MY | ILI9340_MADCTL_MX; 132 | break; 133 | default: 134 | val = ILI9340_MADCTL_MX; 135 | break; 136 | } 137 | /* Memory Access Control */ 138 | write_reg(par, 0x36, val | (par->bgr << 3)); 139 | 140 | return 0; 141 | } 142 | 143 | 144 | static struct fbtft_display display = { 145 | .regwidth = 8, 146 | .width = WIDTH, 147 | .height = HEIGHT, 148 | .fbtftops = { 149 | .init_display = init_display, 150 | .set_addr_win = set_addr_win, 151 | .set_var = set_var, 152 | }, 153 | }; 154 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 155 | 156 | MODULE_ALIAS("spi:" DRVNAME); 157 | MODULE_ALIAS("platform:" DRVNAME); 158 | 159 | MODULE_DESCRIPTION("FB driver for the ILI9340 LCD Controller"); 160 | MODULE_AUTHOR("Noralf Tronnes"); 161 | MODULE_LICENSE("GPL"); 162 | -------------------------------------------------------------------------------- /fb_ili9341.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the ILI9341 LCD display controller 3 | * 4 | * This display uses 9-bit SPI: Data/Command bit + 8 data bits 5 | * For platforms that doesn't support 9-bit, the driver is capable 6 | * of emulating this using 8-bit transfer. 7 | * This is done by transfering eight 9-bit words in 9 bytes. 8 | * 9 | * Copyright (C) 2013 Christian Vogelgsang 10 | * Based on adafruit22fb.c by Noralf Tronnes 11 | * 12 | * This program is free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this program; if not, write to the Free Software 24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "fbtft.h" 33 | 34 | #define DRVNAME "fb_ili9341" 35 | #define WIDTH 240 36 | #define HEIGHT 320 37 | #define TXBUFLEN (4 * PAGE_SIZE) 38 | #define DEFAULT_GAMMA "1F 1A 18 0A 0F 06 45 87 32 0A 07 02 07 05 00\n" \ 39 | "00 25 27 05 10 09 3A 78 4D 05 18 0D 38 3A 1F" 40 | 41 | 42 | static int init_display(struct fbtft_par *par) 43 | { 44 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 45 | 46 | par->fbtftops.reset(par); 47 | 48 | /* startup sequence for MI0283QT-9A */ 49 | write_reg(par, 0x01); /* software reset */ 50 | mdelay(5); 51 | write_reg(par, 0x28); /* display off */ 52 | /* --------------------------------------------------------- */ 53 | write_reg(par, 0xCF, 0x00, 0x83, 0x30); 54 | write_reg(par, 0xED, 0x64, 0x03, 0x12, 0x81); 55 | write_reg(par, 0xE8, 0x85, 0x01, 0x79); 56 | write_reg(par, 0xCB, 0x39, 0X2C, 0x00, 0x34, 0x02); 57 | write_reg(par, 0xF7, 0x20); 58 | write_reg(par, 0xEA, 0x00, 0x00); 59 | /* ------------power control-------------------------------- */ 60 | write_reg(par, 0xC0, 0x26); 61 | write_reg(par, 0xC1, 0x11); 62 | /* ------------VCOM --------- */ 63 | write_reg(par, 0xC5, 0x35, 0x3E); 64 | write_reg(par, 0xC7, 0xBE); 65 | /* ------------memory access control------------------------ */ 66 | write_reg(par, 0x3A, 0x55); /* 16bit pixel */ 67 | /* ------------frame rate----------------------------------- */ 68 | write_reg(par, 0xB1, 0x00, 0x1B); 69 | /* ------------Gamma---------------------------------------- */ 70 | /* write_reg(par, 0xF2, 0x08); */ /* Gamma Function Disable */ 71 | write_reg(par, 0x26, 0x01); 72 | /* ------------display-------------------------------------- */ 73 | write_reg(par, 0xB7, 0x07); /* entry mode set */ 74 | write_reg(par, 0xB6, 0x0A, 0x82, 0x27, 0x00); 75 | write_reg(par, 0x11); /* sleep out */ 76 | mdelay(100); 77 | write_reg(par, 0x29); /* display on */ 78 | mdelay(20); 79 | 80 | return 0; 81 | } 82 | 83 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 84 | { 85 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 86 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 87 | 88 | /* Column address set */ 89 | write_reg(par, 0x2A, 90 | (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF); 91 | 92 | /* Row adress set */ 93 | write_reg(par, 0x2B, 94 | (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF); 95 | 96 | /* Memory write */ 97 | write_reg(par, 0x2C); 98 | } 99 | 100 | #define MEM_Y (7) /* MY row address order */ 101 | #define MEM_X (6) /* MX column address order */ 102 | #define MEM_V (5) /* MV row / column exchange */ 103 | #define MEM_L (4) /* ML vertical refresh order */ 104 | #define MEM_H (2) /* MH horizontal refresh order */ 105 | #define MEM_BGR (3) /* RGB-BGR Order */ 106 | static int set_var(struct fbtft_par *par) 107 | { 108 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 109 | 110 | switch (par->info->var.rotate) { 111 | case 0: 112 | write_reg(par, 0x36, (1 << MEM_X) | (par->bgr << MEM_BGR)); 113 | break; 114 | case 270: 115 | write_reg(par, 0x36, 116 | (1<bgr << MEM_BGR)); 117 | break; 118 | case 180: 119 | write_reg(par, 0x36, (1 << MEM_Y) | (par->bgr << MEM_BGR)); 120 | break; 121 | case 90: 122 | write_reg(par, 0x36, (1 << MEM_Y) | (1 << MEM_X) | 123 | (1 << MEM_V) | (par->bgr << MEM_BGR)); 124 | break; 125 | } 126 | 127 | return 0; 128 | } 129 | 130 | /* 131 | Gamma string format: 132 | Positive: Par1 Par2 [...] Par15 133 | Negative: Par1 Par2 [...] Par15 134 | */ 135 | #define CURVE(num, idx) curves[num*par->gamma.num_values + idx] 136 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 137 | { 138 | int i; 139 | 140 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 141 | 142 | for (i = 0; i < par->gamma.num_curves; i++) 143 | write_reg(par, 0xE0 + i, 144 | CURVE(i, 0), CURVE(i, 1), CURVE(i, 2), 145 | CURVE(i, 3), CURVE(i, 4), CURVE(i, 5), 146 | CURVE(i, 6), CURVE(i, 7), CURVE(i, 8), 147 | CURVE(i, 9), CURVE(i, 10), CURVE(i, 11), 148 | CURVE(i, 12), CURVE(i, 13), CURVE(i, 14)); 149 | 150 | return 0; 151 | } 152 | #undef CURVE 153 | 154 | 155 | static struct fbtft_display display = { 156 | .regwidth = 8, 157 | .width = WIDTH, 158 | .height = HEIGHT, 159 | .txbuflen = TXBUFLEN, 160 | .gamma_num = 2, 161 | .gamma_len = 15, 162 | .gamma = DEFAULT_GAMMA, 163 | .fbtftops = { 164 | .init_display = init_display, 165 | .set_addr_win = set_addr_win, 166 | .set_var = set_var, 167 | .set_gamma = set_gamma, 168 | }, 169 | }; 170 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 171 | 172 | MODULE_ALIAS("spi:" DRVNAME); 173 | MODULE_ALIAS("platform:" DRVNAME); 174 | 175 | MODULE_DESCRIPTION("FB driver for the ILI9341 LCD display controller"); 176 | MODULE_AUTHOR("Christian Vogelgsang"); 177 | MODULE_LICENSE("GPL"); 178 | -------------------------------------------------------------------------------- /fb_pcd8544.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the PCD8544 LCD Controller 3 | * 4 | * The display is monochrome and the video memory is RGB565. 5 | * Any pixel value except 0 turns the pixel on. 6 | * 7 | * Copyright (C) 2013 Noralf Tronnes 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program; if not, write to the Free Software 21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "fbtft.h" 32 | 33 | #define DRVNAME "fb_pcd8544" 34 | #define WIDTH 84 35 | #define HEIGHT 48 36 | #define TXBUFLEN 84*6 37 | #define DEFAULT_GAMMA "40" /* gamma is used to control contrast in this driver */ 38 | 39 | static unsigned tc = 0; 40 | module_param(tc, uint, 0); 41 | MODULE_PARM_DESC(tc, "TC[1:0] Temperature coefficient: 0-3 (default: 0)"); 42 | 43 | static unsigned bs = 4; 44 | module_param(bs, uint, 0); 45 | MODULE_PARM_DESC(bs, "BS[2:0] Bias voltage level: 0-7 (default: 4)"); 46 | 47 | 48 | static int init_display(struct fbtft_par *par) 49 | { 50 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 51 | 52 | par->fbtftops.reset(par); 53 | 54 | /* Function set */ 55 | write_reg(par, 0x21); /* 5:1 1 56 | 2:0 PD - Powerdown control: chip is active 57 | 1:0 V - Entry mode: horizontal addressing 58 | 0:1 H - Extended instruction set control: extended 59 | */ 60 | 61 | /* H=1 Temperature control */ 62 | write_reg(par, 0x04 | (tc & 0x3)); /* 63 | 2:1 1 64 | 1:x TC1 - Temperature Coefficient: 0x10 65 | 0:x TC0 66 | */ 67 | 68 | /* H=1 Bias system */ 69 | write_reg(par, 0x10 | (bs & 0x7)); /* 70 | 4:1 1 71 | 3:0 0 72 | 2:x BS2 - Bias System 73 | 1:x BS1 74 | 0:x BS0 75 | */ 76 | 77 | /* Function set */ 78 | write_reg(par, 0x22); /* 5:1 1 79 | 2:0 PD - Powerdown control: chip is active 80 | 1:1 V - Entry mode: vertical addressing 81 | 0:0 H - Extended instruction set control: basic 82 | */ 83 | 84 | /* H=0 Display control */ 85 | write_reg(par, 0x08 | 4); /* 86 | 3:1 1 87 | 2:1 D - DE: 10=normal mode 88 | 1:0 0 89 | 0:0 E 90 | */ 91 | 92 | return 0; 93 | } 94 | 95 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 96 | { 97 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 98 | 99 | /* H=0 Set X address of RAM */ 100 | write_reg(par, 0x80); /* 7:1 1 101 | 6-0: X[6:0] - 0x00 102 | */ 103 | 104 | /* H=0 Set Y address of RAM */ 105 | write_reg(par, 0x40); /* 7:0 0 106 | 6:1 1 107 | 2-0: Y[2:0] - 0x0 108 | */ 109 | } 110 | 111 | static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) 112 | { 113 | u16 *vmem16 = (u16 *)par->info->screen_base; 114 | u8 *buf = par->txbuf.buf; 115 | int x, y, i; 116 | int ret = 0; 117 | 118 | fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); 119 | 120 | for (x=0;x<84;x++) { 121 | for (y=0;y<6;y++) { 122 | *buf = 0x00; 123 | for (i=0;i<8;i++) { 124 | *buf |= (vmem16[(y*8+i)*84+x] ? 1 : 0) << i; 125 | } 126 | buf++; 127 | } 128 | } 129 | 130 | /* Write data */ 131 | gpio_set_value(par->gpio.dc, 1); 132 | ret = par->fbtftops.write(par, par->txbuf.buf, 6*84); 133 | if (ret < 0) 134 | dev_err(par->info->device, "%s: write failed and returned: %d\n", __func__, ret); 135 | 136 | return ret; 137 | } 138 | 139 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 140 | { 141 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 142 | 143 | /* apply mask */ 144 | curves[0] &= 0x7F; 145 | 146 | write_reg(par, 0x23); /* turn on extended instruction set */ 147 | write_reg(par, 0x80 | curves[0]); 148 | write_reg(par, 0x22); /* turn off extended instruction set */ 149 | 150 | return 0; 151 | } 152 | 153 | 154 | static struct fbtft_display display = { 155 | .regwidth = 8, 156 | .width = WIDTH, 157 | .height = HEIGHT, 158 | .txbuflen = TXBUFLEN, 159 | .gamma_num = 1, 160 | .gamma_len = 1, 161 | .gamma = DEFAULT_GAMMA, 162 | .fbtftops = { 163 | .init_display = init_display, 164 | .set_addr_win = set_addr_win, 165 | .write_vmem = write_vmem, 166 | .set_gamma = set_gamma, 167 | }, 168 | .backlight = 1, 169 | }; 170 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 171 | 172 | MODULE_ALIAS("spi:" DRVNAME); 173 | 174 | MODULE_DESCRIPTION("FB driver for the PCD8544 LCD Controller"); 175 | MODULE_AUTHOR("Noralf Tronnes"); 176 | MODULE_LICENSE("GPL"); 177 | -------------------------------------------------------------------------------- /fb_s6d1121.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the S6D1121 LCD Controller 3 | * 4 | * Copyright (C) 2013 Roman Rolinsky 5 | * 6 | * Based on fb_ili9325.c by Noralf Tronnes 7 | * Based on ili9325.c by Jeroen Domburg 8 | * Init code from UTFT library by Henning Karlsen 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "fbtft.h" 32 | 33 | #define DRVNAME "fb_s6d1121" 34 | #define WIDTH 240 35 | #define HEIGHT 320 36 | #define BPP 16 37 | #define FPS 20 38 | #define DEFAULT_GAMMA "26 09 24 2C 1F 23 24 25 22 26 25 23 0D 00\n" \ 39 | "1C 1A 13 1D 0B 11 12 10 13 15 36 19 00 0D" 40 | 41 | static int init_display(struct fbtft_par *par) 42 | { 43 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 44 | 45 | par->fbtftops.reset(par); 46 | 47 | if (par->gpio.cs != -1) 48 | gpio_set_value(par->gpio.cs, 0); /* Activate chip */ 49 | 50 | /* Initialization sequence from Lib_UTFT */ 51 | 52 | write_reg(par, 0x0011, 0x2004); 53 | write_reg(par, 0x0013, 0xCC00); 54 | write_reg(par, 0x0015, 0x2600); 55 | write_reg(par, 0x0014, 0x252A); 56 | // write_reg(par, 0x0014, 0x002A); 57 | write_reg(par, 0x0012, 0x0033); 58 | write_reg(par, 0x0013, 0xCC04); 59 | // mdelay(1); 60 | write_reg(par, 0x0013, 0xCC06); 61 | // mdelay(1); 62 | write_reg(par, 0x0013, 0xCC4F); 63 | // mdelay(1); 64 | write_reg(par, 0x0013, 0x674F); 65 | write_reg(par, 0x0011, 0x2003); 66 | // mdelay(1); 67 | write_reg(par, 0x0016, 0x0007); 68 | write_reg(par, 0x0002, 0x0013); 69 | write_reg(par, 0x0003, 0x0003); 70 | write_reg(par, 0x0001, 0x0127); 71 | // mdelay(1); 72 | write_reg(par, 0x0008, 0x0303); 73 | write_reg(par, 0x000A, 0x000B); 74 | write_reg(par, 0x000B, 0x0003); 75 | write_reg(par, 0x000C, 0x0000); 76 | write_reg(par, 0x0041, 0x0000); 77 | write_reg(par, 0x0050, 0x0000); 78 | write_reg(par, 0x0060, 0x0005); 79 | write_reg(par, 0x0070, 0x000B); 80 | write_reg(par, 0x0071, 0x0000); 81 | write_reg(par, 0x0078, 0x0000); 82 | write_reg(par, 0x007A, 0x0000); 83 | write_reg(par, 0x0079, 0x0007); 84 | write_reg(par, 0x0007, 0x0051); 85 | // mdelay(1); 86 | write_reg(par, 0x0007, 0x0053); 87 | write_reg(par, 0x0079, 0x0000); 88 | 89 | write_reg(par, 0x0022); /* Write Data to GRAM */ 90 | 91 | return 0; 92 | } 93 | 94 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 95 | { 96 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 97 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 98 | switch (par->info->var.rotate) { 99 | /* R20h = Horizontal GRAM Start Address */ 100 | /* R21h = Vertical GRAM Start Address */ 101 | case 0: 102 | write_reg(par, 0x0020, xs); 103 | write_reg(par, 0x0021, ys); 104 | break; 105 | case 180: 106 | write_reg(par, 0x0020, WIDTH - 1 - xs); 107 | write_reg(par, 0x0021, HEIGHT - 1 - ys); 108 | break; 109 | case 270: 110 | write_reg(par, 0x0020, WIDTH - 1 - ys); 111 | write_reg(par, 0x0021, xs); 112 | break; 113 | case 90: 114 | write_reg(par, 0x0020, ys); 115 | write_reg(par, 0x0021, HEIGHT - 1 - xs); 116 | break; 117 | } 118 | write_reg(par, 0x0022); /* Write Data to GRAM */ 119 | } 120 | 121 | static int set_var(struct fbtft_par *par) 122 | { 123 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 124 | 125 | switch (par->info->var.rotate) { 126 | /* AM: GRAM update direction */ 127 | case 0: 128 | write_reg(par, 0x03, 0x0003 | (par->bgr << 12)); 129 | break; 130 | case 180: 131 | write_reg(par, 0x03, 0x0000 | (par->bgr << 12)); 132 | break; 133 | case 270: 134 | write_reg(par, 0x03, 0x000A | (par->bgr << 12)); 135 | break; 136 | case 90: 137 | write_reg(par, 0x03, 0x0009 | (par->bgr << 12)); 138 | break; 139 | } 140 | 141 | return 0; 142 | } 143 | 144 | /* 145 | Gamma string format: 146 | PKP0 PKP1 PKP2 PKP3 PKP4 PKP5 PKP6 PKP7 PKP8 PKP9 PKP10 PKP11 VRP0 VRP1 147 | PKN0 PKN1 PKN2 PKN3 PKN4 PKN5 PKN6 PKN7 PRN8 PRN9 PRN10 PRN11 VRN0 VRN1 148 | */ 149 | #define CURVE(num, idx) curves[num*par->gamma.num_values + idx] 150 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 151 | { 152 | unsigned long mask[] = { 153 | 0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 154 | 0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 155 | 0b11111, 0b11111, 156 | 0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 157 | 0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 158 | 0b11111, 0b11111 }; 159 | int i, j; 160 | 161 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 162 | 163 | /* apply mask */ 164 | for (i = 0; i < 2; i++) 165 | for (j = 0; j < 14; j++) 166 | CURVE(i, j) &= mask[i*par->gamma.num_values + j]; 167 | 168 | write_reg(par, 0x0030, CURVE(0, 1) << 8 | CURVE(0, 0)); 169 | write_reg(par, 0x0031, CURVE(0, 3) << 8 | CURVE(0, 2)); 170 | write_reg(par, 0x0032, CURVE(0, 5) << 8 | CURVE(0, 3)); 171 | write_reg(par, 0x0033, CURVE(0, 7) << 8 | CURVE(0, 6)); 172 | write_reg(par, 0x0034, CURVE(0, 9) << 8 | CURVE(0, 8)); 173 | write_reg(par, 0x0035, CURVE(0, 11) << 8 | CURVE(0, 10)); 174 | 175 | write_reg(par, 0x0036, CURVE(1, 1) << 8 | CURVE(1, 0)); 176 | write_reg(par, 0x0037, CURVE(1, 3) << 8 | CURVE(1, 2)); 177 | write_reg(par, 0x0038, CURVE(1, 5) << 8 | CURVE(1, 4)); 178 | write_reg(par, 0x0039, CURVE(1, 7) << 8 | CURVE(1, 6)); 179 | write_reg(par, 0x003A, CURVE(1, 9) << 8 | CURVE(1, 8)); 180 | write_reg(par, 0x003B, CURVE(1, 11) << 8 | CURVE(1, 10)); 181 | 182 | write_reg(par, 0x003C, CURVE(0, 13) << 8 | CURVE(0, 12)); 183 | write_reg(par, 0x003D, CURVE(1, 13) << 8 | CURVE(1, 12)); 184 | 185 | return 0; 186 | } 187 | #undef CURVE 188 | 189 | 190 | static struct fbtft_display display = { 191 | .regwidth = 16, 192 | .width = WIDTH, 193 | .height = HEIGHT, 194 | .bpp = BPP, 195 | .fps = FPS, 196 | .gamma_num = 2, 197 | .gamma_len = 14, 198 | .gamma = DEFAULT_GAMMA, 199 | .fbtftops = { 200 | .init_display = init_display, 201 | .set_addr_win = set_addr_win, 202 | .set_var = set_var, 203 | .set_gamma = set_gamma, 204 | }, 205 | }; 206 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 207 | 208 | MODULE_ALIAS("spi:" DRVNAME); 209 | MODULE_ALIAS("platform:" DRVNAME); 210 | 211 | MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller"); 212 | MODULE_AUTHOR("Roman Rolinsky"); 213 | MODULE_LICENSE("GPL"); 214 | -------------------------------------------------------------------------------- /fb_ssd1289.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the SSD1289 LCD Controller 3 | * 4 | * Copyright (C) 2013 Noralf Tronnes 5 | * 6 | * Init sequence taken from ITDB02_Graph16.cpp - (C)2010-2011 Henning Karlsen 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "fbtft.h" 29 | 30 | #define DRVNAME "fb_ssd1289" 31 | #define WIDTH 240 32 | #define HEIGHT 320 33 | #define DEFAULT_GAMMA "02 03 2 5 7 7 4 2 4 2\n" \ 34 | "02 03 2 5 7 5 4 2 4 2" 35 | 36 | static unsigned reg11 = 0x6040; 37 | module_param(reg11, uint, 0); 38 | MODULE_PARM_DESC(reg11, "Register 11h value"); 39 | 40 | 41 | static int init_display(struct fbtft_par *par) 42 | { 43 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 44 | 45 | par->fbtftops.reset(par); 46 | 47 | if (par->gpio.cs != -1) 48 | gpio_set_value(par->gpio.cs, 0); /* Activate chip */ 49 | 50 | write_reg(par, 0x00, 0x0001); 51 | write_reg(par, 0x03, 0xA8A4); 52 | write_reg(par, 0x0C, 0x0000); 53 | write_reg(par, 0x0D, 0x080C); 54 | write_reg(par, 0x0E, 0x2B00); 55 | write_reg(par, 0x1E, 0x00B7); 56 | write_reg(par, 0x01, 57 | (1 << 13) | (par->bgr << 11) | (1 << 9) | (HEIGHT - 1)); 58 | write_reg(par, 0x02, 0x0600); 59 | write_reg(par, 0x10, 0x0000); 60 | write_reg(par, 0x05, 0x0000); 61 | write_reg(par, 0x06, 0x0000); 62 | write_reg(par, 0x16, 0xEF1C); 63 | write_reg(par, 0x17, 0x0003); 64 | write_reg(par, 0x07, 0x0233); 65 | write_reg(par, 0x0B, 0x0000); 66 | write_reg(par, 0x0F, 0x0000); 67 | write_reg(par, 0x41, 0x0000); 68 | write_reg(par, 0x42, 0x0000); 69 | write_reg(par, 0x48, 0x0000); 70 | write_reg(par, 0x49, 0x013F); 71 | write_reg(par, 0x4A, 0x0000); 72 | write_reg(par, 0x4B, 0x0000); 73 | write_reg(par, 0x44, 0xEF00); 74 | write_reg(par, 0x45, 0x0000); 75 | write_reg(par, 0x46, 0x013F); 76 | write_reg(par, 0x23, 0x0000); 77 | write_reg(par, 0x24, 0x0000); 78 | write_reg(par, 0x25, 0x8000); 79 | write_reg(par, 0x4f, 0x0000); 80 | write_reg(par, 0x4e, 0x0000); 81 | write_reg(par, 0x22); 82 | return 0; 83 | } 84 | 85 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 86 | { 87 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 88 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 89 | 90 | switch (par->info->var.rotate) { 91 | /* R4Eh - Set GDDRAM X address counter */ 92 | /* R4Fh - Set GDDRAM Y address counter */ 93 | case 0: 94 | write_reg(par, 0x4e, xs); 95 | write_reg(par, 0x4f, ys); 96 | break; 97 | case 180: 98 | write_reg(par, 0x4e, par->info->var.xres - 1 - xs); 99 | write_reg(par, 0x4f, par->info->var.yres - 1 - ys); 100 | break; 101 | case 270: 102 | write_reg(par, 0x4e, par->info->var.yres - 1 - ys); 103 | write_reg(par, 0x4f, xs); 104 | break; 105 | case 90: 106 | write_reg(par, 0x4e, ys); 107 | write_reg(par, 0x4f, par->info->var.xres - 1 - xs); 108 | break; 109 | } 110 | 111 | /* R22h - RAM data write */ 112 | write_reg(par, 0x22); 113 | } 114 | 115 | static int set_var(struct fbtft_par *par) 116 | { 117 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 118 | 119 | if (par->fbtftops.init_display != init_display) { 120 | /* don't risk messing up register 11h */ 121 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, 122 | "%s: skipping since custom init_display() is used\n", 123 | __func__); 124 | return 0; 125 | } 126 | 127 | switch (par->info->var.rotate) { 128 | case 0: 129 | write_reg(par, 0x11, reg11 | 0b110000); 130 | break; 131 | case 270: 132 | write_reg(par, 0x11, reg11 | 0b101000); 133 | break; 134 | case 180: 135 | write_reg(par, 0x11, reg11 | 0b000000); 136 | break; 137 | case 90: 138 | write_reg(par, 0x11, reg11 | 0b011000); 139 | break; 140 | } 141 | 142 | return 0; 143 | } 144 | 145 | /* 146 | Gamma string format: 147 | VRP0 VRP1 PRP0 PRP1 PKP0 PKP1 PKP2 PKP3 PKP4 PKP5 148 | VRN0 VRN1 PRN0 PRN1 PKN0 PKN1 PKN2 PKN3 PKN4 PKN5 149 | */ 150 | #define CURVE(num, idx) curves[num*par->gamma.num_values + idx] 151 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 152 | { 153 | unsigned long mask[] = { 154 | 0b11111, 0b11111, 0b111, 0b111, 0b111, 155 | 0b111, 0b111, 0b111, 0b111, 0b111, 156 | 0b11111, 0b11111, 0b111, 0b111, 0b111, 157 | 0b111, 0b111, 0b111, 0b111, 0b111 }; 158 | int i, j; 159 | 160 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 161 | 162 | /* apply mask */ 163 | for (i = 0; i < 2; i++) 164 | for (j = 0; j < 10; j++) 165 | CURVE(i, j) &= mask[i*par->gamma.num_values + j]; 166 | 167 | write_reg(par, 0x0030, CURVE(0, 5) << 8 | CURVE(0, 4)); 168 | write_reg(par, 0x0031, CURVE(0, 7) << 8 | CURVE(0, 6)); 169 | write_reg(par, 0x0032, CURVE(0, 9) << 8 | CURVE(0, 8)); 170 | write_reg(par, 0x0033, CURVE(0, 3) << 8 | CURVE(0, 2)); 171 | write_reg(par, 0x0034, CURVE(1, 5) << 8 | CURVE(1, 4)); 172 | write_reg(par, 0x0035, CURVE(1, 7) << 8 | CURVE(1, 6)); 173 | write_reg(par, 0x0036, CURVE(1, 9) << 8 | CURVE(1, 8)); 174 | write_reg(par, 0x0037, CURVE(1, 3) << 8 | CURVE(1, 2)); 175 | write_reg(par, 0x003A, CURVE(0, 1) << 8 | CURVE(0, 0)); 176 | write_reg(par, 0x003B, CURVE(1, 1) << 8 | CURVE(1, 0)); 177 | 178 | return 0; 179 | } 180 | #undef CURVE 181 | 182 | 183 | static struct fbtft_display display = { 184 | .regwidth = 16, 185 | .width = WIDTH, 186 | .height = HEIGHT, 187 | .gamma_num = 2, 188 | .gamma_len = 10, 189 | .gamma = DEFAULT_GAMMA, 190 | .fbtftops = { 191 | .init_display = init_display, 192 | .set_addr_win = set_addr_win, 193 | .set_var = set_var, 194 | .set_gamma = set_gamma, 195 | }, 196 | }; 197 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 198 | 199 | MODULE_ALIAS("spi:" DRVNAME); 200 | MODULE_ALIAS("platform:" DRVNAME); 201 | 202 | MODULE_DESCRIPTION("FB driver for the SSD1289 LCD Controller"); 203 | MODULE_AUTHOR("Noralf Tronnes"); 204 | MODULE_LICENSE("GPL"); 205 | -------------------------------------------------------------------------------- /fb_ssd1306.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the SSD1306 OLED Controller 3 | * 4 | * Copyright (C) 2013 Noralf Tronnes 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "fbtft.h" 28 | 29 | #define DRVNAME "fb_ssd1306" 30 | #define WIDTH 128 31 | #define HEIGHT 64 32 | 33 | 34 | /* 35 | write_reg() caveat: 36 | 37 | This doesn't work because D/C has to be LOW for both values: 38 | write_reg(par, val1, val2); 39 | 40 | Do it like this: 41 | write_reg(par, val1); 42 | write_reg(par, val2); 43 | */ 44 | 45 | /* Init sequence taken from the Adafruit SSD1306 Arduino library */ 46 | static int init_display(struct fbtft_par *par) 47 | { 48 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 49 | 50 | par->fbtftops.reset(par); 51 | 52 | if (par->gamma.curves[0] == 0) { 53 | mutex_lock(&par->gamma.lock); 54 | if (par->info->var.yres == 64) 55 | par->gamma.curves[0] = 0xCF; 56 | else 57 | par->gamma.curves[0] = 0x8F; 58 | mutex_unlock(&par->gamma.lock); 59 | } 60 | 61 | /* Set Display OFF */ 62 | write_reg(par, 0xAE); 63 | 64 | /* Set Display Clock Divide Ratio/ Oscillator Frequency */ 65 | write_reg(par, 0xD5); 66 | write_reg(par, 0x80); 67 | 68 | /* Set Multiplex Ratio */ 69 | write_reg(par, 0xA8); 70 | if (par->info->var.yres == 64) 71 | write_reg(par, 0x3F); 72 | else 73 | write_reg(par, 0x1F); 74 | 75 | /* Set Display Offset */ 76 | write_reg(par, 0xD3); 77 | write_reg(par, 0x0); 78 | 79 | /* Set Display Start Line */ 80 | write_reg(par, 0x40 | 0x0); 81 | 82 | /* Charge Pump Setting */ 83 | write_reg(par, 0x8D); 84 | /* A[2] = 1b, Enable charge pump during display on */ 85 | write_reg(par, 0x14); 86 | 87 | /* Set Memory Addressing Mode */ 88 | write_reg(par, 0x20); 89 | /* Vertical addressing mode */ 90 | write_reg(par, 0x01); 91 | 92 | /*Set Segment Re-map */ 93 | /* column address 127 is mapped to SEG0 */ 94 | write_reg(par, 0xA0 | 0x1); 95 | 96 | /* Set COM Output Scan Direction */ 97 | /* remapped mode. Scan from COM[N-1] to COM0 */ 98 | write_reg(par, 0xC8); 99 | 100 | /* Set COM Pins Hardware Configuration */ 101 | write_reg(par, 0xDA); 102 | if (par->info->var.yres == 64) 103 | /* A[4]=1b, Alternative COM pin configuration */ 104 | write_reg(par, 0x12); 105 | else 106 | /* A[4]=0b, Sequential COM pin configuration */ 107 | write_reg(par, 0x02); 108 | 109 | /* Set Pre-charge Period */ 110 | write_reg(par, 0xD9); 111 | write_reg(par, 0xF1); 112 | 113 | /* Set VCOMH Deselect Level */ 114 | write_reg(par, 0xDB); 115 | /* according to the datasheet, this value is out of bounds */ 116 | write_reg(par, 0x40); 117 | 118 | /* Entire Display ON */ 119 | /* Resume to RAM content display. Output follows RAM content */ 120 | write_reg(par, 0xA4); 121 | 122 | /* Set Normal Display 123 | 0 in RAM: OFF in display panel 124 | 1 in RAM: ON in display panel */ 125 | write_reg(par, 0xA6); 126 | 127 | /* Set Display ON */ 128 | write_reg(par, 0xAF); 129 | 130 | return 0; 131 | } 132 | 133 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 134 | { 135 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 136 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 137 | 138 | /* Set Lower Column Start Address for Page Addressing Mode */ 139 | write_reg(par, 0x00 | 0x0); 140 | /* Set Higher Column Start Address for Page Addressing Mode */ 141 | write_reg(par, 0x10 | 0x0); 142 | /* Set Display Start Line */ 143 | write_reg(par, 0x40 | 0x0); 144 | } 145 | 146 | static int blank(struct fbtft_par *par, bool on) 147 | { 148 | fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n", 149 | __func__, on ? "true" : "false"); 150 | 151 | if (on) 152 | write_reg(par, 0xAE); 153 | else 154 | write_reg(par, 0xAF); 155 | return 0; 156 | } 157 | 158 | /* Gamma is used to control Contrast */ 159 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 160 | { 161 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 162 | 163 | /* apply mask */ 164 | curves[0] &= 0xFF; 165 | 166 | /* Set Contrast Control for BANK0 */ 167 | write_reg(par, 0x81); 168 | write_reg(par, curves[0]); 169 | 170 | return 0; 171 | } 172 | 173 | static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) 174 | { 175 | u16 *vmem16 = (u16 *)par->info->screen_base; 176 | u8 *buf = par->txbuf.buf; 177 | int x, y, i; 178 | int ret = 0; 179 | 180 | fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); 181 | 182 | for (x = 0; x < par->info->var.xres; x++) { 183 | for (y = 0; y < par->info->var.yres/8; y++) { 184 | *buf = 0x00; 185 | for (i = 0; i < 8; i++) 186 | *buf |= (vmem16[(y*8+i)*par->info->var.xres+x] ? 1 : 0) << i; 187 | buf++; 188 | } 189 | } 190 | 191 | /* Write data */ 192 | gpio_set_value(par->gpio.dc, 1); 193 | ret = par->fbtftops.write(par, par->txbuf.buf, 194 | par->info->var.xres*par->info->var.yres/8); 195 | if (ret < 0) 196 | dev_err(par->info->device, 197 | "%s: write failed and returned: %d\n", __func__, ret); 198 | 199 | return ret; 200 | } 201 | 202 | 203 | static struct fbtft_display display = { 204 | .regwidth = 8, 205 | .width = WIDTH, 206 | .height = HEIGHT, 207 | .gamma_num = 1, 208 | .gamma_len = 1, 209 | .gamma = "00", 210 | .fbtftops = { 211 | .write_vmem = write_vmem, 212 | .init_display = init_display, 213 | .set_addr_win = set_addr_win, 214 | .blank = blank, 215 | .set_gamma = set_gamma, 216 | }, 217 | }; 218 | 219 | 220 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 221 | 222 | MODULE_ALIAS("spi:" DRVNAME); 223 | MODULE_ALIAS("platform:" DRVNAME); 224 | 225 | MODULE_DESCRIPTION("SSD1306 OLED Driver"); 226 | MODULE_AUTHOR("Noralf Tronnes"); 227 | MODULE_LICENSE("GPL"); 228 | -------------------------------------------------------------------------------- /fb_ssd1331.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "fbtft.h" 9 | 10 | #define DRVNAME "fb_ssd1331" 11 | #define WIDTH 96 12 | #define HEIGHT 64 13 | #define GAMMA_NUM 1 14 | #define GAMMA_LEN 63 15 | #define DEFAULT_GAMMA "0 2 2 2 2 2 2 2 " \ 16 | "2 2 2 2 2 2 2 2 " \ 17 | "2 2 2 2 2 2 2 2 " \ 18 | "2 2 2 2 2 2 2 2 " \ 19 | "2 2 2 2 2 2 2 2 " \ 20 | "2 2 2 2 2 2 2 2 " \ 21 | "2 2 2 2 2 2 2 2 " \ 22 | "2 2 2 2 2 2 2" \ 23 | 24 | static int init_display(struct fbtft_par *par) 25 | { 26 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 27 | 28 | par->fbtftops.reset(par); 29 | 30 | write_reg(par, 0xae); /* Display Off */ 31 | write_reg(par, 0xa0, 0x70 | (par->bgr << 2)); /* Set Colour Depth */ 32 | write_reg(par, 0x72); // RGB colour 33 | write_reg(par, 0xa1, 0x00); /* Set Display Start Line */ 34 | write_reg(par, 0xa2, 0x00); /* Set Display Offset */ 35 | write_reg(par, 0xa4); /* NORMALDISPLAY */ 36 | write_reg(par, 0xa8, 0x3f); // Set multiplex 37 | write_reg(par, 0xad, 0x8e); // Set master 38 | // write_reg(par, 0xb0, 0x0b); // Set power mode 39 | write_reg(par, 0xb1, 0x31); // Precharge 40 | write_reg(par, 0xb3, 0xf0); // Clock div 41 | write_reg(par, 0x8a, 0x64); // Precharge A 42 | write_reg(par, 0x8b, 0x78); // Precharge B 43 | write_reg(par, 0x8c, 0x64); // Precharge C 44 | write_reg(par, 0xbb, 0x3a); // Precharge level 45 | write_reg(par, 0xbe, 0x3e); // vcomh 46 | write_reg(par, 0x87, 0x06); // Master current 47 | write_reg(par, 0x81, 0x91); // Contrast A 48 | write_reg(par, 0x82, 0x50); // Contrast B 49 | write_reg(par, 0x83, 0x7d); // Contrast C 50 | write_reg(par, 0xaf); /* Set Sleep Mode Display On */ 51 | 52 | return 0; 53 | } 54 | 55 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 56 | { 57 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 58 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 59 | 60 | write_reg(par, 0x15, xs, xe); 61 | write_reg(par, 0x75, ys, ye); 62 | } 63 | 64 | static void write_reg8_bus8(struct fbtft_par *par, int len, ...) 65 | { 66 | va_list args; 67 | int i, ret; 68 | u8 *buf = (u8 *)par->buf; 69 | 70 | if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { 71 | va_start(args, len); 72 | for (i = 0; i < len; i++) { 73 | buf[i] = (u8)va_arg(args, unsigned int); 74 | } 75 | va_end(args); 76 | fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, u8, buf, len, "%s: ", __func__); 77 | } 78 | 79 | va_start(args, len); 80 | 81 | *buf = (u8)va_arg(args, unsigned int); 82 | if (par->gpio.dc != -1) 83 | gpio_set_value(par->gpio.dc, 0); 84 | ret = par->fbtftops.write(par, par->buf, sizeof(u8)); 85 | if (ret < 0) { 86 | va_end(args); 87 | dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret); 88 | return; 89 | } 90 | len--; 91 | 92 | if (len) { 93 | i = len; 94 | while (i--) { 95 | *buf++ = (u8)va_arg(args, unsigned int); 96 | } 97 | ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8))); 98 | if (ret < 0) { 99 | va_end(args); 100 | dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret); 101 | return; 102 | } 103 | } 104 | if (par->gpio.dc != -1) 105 | gpio_set_value(par->gpio.dc, 1); 106 | va_end(args); 107 | } 108 | 109 | /* 110 | Grayscale Lookup Table 111 | GS1 - GS63 112 | The driver Gamma curve contains the relative values between the entries 113 | in the Lookup table. 114 | 115 | From datasheet: 116 | 8.8 Gray Scale Decoder 117 | 118 | there are total 180 Gamma Settings (Setting 0 to Setting 180) 119 | available for the Gray Scale table. 120 | 121 | The gray scale is defined in incremental way, with reference 122 | to the length of previous table entry: 123 | Setting of GS1 has to be >= 0 124 | Setting of GS2 has to be > Setting of GS1 +1 125 | Setting of GS3 has to be > Setting of GS2 +1 126 | : 127 | Setting of GS63 has to be > Setting of GS62 +1 128 | 129 | 130 | */ 131 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 132 | { 133 | unsigned long tmp[GAMMA_NUM * GAMMA_LEN]; 134 | int i, acc = 0; 135 | 136 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 137 | 138 | for (i = 0; i < 63; i++) { 139 | if (i > 0 && curves[i] < 2) { 140 | dev_err(par->info->device, 141 | "Illegal value in Grayscale Lookup Table at index %d. " \ 142 | "Must be greater than 1\n", i); 143 | return -EINVAL; 144 | } 145 | acc += curves[i]; 146 | tmp[i] = acc; 147 | if (acc > 180) { 148 | dev_err(par->info->device, 149 | "Illegal value(s) in Grayscale Lookup Table. " \ 150 | "At index=%d, the accumulated value has exceeded 180\n", i); 151 | return -EINVAL; 152 | } 153 | } 154 | 155 | write_reg(par, 0xB8, 156 | tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], 157 | tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15], 158 | tmp[16], tmp[17], tmp[18], tmp[19], tmp[20], tmp[21], tmp[22], tmp[23], 159 | tmp[24], tmp[25], tmp[26], tmp[27], tmp[28], tmp[29], tmp[30], tmp[31], 160 | tmp[32], tmp[33], tmp[34], tmp[35], tmp[36], tmp[37], tmp[38], tmp[39], 161 | tmp[40], tmp[41], tmp[42], tmp[43], tmp[44], tmp[45], tmp[46], tmp[47], 162 | tmp[48], tmp[49], tmp[50], tmp[51], tmp[52], tmp[53], tmp[54], tmp[55], 163 | tmp[56], tmp[57], tmp[58], tmp[59], tmp[60], tmp[61], tmp[62]); 164 | 165 | return 0; 166 | } 167 | 168 | static int blank(struct fbtft_par *par, bool on) 169 | { 170 | fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n", 171 | __func__, on ? "true" : "false"); 172 | if (on) 173 | write_reg(par, 0xAE); 174 | else 175 | write_reg(par, 0xAF); 176 | return 0; 177 | } 178 | 179 | 180 | static struct fbtft_display display = { 181 | .regwidth = 8, 182 | .width = WIDTH, 183 | .height = HEIGHT, 184 | .gamma_num = GAMMA_NUM, 185 | .gamma_len = GAMMA_LEN, 186 | .gamma = DEFAULT_GAMMA, 187 | .fbtftops = { 188 | .write_register = write_reg8_bus8, 189 | .init_display = init_display, 190 | .set_addr_win = set_addr_win, 191 | .set_gamma = set_gamma, 192 | .blank = blank, 193 | }, 194 | }; 195 | 196 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 197 | 198 | MODULE_ALIAS("spi:" DRVNAME); 199 | MODULE_ALIAS("platform:" DRVNAME); 200 | 201 | MODULE_DESCRIPTION("SSD1331 OLED Driver"); 202 | MODULE_AUTHOR("Alec Smecher (adapted from SSD1351 by James Davies)"); 203 | MODULE_LICENSE("GPL"); 204 | -------------------------------------------------------------------------------- /fb_ssd1351.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "fbtft.h" 9 | 10 | #define DRVNAME "fb_ssd1351" 11 | #define WIDTH 128 12 | #define HEIGHT 128 13 | #define GAMMA_NUM 1 14 | #define GAMMA_LEN 63 15 | #define DEFAULT_GAMMA "0 2 2 2 2 2 2 2 " \ 16 | "2 2 2 2 2 2 2 2 " \ 17 | "2 2 2 2 2 2 2 2 " \ 18 | "2 2 2 2 2 2 2 2 " \ 19 | "2 2 2 2 2 2 2 2 " \ 20 | "2 2 2 2 2 2 2 2 " \ 21 | "2 2 2 2 2 2 2 2 " \ 22 | "2 2 2 2 2 2 2" \ 23 | 24 | static void register_onboard_backlight(struct fbtft_par *par); 25 | 26 | static int init_display(struct fbtft_par *par) 27 | { 28 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 29 | 30 | if (par->pdata 31 | && par->pdata->display.backlight == FBTFT_ONBOARD_BACKLIGHT) { 32 | /* module uses onboard GPIO for panel power */ 33 | par->fbtftops.register_backlight = register_onboard_backlight; 34 | } 35 | 36 | par->fbtftops.reset(par); 37 | 38 | write_reg(par, 0xfd, 0x12); /* Command Lock */ 39 | write_reg(par, 0xfd, 0xb1); /* Command Lock */ 40 | write_reg(par, 0xae); /* Display Off */ 41 | write_reg(par, 0xb3, 0xf1); /* Front Clock Div */ 42 | write_reg(par, 0xca, 0x7f); /* Set Mux Ratio */ 43 | write_reg(par, 0xa0, 0x70 | (par->bgr << 2)); /* Set Colour Depth */ 44 | write_reg(par, 0x15, 0x00, 0x7f); /* Set Column Address */ 45 | write_reg(par, 0x75, 0x00, 0x7f); /* Set Row Address */ 46 | write_reg(par, 0xa1, 0x00); /* Set Display Start Line */ 47 | write_reg(par, 0xa2, 0x00); /* Set Display Offset */ 48 | write_reg(par, 0xb5, 0x00); /* Set GPIO */ 49 | write_reg(par, 0xab, 0x01); /* Set Function Selection */ 50 | write_reg(par, 0xb1, 0x32); /* Set Phase Length */ 51 | write_reg(par, 0xb4, 0xa0, 0xb5, 0x55); /* Set Segment Low Voltage */ 52 | write_reg(par, 0xbb, 0x17); /* Set Precharge Voltage */ 53 | write_reg(par, 0xbe, 0x05); /* Set VComH Voltage */ 54 | write_reg(par, 0xc1, 0xc8, 0x80, 0xc8); /* Set Contrast */ 55 | write_reg(par, 0xc7, 0x0f); /* Set Master Contrast */ 56 | write_reg(par, 0xb6, 0x01); /* Set Second Precharge Period */ 57 | write_reg(par, 0xa6); /* Set Display Mode Reset */ 58 | write_reg(par, 0xaf); /* Set Sleep Mode Display On */ 59 | 60 | return 0; 61 | } 62 | 63 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 64 | { 65 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 66 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 67 | 68 | write_reg(par, 0x15, xs, xe); 69 | write_reg(par, 0x75, ys, ye); 70 | write_reg(par, 0x5c); 71 | } 72 | 73 | /* 74 | Grayscale Lookup Table 75 | GS1 - GS63 76 | The driver Gamma curve contains the relative values between the entries 77 | in the Lookup table. 78 | 79 | From datasheet: 80 | 8.8 Gray Scale Decoder 81 | 82 | there are total 180 Gamma Settings (Setting 0 to Setting 180) 83 | available for the Gray Scale table. 84 | 85 | The gray scale is defined in incremental way, with reference 86 | to the length of previous table entry: 87 | Setting of GS1 has to be >= 0 88 | Setting of GS2 has to be > Setting of GS1 +1 89 | Setting of GS3 has to be > Setting of GS2 +1 90 | : 91 | Setting of GS63 has to be > Setting of GS62 +1 92 | 93 | 94 | */ 95 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 96 | { 97 | unsigned long tmp[GAMMA_NUM * GAMMA_LEN]; 98 | int i, acc = 0; 99 | 100 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 101 | 102 | for (i = 0; i < 63; i++) { 103 | if (i > 0 && curves[i] < 2) { 104 | dev_err(par->info->device, 105 | "Illegal value in Grayscale Lookup Table at index %d. " \ 106 | "Must be greater than 1\n", i); 107 | return -EINVAL; 108 | } 109 | acc += curves[i]; 110 | tmp[i] = acc; 111 | if (acc > 180) { 112 | dev_err(par->info->device, 113 | "Illegal value(s) in Grayscale Lookup Table. " \ 114 | "At index=%d, the accumulated value has exceeded 180\n", i); 115 | return -EINVAL; 116 | } 117 | } 118 | 119 | write_reg(par, 0xB8, 120 | tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], 121 | tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15], 122 | tmp[16], tmp[17], tmp[18], tmp[19], tmp[20], tmp[21], tmp[22], tmp[23], 123 | tmp[24], tmp[25], tmp[26], tmp[27], tmp[28], tmp[29], tmp[30], tmp[31], 124 | tmp[32], tmp[33], tmp[34], tmp[35], tmp[36], tmp[37], tmp[38], tmp[39], 125 | tmp[40], tmp[41], tmp[42], tmp[43], tmp[44], tmp[45], tmp[46], tmp[47], 126 | tmp[48], tmp[49], tmp[50], tmp[51], tmp[52], tmp[53], tmp[54], tmp[55], 127 | tmp[56], tmp[57], tmp[58], tmp[59], tmp[60], tmp[61], tmp[62]); 128 | 129 | return 0; 130 | } 131 | 132 | static int blank(struct fbtft_par *par, bool on) 133 | { 134 | fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n", 135 | __func__, on ? "true" : "false"); 136 | if (on) 137 | write_reg(par, 0xAE); 138 | else 139 | write_reg(par, 0xAF); 140 | return 0; 141 | } 142 | 143 | 144 | static struct fbtft_display display = { 145 | .regwidth = 8, 146 | .width = WIDTH, 147 | .height = HEIGHT, 148 | .gamma_num = GAMMA_NUM, 149 | .gamma_len = GAMMA_LEN, 150 | .gamma = DEFAULT_GAMMA, 151 | .fbtftops = { 152 | .init_display = init_display, 153 | .set_addr_win = set_addr_win, 154 | .set_gamma = set_gamma, 155 | .blank = blank, 156 | }, 157 | }; 158 | 159 | static int update_onboard_backlight(struct backlight_device *bd) 160 | { 161 | struct fbtft_par *par = bl_get_data(bd); 162 | bool on; 163 | 164 | fbtft_par_dbg(DEBUG_BACKLIGHT, par, 165 | "%s: power=%d, fb_blank=%d\n", 166 | __func__, bd->props.power, bd->props.fb_blank); 167 | 168 | on = (bd->props.power == FB_BLANK_UNBLANK) 169 | && (bd->props.fb_blank == FB_BLANK_UNBLANK); 170 | /* Onboard backlight connected to GPIO0 on SSD1351, GPIO1 unused */ 171 | write_reg(par, 0xB5, on ? 0x03 : 0x02); 172 | 173 | return 0; 174 | } 175 | 176 | static void register_onboard_backlight(struct fbtft_par *par) 177 | { 178 | struct backlight_device *bd; 179 | struct backlight_properties bl_props = { 0, }; 180 | struct backlight_ops *bl_ops; 181 | 182 | fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s()\n", __func__); 183 | 184 | bl_ops = kzalloc(sizeof(struct backlight_ops), GFP_KERNEL); 185 | if (!bl_ops) { 186 | dev_err(par->info->device, 187 | "%s: could not allocate memory for backlight operations.\n", 188 | __func__); 189 | return; 190 | } 191 | 192 | bl_ops->update_status = update_onboard_backlight; 193 | bl_props.type = BACKLIGHT_RAW; 194 | bl_props.power = FB_BLANK_POWERDOWN; 195 | 196 | bd = backlight_device_register(dev_driver_string(par->info->device), 197 | par->info->device, par, bl_ops, &bl_props); 198 | if (IS_ERR(bd)) { 199 | dev_err(par->info->device, 200 | "cannot register backlight device (%ld)\n", 201 | PTR_ERR(bd)); 202 | goto failed; 203 | } 204 | par->info->bl_dev = bd; 205 | 206 | if (!par->fbtftops.unregister_backlight) 207 | par->fbtftops.unregister_backlight = fbtft_unregister_backlight; 208 | 209 | return; 210 | failed: 211 | kfree(bl_ops); 212 | } 213 | 214 | 215 | 216 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 217 | 218 | MODULE_ALIAS("spi:" DRVNAME); 219 | MODULE_ALIAS("platform:" DRVNAME); 220 | 221 | MODULE_DESCRIPTION("SSD1351 OLED Driver"); 222 | MODULE_AUTHOR("James Davies"); 223 | MODULE_LICENSE("GPL"); 224 | -------------------------------------------------------------------------------- /fb_st7735r.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the ST7735R LCD Controller 3 | * 4 | * Copyright (C) 2013 Noralf Tronnes 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "fbtft.h" 26 | 27 | #define DRVNAME "fb_st7735r" 28 | #define DEFAULT_GAMMA "0F 1A 0F 18 2F 28 20 22 1F 1B 23 37 00 07 02 10\n" \ 29 | "0F 1B 0F 17 33 2C 29 2E 30 30 39 3F 00 07 03 10" 30 | 31 | 32 | static int default_init_sequence[] = { 33 | /* SWRESET - Software reset */ 34 | -1, 0x01, 35 | -2, 150, /* delay */ 36 | 37 | /* SLPOUT - Sleep out & booster on */ 38 | -1, 0x11, 39 | -2, 500, /* delay */ 40 | 41 | /* FRMCTR1 - frame rate control: normal mode 42 | frame rate = fosc / (1 x 2 + 40) * (LINE + 2C + 2D) */ 43 | -1, 0xB1, 0x01, 0x2C, 0x2D, 44 | 45 | /* FRMCTR2 - frame rate control: idle mode 46 | frame rate = fosc / (1 x 2 + 40) * (LINE + 2C + 2D) */ 47 | -1, 0xB2, 0x01, 0x2C, 0x2D, 48 | 49 | /* FRMCTR3 - frame rate control - partial mode 50 | dot inversion mode, line inversion mode */ 51 | -1, 0xB3, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D, 52 | 53 | /* INVCTR - display inversion control 54 | no inversion */ 55 | -1, 0xB4, 0x07, 56 | 57 | /* PWCTR1 - Power Control 58 | -4.6V, AUTO mode */ 59 | -1, 0xC0, 0xA2, 0x02, 0x84, 60 | 61 | /* PWCTR2 - Power Control 62 | VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD */ 63 | -1, 0xC1, 0xC5, 64 | 65 | /* PWCTR3 - Power Control 66 | Opamp current small, Boost frequency */ 67 | -1, 0xC2, 0x0A, 0x00, 68 | 69 | /* PWCTR4 - Power Control 70 | BCLK/2, Opamp current small & Medium low */ 71 | -1, 0xC3,0x8A,0x2A, 72 | 73 | /* PWCTR5 - Power Control */ 74 | -1, 0xC4, 0x8A, 0xEE, 75 | 76 | /* VMCTR1 - Power Control */ 77 | -1, 0xC5, 0x0E, 78 | 79 | /* INVOFF - Display inversion off */ 80 | -1, 0x20, 81 | 82 | /* COLMOD - Interface pixel format */ 83 | -1, 0x3A, 0x05, 84 | 85 | /* DISPON - Display On */ 86 | -1, 0x29, 87 | -2, 100, /* delay */ 88 | 89 | /* NORON - Partial off (Normal) */ 90 | -1, 0x13, 91 | -2, 10, /* delay */ 92 | 93 | /* end marker */ 94 | -3 95 | }; 96 | 97 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 98 | { 99 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 100 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 101 | 102 | /* Column address */ 103 | write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF); 104 | 105 | /* Row adress */ 106 | write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF); 107 | 108 | /* Memory write */ 109 | write_reg(par, 0x2C); 110 | } 111 | 112 | #define MY (1 << 7) 113 | #define MX (1 << 6) 114 | #define MV (1 << 5) 115 | static int set_var(struct fbtft_par *par) 116 | { 117 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 118 | 119 | /* MADCTL - Memory data access control 120 | RGB/BGR: 121 | 1. Mode selection pin SRGB 122 | RGB H/W pin for color filter setting: 0=RGB, 1=BGR 123 | 2. MADCTL RGB bit 124 | RGB-BGR ORDER color filter panel: 0=RGB, 1=BGR */ 125 | switch (par->info->var.rotate) { 126 | case 0: 127 | write_reg(par, 0x36, MX | MY | (par->bgr << 3)); 128 | break; 129 | case 270: 130 | write_reg(par, 0x36, MY | MV | (par->bgr << 3)); 131 | break; 132 | case 180: 133 | write_reg(par, 0x36, (par->bgr << 3)); 134 | break; 135 | case 90: 136 | write_reg(par, 0x36, MX | MV | (par->bgr << 3)); 137 | break; 138 | } 139 | 140 | return 0; 141 | } 142 | 143 | /* 144 | Gamma string format: 145 | VRF0P VOS0P PK0P PK1P PK2P PK3P PK4P PK5P PK6P PK7P PK8P PK9P SELV0P SELV1P SELV62P SELV63P 146 | VRF0N VOS0N PK0N PK1N PK2N PK3N PK4N PK5N PK6N PK7N PK8N PK9N SELV0N SELV1N SELV62N SELV63N 147 | */ 148 | #define CURVE(num, idx) curves[num*par->gamma.num_values + idx] 149 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) 150 | { 151 | int i,j; 152 | 153 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 154 | 155 | /* apply mask */ 156 | for (i = 0; i < par->gamma.num_curves; i++) 157 | for (j = 0; j < par->gamma.num_values; j++) 158 | CURVE(i,j) &= 0b111111; 159 | 160 | for (i = 0; i < par->gamma.num_curves; i++) 161 | write_reg(par, 0xE0 + i, 162 | CURVE(i, 0), CURVE(i, 1), CURVE(i, 2), CURVE(i, 3), 163 | CURVE(i, 4), CURVE(i, 5), CURVE(i, 6), CURVE(i, 7), 164 | CURVE(i, 8), CURVE(i, 9), CURVE(i, 10), CURVE(i, 11), 165 | CURVE(i, 12), CURVE(i, 13), CURVE(i, 14), CURVE(i,15)); 166 | 167 | return 0; 168 | } 169 | #undef CURVE 170 | 171 | 172 | static struct fbtft_display display = { 173 | .regwidth = 8, 174 | .width = 128, 175 | .height = 160, 176 | .init_sequence = default_init_sequence, 177 | .gamma_num = 2, 178 | .gamma_len = 16, 179 | .gamma = DEFAULT_GAMMA, 180 | .fbtftops = { 181 | .set_addr_win = set_addr_win, 182 | .set_var = set_var, 183 | .set_gamma = set_gamma, 184 | }, 185 | }; 186 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 187 | 188 | MODULE_ALIAS("spi:" DRVNAME); 189 | MODULE_ALIAS("platform:" DRVNAME); 190 | 191 | MODULE_DESCRIPTION("FB driver for the ST7735R LCD Controller"); 192 | MODULE_AUTHOR("Noralf Tronnes"); 193 | MODULE_LICENSE("GPL"); 194 | -------------------------------------------------------------------------------- /fb_watterott.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FB driver for the Watterott LCD Controller 3 | * 4 | * Copyright (C) 2013 Noralf Tronnes 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "fbtft.h" 28 | 29 | #define DRVNAME "fb_watterott" 30 | #define WIDTH 320 31 | #define HEIGHT 240 32 | #define FPS 5 33 | #define TXBUFLEN 1024 34 | #define DEFAULT_BRIGHTNESS 50 35 | 36 | #define CMD_VERSION 0x01 37 | #define CMD_LCD_LED 0x10 38 | #define CMD_LCD_RESET 0x11 39 | #define CMD_LCD_ORIENTATION 0x20 40 | #define CMD_LCD_DRAWIMAGE 0x27 41 | #define COLOR_RGB323 8 42 | #define COLOR_RGB332 9 43 | #define COLOR_RGB233 10 44 | #define COLOR_RGB565 16 45 | 46 | 47 | static short mode = 565; 48 | module_param(mode, short, 0); 49 | MODULE_PARM_DESC(mode, "RGB color transfer mode: 332, 565 (default)"); 50 | 51 | static void write_reg8_bus8(struct fbtft_par *par, int len, ...) 52 | { 53 | va_list args; 54 | int i, ret; 55 | u8 *buf = par->buf; 56 | 57 | va_start(args, len); 58 | for (i = 0; i < len; i++) 59 | *buf++ = (u8)va_arg(args, unsigned int); 60 | va_end(args); 61 | 62 | fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, 63 | par->info->device, u8, par->buf, len, "%s: ", __func__); 64 | 65 | ret = par->fbtftops.write(par, par->buf, len); 66 | if (ret < 0) { 67 | dev_err(par->info->device, 68 | "%s: write() failed and returned %d\n", __func__, ret); 69 | return; 70 | } 71 | } 72 | 73 | static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) 74 | { 75 | unsigned start_line, end_line; 76 | u16 *vmem16 = (u16 *)(par->info->screen_base + offset); 77 | u16 *pos = par->txbuf.buf + 1; 78 | u16 *buf16 = par->txbuf.buf + 10; 79 | int i, j; 80 | int ret = 0; 81 | 82 | fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); 83 | 84 | start_line = offset / par->info->fix.line_length; 85 | end_line = start_line + (len / par->info->fix.line_length) - 1; 86 | 87 | /* Set command header. pos: x, y, w, h */ 88 | ((u8 *)par->txbuf.buf)[0] = CMD_LCD_DRAWIMAGE; 89 | pos[0] = 0; 90 | pos[2] = cpu_to_be16(par->info->var.xres); 91 | pos[3] = cpu_to_be16(1); 92 | ((u8 *)par->txbuf.buf)[9] = COLOR_RGB565; 93 | 94 | for (i = start_line; i <= end_line; i++) { 95 | pos[1] = cpu_to_be16(i); 96 | for (j = 0; j < par->info->var.xres; j++) 97 | buf16[j] = cpu_to_be16(*vmem16++); 98 | ret = par->fbtftops.write(par, 99 | par->txbuf.buf, 10 + par->info->fix.line_length); 100 | if (ret < 0) 101 | return ret; 102 | udelay(300); 103 | } 104 | 105 | return 0; 106 | } 107 | 108 | #define RGB565toRGB323(c) (((c&0xE000)>>8) | ((c&0600)>>6) | ((c&0x001C)>>2)) 109 | #define RGB565toRGB332(c) (((c&0xE000)>>8) | ((c&0700)>>6) | ((c&0x0018)>>3)) 110 | #define RGB565toRGB233(c) (((c&0xC000)>>8) | ((c&0700)>>5) | ((c&0x001C)>>2)) 111 | 112 | static int write_vmem_8bit(struct fbtft_par *par, size_t offset, size_t len) 113 | { 114 | unsigned start_line, end_line; 115 | u16 *vmem16 = (u16 *)(par->info->screen_base + offset); 116 | u16 *pos = par->txbuf.buf + 1; 117 | u8 *buf8 = par->txbuf.buf + 10; 118 | int i, j; 119 | int ret = 0; 120 | 121 | fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); 122 | 123 | start_line = offset / par->info->fix.line_length; 124 | end_line = start_line + (len / par->info->fix.line_length) - 1; 125 | 126 | /* Set command header. pos: x, y, w, h */ 127 | ((u8 *)par->txbuf.buf)[0] = CMD_LCD_DRAWIMAGE; 128 | pos[0] = 0; 129 | pos[2] = cpu_to_be16(par->info->var.xres); 130 | pos[3] = cpu_to_be16(1); 131 | ((u8 *)par->txbuf.buf)[9] = COLOR_RGB332; 132 | 133 | for (i = start_line; i <= end_line; i++) { 134 | pos[1] = cpu_to_be16(i); 135 | for (j = 0; j < par->info->var.xres; j++) { 136 | buf8[j] = RGB565toRGB332(*vmem16); 137 | vmem16++; 138 | } 139 | ret = par->fbtftops.write(par, 140 | par->txbuf.buf, 10 + par->info->var.xres); 141 | if (ret < 0) 142 | return ret; 143 | udelay(700); 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | static unsigned firmware_version(struct fbtft_par *par) 150 | { 151 | u8 rxbuf[4] = {0, }; 152 | 153 | write_reg(par, CMD_VERSION); 154 | par->fbtftops.read(par, rxbuf, 4); 155 | if (rxbuf[1] != '.') 156 | return 0; 157 | 158 | return (rxbuf[0] - '0') << 8 | (rxbuf[2] - '0') << 4 | (rxbuf[3] - '0'); 159 | } 160 | 161 | static int init_display(struct fbtft_par *par) 162 | { 163 | int ret; 164 | unsigned version; 165 | u8 save_mode; 166 | 167 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 168 | 169 | /* enable SPI interface by having CS and MOSI low during reset */ 170 | save_mode = par->spi->mode; 171 | par->spi->mode |= SPI_CS_HIGH; 172 | ret = par->spi->master->setup(par->spi); /* set CS inactive low */ 173 | if (ret) { 174 | dev_err(par->info->device, "Could not set SPI_CS_HIGH\n"); 175 | return ret; 176 | } 177 | write_reg(par, 0x00); /* make sure mode is set */ 178 | 179 | mdelay(50); 180 | par->fbtftops.reset(par); 181 | mdelay(1000); 182 | par->spi->mode = save_mode; 183 | ret = par->spi->master->setup(par->spi); 184 | if (ret) { 185 | dev_err(par->info->device, "Could not restore SPI mode\n"); 186 | return ret; 187 | } 188 | write_reg(par, 0x00); 189 | 190 | version = firmware_version(par); 191 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "Firmware version: %x.%02x\n", 192 | version >> 8, version & 0xFF); 193 | 194 | if (mode == 332) 195 | par->fbtftops.write_vmem = write_vmem_8bit; 196 | return 0; 197 | } 198 | 199 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 200 | { 201 | /* not used on this controller */ 202 | } 203 | 204 | static int set_var(struct fbtft_par *par) 205 | { 206 | u8 rotate; 207 | 208 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); 209 | 210 | /* this controller rotates clock wise */ 211 | switch (par->info->var.rotate) { 212 | case 90: 213 | rotate = 27; 214 | break; 215 | case 180: 216 | rotate = 18; 217 | break; 218 | case 270: 219 | rotate = 9; 220 | break; 221 | default: 222 | rotate = 0; 223 | } 224 | write_reg(par, CMD_LCD_ORIENTATION, rotate); 225 | 226 | return 0; 227 | } 228 | 229 | static int verify_gpios(struct fbtft_par *par) 230 | { 231 | if (par->gpio.reset < 0) { 232 | dev_err(par->info->device, "Missing 'reset' gpio. Aborting.\n"); 233 | return -EINVAL; 234 | } 235 | return 0; 236 | } 237 | 238 | static int backlight_chip_update_status(struct backlight_device *bd) 239 | { 240 | struct fbtft_par *par = bl_get_data(bd); 241 | int brightness = bd->props.brightness; 242 | 243 | fbtft_par_dbg(DEBUG_BACKLIGHT, par, 244 | "%s: brightness=%d, power=%d, fb_blank=%d\n", 245 | __func__, bd->props.brightness, bd->props.power, 246 | bd->props.fb_blank); 247 | 248 | if (bd->props.power != FB_BLANK_UNBLANK) 249 | brightness = 0; 250 | 251 | if (bd->props.fb_blank != FB_BLANK_UNBLANK) 252 | brightness = 0; 253 | 254 | write_reg(par, CMD_LCD_LED, brightness); 255 | 256 | return 0; 257 | } 258 | 259 | static void register_chip_backlight(struct fbtft_par *par) 260 | { 261 | struct backlight_device *bd; 262 | struct backlight_properties bl_props = { 0, }; 263 | struct backlight_ops *bl_ops; 264 | 265 | fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s()\n", __func__); 266 | 267 | bl_ops = kzalloc(sizeof(struct backlight_ops), GFP_KERNEL); 268 | if (!bl_ops) { 269 | dev_err(par->info->device, 270 | "%s: could not allocate memory for backlight operations.\n", 271 | __func__); 272 | return; 273 | } 274 | 275 | bl_ops->update_status = backlight_chip_update_status; 276 | bl_props.type = BACKLIGHT_RAW; 277 | bl_props.power = FB_BLANK_POWERDOWN; 278 | bl_props.max_brightness = 100; 279 | bl_props.brightness = DEFAULT_BRIGHTNESS; 280 | 281 | bd = backlight_device_register(dev_driver_string(par->info->device), 282 | par->info->device, par, bl_ops, &bl_props); 283 | if (IS_ERR(bd)) { 284 | dev_err(par->info->device, 285 | "cannot register backlight device (%ld)\n", 286 | PTR_ERR(bd)); 287 | goto failed; 288 | } 289 | par->info->bl_dev = bd; 290 | 291 | if (!par->fbtftops.unregister_backlight) 292 | par->fbtftops.unregister_backlight = fbtft_unregister_backlight; 293 | 294 | return; 295 | failed: 296 | kfree(bl_ops); 297 | } 298 | 299 | 300 | static struct fbtft_display display = { 301 | .regwidth = 8, 302 | .buswidth = 8, 303 | .width = WIDTH, 304 | .height = HEIGHT, 305 | .fps = FPS, 306 | .txbuflen = TXBUFLEN, 307 | .fbtftops = { 308 | .write_register = write_reg8_bus8, 309 | .write_vmem = write_vmem, 310 | .init_display = init_display, 311 | .set_addr_win = set_addr_win, 312 | .set_var = set_var, 313 | .verify_gpios = verify_gpios, 314 | .register_backlight = register_chip_backlight, 315 | }, 316 | }; 317 | FBTFT_REGISTER_DRIVER(DRVNAME, &display); 318 | 319 | MODULE_ALIAS("spi:" DRVNAME); 320 | 321 | MODULE_DESCRIPTION("FB driver for the Watterott LCD Controller"); 322 | MODULE_AUTHOR("Noralf Tronnes"); 323 | MODULE_LICENSE("GPL"); 324 | -------------------------------------------------------------------------------- /fbtft-bus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "fbtft.h" 6 | 7 | 8 | 9 | 10 | /***************************************************************************** 11 | * 12 | * void (*write_reg)(struct fbtft_par *par, int len, ...); 13 | * 14 | *****************************************************************************/ 15 | 16 | #define define_fbtft_write_reg(func, type, modifier) \ 17 | void func(struct fbtft_par *par, int len, ...) \ 18 | { \ 19 | va_list args; \ 20 | int i, ret; \ 21 | int offset = 0; \ 22 | type *buf = (type *)par->buf; \ 23 | \ 24 | if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { \ 25 | va_start(args, len); \ 26 | for (i = 0; i < len; i++) { \ 27 | buf[i] = (type)va_arg(args, unsigned int); \ 28 | } \ 29 | va_end(args); \ 30 | fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, type, buf, len, "%s: ", __func__); \ 31 | } \ 32 | \ 33 | va_start(args, len); \ 34 | \ 35 | if (par->startbyte) { \ 36 | *(u8 *)par->buf = par->startbyte; \ 37 | buf = (type *)(par->buf + 1); \ 38 | offset = 1; \ 39 | } \ 40 | \ 41 | *buf = modifier((type)va_arg(args, unsigned int)); \ 42 | if (par->gpio.dc != -1) \ 43 | gpio_set_value(par->gpio.dc, 0); \ 44 | ret = par->fbtftops.write(par, par->buf, sizeof(type)+offset); \ 45 | if (ret < 0) { \ 46 | va_end(args); \ 47 | dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret); \ 48 | return; \ 49 | } \ 50 | len--; \ 51 | \ 52 | if (par->startbyte) \ 53 | *(u8 *)par->buf = par->startbyte | 0x2; \ 54 | \ 55 | if (len) { \ 56 | i = len; \ 57 | while (i--) { \ 58 | *buf++ = modifier((type)va_arg(args, unsigned int)); \ 59 | } \ 60 | if (par->gpio.dc != -1) \ 61 | gpio_set_value(par->gpio.dc, 1); \ 62 | ret = par->fbtftops.write(par, par->buf, len * (sizeof(type)+offset)); \ 63 | if (ret < 0) { \ 64 | va_end(args); \ 65 | dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret); \ 66 | return; \ 67 | } \ 68 | } \ 69 | va_end(args); \ 70 | } \ 71 | EXPORT_SYMBOL(func); 72 | 73 | define_fbtft_write_reg(fbtft_write_reg8_bus8, u8, ) 74 | define_fbtft_write_reg(fbtft_write_reg16_bus8, u16, cpu_to_be16) 75 | define_fbtft_write_reg(fbtft_write_reg16_bus16, u16, ) 76 | 77 | void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...) 78 | { 79 | va_list args; 80 | int i, ret; 81 | int pad = 0; 82 | u16 *buf = (u16 *)par->buf; 83 | 84 | if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { 85 | va_start(args, len); 86 | for (i = 0; i < len; i++) 87 | *(((u8 *)buf) + i) = (u8)va_arg(args, unsigned int); 88 | va_end(args); 89 | fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, 90 | par->info->device, u8, buf, len, "%s: ", __func__); 91 | } 92 | if (len <= 0) 93 | return; 94 | 95 | if (par->spi && (par->spi->bits_per_word == 8)) { 96 | /* we're emulating 9-bit, pad start of buffer with no-ops 97 | (assuming here that zero is a no-op) */ 98 | pad = (len % 4) ? 4 - (len % 4) : 0; 99 | for (i = 0; i < pad; i++) 100 | *buf++ = 0x000; 101 | } 102 | 103 | va_start(args, len); 104 | *buf++ = (u8)va_arg(args, unsigned int); 105 | i = len - 1; 106 | while (i--) { 107 | *buf = (u8)va_arg(args, unsigned int); 108 | *buf++ |= 0x100; /* dc=1 */ 109 | } 110 | va_end(args); 111 | ret = par->fbtftops.write(par, par->buf, (len + pad) * sizeof(u16)); 112 | if (ret < 0) { 113 | dev_err(par->info->device, 114 | "%s: write() failed and returned %d\n", __func__, ret); 115 | return; 116 | } 117 | } 118 | EXPORT_SYMBOL(fbtft_write_reg8_bus9); 119 | 120 | 121 | 122 | 123 | /***************************************************************************** 124 | * 125 | * int (*write_vmem)(struct fbtft_par *par); 126 | * 127 | *****************************************************************************/ 128 | 129 | /* 16 bit pixel over 8-bit databus */ 130 | int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len) 131 | { 132 | u16 *vmem16; 133 | u16 *txbuf16 = (u16 *)par->txbuf.buf; 134 | size_t remain; 135 | size_t to_copy; 136 | size_t tx_array_size; 137 | int i; 138 | int ret = 0; 139 | size_t startbyte_size = 0; 140 | 141 | fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n", 142 | __func__, offset, len); 143 | 144 | remain = len / 2; 145 | vmem16 = (u16 *)(par->info->screen_base + offset); 146 | 147 | if (par->gpio.dc != -1) 148 | gpio_set_value(par->gpio.dc, 1); 149 | 150 | /* non buffered write */ 151 | if (!par->txbuf.buf) 152 | return par->fbtftops.write(par, vmem16, len); 153 | 154 | /* buffered write */ 155 | tx_array_size = par->txbuf.len / 2; 156 | 157 | if (par->startbyte) { 158 | txbuf16 = (u16 *)(par->txbuf.buf + 1); 159 | tx_array_size -= 2; 160 | *(u8 *)(par->txbuf.buf) = par->startbyte | 0x2; 161 | startbyte_size = 1; 162 | } 163 | 164 | while (remain) { 165 | to_copy = remain > tx_array_size ? tx_array_size : remain; 166 | dev_dbg(par->info->device, " to_copy=%zu, remain=%zu\n", 167 | to_copy, remain - to_copy); 168 | 169 | for (i = 0; i < to_copy; i++) 170 | txbuf16[i] = cpu_to_be16(vmem16[i]); 171 | 172 | vmem16 = vmem16 + to_copy; 173 | ret = par->fbtftops.write(par, par->txbuf.buf, 174 | startbyte_size + to_copy * 2); 175 | if (ret < 0) 176 | return ret; 177 | remain -= to_copy; 178 | } 179 | 180 | return ret; 181 | } 182 | EXPORT_SYMBOL(fbtft_write_vmem16_bus8); 183 | 184 | /* 16 bit pixel over 9-bit SPI bus: dc + high byte, dc + low byte */ 185 | int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len) 186 | { 187 | u8 *vmem8; 188 | u16 *txbuf16 = par->txbuf.buf; 189 | size_t remain; 190 | size_t to_copy; 191 | size_t tx_array_size; 192 | int i; 193 | int ret = 0; 194 | 195 | fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n", 196 | __func__, offset, len); 197 | 198 | if (!par->txbuf.buf) { 199 | dev_err(par->info->device, "%s: txbuf.buf is NULL\n", __func__); 200 | return -1; 201 | } 202 | 203 | remain = len; 204 | vmem8 = par->info->screen_base + offset; 205 | 206 | tx_array_size = par->txbuf.len / 2; 207 | 208 | while (remain) { 209 | to_copy = remain > tx_array_size ? tx_array_size : remain; 210 | dev_dbg(par->info->device, " to_copy=%zu, remain=%zu\n", 211 | to_copy, remain - to_copy); 212 | 213 | #ifdef __LITTLE_ENDIAN 214 | for (i = 0; i < to_copy; i += 2) { 215 | txbuf16[i] = 0x0100 | vmem8[i+1]; 216 | txbuf16[i+1] = 0x0100 | vmem8[i]; 217 | } 218 | #else 219 | for (i = 0; i < to_copy; i++) 220 | txbuf16[i] = 0x0100 | vmem8[i]; 221 | #endif 222 | vmem8 = vmem8 + to_copy; 223 | ret = par->fbtftops.write(par, par->txbuf.buf, to_copy*2); 224 | if (ret < 0) 225 | return ret; 226 | remain -= to_copy; 227 | } 228 | 229 | return ret; 230 | } 231 | EXPORT_SYMBOL(fbtft_write_vmem16_bus9); 232 | 233 | int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len) 234 | { 235 | dev_err(par->info->device, "%s: function not implemented\n", __func__); 236 | return -1; 237 | } 238 | EXPORT_SYMBOL(fbtft_write_vmem8_bus8); 239 | 240 | /* 16 bit pixel over 16-bit databus */ 241 | int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len) 242 | { 243 | u16 *vmem16; 244 | 245 | fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n", 246 | __func__, offset, len); 247 | 248 | vmem16 = (u16 *)(par->info->screen_base + offset); 249 | 250 | if (par->gpio.dc != -1) 251 | gpio_set_value(par->gpio.dc, 1); 252 | 253 | /* no need for buffered write with 16-bit bus */ 254 | return par->fbtftops.write(par, vmem16, len); 255 | } 256 | EXPORT_SYMBOL(fbtft_write_vmem16_bus16); 257 | -------------------------------------------------------------------------------- /fbtft-io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #ifdef CONFIG_ARCH_BCM2708 6 | #include 7 | #endif 8 | #include "fbtft.h" 9 | 10 | int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len) 11 | { 12 | struct spi_transfer t = { 13 | .tx_buf = buf, 14 | .len = len, 15 | }; 16 | struct spi_message m; 17 | 18 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, 19 | "%s(len=%d): ", __func__, len); 20 | 21 | if (!par->spi) { 22 | dev_err(par->info->device, 23 | "%s: par->spi is unexpectedly NULL\n", __func__); 24 | return -1; 25 | } 26 | 27 | spi_message_init(&m); 28 | if (par->txbuf.dma && buf == par->txbuf.buf) { 29 | t.tx_dma = par->txbuf.dma; 30 | m.is_dma_mapped = 1; 31 | } 32 | spi_message_add_tail(&t, &m); 33 | return spi_sync(par->spi, &m); 34 | } 35 | EXPORT_SYMBOL(fbtft_write_spi); 36 | 37 | /** 38 | * fbtft_write_spi_emulate_9() - write SPI emulating 9-bit 39 | * @par: Driver data 40 | * @buf: Buffer to write 41 | * @len: Length of buffer (must be divisible by 8) 42 | * 43 | * When 9-bit SPI is not available, this function can be used to emulate that. 44 | * par->extra must hold a transformation buffer used for transfer. 45 | */ 46 | int fbtft_write_spi_emulate_9(struct fbtft_par *par, void *buf, size_t len) 47 | { 48 | u16 *src = buf; 49 | u8 *dst = par->extra; 50 | size_t size = len / 2; 51 | size_t added = 0; 52 | int bits, i, j; 53 | u64 val, dc, tmp; 54 | 55 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, 56 | "%s(len=%d): ", __func__, len); 57 | 58 | if (!par->extra) { 59 | dev_err(par->info->device, "%s: error: par->extra is NULL\n", 60 | __func__); 61 | return -EINVAL; 62 | } 63 | if ((len % 8) != 0) { 64 | dev_err(par->info->device, 65 | "%s: error: len=%d must be divisible by 8\n", 66 | __func__, len); 67 | return -EINVAL; 68 | } 69 | 70 | for (i = 0; i < size; i += 8) { 71 | tmp = 0; 72 | bits = 63; 73 | for (j = 0; j < 7; j++) { 74 | dc = (*src & 0x0100) ? 1 : 0; 75 | val = *src & 0x00FF; 76 | tmp |= dc << bits; 77 | bits -= 8; 78 | tmp |= val << bits--; 79 | src++; 80 | } 81 | tmp |= ((*src & 0x0100) ? 1 : 0); 82 | *(u64 *)dst = cpu_to_be64(tmp); 83 | dst += 8; 84 | *dst++ = (u8)(*src++ & 0x00FF); 85 | added++; 86 | } 87 | 88 | return spi_write(par->spi, par->extra, size + added); 89 | } 90 | EXPORT_SYMBOL(fbtft_write_spi_emulate_9); 91 | 92 | int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len) 93 | { 94 | int ret; 95 | u8 txbuf[32] = { 0, }; 96 | struct spi_transfer t = { 97 | .speed_hz = 2000000, 98 | .rx_buf = buf, 99 | .len = len, 100 | }; 101 | struct spi_message m; 102 | 103 | if (!par->spi) { 104 | dev_err(par->info->device, 105 | "%s: par->spi is unexpectedly NULL\n", __func__); 106 | return -ENODEV; 107 | } 108 | 109 | if (par->startbyte) { 110 | if (len > 32) { 111 | dev_err(par->info->device, 112 | "%s: len=%d can't be larger than 32 when using 'startbyte'\n", 113 | __func__, len); 114 | return -EINVAL; 115 | } 116 | txbuf[0] = par->startbyte | 0x3; 117 | t.tx_buf = txbuf; 118 | fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8, 119 | txbuf, len, "%s(len=%d) txbuf => ", __func__, len); 120 | } 121 | 122 | spi_message_init(&m); 123 | spi_message_add_tail(&t, &m); 124 | ret = spi_sync(par->spi, &m); 125 | fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8, buf, len, 126 | "%s(len=%d) buf <= ", __func__, len); 127 | 128 | return ret; 129 | } 130 | EXPORT_SYMBOL(fbtft_read_spi); 131 | 132 | 133 | #ifdef CONFIG_ARCH_BCM2708 134 | 135 | /* 136 | * Raspberry Pi 137 | * - writing directly to the registers is 40-50% faster than 138 | * optimized use of gpiolib 139 | */ 140 | 141 | #define GPIOSET(no, ishigh) \ 142 | do { \ 143 | if (ishigh) \ 144 | set |= (1 << (no)); \ 145 | else \ 146 | reset |= (1 << (no)); \ 147 | } while (0) 148 | 149 | int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len) 150 | { 151 | unsigned int set = 0; 152 | unsigned int reset = 0; 153 | u8 data; 154 | 155 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, 156 | "%s(len=%d): ", __func__, len); 157 | 158 | while (len--) { 159 | data = *(u8 *) buf; 160 | buf++; 161 | 162 | /* Set data */ 163 | GPIOSET(par->gpio.db[0], (data&0x01)); 164 | GPIOSET(par->gpio.db[1], (data&0x02)); 165 | GPIOSET(par->gpio.db[2], (data&0x04)); 166 | GPIOSET(par->gpio.db[3], (data&0x08)); 167 | GPIOSET(par->gpio.db[4], (data&0x10)); 168 | GPIOSET(par->gpio.db[5], (data&0x20)); 169 | GPIOSET(par->gpio.db[6], (data&0x40)); 170 | GPIOSET(par->gpio.db[7], (data&0x80)); 171 | writel(set, __io_address(GPIO_BASE+0x1C)); 172 | writel(reset, __io_address(GPIO_BASE+0x28)); 173 | 174 | /* Pulse /WR low */ 175 | writel((1<gpio.wr), __io_address(GPIO_BASE+0x28)); 176 | writel(0, __io_address(GPIO_BASE+0x28)); /* used as a delay */ 177 | writel((1<gpio.wr), __io_address(GPIO_BASE+0x1C)); 178 | 179 | set = 0; 180 | reset = 0; 181 | } 182 | 183 | return 0; 184 | } 185 | EXPORT_SYMBOL(fbtft_write_gpio8_wr); 186 | 187 | int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len) 188 | { 189 | unsigned int set = 0; 190 | unsigned int reset = 0; 191 | u16 data; 192 | 193 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, 194 | "%s(len=%d): ", __func__, len); 195 | 196 | while (len) { 197 | len -= 2; 198 | data = *(u16 *) buf; 199 | buf += 2; 200 | 201 | /* Start writing by pulling down /WR */ 202 | gpio_set_value(par->gpio.wr, 0); 203 | 204 | /* Set data */ 205 | GPIOSET(par->gpio.db[0], (data&0x0001)); 206 | GPIOSET(par->gpio.db[1], (data&0x0002)); 207 | GPIOSET(par->gpio.db[2], (data&0x0004)); 208 | GPIOSET(par->gpio.db[3], (data&0x0008)); 209 | GPIOSET(par->gpio.db[4], (data&0x0010)); 210 | GPIOSET(par->gpio.db[5], (data&0x0020)); 211 | GPIOSET(par->gpio.db[6], (data&0x0040)); 212 | GPIOSET(par->gpio.db[7], (data&0x0080)); 213 | 214 | GPIOSET(par->gpio.db[8], (data&0x0100)); 215 | GPIOSET(par->gpio.db[9], (data&0x0200)); 216 | GPIOSET(par->gpio.db[10], (data&0x0400)); 217 | GPIOSET(par->gpio.db[11], (data&0x0800)); 218 | GPIOSET(par->gpio.db[12], (data&0x1000)); 219 | GPIOSET(par->gpio.db[13], (data&0x2000)); 220 | GPIOSET(par->gpio.db[14], (data&0x4000)); 221 | GPIOSET(par->gpio.db[15], (data&0x8000)); 222 | 223 | writel(set, __io_address(GPIO_BASE+0x1C)); 224 | writel(reset, __io_address(GPIO_BASE+0x28)); 225 | 226 | /* Pullup /WR */ 227 | gpio_set_value(par->gpio.wr, 1); 228 | 229 | set = 0; 230 | reset = 0; 231 | } 232 | 233 | return 0; 234 | } 235 | EXPORT_SYMBOL(fbtft_write_gpio16_wr); 236 | 237 | int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len) 238 | { 239 | unsigned int set = 0; 240 | unsigned int reset = 0; 241 | u16 data; 242 | 243 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, 244 | "%s(len=%d): ", __func__, len); 245 | 246 | while (len) { 247 | len -= 2; 248 | data = *(u16 *) buf; 249 | buf += 2; 250 | 251 | /* Start writing by pulling down /WR */ 252 | gpio_set_value(par->gpio.wr, 0); 253 | 254 | /* Low byte */ 255 | GPIOSET(par->gpio.db[0], (data&0x0001)); 256 | GPIOSET(par->gpio.db[1], (data&0x0002)); 257 | GPIOSET(par->gpio.db[2], (data&0x0004)); 258 | GPIOSET(par->gpio.db[3], (data&0x0008)); 259 | GPIOSET(par->gpio.db[4], (data&0x0010)); 260 | GPIOSET(par->gpio.db[5], (data&0x0020)); 261 | GPIOSET(par->gpio.db[6], (data&0x0040)); 262 | GPIOSET(par->gpio.db[7], (data&0x0080)); 263 | writel(set, __io_address(GPIO_BASE+0x1C)); 264 | writel(reset, __io_address(GPIO_BASE+0x28)); 265 | 266 | /* Pulse 'latch' high */ 267 | gpio_set_value(par->gpio.latch, 1); 268 | gpio_set_value(par->gpio.latch, 0); 269 | 270 | /* High byte */ 271 | GPIOSET(par->gpio.db[0], (data&0x0100)); 272 | GPIOSET(par->gpio.db[1], (data&0x0200)); 273 | GPIOSET(par->gpio.db[2], (data&0x0400)); 274 | GPIOSET(par->gpio.db[3], (data&0x0800)); 275 | GPIOSET(par->gpio.db[4], (data&0x1000)); 276 | GPIOSET(par->gpio.db[5], (data&0x2000)); 277 | GPIOSET(par->gpio.db[6], (data&0x4000)); 278 | GPIOSET(par->gpio.db[7], (data&0x8000)); 279 | writel(set, __io_address(GPIO_BASE+0x1C)); 280 | writel(reset, __io_address(GPIO_BASE+0x28)); 281 | 282 | /* Pullup /WR */ 283 | gpio_set_value(par->gpio.wr, 1); 284 | 285 | set = 0; 286 | reset = 0; 287 | } 288 | 289 | return 0; 290 | } 291 | EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched); 292 | 293 | #undef GPIOSET 294 | 295 | #else 296 | 297 | /* 298 | * Optimized use of gpiolib is twice as fast as no optimization 299 | * only one driver can use the optimized version at a time 300 | */ 301 | int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len) 302 | { 303 | u8 data; 304 | int i; 305 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO 306 | static u8 prev_data; 307 | #endif 308 | 309 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, 310 | "%s(len=%d): ", __func__, len); 311 | 312 | while (len--) { 313 | data = *(u8 *) buf; 314 | 315 | /* Start writing by pulling down /WR */ 316 | gpio_set_value(par->gpio.wr, 0); 317 | 318 | /* Set data */ 319 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO 320 | if (data == prev_data) { 321 | gpio_set_value(par->gpio.wr, 0); /* used as delay */ 322 | } else { 323 | for (i = 0; i < 8; i++) { 324 | if ((data & 1) != (prev_data & 1)) 325 | gpio_set_value(par->gpio.db[i], 326 | (data & 1)); 327 | data >>= 1; 328 | prev_data >>= 1; 329 | } 330 | } 331 | #else 332 | for (i = 0; i < 8; i++) { 333 | gpio_set_value(par->gpio.db[i], (data & 1)); 334 | data >>= 1; 335 | } 336 | #endif 337 | 338 | /* Pullup /WR */ 339 | gpio_set_value(par->gpio.wr, 1); 340 | 341 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO 342 | prev_data = *(u8 *) buf; 343 | #endif 344 | buf++; 345 | } 346 | 347 | return 0; 348 | } 349 | EXPORT_SYMBOL(fbtft_write_gpio8_wr); 350 | 351 | int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len) 352 | { 353 | u16 data; 354 | int i; 355 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO 356 | static u16 prev_data; 357 | #endif 358 | 359 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, 360 | "%s(len=%d): ", __func__, len); 361 | 362 | while (len) { 363 | data = *(u16 *) buf; 364 | 365 | /* Start writing by pulling down /WR */ 366 | gpio_set_value(par->gpio.wr, 0); 367 | 368 | /* Set data */ 369 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO 370 | if (data == prev_data) { 371 | gpio_set_value(par->gpio.wr, 0); /* used as delay */ 372 | } else { 373 | for (i = 0; i < 16; i++) { 374 | if ((data & 1) != (prev_data & 1)) 375 | gpio_set_value(par->gpio.db[i], 376 | (data & 1)); 377 | data >>= 1; 378 | prev_data >>= 1; 379 | } 380 | } 381 | #else 382 | for (i = 0; i < 16; i++) { 383 | gpio_set_value(par->gpio.db[i], (data & 1)); 384 | data >>= 1; 385 | } 386 | #endif 387 | 388 | /* Pullup /WR */ 389 | gpio_set_value(par->gpio.wr, 1); 390 | 391 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO 392 | prev_data = *(u16 *) buf; 393 | #endif 394 | buf += 2; 395 | len -= 2; 396 | } 397 | 398 | return 0; 399 | } 400 | EXPORT_SYMBOL(fbtft_write_gpio16_wr); 401 | 402 | int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len) 403 | { 404 | dev_err(par->info->device, "%s: function not implemented\n", __func__); 405 | return -1; 406 | } 407 | EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched); 408 | 409 | #endif /* CONFIG_ARCH_BCM2708 */ 410 | -------------------------------------------------------------------------------- /fbtft-sysfs.c: -------------------------------------------------------------------------------- 1 | #include "fbtft.h" 2 | 3 | 4 | static int get_next_ulong(char **str_p, unsigned long *val, char *sep, int base) 5 | { 6 | char *p_val; 7 | int ret; 8 | 9 | if (!str_p || !(*str_p)) 10 | return -EINVAL; 11 | 12 | p_val = strsep(str_p, sep); 13 | 14 | if (!p_val) 15 | return -EINVAL; 16 | 17 | ret = kstrtoul(p_val, base, val); 18 | if (ret) 19 | return -EINVAL; 20 | 21 | return 0; 22 | } 23 | 24 | int fbtft_gamma_parse_str(struct fbtft_par *par, unsigned long *curves, 25 | const char *str, int size) 26 | { 27 | char *str_p, *curve_p = NULL; 28 | char *tmp; 29 | unsigned long val = 0; 30 | int ret = 0; 31 | int curve_counter, value_counter; 32 | 33 | fbtft_par_dbg(DEBUG_SYSFS, par, "%s() str=\n", __func__); 34 | 35 | if (!str || !curves) 36 | return -EINVAL; 37 | 38 | fbtft_par_dbg(DEBUG_SYSFS, par, "%s\n", str); 39 | 40 | tmp = kmalloc(size+1, GFP_KERNEL); 41 | if (!tmp) 42 | return -ENOMEM; 43 | memcpy(tmp, str, size+1); 44 | 45 | /* replace optional separators */ 46 | str_p = tmp; 47 | while (*str_p) { 48 | if (*str_p == ',') 49 | *str_p = ' '; 50 | if (*str_p == ';') 51 | *str_p = '\n'; 52 | str_p++; 53 | } 54 | 55 | str_p = strim(tmp); 56 | 57 | curve_counter = 0; 58 | while (str_p) { 59 | if (curve_counter == par->gamma.num_curves) { 60 | dev_err(par->info->device, "Gamma: Too many curves\n"); 61 | ret = -EINVAL; 62 | goto out; 63 | } 64 | curve_p = strsep(&str_p, "\n"); 65 | value_counter = 0; 66 | while (curve_p) { 67 | if (value_counter == par->gamma.num_values) { 68 | dev_err(par->info->device, 69 | "Gamma: Too many values\n"); 70 | ret = -EINVAL; 71 | goto out; 72 | } 73 | ret = get_next_ulong(&curve_p, &val, " ", 16); 74 | if (ret) 75 | goto out; 76 | curves[curve_counter * par->gamma.num_values + value_counter] = val; 77 | value_counter++; 78 | } 79 | if (value_counter != par->gamma.num_values) { 80 | dev_err(par->info->device, "Gamma: Too few values\n"); 81 | ret = -EINVAL; 82 | goto out; 83 | } 84 | curve_counter++; 85 | } 86 | if (curve_counter != par->gamma.num_curves) { 87 | dev_err(par->info->device, "Gamma: Too few curves\n"); 88 | ret = -EINVAL; 89 | goto out; 90 | } 91 | 92 | out: 93 | kfree(tmp); 94 | return ret; 95 | } 96 | 97 | static ssize_t 98 | sprintf_gamma(struct fbtft_par *par, unsigned long *curves, char *buf) 99 | { 100 | ssize_t len = 0; 101 | unsigned int i, j; 102 | 103 | mutex_lock(&par->gamma.lock); 104 | for (i = 0; i < par->gamma.num_curves; i++) { 105 | for (j = 0; j < par->gamma.num_values; j++) 106 | len += scnprintf(&buf[len], PAGE_SIZE, 107 | "%04lx ", curves[i*par->gamma.num_values + j]); 108 | buf[len-1] = '\n'; 109 | } 110 | mutex_unlock(&par->gamma.lock); 111 | 112 | return len; 113 | } 114 | 115 | static ssize_t store_gamma_curve(struct device *device, 116 | struct device_attribute *attr, 117 | const char *buf, size_t count) 118 | { 119 | struct fb_info *fb_info = dev_get_drvdata(device); 120 | struct fbtft_par *par = fb_info->par; 121 | unsigned long tmp_curves[FBTFT_GAMMA_MAX_VALUES_TOTAL]; 122 | int ret; 123 | 124 | ret = fbtft_gamma_parse_str(par, tmp_curves, buf, count); 125 | if (ret) 126 | return ret; 127 | 128 | ret = par->fbtftops.set_gamma(par, tmp_curves); 129 | if (ret) 130 | return ret; 131 | 132 | mutex_lock(&par->gamma.lock); 133 | memcpy(par->gamma.curves, tmp_curves, 134 | par->gamma.num_curves * par->gamma.num_values * sizeof(tmp_curves[0])); 135 | mutex_unlock(&par->gamma.lock); 136 | 137 | return count; 138 | } 139 | 140 | static ssize_t show_gamma_curve(struct device *device, 141 | struct device_attribute *attr, char *buf) 142 | { 143 | struct fb_info *fb_info = dev_get_drvdata(device); 144 | struct fbtft_par *par = fb_info->par; 145 | 146 | return sprintf_gamma(par, par->gamma.curves, buf); 147 | } 148 | 149 | static struct device_attribute gamma_device_attrs[] = { 150 | __ATTR(gamma, S_IRUGO | S_IWUGO, show_gamma_curve, store_gamma_curve), 151 | }; 152 | 153 | 154 | void fbtft_expand_debug_value(unsigned long *debug) 155 | { 156 | switch (*debug & 0b111) { 157 | case 1: 158 | *debug |= DEBUG_LEVEL_1; 159 | break; 160 | case 2: 161 | *debug |= DEBUG_LEVEL_2; 162 | break; 163 | case 3: 164 | *debug |= DEBUG_LEVEL_3; 165 | break; 166 | case 4: 167 | *debug |= DEBUG_LEVEL_4; 168 | break; 169 | case 5: 170 | *debug |= DEBUG_LEVEL_5; 171 | break; 172 | case 6: 173 | *debug |= DEBUG_LEVEL_6; 174 | break; 175 | case 7: 176 | *debug = 0xFFFFFFFF; 177 | break; 178 | } 179 | } 180 | 181 | static ssize_t store_debug(struct device *device, 182 | struct device_attribute *attr, 183 | const char *buf, size_t count) 184 | { 185 | struct fb_info *fb_info = dev_get_drvdata(device); 186 | struct fbtft_par *par = fb_info->par; 187 | int ret; 188 | 189 | ret = kstrtoul(buf, 10, &par->debug); 190 | if (ret) 191 | return ret; 192 | fbtft_expand_debug_value(&par->debug); 193 | 194 | return count; 195 | } 196 | 197 | static ssize_t show_debug(struct device *device, 198 | struct device_attribute *attr, char *buf) 199 | { 200 | struct fb_info *fb_info = dev_get_drvdata(device); 201 | struct fbtft_par *par = fb_info->par; 202 | 203 | return snprintf(buf, PAGE_SIZE, "%lu\n", par->debug); 204 | } 205 | 206 | static struct device_attribute debug_device_attr = \ 207 | __ATTR(debug, S_IRUGO | S_IWUGO, show_debug, store_debug); 208 | 209 | 210 | void fbtft_sysfs_init(struct fbtft_par *par) 211 | { 212 | device_create_file(par->info->dev, &debug_device_attr); 213 | if (par->gamma.curves && par->fbtftops.set_gamma) 214 | device_create_file(par->info->dev, &gamma_device_attrs[0]); 215 | } 216 | 217 | void fbtft_sysfs_exit(struct fbtft_par *par) 218 | { 219 | device_remove_file(par->info->dev, &debug_device_attr); 220 | if (par->gamma.curves && par->fbtftops.set_gamma) 221 | device_remove_file(par->info->dev, &gamma_device_attrs[0]); 222 | } 223 | -------------------------------------------------------------------------------- /fbtft.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Noralf Tronnes 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | */ 18 | 19 | #ifndef __LINUX_FBTFT_H 20 | #define __LINUX_FBTFT_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | 28 | #define FBTFT_NOP 0x00 29 | #define FBTFT_SWRESET 0x01 30 | #define FBTFT_RDDID 0x04 31 | #define FBTFT_RDDST 0x09 32 | #define FBTFT_CASET 0x2A 33 | #define FBTFT_RASET 0x2B 34 | #define FBTFT_RAMWR 0x2C 35 | 36 | #define FBTFT_ONBOARD_BACKLIGHT 2 37 | 38 | #define FBTFT_GPIO_NO_MATCH 0xFFFF 39 | #define FBTFT_GPIO_NAME_SIZE 32 40 | #define FBTFT_MAX_INIT_SEQUENCE 512 41 | #define FBTFT_GAMMA_MAX_VALUES_TOTAL 128 42 | 43 | /** 44 | * struct fbtft_gpio - Structure that holds one pinname to gpio mapping 45 | * @name: pinname (reset, dc, etc.) 46 | * @gpio: GPIO number 47 | * 48 | */ 49 | struct fbtft_gpio { 50 | char name[FBTFT_GPIO_NAME_SIZE]; 51 | unsigned gpio; 52 | }; 53 | 54 | struct fbtft_par; 55 | 56 | /** 57 | * struct fbtft_ops - FBTFT operations structure 58 | * @write: Writes to interface bus 59 | * @read: Reads from interface bus 60 | * @write_vmem: Writes video memory to display 61 | * @write_reg: Writes to controller register 62 | * @set_addr_win: Set the GRAM update window 63 | * @reset: Reset the LCD controller 64 | * @mkdirty: Marks display lines for update 65 | * @update_display: Updates the display 66 | * @init_display: Initializes the display 67 | * @blank: Blank the display (optional) 68 | * @request_gpios_match: Do pinname to gpio matching 69 | * @request_gpios: Request gpios from the kernel 70 | * @free_gpios: Free previously requested gpios 71 | * @verify_gpios: Verify that necessary gpios is present (optional) 72 | * @register_backlight: Used to register backlight device (optional) 73 | * @unregister_backlight: Unregister backlight device (optional) 74 | * @set_var: Configure LCD with values from variables like @rotate and @bgr 75 | * (optional) 76 | * @set_gamma: Set Gamma curve (optional) 77 | * 78 | * Most of these operations have default functions assigned to them in 79 | * fbtft_framebuffer_alloc() 80 | */ 81 | struct fbtft_ops { 82 | int (*write)(struct fbtft_par *par, void *buf, size_t len); 83 | int (*read)(struct fbtft_par *par, void *buf, size_t len); 84 | int (*write_vmem)(struct fbtft_par *par, size_t offset, size_t len); 85 | void (*write_register)(struct fbtft_par *par, int len, ...); 86 | 87 | void (*set_addr_win)(struct fbtft_par *par, 88 | int xs, int ys, int xe, int ye); 89 | void (*reset)(struct fbtft_par *par); 90 | void (*mkdirty)(struct fb_info *info, int from, int to); 91 | void (*update_display)(struct fbtft_par *par, 92 | unsigned start_line, unsigned end_line); 93 | int (*init_display)(struct fbtft_par *par); 94 | int (*blank)(struct fbtft_par *par, bool on); 95 | 96 | unsigned long (*request_gpios_match)(struct fbtft_par *par, 97 | const struct fbtft_gpio *gpio); 98 | int (*request_gpios)(struct fbtft_par *par); 99 | void (*free_gpios)(struct fbtft_par *par); 100 | int (*verify_gpios)(struct fbtft_par *par); 101 | 102 | void (*register_backlight)(struct fbtft_par *par); 103 | void (*unregister_backlight)(struct fbtft_par *par); 104 | 105 | int (*set_var)(struct fbtft_par *par); 106 | int (*set_gamma)(struct fbtft_par *par, unsigned long *curves); 107 | }; 108 | 109 | /** 110 | * struct fbtft_display - Describes the display properties 111 | * @width: Width of display in pixels 112 | * @height: Height of display in pixels 113 | * @regwidth: LCD Controller Register width in bits 114 | * @buswidth: Display interface bus width in bits 115 | * @backlight: Backlight type. 116 | * @fbtftops: FBTFT operations provided by driver or device (platform_data) 117 | * @bpp: Bits per pixel 118 | * @fps: Frames per second 119 | * @txbuflen: Size of transmit buffer 120 | * @init_sequence: Pointer to LCD initialization array 121 | * @gamma: String representation of Gamma curve(s) 122 | * @gamma_num: Number of Gamma curves 123 | * @gamma_len: Number of values per Gamma curve 124 | * @debug: Initial debug value 125 | * 126 | * This structure is not stored by FBTFT except for init_sequence. 127 | */ 128 | struct fbtft_display { 129 | unsigned width; 130 | unsigned height; 131 | unsigned regwidth; 132 | unsigned buswidth; 133 | unsigned backlight; 134 | struct fbtft_ops fbtftops; 135 | unsigned bpp; 136 | unsigned fps; 137 | int txbuflen; 138 | int *init_sequence; 139 | char *gamma; 140 | int gamma_num; 141 | int gamma_len; 142 | unsigned long debug; 143 | }; 144 | 145 | /** 146 | * struct fbtft_platform_data - Passes display specific data to the driver 147 | * @display: Display properties 148 | * @gpios: Pointer to an array of piname to gpio mappings 149 | * @rotate: Display rotation angle 150 | * @bgr: LCD Controller BGR bit 151 | * @fps: Frames per second (this will go away, use @fps in @fbtft_display) 152 | * @txbuflen: Size of transmit buffer 153 | * @startbyte: When set, enables use of Startbyte in transfers 154 | * @gamma: String representation of Gamma curve(s) 155 | * @extra: A way to pass extra info 156 | */ 157 | struct fbtft_platform_data { 158 | struct fbtft_display display; 159 | const struct fbtft_gpio *gpios; 160 | unsigned rotate; 161 | bool bgr; 162 | unsigned fps; 163 | int txbuflen; 164 | u8 startbyte; 165 | char *gamma; 166 | void *extra; 167 | }; 168 | 169 | /** 170 | * struct fbtft_par - Main FBTFT data structure 171 | * 172 | * This structure holds all relevant data to operate the display 173 | * 174 | * See sourcefile for documentation since nested structs is not 175 | * supported by kernel-doc. 176 | * 177 | */ 178 | /* @spi: Set if it is a SPI device 179 | * @pdev: Set if it is a platform device 180 | * @info: Pointer to framebuffer fb_info structure 181 | * @pdata: Pointer to platform data 182 | * @ssbuf: Not used 183 | * @pseudo_palette: Used by fb_set_colreg() 184 | * @txbuf.buf: Transmit buffer 185 | * @txbuf.len: Transmit buffer length 186 | * @buf: Small buffer used when writing init data over SPI 187 | * @startbyte: Used by some controllers when in SPI mode. 188 | * Format: 6 bit Device id + RS bit + RW bit 189 | * @fbtftops: FBTFT operations provided by driver or device (platform_data) 190 | * @dirty_lock: Protects dirty_lines_start and dirty_lines_end 191 | * @dirty_lines_start: Where to begin updating display 192 | * @dirty_lines_end: Where to end updating display 193 | * @gpio.reset: GPIO used to reset display 194 | * @gpio.dc: Data/Command signal, also known as RS 195 | * @gpio.rd: Read latching signal 196 | * @gpio.wr: Write latching signal 197 | * @gpio.latch: Bus latch signal, eg. 16->8 bit bus latch 198 | * @gpio.cs: LCD Chip Select with parallel interface bus 199 | * @gpio.db[16]: Parallel databus 200 | * @gpio.led[16]: Led control signals 201 | * @gpio.aux[16]: Auxillary signals, not used by core 202 | * @init_sequence: Pointer to LCD initialization array 203 | * @gamma.lock: Mutex for Gamma curve locking 204 | * @gamma.curves: Pointer to Gamma curve array 205 | * @gamma.num_values: Number of values per Gamma curve 206 | * @gamma.num_curves: Number of Gamma curves 207 | * @debug: Pointer to debug value 208 | * @current_debug: 209 | * @first_update_done: Used to only time the first display update 210 | * @update_time: Used to calculate 'fps' in debug output 211 | * @bgr: BGR mode/\n 212 | * @extra: Extra info needed by driver 213 | */ 214 | struct fbtft_par { 215 | struct spi_device *spi; 216 | struct platform_device *pdev; 217 | struct fb_info *info; 218 | struct fbtft_platform_data *pdata; 219 | u16 *ssbuf; 220 | u32 pseudo_palette[16]; 221 | struct { 222 | void *buf; 223 | dma_addr_t dma; 224 | size_t len; 225 | } txbuf; 226 | u8 *buf; 227 | u8 startbyte; 228 | struct fbtft_ops fbtftops; 229 | spinlock_t dirty_lock; 230 | unsigned dirty_lines_start; 231 | unsigned dirty_lines_end; 232 | struct { 233 | int reset; 234 | int dc; 235 | int rd; 236 | int wr; 237 | int latch; 238 | int cs; 239 | int db[16]; 240 | int led[16]; 241 | int aux[16]; 242 | } gpio; 243 | int *init_sequence; 244 | struct { 245 | struct mutex lock; 246 | unsigned long *curves; 247 | int num_values; 248 | int num_curves; 249 | } gamma; 250 | unsigned long debug; 251 | bool first_update_done; 252 | struct timespec update_time; 253 | bool bgr; 254 | void *extra; 255 | }; 256 | 257 | #define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int)) 258 | 259 | #define write_reg(par, ...) \ 260 | do { \ 261 | par->fbtftops.write_register(par, NUMARGS(__VA_ARGS__), __VA_ARGS__); \ 262 | } while (0) 263 | 264 | /* fbtft-core.c */ 265 | extern void fbtft_dbg_hex(const struct device *dev, 266 | int groupsize, void *buf, size_t len, const char *fmt, ...); 267 | extern struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, 268 | struct device *dev); 269 | extern void fbtft_framebuffer_release(struct fb_info *info); 270 | extern int fbtft_register_framebuffer(struct fb_info *fb_info); 271 | extern int fbtft_unregister_framebuffer(struct fb_info *fb_info); 272 | extern void fbtft_register_backlight(struct fbtft_par *par); 273 | extern void fbtft_unregister_backlight(struct fbtft_par *par); 274 | extern int fbtft_init_display(struct fbtft_par *par); 275 | extern int fbtft_probe_common(struct fbtft_display *display, 276 | struct spi_device *sdev, struct platform_device *pdev); 277 | extern int fbtft_remove_common(struct device *dev, struct fb_info *info); 278 | 279 | /* fbtft-io.c */ 280 | extern int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len); 281 | extern int fbtft_write_spi_emulate_9(struct fbtft_par *par, 282 | void *buf, size_t len); 283 | extern int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len); 284 | extern int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len); 285 | extern int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len); 286 | extern int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, 287 | void *buf, size_t len); 288 | 289 | /* fbtft-bus.c */ 290 | extern int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len); 291 | extern int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len); 292 | extern int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len); 293 | extern int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len); 294 | extern void fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...); 295 | extern void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...); 296 | extern void fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...); 297 | extern void fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...); 298 | 299 | 300 | #define FBTFT_REGISTER_DRIVER(_name, _display) \ 301 | \ 302 | static int fbtft_driver_probe_spi(struct spi_device *spi) \ 303 | { \ 304 | return fbtft_probe_common(_display, spi, NULL); \ 305 | } \ 306 | \ 307 | static int fbtft_driver_remove_spi(struct spi_device *spi) \ 308 | { \ 309 | struct fb_info *info = spi_get_drvdata(spi); \ 310 | \ 311 | return fbtft_remove_common(&spi->dev, info); \ 312 | } \ 313 | \ 314 | static int fbtft_driver_probe_pdev(struct platform_device *pdev) \ 315 | { \ 316 | return fbtft_probe_common(_display, NULL, pdev); \ 317 | } \ 318 | \ 319 | static int fbtft_driver_remove_pdev(struct platform_device *pdev) \ 320 | { \ 321 | struct fb_info *info = platform_get_drvdata(pdev); \ 322 | \ 323 | return fbtft_remove_common(&pdev->dev, info); \ 324 | } \ 325 | \ 326 | static struct spi_driver fbtft_driver_spi_driver = { \ 327 | .driver = { \ 328 | .name = _name, \ 329 | .owner = THIS_MODULE, \ 330 | }, \ 331 | .probe = fbtft_driver_probe_spi, \ 332 | .remove = fbtft_driver_remove_spi, \ 333 | }; \ 334 | \ 335 | static struct platform_driver fbtft_driver_platform_driver = { \ 336 | .driver = { \ 337 | .name = _name, \ 338 | .owner = THIS_MODULE, \ 339 | }, \ 340 | .probe = fbtft_driver_probe_pdev, \ 341 | .remove = fbtft_driver_remove_pdev, \ 342 | }; \ 343 | \ 344 | static int __init fbtft_driver_module_init(void) \ 345 | { \ 346 | int ret; \ 347 | \ 348 | ret = spi_register_driver(&fbtft_driver_spi_driver); \ 349 | if (ret < 0) \ 350 | return ret; \ 351 | return platform_driver_register(&fbtft_driver_platform_driver); \ 352 | } \ 353 | \ 354 | static void __exit fbtft_driver_module_exit(void) \ 355 | { \ 356 | spi_unregister_driver(&fbtft_driver_spi_driver); \ 357 | platform_driver_unregister(&fbtft_driver_platform_driver); \ 358 | } \ 359 | \ 360 | module_init(fbtft_driver_module_init); \ 361 | module_exit(fbtft_driver_module_exit); 362 | 363 | 364 | /* Debug macros */ 365 | 366 | /* shorthand debug levels */ 367 | #define DEBUG_LEVEL_1 DEBUG_REQUEST_GPIOS 368 | #define DEBUG_LEVEL_2 (DEBUG_LEVEL_1 | DEBUG_DRIVER_INIT_FUNCTIONS | DEBUG_TIME_FIRST_UPDATE) 369 | #define DEBUG_LEVEL_3 (DEBUG_LEVEL_2 | DEBUG_RESET | DEBUG_INIT_DISPLAY | DEBUG_BLANK | DEBUG_FREE_GPIOS | DEBUG_VERIFY_GPIOS | DEBUG_BACKLIGHT | DEBUG_SYSFS) 370 | #define DEBUG_LEVEL_4 (DEBUG_LEVEL_2 | DEBUG_FB_READ | DEBUG_FB_WRITE | DEBUG_FB_FILLRECT | DEBUG_FB_COPYAREA | DEBUG_FB_IMAGEBLIT | DEBUG_FB_BLANK) 371 | #define DEBUG_LEVEL_5 (DEBUG_LEVEL_3 | DEBUG_UPDATE_DISPLAY) 372 | #define DEBUG_LEVEL_6 (DEBUG_LEVEL_4 | DEBUG_LEVEL_5) 373 | #define DEBUG_LEVEL_7 0xFFFFFFFF 374 | 375 | #define DEBUG_DRIVER_INIT_FUNCTIONS (1<<3) 376 | #define DEBUG_TIME_FIRST_UPDATE (1<<4) 377 | #define DEBUG_TIME_EACH_UPDATE (1<<5) 378 | #define DEBUG_DEFERRED_IO (1<<6) 379 | #define DEBUG_FBTFT_INIT_FUNCTIONS (1<<7) 380 | 381 | /* fbops */ 382 | #define DEBUG_FB_READ (1<<8) 383 | #define DEBUG_FB_WRITE (1<<9) 384 | #define DEBUG_FB_FILLRECT (1<<10) 385 | #define DEBUG_FB_COPYAREA (1<<11) 386 | #define DEBUG_FB_IMAGEBLIT (1<<12) 387 | #define DEBUG_FB_SETCOLREG (1<<13) 388 | #define DEBUG_FB_BLANK (1<<14) 389 | 390 | #define DEBUG_SYSFS (1<<16) 391 | 392 | /* fbtftops */ 393 | #define DEBUG_BACKLIGHT (1<<17) 394 | #define DEBUG_READ (1<<18) 395 | #define DEBUG_WRITE (1<<19) 396 | #define DEBUG_WRITE_VMEM (1<<20) 397 | #define DEBUG_WRITE_REGISTER (1<<21) 398 | #define DEBUG_SET_ADDR_WIN (1<<22) 399 | #define DEBUG_RESET (1<<23) 400 | #define DEBUG_MKDIRTY (1<<24) 401 | #define DEBUG_UPDATE_DISPLAY (1<<25) 402 | #define DEBUG_INIT_DISPLAY (1<<26) 403 | #define DEBUG_BLANK (1<<27) 404 | #define DEBUG_REQUEST_GPIOS (1<<28) 405 | #define DEBUG_FREE_GPIOS (1<<29) 406 | #define DEBUG_REQUEST_GPIOS_MATCH (1<<30) 407 | #define DEBUG_VERIFY_GPIOS (1<<31) 408 | 409 | 410 | #define fbtft_init_dbg(dev, format, arg...) \ 411 | do { \ 412 | if (unlikely((dev)->platform_data && \ 413 | (((struct fbtft_platform_data *)(dev)->platform_data)->display.debug & DEBUG_DRIVER_INIT_FUNCTIONS))) \ 414 | dev_info(dev, format, ##arg); \ 415 | } while (0) 416 | 417 | #define fbtft_par_dbg(level, par, format, arg...) \ 418 | do { \ 419 | if (unlikely(par->debug & level)) \ 420 | dev_info(par->info->device, format, ##arg); \ 421 | } while (0) 422 | 423 | #define fbtft_dev_dbg(level, par, dev, format, arg...) \ 424 | do { \ 425 | if (unlikely(par->debug & level)) \ 426 | dev_info(dev, format, ##arg); \ 427 | } while (0) 428 | 429 | #define fbtft_par_dbg_hex(level, par, dev, type, buf, num, format, arg...) \ 430 | do { \ 431 | if (unlikely(par->debug & level)) \ 432 | fbtft_dbg_hex(dev, sizeof(type), buf, num * sizeof(type), format, ##arg); \ 433 | } while (0) 434 | 435 | #endif /* __LINUX_FBTFT_H */ 436 | -------------------------------------------------------------------------------- /fbtft_device.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2013, Noralf Tronnes 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "fbtft.h" 27 | 28 | #define DRVNAME "fbtft_device" 29 | 30 | #define MAX_GPIOS 32 31 | 32 | struct spi_device *spi_device; 33 | struct platform_device *p_device; 34 | 35 | static char *name; 36 | module_param(name, charp, 0); 37 | MODULE_PARM_DESC(name, "Devicename (required). " \ 38 | "name=list => list all supported devices."); 39 | 40 | static unsigned rotate; 41 | module_param(rotate, uint, 0); 42 | MODULE_PARM_DESC(rotate, 43 | "Angle to rotate display counter clockwise: 0, 90, 180, 270"); 44 | 45 | static unsigned busnum; 46 | module_param(busnum, uint, 0); 47 | MODULE_PARM_DESC(busnum, "SPI bus number (default=0)"); 48 | 49 | static unsigned cs; 50 | module_param(cs, uint, 0); 51 | MODULE_PARM_DESC(cs, "SPI chip select (default=0)"); 52 | 53 | static unsigned speed; 54 | module_param(speed, uint, 0); 55 | MODULE_PARM_DESC(speed, "SPI speed (override device default)"); 56 | 57 | static int mode = -1; 58 | module_param(mode, int, 0); 59 | MODULE_PARM_DESC(mode, "SPI mode (override device default)"); 60 | 61 | static char *gpios[MAX_GPIOS] = { NULL, }; 62 | static int gpios_num; 63 | module_param_array(gpios, charp, &gpios_num, 0); 64 | MODULE_PARM_DESC(gpios, 65 | "List of gpios. Comma separated with the form: reset:23,dc:24 " \ 66 | "(when overriding the default, all gpios must be specified)"); 67 | 68 | static unsigned fps; 69 | module_param(fps, uint, 0); 70 | MODULE_PARM_DESC(fps, "Frames per second (override driver default)"); 71 | 72 | static char *gamma; 73 | module_param(gamma, charp, 0); 74 | MODULE_PARM_DESC(gamma, 75 | "String representation of Gamma Curve(s). Driver specific."); 76 | 77 | static int txbuflen; 78 | module_param(txbuflen, int, 0); 79 | MODULE_PARM_DESC(txbuflen, "txbuflen (override driver default)"); 80 | 81 | static int bgr = -1; 82 | module_param(bgr, int, 0); 83 | MODULE_PARM_DESC(bgr, 84 | "BGR bit (supported by some drivers)."); 85 | 86 | static unsigned startbyte; 87 | module_param(startbyte, uint, 0); 88 | MODULE_PARM_DESC(startbyte, "Sets the Start byte used by some SPI displays."); 89 | 90 | static bool custom; 91 | module_param(custom, bool, 0); 92 | MODULE_PARM_DESC(custom, "Add a custom display device. " \ 93 | "Use speed= argument to make it a SPI device, else platform_device"); 94 | 95 | static unsigned width; 96 | module_param(width, uint, 0); 97 | MODULE_PARM_DESC(width, "Display width, used with the custom argument"); 98 | 99 | static unsigned height; 100 | module_param(height, uint, 0); 101 | MODULE_PARM_DESC(height, "Display height, used with the custom argument"); 102 | 103 | static unsigned buswidth; 104 | module_param(buswidth, uint, 0); 105 | MODULE_PARM_DESC(buswidth, "Display bus width, used with the custom argument"); 106 | 107 | static int init[FBTFT_MAX_INIT_SEQUENCE]; 108 | static int init_num; 109 | module_param_array(init, int, &init_num, 0); 110 | MODULE_PARM_DESC(init, "Init sequence, used with the custom argument"); 111 | 112 | static unsigned long debug; 113 | module_param(debug, ulong , 0); 114 | MODULE_PARM_DESC(debug, 115 | "level: 0-7 (the remaining 29 bits is for advanced usage)"); 116 | 117 | static unsigned verbose = 3; 118 | module_param(verbose, uint, 0); 119 | MODULE_PARM_DESC(verbose, 120 | "0 silent, >0 show gpios, >1 show devices, >2 show devices before (default=3)"); 121 | 122 | 123 | struct fbtft_device_display { 124 | char *name; 125 | struct spi_board_info *spi; 126 | struct platform_device *pdev; 127 | }; 128 | 129 | static void fbtft_device_pdev_release(struct device *dev); 130 | 131 | static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len); 132 | static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par, 133 | int xs, int ys, int xe, int ye); 134 | 135 | #define ADAFRUIT18_GAMMA \ 136 | "02 1c 07 12 37 32 29 2d 29 25 2B 39 00 01 03 10\n" \ 137 | "03 1d 07 06 2E 2C 29 2D 2E 2E 37 3F 00 00 02 10" 138 | 139 | /* Supported displays in alphabetical order */ 140 | static struct fbtft_device_display displays[] = { 141 | { 142 | .name = "adafruit18", 143 | .spi = &(struct spi_board_info) { 144 | .modalias = "fb_st7735r", 145 | .max_speed_hz = 32000000, 146 | .mode = SPI_MODE_0, 147 | .platform_data = &(struct fbtft_platform_data) { 148 | .display = { 149 | .buswidth = 8, 150 | .backlight = 1, 151 | }, 152 | .gpios = (const struct fbtft_gpio []) { 153 | { "reset", 25 }, 154 | { "dc", 24 }, 155 | { "led", 18 }, 156 | {}, 157 | }, 158 | .gamma = ADAFRUIT18_GAMMA, 159 | } 160 | } 161 | }, { 162 | .name = "adafruit18_green", 163 | .spi = &(struct spi_board_info) { 164 | .modalias = "fb_st7735r", 165 | .max_speed_hz = 4000000, 166 | .mode = SPI_MODE_0, 167 | .platform_data = &(struct fbtft_platform_data) { 168 | .display = { 169 | .buswidth = 8, 170 | .backlight = 1, 171 | .fbtftops.set_addr_win = \ 172 | adafruit18_green_tab_set_addr_win, 173 | }, 174 | .bgr = true, 175 | .gpios = (const struct fbtft_gpio []) { 176 | { "reset", 25 }, 177 | { "dc", 24 }, 178 | { "led", 18 }, 179 | {}, 180 | }, 181 | .gamma = ADAFRUIT18_GAMMA, 182 | } 183 | } 184 | }, { 185 | .name = "adafruit22", 186 | .spi = &(struct spi_board_info) { 187 | .modalias = "fb_hx8340bn", 188 | .max_speed_hz = 32000000, 189 | .mode = SPI_MODE_0, 190 | .platform_data = &(struct fbtft_platform_data) { 191 | .display = { 192 | .buswidth = 9, 193 | .backlight = 1, 194 | }, 195 | .bgr = true, 196 | .gpios = (const struct fbtft_gpio []) { 197 | { "reset", 25 }, 198 | { "led", 23 }, 199 | {}, 200 | }, 201 | } 202 | } 203 | }, { 204 | .name = "adafruit22a", 205 | .spi = &(struct spi_board_info) { 206 | .modalias = "fb_ili9340", 207 | .max_speed_hz = 32000000, 208 | .mode = SPI_MODE_0, 209 | .platform_data = &(struct fbtft_platform_data) { 210 | .display = { 211 | .buswidth = 8, 212 | .backlight = 1, 213 | }, 214 | .bgr = true, 215 | .gpios = (const struct fbtft_gpio []) { 216 | { "reset", 25 }, 217 | { "dc", 24 }, 218 | { "led", 18 }, 219 | {}, 220 | }, 221 | } 222 | } 223 | }, { 224 | .name = "adafruit13m", 225 | .spi = &(struct spi_board_info) { 226 | .modalias = "fb_ssd1306", 227 | .max_speed_hz = 16000000, 228 | .mode = SPI_MODE_0, 229 | .platform_data = &(struct fbtft_platform_data) { 230 | .display = { 231 | .buswidth = 8, 232 | }, 233 | .gpios = (const struct fbtft_gpio []) { 234 | { "reset", 25 }, 235 | { "dc", 24 }, 236 | {}, 237 | }, 238 | } 239 | } 240 | }, { 241 | .name = "flexfb", 242 | .spi = &(struct spi_board_info) { 243 | .modalias = "flexfb", 244 | .max_speed_hz = 32000000, 245 | .mode = SPI_MODE_0, 246 | .platform_data = &(struct fbtft_platform_data) { 247 | .gpios = (const struct fbtft_gpio []) { 248 | { "reset", 25 }, 249 | { "dc", 24 }, 250 | {}, 251 | }, 252 | } 253 | } 254 | }, { 255 | .name = "flexpfb", 256 | .pdev = &(struct platform_device) { 257 | .name = "flexpfb", 258 | .id = 0, 259 | .dev = { 260 | .release = fbtft_device_pdev_release, 261 | .platform_data = &(struct fbtft_platform_data) { 262 | .gpios = (const struct fbtft_gpio []) { 263 | { "reset", 17 }, 264 | { "dc", 1 }, 265 | { "wr", 0 }, 266 | { "cs", 21 }, 267 | { "db00", 9 }, 268 | { "db01", 11 }, 269 | { "db02", 18 }, 270 | { "db03", 23 }, 271 | { "db04", 24 }, 272 | { "db05", 25 }, 273 | { "db06", 8 }, 274 | { "db07", 7 }, 275 | { "led", 4 }, 276 | {}, 277 | }, 278 | }, 279 | } 280 | } 281 | }, { 282 | .name = "freetronicsoled128", 283 | .spi = &(struct spi_board_info) { 284 | .modalias = "fb_ssd1351", 285 | .max_speed_hz = 20000000, 286 | .mode = SPI_MODE_0, 287 | .platform_data = &(struct fbtft_platform_data) { 288 | .display = { 289 | .buswidth = 8, 290 | .backlight = FBTFT_ONBOARD_BACKLIGHT, 291 | }, 292 | .bgr = true, 293 | .gpios = (const struct fbtft_gpio []) { 294 | { "reset", 24 }, 295 | { "dc", 25 }, 296 | {}, 297 | }, 298 | } 299 | } 300 | }, { 301 | .name = "ssd1331", 302 | .spi = &(struct spi_board_info) { 303 | .modalias = "fb_ssd1331", 304 | .max_speed_hz = 20000000, 305 | .mode = SPI_MODE_3, 306 | .platform_data = &(struct fbtft_platform_data) { 307 | .display = { 308 | .buswidth = 8, 309 | }, 310 | .gpios = (const struct fbtft_gpio []) { 311 | { "reset", 24 }, 312 | { "dc", 25 }, 313 | {}, 314 | }, 315 | } 316 | } 317 | }, { 318 | .name = "hy28a", 319 | .spi = &(struct spi_board_info) { 320 | .modalias = "fb_ili9320", 321 | .max_speed_hz = 32000000, 322 | .mode = SPI_MODE_3, 323 | .platform_data = &(struct fbtft_platform_data) { 324 | .display = { 325 | .buswidth = 8, 326 | .backlight = 1, 327 | }, 328 | .startbyte = 0b01110000, 329 | .bgr = true, 330 | .gpios = (const struct fbtft_gpio []) { 331 | { "reset", 25 }, 332 | { "led", 18 }, 333 | {}, 334 | }, 335 | } 336 | } 337 | }, { 338 | .name = "itdb24", 339 | .pdev = &(struct platform_device) { 340 | .name = "fb_s6d1121", 341 | .id = 0, 342 | .dev = { 343 | .release = fbtft_device_pdev_release, 344 | .platform_data = &(struct fbtft_platform_data) { 345 | .display = { 346 | .buswidth = 8, 347 | .backlight = 1, 348 | }, 349 | .bgr = false, 350 | .gpios = (const struct fbtft_gpio []) { 351 | /* Wiring for LCD adapter kit */ 352 | { "reset", 7 }, 353 | { "dc", 0 }, /* rev 2: 2 */ 354 | { "wr", 1 }, /* rev 2: 3 */ 355 | { "cs", 8 }, 356 | { "db00", 17 }, 357 | { "db01", 18 }, 358 | { "db02", 21 }, /* rev 2: 27 */ 359 | { "db03", 22 }, 360 | { "db04", 23 }, 361 | { "db05", 24 }, 362 | { "db06", 25 }, 363 | { "db07", 4 }, 364 | {} 365 | }, 366 | }, 367 | } 368 | } 369 | }, { 370 | .name = "itdb28", 371 | .pdev = &(struct platform_device) { 372 | .name = "fb_ili9325", 373 | .id = 0, 374 | .dev = { 375 | .release = fbtft_device_pdev_release, 376 | .platform_data = &(struct fbtft_platform_data) { 377 | .display = { 378 | .buswidth = 8, 379 | .backlight = 1, 380 | }, 381 | .bgr = true, 382 | .gpios = (const struct fbtft_gpio []) { 383 | {}, 384 | }, 385 | }, 386 | } 387 | } 388 | }, { 389 | .name = "itdb28_spi", 390 | .spi = &(struct spi_board_info) { 391 | .modalias = "fb_ili9325", 392 | .max_speed_hz = 32000000, 393 | .mode = SPI_MODE_0, 394 | .platform_data = &(struct fbtft_platform_data) { 395 | .display = { 396 | .buswidth = 8, 397 | .backlight = 1, 398 | }, 399 | .bgr = true, 400 | .gpios = (const struct fbtft_gpio []) { 401 | { "reset", 25 }, 402 | { "dc", 24 }, 403 | {}, 404 | }, 405 | } 406 | } 407 | }, { 408 | .name = "mi0283qt-2", 409 | .spi = &(struct spi_board_info) { 410 | .modalias = "fb_hx8347d", 411 | .max_speed_hz = 32000000, 412 | .mode = SPI_MODE_0, 413 | .platform_data = &(struct fbtft_platform_data) { 414 | .display = { 415 | .buswidth = 8, 416 | .backlight = 1, 417 | }, 418 | .startbyte = 0b01110000, 419 | .bgr = true, 420 | .gpios = (const struct fbtft_gpio []) { 421 | { "reset", 25 }, 422 | { "dc", 24 }, 423 | { "led", 18 }, 424 | {}, 425 | }, 426 | } 427 | } 428 | }, { 429 | .name = "mi0283qt-9a", 430 | .spi = &(struct spi_board_info) { 431 | .modalias = "fb_ili9341", 432 | .max_speed_hz = 32000000, 433 | .mode = SPI_MODE_0, 434 | .platform_data = &(struct fbtft_platform_data) { 435 | .display = { 436 | .buswidth = 9, 437 | .backlight = 1, 438 | }, 439 | .bgr = true, 440 | .gpios = (const struct fbtft_gpio []) { 441 | { "reset", 25 }, 442 | { "led", 18 }, 443 | {}, 444 | }, 445 | } 446 | } 447 | }, { 448 | .name = "mi0283qt-v2", 449 | .spi = &(struct spi_board_info) { 450 | .modalias = "fb_watterott", 451 | .max_speed_hz = 4000000, 452 | .mode = SPI_MODE_3, 453 | .platform_data = &(struct fbtft_platform_data) { 454 | .gpios = (const struct fbtft_gpio []) { 455 | { "reset", 25 }, 456 | {}, 457 | }, 458 | } 459 | } 460 | }, { 461 | .name = "nokia3310", 462 | .spi = &(struct spi_board_info) { 463 | .modalias = "fb_pcd8544", 464 | .max_speed_hz = 400000, 465 | .mode = SPI_MODE_0, 466 | .platform_data = &(struct fbtft_platform_data) { 467 | .display = { 468 | .buswidth = 8, 469 | }, 470 | .gpios = (const struct fbtft_gpio []) { 471 | { "reset", 25 }, 472 | { "dc", 24 }, 473 | { "led", 23 }, 474 | {}, 475 | }, 476 | } 477 | } 478 | }, { 479 | .name = "pioled", 480 | .spi = &(struct spi_board_info) { 481 | .modalias = "fb_ssd1351", 482 | .max_speed_hz = 20000000, 483 | .mode = SPI_MODE_0, 484 | .platform_data = &(struct fbtft_platform_data) { 485 | .display = { 486 | .buswidth = 8, 487 | }, 488 | .bgr = true, 489 | .gpios = (const struct fbtft_gpio []) { 490 | { "reset", 24 }, 491 | { "dc", 25 }, 492 | {}, 493 | }, 494 | .gamma = "0 2 2 2 2 2 2 2 " \ 495 | "2 2 2 2 2 2 2 2 " \ 496 | "2 2 2 2 2 2 2 2 " \ 497 | "2 2 2 2 2 2 2 3 " \ 498 | "3 3 3 3 3 3 3 3 " \ 499 | "3 3 3 3 3 3 3 3 " \ 500 | "3 3 3 4 4 4 4 4 " \ 501 | "4 4 4 4 4 4 4" 502 | } 503 | } 504 | }, { 505 | .name = "sainsmart18", 506 | .spi = &(struct spi_board_info) { 507 | .modalias = "fb_st7735r", 508 | .max_speed_hz = 32000000, 509 | .mode = SPI_MODE_0, 510 | .platform_data = &(struct fbtft_platform_data) { 511 | .display = { 512 | .buswidth = 8, 513 | }, 514 | .gpios = (const struct fbtft_gpio []) { 515 | { "reset", 25 }, 516 | { "dc", 24 }, 517 | {}, 518 | }, 519 | } 520 | } 521 | }, { 522 | .name = "sainsmart32", 523 | .pdev = &(struct platform_device) { 524 | .name = "fb_ssd1289", 525 | .id = 0, 526 | .dev = { 527 | .release = fbtft_device_pdev_release, 528 | .platform_data = &(struct fbtft_platform_data) { 529 | .display = { 530 | .buswidth = 16, 531 | .txbuflen = -2, /* disable buffer */ 532 | .backlight = 1, 533 | .fbtftops.write = write_gpio16_wr_slow, 534 | }, 535 | .bgr = true, 536 | .gpios = (const struct fbtft_gpio []) { 537 | {}, 538 | }, 539 | }, 540 | }, 541 | } 542 | }, { 543 | .name = "sainsmart32_fast", 544 | .pdev = &(struct platform_device) { 545 | .name = "fb_ssd1289", 546 | .id = 0, 547 | .dev = { 548 | .release = fbtft_device_pdev_release, 549 | .platform_data = &(struct fbtft_platform_data) { 550 | .display = { 551 | .buswidth = 16, 552 | .txbuflen = -2, /* disable buffer */ 553 | .backlight = 1, 554 | }, 555 | .bgr = true, 556 | .gpios = (const struct fbtft_gpio []) { 557 | {}, 558 | }, 559 | }, 560 | }, 561 | } 562 | }, { 563 | .name = "sainsmart32_latched", 564 | .pdev = &(struct platform_device) { 565 | .name = "fb_ssd1289", 566 | .id = 0, 567 | .dev = { 568 | .release = fbtft_device_pdev_release, 569 | .platform_data = &(struct fbtft_platform_data) { 570 | .display = { 571 | .buswidth = 16, 572 | .txbuflen = -2, /* disable buffer */ 573 | .backlight = 1, 574 | .fbtftops.write = \ 575 | fbtft_write_gpio16_wr_latched, 576 | }, 577 | .bgr = true, 578 | .gpios = (const struct fbtft_gpio []) { 579 | {}, 580 | }, 581 | }, 582 | }, 583 | } 584 | }, { 585 | .name = "sainsmart32_spi", 586 | .spi = &(struct spi_board_info) { 587 | .modalias = "fb_ssd1289", 588 | .max_speed_hz = 16000000, 589 | .mode = SPI_MODE_0, 590 | .platform_data = &(struct fbtft_platform_data) { 591 | .display = { 592 | .buswidth = 8, 593 | .backlight = 1, 594 | }, 595 | .bgr = true, 596 | .gpios = (const struct fbtft_gpio []) { 597 | { "reset", 25 }, 598 | { "dc", 24 }, 599 | {}, 600 | }, 601 | } 602 | } 603 | }, { 604 | .name = "spidev", 605 | .spi = &(struct spi_board_info) { 606 | .modalias = "spidev", 607 | .max_speed_hz = 500000, 608 | .bus_num = 0, 609 | .chip_select = 0, 610 | .mode = SPI_MODE_0, 611 | .platform_data = &(struct fbtft_platform_data) { 612 | .gpios = (const struct fbtft_gpio []) { 613 | {}, 614 | }, 615 | } 616 | } 617 | }, { 618 | .name = "tm022hdh26", 619 | .spi = &(struct spi_board_info) { 620 | .modalias = "fb_ili9341", 621 | .max_speed_hz = 32000000, 622 | .mode = SPI_MODE_0, 623 | .platform_data = &(struct fbtft_platform_data) { 624 | .display = { 625 | .buswidth = 8, 626 | .backlight = 1, 627 | }, 628 | .bgr = true, 629 | .gpios = (const struct fbtft_gpio []) { 630 | { "reset", 25 }, 631 | { "dc", 24 }, 632 | { "led", 18 }, 633 | {}, 634 | }, 635 | } 636 | } 637 | }, { 638 | /* This should be the last item. 639 | Used with the custom argument */ 640 | .name = "", 641 | .spi = &(struct spi_board_info) { 642 | .modalias = "", 643 | .max_speed_hz = 0, 644 | .mode = SPI_MODE_0, 645 | .platform_data = &(struct fbtft_platform_data) { 646 | .gpios = (const struct fbtft_gpio []) { 647 | {}, 648 | }, 649 | } 650 | }, 651 | .pdev = &(struct platform_device) { 652 | .name = "", 653 | .id = 0, 654 | .dev = { 655 | .release = fbtft_device_pdev_release, 656 | .platform_data = &(struct fbtft_platform_data) { 657 | .gpios = (const struct fbtft_gpio []) { 658 | {}, 659 | }, 660 | }, 661 | }, 662 | }, 663 | } 664 | }; 665 | 666 | static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len) 667 | { 668 | u16 data; 669 | int i; 670 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO 671 | static u16 prev_data; 672 | #endif 673 | 674 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, 675 | "%s(len=%d): ", __func__, len); 676 | 677 | while (len) { 678 | data = *(u16 *) buf; 679 | 680 | /* Start writing by pulling down /WR */ 681 | gpio_set_value(par->gpio.wr, 0); 682 | 683 | /* Set data */ 684 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO 685 | if (data == prev_data) { 686 | gpio_set_value(par->gpio.wr, 0); /* used as delay */ 687 | } else { 688 | for (i = 0; i < 16; i++) { 689 | if ((data & 1) != (prev_data & 1)) 690 | gpio_set_value(par->gpio.db[i], 691 | (data & 1)); 692 | data >>= 1; 693 | prev_data >>= 1; 694 | } 695 | } 696 | #else 697 | for (i = 0; i < 16; i++) { 698 | gpio_set_value(par->gpio.db[i], (data & 1)); 699 | data >>= 1; 700 | } 701 | #endif 702 | 703 | /* Pullup /WR */ 704 | gpio_set_value(par->gpio.wr, 1); 705 | 706 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO 707 | prev_data = *(u16 *) buf; 708 | #endif 709 | buf += 2; 710 | len -= 2; 711 | } 712 | 713 | return 0; 714 | } 715 | 716 | static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par, 717 | int xs, int ys, int xe, int ye) 718 | { 719 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, 720 | "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 721 | write_reg(par, 0x2A, 0, xs + 2, 0, xe + 2); 722 | write_reg(par, 0x2B, 0, ys + 1, 0, ye + 1); 723 | write_reg(par, 0x2C); 724 | } 725 | 726 | /* used if gpios parameter is present */ 727 | static struct fbtft_gpio fbtft_device_param_gpios[MAX_GPIOS+1] = { }; 728 | 729 | static void fbtft_device_pdev_release(struct device *dev) 730 | { 731 | /* Needed to silence this message: 732 | Device 'xxx' does not have a release() function, it is broken and must be fixed 733 | */ 734 | } 735 | 736 | static int spi_device_found(struct device *dev, void *data) 737 | { 738 | struct spi_device *spi = container_of(dev, struct spi_device, dev); 739 | 740 | pr_info(DRVNAME": %s %s %dkHz %d bits mode=0x%02X\n", 741 | spi->modalias, dev_name(dev), spi->max_speed_hz/1000, 742 | spi->bits_per_word, spi->mode); 743 | 744 | return 0; 745 | } 746 | 747 | static void pr_spi_devices(void) 748 | { 749 | pr_info(DRVNAME": SPI devices registered:\n"); 750 | bus_for_each_dev(&spi_bus_type, NULL, NULL, spi_device_found); 751 | } 752 | 753 | static int p_device_found(struct device *dev, void *data) 754 | { 755 | struct platform_device 756 | *pdev = container_of(dev, struct platform_device, dev); 757 | 758 | if (strstr(pdev->name, "fb")) 759 | pr_info(DRVNAME": %s id=%d pdata? %s\n", 760 | pdev->name, pdev->id, 761 | pdev->dev.platform_data ? "yes" : "no"); 762 | 763 | return 0; 764 | } 765 | 766 | static void pr_p_devices(void) 767 | { 768 | pr_info(DRVNAME": 'fb' Platform devices registered:\n"); 769 | bus_for_each_dev(&platform_bus_type, NULL, NULL, p_device_found); 770 | } 771 | 772 | static void fbtft_device_delete(struct spi_master *master, unsigned cs) 773 | { 774 | struct device *dev; 775 | char str[32]; 776 | 777 | snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs); 778 | 779 | dev = bus_find_device_by_name(&spi_bus_type, NULL, str); 780 | if (dev) { 781 | pr_err(DRVNAME": Deleting %s\n", str); 782 | device_del(dev); 783 | } 784 | } 785 | 786 | static int __init fbtft_device_init(void) 787 | { 788 | struct spi_master *master = NULL; 789 | struct spi_board_info *spi = NULL; 790 | struct fbtft_platform_data *pdata; 791 | const struct fbtft_gpio *gpio = NULL; 792 | char *p_name, *p_num; 793 | bool found = false; 794 | int i; 795 | long val; 796 | int ret = 0; 797 | 798 | pr_debug("\n\n"DRVNAME": init\n"); 799 | 800 | if (init_num > FBTFT_MAX_INIT_SEQUENCE) { 801 | pr_err(DRVNAME \ 802 | ": init parameter: exceeded max array size: %d\n", 803 | FBTFT_MAX_INIT_SEQUENCE); 804 | return -EINVAL; 805 | } 806 | 807 | /* parse module parameter: gpios */ 808 | if (gpios_num > MAX_GPIOS) { 809 | pr_err(DRVNAME \ 810 | ": gpios parameter: exceeded max array size: %d\n", 811 | MAX_GPIOS); 812 | return -EINVAL; 813 | } 814 | if (gpios_num > 0) { 815 | for (i = 0; i < gpios_num; i++) { 816 | if (strchr(gpios[i], ':') == NULL) { 817 | pr_err(DRVNAME \ 818 | ": error: missing ':' in gpios parameter: %s\n", 819 | gpios[i]); 820 | return -EINVAL; 821 | } 822 | p_num = gpios[i]; 823 | p_name = strsep(&p_num, ":"); 824 | if (p_name == NULL || p_num == NULL) { 825 | pr_err(DRVNAME \ 826 | ": something bad happened parsing gpios parameter: %s\n", 827 | gpios[i]); 828 | return -EINVAL; 829 | } 830 | ret = kstrtol(p_num, 10, &val); 831 | if (ret) { 832 | pr_err(DRVNAME \ 833 | ": could not parse number in gpios parameter: %s:%s\n", 834 | p_name, p_num); 835 | return -EINVAL; 836 | } 837 | strcpy(fbtft_device_param_gpios[i].name, p_name); 838 | fbtft_device_param_gpios[i].gpio = (int) val; 839 | } 840 | gpio = fbtft_device_param_gpios; 841 | } 842 | 843 | if (verbose > 2) 844 | pr_spi_devices(); /* print list of registered SPI devices */ 845 | 846 | if (verbose > 2) 847 | pr_p_devices(); /* print list of 'fb' platform devices */ 848 | 849 | if (name == NULL) { 850 | pr_err(DRVNAME": missing module parameter: 'name'\n"); 851 | return -EINVAL; 852 | } 853 | 854 | pr_debug(DRVNAME": name='%s', busnum=%d, cs=%d\n", name, busnum, cs); 855 | 856 | if (rotate > 0 && rotate < 4) { 857 | rotate = (4 - rotate) * 90; 858 | pr_warn("argument 'rotate' should be an angle. Values 1-3 is deprecated. Setting it to %d.\n", 859 | rotate); 860 | } 861 | if (rotate != 0 && rotate != 90 && rotate != 180 && rotate != 270) { 862 | pr_warn("argument 'rotate' illegal value: %d. Setting it to 0.\n", 863 | rotate); 864 | rotate = 0; 865 | } 866 | 867 | /* name=list lists all supported displays */ 868 | if (strncmp(name, "list", 32) == 0) { 869 | pr_info(DRVNAME": Supported displays:\n"); 870 | 871 | for (i = 0; i < ARRAY_SIZE(displays); i++) 872 | pr_info(DRVNAME": %s\n", displays[i].name); 873 | return -ECANCELED; 874 | } 875 | 876 | if (custom) { 877 | i = ARRAY_SIZE(displays) - 1; 878 | displays[i].name = name; 879 | if (speed == 0) { 880 | displays[i].pdev->name = name; 881 | displays[i].spi = NULL; 882 | } else { 883 | strncpy(displays[i].spi->modalias, name, SPI_NAME_SIZE); 884 | displays[i].pdev = NULL; 885 | } 886 | } 887 | 888 | for (i = 0; i < ARRAY_SIZE(displays); i++) { 889 | if (strncmp(name, displays[i].name, 32) == 0) { 890 | if (displays[i].spi) { 891 | master = spi_busnum_to_master(busnum); 892 | if (!master) { 893 | pr_err(DRVNAME \ 894 | ": spi_busnum_to_master(%d) returned NULL\n", 895 | busnum); 896 | return -EINVAL; 897 | } 898 | /* make sure bus:cs is available */ 899 | fbtft_device_delete(master, cs); 900 | spi = displays[i].spi; 901 | spi->chip_select = cs; 902 | spi->bus_num = busnum; 903 | if (speed) 904 | spi->max_speed_hz = speed; 905 | if (mode != -1) 906 | spi->mode = mode; 907 | pdata = (void *)spi->platform_data; 908 | } else if (displays[i].pdev) { 909 | p_device = displays[i].pdev; 910 | pdata = p_device->dev.platform_data; 911 | } else { 912 | pr_err(DRVNAME": broken displays array\n"); 913 | return -EINVAL; 914 | } 915 | 916 | pdata->rotate = rotate; 917 | if (bgr == 0) 918 | pdata->bgr = false; 919 | else if (bgr == 1) 920 | pdata->bgr = true; 921 | if (startbyte) 922 | pdata->startbyte = startbyte; 923 | if (gamma) 924 | pdata->gamma = gamma; 925 | pdata->display.debug = debug; 926 | if (fps) 927 | pdata->fps = fps; 928 | if (txbuflen) 929 | pdata->txbuflen = txbuflen; 930 | if (init_num) 931 | pdata->display.init_sequence = init; 932 | if (gpio) 933 | pdata->gpios = gpio; 934 | if (custom) { 935 | pdata->display.width = width; 936 | pdata->display.height = height; 937 | pdata->display.buswidth = buswidth; 938 | } 939 | 940 | if (displays[i].spi) { 941 | spi_device = spi_new_device(master, spi); 942 | put_device(&master->dev); 943 | if (!spi_device) { 944 | pr_err(DRVNAME \ 945 | ": spi_new_device() returned NULL\n"); 946 | return -EPERM; 947 | } 948 | found = true; 949 | break; 950 | } else { 951 | ret = platform_device_register(p_device); 952 | if (ret < 0) { 953 | pr_err(DRVNAME \ 954 | ": platform_device_register() returned %d\n", 955 | ret); 956 | return ret; 957 | } 958 | found = true; 959 | break; 960 | } 961 | } 962 | } 963 | 964 | if (!found) { 965 | pr_err(DRVNAME": display not supported: '%s'\n", name); 966 | return -EINVAL; 967 | } 968 | 969 | if (verbose && pdata && pdata->gpios) { 970 | gpio = pdata->gpios; 971 | pr_info(DRVNAME": GPIOS used by '%s':\n", name); 972 | found = false; 973 | while (verbose && gpio->name[0]) { 974 | pr_info(DRVNAME": '%s' = GPIO%d\n", 975 | gpio->name, gpio->gpio); 976 | gpio++; 977 | found = true; 978 | } 979 | if (!found) 980 | pr_info(DRVNAME": (none)\n"); 981 | } 982 | 983 | if (spi_device && (verbose > 1)) 984 | pr_spi_devices(); 985 | if (p_device && (verbose > 1)) 986 | pr_p_devices(); 987 | 988 | return 0; 989 | } 990 | 991 | static void __exit fbtft_device_exit(void) 992 | { 993 | pr_debug(DRVNAME" - exit\n"); 994 | 995 | if (spi_device) { 996 | device_del(&spi_device->dev); 997 | kfree(spi_device); 998 | } 999 | 1000 | if (p_device) 1001 | platform_device_unregister(p_device); 1002 | 1003 | } 1004 | 1005 | module_init(fbtft_device_init); 1006 | module_exit(fbtft_device_exit); 1007 | 1008 | MODULE_DESCRIPTION("Add a FBTFT device."); 1009 | MODULE_AUTHOR("Noralf Tronnes"); 1010 | MODULE_LICENSE("GPL"); 1011 | -------------------------------------------------------------------------------- /flexfb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Generic FB driver for TFT LCD displays 3 | * 4 | * Copyright (C) 2013 Noralf Tronnes 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "fbtft.h" 30 | 31 | #define DRVNAME "flexfb" 32 | 33 | 34 | static char *chip = NULL; 35 | module_param(chip, charp, 0); 36 | MODULE_PARM_DESC(chip, "LCD controller"); 37 | 38 | static unsigned int width = 0; 39 | module_param(width, uint, 0); 40 | MODULE_PARM_DESC(width, "Display width"); 41 | 42 | static unsigned int height = 0; 43 | module_param(height, uint, 0); 44 | MODULE_PARM_DESC(height, "Display height"); 45 | 46 | static int init[512]; 47 | static int init_num = 0; 48 | module_param_array(init, int, &init_num, 0); 49 | MODULE_PARM_DESC(init, "Init sequence"); 50 | 51 | static unsigned int setaddrwin = 0; 52 | module_param(setaddrwin, uint, 0); 53 | MODULE_PARM_DESC(setaddrwin, "Which set_addr_win() implementation to use"); 54 | 55 | static unsigned int buswidth = 8; 56 | module_param(buswidth, uint, 0); 57 | MODULE_PARM_DESC(buswidth, "Width of databus (default: 8)"); 58 | 59 | static unsigned int regwidth = 8; 60 | module_param(regwidth, uint, 0); 61 | MODULE_PARM_DESC(regwidth, "Width of controller register (default: 8)"); 62 | 63 | static bool nobacklight = false; 64 | module_param(nobacklight, bool, 0); 65 | MODULE_PARM_DESC(nobacklight, "Turn off backlight functionality."); 66 | 67 | static bool latched = false; 68 | module_param(latched, bool, 0); 69 | MODULE_PARM_DESC(latched, "Use with latched 16-bit databus"); 70 | 71 | 72 | static int *initp = NULL; 73 | static int initp_num = 0; 74 | 75 | /* default init sequences */ 76 | static int st7735r_init[] = { \ 77 | -1,0x01,-2,150,-1,0x11,-2,500,-1,0xB1,0x01,0x2C,0x2D,-1,0xB2,0x01,0x2C,0x2D,-1,0xB3,0x01,0x2C,0x2D,0x01,0x2C,0x2D, \ 78 | -1,0xB4,0x07,-1,0xC0,0xA2,0x02,0x84,-1,0xC1,0xC5,-1,0xC2,0x0A,0x00,-1,0xC3,0x8A,0x2A,-1,0xC4,0x8A,0xEE,-1,0xC5,0x0E, \ 79 | -1,0x20,-1,0x36,0xC0,-1,0x3A,0x05,-1,0xE0,0x0f,0x1a,0x0f,0x18,0x2f,0x28,0x20,0x22,0x1f,0x1b,0x23,0x37,0x00,0x07,0x02,0x10, \ 80 | -1,0xE1,0x0f,0x1b,0x0f,0x17,0x33,0x2c,0x29,0x2e,0x30,0x30,0x39,0x3f,0x00,0x07,0x03,0x10,-1,0x29,-2,100,-1,0x13,-2,10,-3 }; 81 | 82 | static int ssd1289_init[] = { \ 83 | -1,0x00,0x0001,-1,0x03,0xA8A4,-1,0x0C,0x0000,-1,0x0D,0x080C,-1,0x0E,0x2B00,-1,0x1E,0x00B7,-1,0x01,0x2B3F,-1,0x02,0x0600, \ 84 | -1,0x10,0x0000,-1,0x11,0x6070,-1,0x05,0x0000,-1,0x06,0x0000,-1,0x16,0xEF1C,-1,0x17,0x0003,-1,0x07,0x0233,-1,0x0B,0x0000, \ 85 | -1,0x0F,0x0000,-1,0x41,0x0000,-1,0x42,0x0000,-1,0x48,0x0000,-1,0x49,0x013F,-1,0x4A,0x0000,-1,0x4B,0x0000,-1,0x44,0xEF00, \ 86 | -1,0x45,0x0000,-1,0x46,0x013F,-1,0x30,0x0707,-1,0x31,0x0204,-1,0x32,0x0204,-1,0x33,0x0502,-1,0x34,0x0507,-1,0x35,0x0204, \ 87 | -1,0x36,0x0204,-1,0x37,0x0502,-1,0x3A,0x0302,-1,0x3B,0x0302,-1,0x23,0x0000,-1,0x24,0x0000,-1,0x25,0x8000,-1,0x4f,0x0000, \ 88 | -1,0x4e,0x0000,-1,0x22,-3 }; 89 | 90 | static int hx8340bn_init[] = { \ 91 | -1,0xC1,0xFF,0x83,0x40,-1,0x11,-2,150,-1,0xCA,0x70,0x00,0xD9,-1,0xB0,0x01,0x11, \ 92 | -1,0xC9,0x90,0x49,0x10,0x28,0x28,0x10,0x00,0x06,-2,20,-1,0xC2,0x60,0x71,0x01,0x0E,0x05,0x02,0x09,0x31,0x0A, \ 93 | -1,0xC3,0x67,0x30,0x61,0x17,0x48,0x07,0x05,0x33,-2,10,-1,0xB5,0x35,0x20,0x45,-1,0xB4,0x33,0x25,0x4C,-2,10, \ 94 | -1,0x3A,0x05,-1,0x29,-2,10,-3 }; 95 | 96 | static int ili9225_init[] = { \ 97 | -1,0x0001,0x011C,-1,0x0002,0x0100,-1,0x0003,0x1030,-1,0x0008,0x0808,-1,0x000C,0x0000,-1,0x000F,0x0A01,-1,0x0020,0x0000, \ 98 | -1,0x0021,0x0000,-2,50,-1,0x0010,0x0A00,-1,0x0011,0x1038,-2,50,-1,0x0012,0x1121,-1,0x0013,0x004E,-1,0x0014,0x676F, \ 99 | -1,0x0030,0x0000,-1,0x0031,0x00DB,-1,0x0032,0x0000,-1,0x0033,0x0000,-1,0x0034,0x00DB,-1,0x0035,0x0000,-1,0x0036,0x00AF, \ 100 | -1,0x0037,0x0000,-1,0x0038,0x00DB,-1,0x0039,0x0000,-1,0x0050,0x0000,-1,0x0051,0x060A,-1,0x0052,0x0D0A,-1,0x0053,0x0303, \ 101 | -1,0x0054,0x0A0D,-1,0x0055,0x0A06,-1,0x0056,0x0000,-1,0x0057,0x0303,-1,0x0058,0x0000,-1,0x0059,0x0000,-2,50, \ 102 | -1,0x0007,0x1017,-2,50,-3 }; 103 | 104 | static int ili9320_init[] = { \ 105 | -1,0x00E5,0x8000,-1,0x0000,0x0001,-1,0x0001,0x0100,-1,0x0002,0x0700,-1,0x0003,0x1030,-1,0x0004,0x0000,-1,0x0008,0x0202, \ 106 | -1,0x0009,0x0000,-1,0x000A,0x0000,-1,0x000C,0x0000,-1,0x000D,0x0000,-1,0x000F,0x0000,-1,0x0010,0x0000,-1,0x0011,0x0007, \ 107 | -1,0x0012,0x0000,-1,0x0013,0x0000,-2,200,-1,0x0010,0x17B0,-1,0x0011,0x0031,-2,50,-1,0x0012,0x0138,-2,50,-1,0x0013,0x1800, \ 108 | -1,0x0029,0x0008,-2,50,-1,0x0020,0x0000,-1,0x0021,0x0000,-1,0x0030,0x0000,-1,0x0031,0x0505,-1,0x0032,0x0004, \ 109 | -1,0x0035,0x0006,-1,0x0036,0x0707,-1,0x0037,0x0105,-1,0x0038,0x0002,-1,0x0039,0x0707,-1,0x003C,0x0704,-1,0x003D,0x0807, \ 110 | -1,0x0050,0x0000,-1,0x0051,0x00EF,-1,0x0052,0x0000,-1,0x0053,0x013F,-1,0x0060,0x2700,-1,0x0061,0x0001,-1,0x006A,0x0000, \ 111 | -1,0x0080,0x0000,-1,0x0081,0x0000,-1,0x0082,0x0000,-1,0x0083,0x0000,-1,0x0084,0x0000,-1,0x0085,0x0000,-1,0x0090,0x0010, \ 112 | -1,0x0092,0x0000,-1,0x0093,0x0003,-1,0x0095,0x0110,-1,0x0097,0x0000,-1,0x0098,0x0000,-1,0x0007,0x0173,-3 }; 113 | 114 | static int ili9325_init[] = { \ 115 | -1,0x00E3,0x3008,-1,0x00E7,0x0012,-1,0x00EF,0x1231,-1,0x0001,0x0100,-1,0x0002,0x0700,-1,0x0003,0x1030,-1,0x0004,0x0000, \ 116 | -1,0x0008,0x0207,-1,0x0009,0x0000,-1,0x000A,0x0000,-1,0x000C,0x0000,-1,0x000D,0x0000,-1,0x000F,0x0000,-1,0x0010,0x0000, \ 117 | -1,0x0011,0x0007,-1,0x0012,0x0000,-1,0x0013,0x0000,-2,200,-1,0x0010,0x1690,-1,0x0011,0x0223,-2,50,-1,0x0012,0x000D,-2,50, \ 118 | -1,0x0013,0x1200,-1,0x0029,0x000A,-1,0x002B,0x000C,-2,50,-1,0x0020,0x0000,-1,0x0021,0x0000,-1,0x0030,0x0000, \ 119 | -1,0x0031,0x0506,-1,0x0032,0x0104,-1,0x0035,0x0207,-1,0x0036,0x000F,-1,0x0037,0x0306,-1,0x0038,0x0102,-1,0x0039,0x0707, \ 120 | -1,0x003C,0x0702,-1,0x003D,0x1604,-1,0x0050,0x0000,-1,0x0051,0x00EF,-1,0x0052,0x0000,-1,0x0053,0x013F,-1,0x0060,0xA700, \ 121 | -1,0x0061,0x0001,-1,0x006A,0x0000,-1,0x0080,0x0000,-1,0x0081,0x0000,-1,0x0082,0x0000,-1,0x0083,0x0000,-1,0x0084,0x0000, \ 122 | -1,0x0085,0x0000,-1,0x0090,0x0010,-1,0x0092,0x0600,-1,0x0007,0x0133,-3 }; 123 | 124 | static int ili9341_init[] = { \ 125 | -1,0x28,-2,20,-1,0xCF,0x00,0x83,0x30,-1,0xED,0x64,0x03,0x12,0x81,-1,0xE8,0x85,0x01,0x79, \ 126 | -1,0xCB,0x39,0x2c,0x00,0x34,0x02,-1,0xF7,0x20,-1,0xEA,0x00,0x00,-1,0xC0,0x26,-1,0xC1,0x11, \ 127 | -1,0xC5,0x35,0x3E,-1,0xC7,0xBE,-1,0xB1,0x00,0x1B,-1,0xB6,0x0a,0x82,0x27,0x00,-1,0xB7,0x07, \ 128 | -1,0x3A,0x55,-1,0x36,0x48,-1,0x11,-2,120,-1,0x29,-2,20,-3 }; 129 | 130 | static int ssd1351_init[] = { -1,0xfd,0x12,-1,0xfd,0xb1,-1,0xae,-1,0xb3,0xf1,-1,0xca,0x7f,-1,0xa0,0x74, \ 131 | -1,0x15,0x00,0x7f,-1,0x75,0x00,0x7f,-1,0xa1,0x00,-1,0xa2,0x00,-1,0xb5,0x00, \ 132 | -1,0xab,0x01,-1,0xb1,0x32,-1,0xb4,0xa0,0xb5,0x55,-1,0xbb,0x17,-1,0xbe,0x05, \ 133 | -1,0xc1,0xc8,0x80,0xc8,-1,0xc7,0x0f,-1,0xb6,0x01,-1,0xa6,-1,0xaf,-3 }; 134 | 135 | 136 | /* ili9320, ili9325 */ 137 | static void flexfb_set_addr_win_1(struct fbtft_par *par, int xs, int ys, int xe, int ye) 138 | { 139 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 140 | switch (par->info->var.rotate) { 141 | /* R20h = Horizontal GRAM Start Address */ 142 | /* R21h = Vertical GRAM Start Address */ 143 | case 0: 144 | write_reg(par, 0x0020, xs); 145 | write_reg(par, 0x0021, ys); 146 | break; 147 | case 180: 148 | write_reg(par, 0x0020, width - 1 - xs); 149 | write_reg(par, 0x0021, height - 1 - ys); 150 | break; 151 | case 270: 152 | write_reg(par, 0x0020, width - 1 - ys); 153 | write_reg(par, 0x0021, xs); 154 | break; 155 | case 90: 156 | write_reg(par, 0x0020, ys); 157 | write_reg(par, 0x0021, height - 1 - xs); 158 | break; 159 | } 160 | write_reg(par, 0x0022); /* Write Data to GRAM */ 161 | } 162 | 163 | /* ssd1289 */ 164 | static void flexfb_set_addr_win_2(struct fbtft_par *par, int xs, int ys, int xe, int ye) 165 | { 166 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 167 | 168 | switch (par->info->var.rotate) { 169 | /* R4Eh - Set GDDRAM X address counter */ 170 | /* R4Fh - Set GDDRAM Y address counter */ 171 | case 0: 172 | write_reg(par, 0x4e, xs); 173 | write_reg(par, 0x4f, ys); 174 | break; 175 | case 180: 176 | write_reg(par, 0x4e, par->info->var.xres - 1 - xs); 177 | write_reg(par, 0x4f, par->info->var.yres - 1 - ys); 178 | break; 179 | case 270: 180 | write_reg(par, 0x4e, par->info->var.yres - 1 - ys); 181 | write_reg(par, 0x4f, xs); 182 | break; 183 | case 90: 184 | write_reg(par, 0x4e, ys); 185 | write_reg(par, 0x4f, par->info->var.xres - 1 - xs); 186 | break; 187 | } 188 | 189 | /* R22h - RAM data write */ 190 | write_reg(par, 0x22, 0); 191 | } 192 | 193 | /* ssd1351 */ 194 | static void set_addr_win_3(struct fbtft_par *par, int xs, int ys, int xe, int ye) 195 | { 196 | fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); 197 | 198 | write_reg(par, 0x15, xs, xe); 199 | write_reg(par, 0x75, ys, ye); 200 | write_reg(par, 0x5C); 201 | } 202 | 203 | static int flexfb_verify_gpios_dc(struct fbtft_par *par) 204 | { 205 | fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__); 206 | 207 | if (par->gpio.dc < 0) { 208 | dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n"); 209 | return -EINVAL; 210 | } 211 | 212 | return 0; 213 | } 214 | 215 | static int flexfb_verify_gpios_db(struct fbtft_par *par) 216 | { 217 | int i; 218 | int num_db = buswidth; 219 | 220 | fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__); 221 | 222 | if (par->gpio.dc < 0) { 223 | dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n"); 224 | return -EINVAL; 225 | } 226 | if (par->gpio.wr < 0) { 227 | dev_err(par->info->device, "Missing info about 'wr' gpio. Aborting.\n"); 228 | return -EINVAL; 229 | } 230 | if (latched && (par->gpio.latch < 0)) { 231 | dev_err(par->info->device, "Missing info about 'latch' gpio. Aborting.\n"); 232 | return -EINVAL; 233 | } 234 | if (latched) 235 | num_db=buswidth/2; 236 | for (i=0;i < num_db;i++) { 237 | if (par->gpio.db[i] < 0) { 238 | dev_err(par->info->device, "Missing info about 'db%02d' gpio. Aborting.\n", i); 239 | return -EINVAL; 240 | } 241 | } 242 | 243 | return 0; 244 | } 245 | 246 | static struct fbtft_display flex_display = { }; 247 | 248 | static int flexfb_probe_common(struct spi_device *sdev, struct platform_device *pdev) 249 | { 250 | struct device *dev; 251 | struct fb_info *info; 252 | struct fbtft_par *par; 253 | int ret; 254 | 255 | initp = init; 256 | initp_num = init_num; 257 | 258 | if (sdev) 259 | dev = &sdev->dev; 260 | else 261 | dev = &pdev->dev; 262 | 263 | fbtft_init_dbg(dev, "%s(%s)\n", __func__, sdev ? "'SPI device'" : "'Platform device'"); 264 | 265 | if (chip) { 266 | 267 | if (!strcmp(chip, "st7735r")) { 268 | if (!width) 269 | width = 128; 270 | if (!height) 271 | height = 160; 272 | if (init_num == 0) { 273 | initp = st7735r_init; 274 | initp_num = ARRAY_SIZE(st7735r_init); 275 | } 276 | 277 | 278 | } else if (!strcmp(chip, "hx8340bn")) { 279 | if (!width) 280 | width = 176; 281 | if (!height) 282 | height = 220; 283 | setaddrwin = 0; 284 | if (init_num == 0) { 285 | initp = hx8340bn_init; 286 | initp_num = ARRAY_SIZE(hx8340bn_init); 287 | } 288 | 289 | 290 | } else if (!strcmp(chip, "ili9225")) { 291 | if (!width) 292 | width = 176; 293 | if (!height) 294 | height = 220; 295 | setaddrwin = 0; 296 | regwidth = 16; 297 | if (init_num == 0) { 298 | initp = ili9225_init; 299 | initp_num = ARRAY_SIZE(ili9225_init); 300 | } 301 | 302 | 303 | 304 | } else if (!strcmp(chip, "ili9320")) { 305 | if (!width) 306 | width = 240; 307 | if (!height) 308 | height = 320; 309 | setaddrwin = 1; 310 | regwidth = 16; 311 | if (init_num == 0) { 312 | initp = ili9320_init; 313 | initp_num = ARRAY_SIZE(ili9320_init); 314 | } 315 | 316 | 317 | } else if (!strcmp(chip, "ili9325")) { 318 | if (!width) 319 | width = 240; 320 | if (!height) 321 | height = 320; 322 | setaddrwin = 1; 323 | regwidth = 16; 324 | if (init_num == 0) { 325 | initp = ili9325_init; 326 | initp_num = ARRAY_SIZE(ili9325_init); 327 | } 328 | 329 | } else if (!strcmp(chip, "ili9341")) { 330 | if (!width) 331 | width = 240; 332 | if (!height) 333 | height = 320; 334 | setaddrwin = 0; 335 | regwidth = 8; 336 | if (init_num == 0) { 337 | initp = ili9341_init; 338 | initp_num = ARRAY_SIZE(ili9341_init); 339 | } 340 | 341 | 342 | } else if (!strcmp(chip, "ssd1289")) { 343 | if (!width) 344 | width = 240; 345 | if (!height) 346 | height = 320; 347 | setaddrwin = 2; 348 | regwidth = 16; 349 | if (init_num == 0) { 350 | initp = ssd1289_init; 351 | initp_num = ARRAY_SIZE(ssd1289_init); 352 | } 353 | 354 | 355 | 356 | } else if (!strcmp(chip, "ssd1351")) { 357 | if (!width) 358 | width = 128; 359 | if (!height) 360 | height = 128; 361 | setaddrwin = 3; 362 | if (init_num == 0) { 363 | initp = ssd1351_init; 364 | initp_num = ARRAY_SIZE(ssd1351_init); 365 | } 366 | } else { 367 | dev_err(dev, "chip=%s is not supported\n", chip); 368 | return -EINVAL; 369 | } 370 | } 371 | 372 | if (width == 0 || height == 0) { 373 | dev_err(dev, "argument(s) missing: width and height has to be set.\n"); 374 | return -EINVAL; 375 | } 376 | flex_display.width = width; 377 | flex_display.height = height; 378 | fbtft_init_dbg(dev, "Display resolution: %dx%d\n", width, height); 379 | fbtft_init_dbg(dev, "chip = %s\n", chip ? chip : "not set"); 380 | fbtft_init_dbg(dev, "setaddrwin = %d\n", setaddrwin); 381 | fbtft_init_dbg(dev, "regwidth = %d\n", regwidth); 382 | fbtft_init_dbg(dev, "buswidth = %d\n", buswidth); 383 | 384 | info = fbtft_framebuffer_alloc(&flex_display, dev); 385 | if (!info) 386 | return -ENOMEM; 387 | 388 | par = info->par; 389 | if (sdev) 390 | par->spi = sdev; 391 | else 392 | par->pdev = pdev; 393 | if (!par->init_sequence) 394 | par->init_sequence = initp; 395 | par->fbtftops.init_display = fbtft_init_display; 396 | 397 | /* registerwrite functions */ 398 | switch (regwidth) { 399 | case 8: 400 | par->fbtftops.write_register = fbtft_write_reg8_bus8; 401 | break; 402 | case 16: 403 | par->fbtftops.write_register = fbtft_write_reg16_bus8; 404 | break; 405 | default: 406 | dev_err(dev, "argument 'regwidth': %d is not supported.\n", regwidth); 407 | return -EINVAL; 408 | } 409 | 410 | /* bus functions */ 411 | if (sdev) { 412 | par->fbtftops.write = fbtft_write_spi; 413 | switch (buswidth) { 414 | case 8: 415 | par->fbtftops.write_vmem = fbtft_write_vmem16_bus8; 416 | if (!par->startbyte) 417 | par->fbtftops.verify_gpios = flexfb_verify_gpios_dc; 418 | break; 419 | case 9: 420 | if (regwidth == 16) { 421 | dev_err(dev, "argument 'regwidth': %d is not supported with buswidth=%d and SPI.\n", regwidth, buswidth); 422 | return -EINVAL; 423 | } 424 | par->fbtftops.write_register = fbtft_write_reg8_bus9; 425 | par->fbtftops.write_vmem = fbtft_write_vmem16_bus9; 426 | sdev->bits_per_word=9; 427 | ret = sdev->master->setup(sdev); 428 | if (ret) { 429 | dev_warn(dev, 430 | "9-bit SPI not available, emulating using 8-bit.\n"); 431 | sdev->bits_per_word = 8; 432 | ret = sdev->master->setup(sdev); 433 | if (ret) 434 | goto out_release; 435 | /* allocate buffer with room for dc bits */ 436 | par->extra = vzalloc(par->txbuf.len + 437 | (par->txbuf.len / 8) + 8); 438 | if (!par->extra) { 439 | ret = -ENOMEM; 440 | goto out_release; 441 | } 442 | par->fbtftops.write = fbtft_write_spi_emulate_9; 443 | } 444 | break; 445 | default: 446 | dev_err(dev, "argument 'buswidth': %d is not supported with SPI.\n", buswidth); 447 | return -EINVAL; 448 | } 449 | } else { 450 | par->fbtftops.verify_gpios = flexfb_verify_gpios_db; 451 | switch (buswidth) { 452 | case 8: 453 | par->fbtftops.write = fbtft_write_gpio8_wr; 454 | par->fbtftops.write_vmem = fbtft_write_vmem16_bus8; 455 | break; 456 | case 16: 457 | par->fbtftops.write_register = fbtft_write_reg16_bus16; 458 | if (latched) 459 | par->fbtftops.write = fbtft_write_gpio16_wr_latched; 460 | else 461 | par->fbtftops.write = fbtft_write_gpio16_wr; 462 | par->fbtftops.write_vmem = fbtft_write_vmem16_bus16; 463 | break; 464 | default: 465 | dev_err(dev, "argument 'buswidth': %d is not supported with parallel.\n", buswidth); 466 | return -EINVAL; 467 | } 468 | } 469 | 470 | /* set_addr_win function */ 471 | switch (setaddrwin) { 472 | case 0: 473 | /* use default */ 474 | break; 475 | case 1: 476 | par->fbtftops.set_addr_win = flexfb_set_addr_win_1; 477 | break; 478 | case 2: 479 | par->fbtftops.set_addr_win = flexfb_set_addr_win_2; 480 | break; 481 | case 3: 482 | par->fbtftops.set_addr_win = set_addr_win_3; 483 | break; 484 | default: 485 | dev_err(dev, "argument 'setaddrwin': unknown value %d.\n", setaddrwin); 486 | return -EINVAL; 487 | } 488 | 489 | if (!nobacklight) 490 | par->fbtftops.register_backlight = fbtft_register_backlight; 491 | 492 | ret = fbtft_register_framebuffer(info); 493 | if (ret < 0) 494 | goto out_release; 495 | 496 | return 0; 497 | 498 | out_release: 499 | fbtft_framebuffer_release(info); 500 | 501 | return ret; 502 | } 503 | 504 | static int flexfb_remove_common(struct device *dev, struct fb_info *info) 505 | { 506 | struct fbtft_par *par; 507 | 508 | if (!info) 509 | return -EINVAL; 510 | par = info->par; 511 | if (par) { 512 | fbtft_par_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, par, 513 | "%s()\n", __func__); 514 | if (par->extra) 515 | vfree(par->extra); 516 | } 517 | fbtft_unregister_framebuffer(info); 518 | fbtft_framebuffer_release(info); 519 | 520 | return 0; 521 | } 522 | 523 | static int flexfb_probe_spi(struct spi_device *spi) 524 | { 525 | return flexfb_probe_common(spi, NULL); 526 | } 527 | 528 | static int flexfb_remove_spi(struct spi_device *spi) 529 | { 530 | struct fb_info *info = spi_get_drvdata(spi); 531 | 532 | return flexfb_remove_common(&spi->dev, info); 533 | } 534 | 535 | static int flexfb_probe_pdev(struct platform_device *pdev) 536 | { 537 | return flexfb_probe_common(NULL, pdev); 538 | } 539 | 540 | static int flexfb_remove_pdev(struct platform_device *pdev) 541 | { 542 | struct fb_info *info = platform_get_drvdata(pdev); 543 | 544 | return flexfb_remove_common(&pdev->dev, info); 545 | } 546 | 547 | static struct spi_driver flexfb_spi_driver = { 548 | .driver = { 549 | .name = DRVNAME, 550 | .owner = THIS_MODULE, 551 | }, 552 | .probe = flexfb_probe_spi, 553 | .remove = flexfb_remove_spi, 554 | }; 555 | 556 | static const struct platform_device_id flexfb_platform_ids[] = { 557 | { "flexpfb", 0 }, 558 | { }, 559 | }; 560 | 561 | static struct platform_driver flexfb_platform_driver = { 562 | .driver = { 563 | .name = DRVNAME, 564 | .owner = THIS_MODULE, 565 | }, 566 | .id_table = flexfb_platform_ids, 567 | .probe = flexfb_probe_pdev, 568 | .remove = flexfb_remove_pdev, 569 | }; 570 | 571 | static int __init flexfb_init(void) 572 | { 573 | int ret, ret2; 574 | 575 | ret = spi_register_driver(&flexfb_spi_driver); 576 | ret2 = platform_driver_register(&flexfb_platform_driver); 577 | if (ret < 0) 578 | return ret; 579 | return ret2; 580 | } 581 | 582 | static void __exit flexfb_exit(void) 583 | { 584 | spi_unregister_driver(&flexfb_spi_driver); 585 | platform_driver_unregister(&flexfb_platform_driver); 586 | } 587 | 588 | /* ------------------------------------------------------------------------- */ 589 | 590 | module_init(flexfb_init); 591 | module_exit(flexfb_exit); 592 | 593 | MODULE_DESCRIPTION("Generic FB driver for TFT LCD displays"); 594 | MODULE_AUTHOR("Noralf Tronnes"); 595 | MODULE_LICENSE("GPL"); 596 | --------------------------------------------------------------------------------