1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * FB driver for the UltraChip UC1611 LCD controller 4 * 5 * The display is 4-bit grayscale (16 shades) 240x160. 6 * 7 * Copyright (C) 2015 Henri Chain 8 */ 9 10 #include <linux/module.h> 11 #include <linux/kernel.h> 12 #include <linux/init.h> 13 #include <linux/gpio.h> 14 #include <linux/spi/spi.h> 15 #include <linux/delay.h> 16 17 #include "fbtft.h" 18 19 #define DRVNAME "fb_uc1611" 20 #define WIDTH 240 21 #define HEIGHT 160 22 #define BPP 8 23 #define FPS 40 24 25 /* 26 * LCD voltage is a combination of ratio, gain, pot and temp 27 * 28 * V_LCD = V_BIAS * ratio 29 * V_LCD = (C_V0 + C_PM × pot) * (1 + (T - 25) * temp) 30 * C_V0 and C_PM depend on ratio and gain 31 * T is ambient temperature 32 */ 33 34 /* BR -> actual ratio: 0-3 -> 5, 10, 11, 13 */ 35 static unsigned int ratio = 2; 36 module_param(ratio, uint, 0000); 37 MODULE_PARM_DESC(ratio, "BR[1:0] Bias voltage ratio: 0-3 (default: 2)"); 38 39 static unsigned int gain = 3; 40 module_param(gain, uint, 0000); 41 MODULE_PARM_DESC(gain, "GN[1:0] Bias voltage gain: 0-3 (default: 3)"); 42 43 static unsigned int pot = 16; 44 module_param(pot, uint, 0000); 45 MODULE_PARM_DESC(pot, "PM[6:0] Bias voltage pot.: 0-63 (default: 16)"); 46 47 /* TC -> % compensation per deg C: 0-3 -> -.05, -.10, -.015, -.20 */ 48 static unsigned int temp; 49 module_param(temp, uint, 0000); 50 MODULE_PARM_DESC(temp, "TC[1:0] Temperature compensation: 0-3 (default: 0)"); 51 52 /* PC[1:0] -> LCD capacitance: 0-3 -> <20nF, 20-28 nF, 29-40 nF, 40-56 nF */ 53 static unsigned int load = 1; 54 module_param(load, uint, 0000); 55 MODULE_PARM_DESC(load, "PC[1:0] Panel Loading: 0-3 (default: 1)"); 56 57 /* PC[3:2] -> V_LCD: 0, 1, 3 -> ext., int. with ratio = 5, int. standard */ 58 static unsigned int pump = 3; 59 module_param(pump, uint, 0000); 60 MODULE_PARM_DESC(pump, "PC[3:2] Pump control: 0,1,3 (default: 3)"); 61 62 static int init_display(struct fbtft_par *par) 63 { 64 int ret; 65 66 /* Set CS active high */ 67 par->spi->mode |= SPI_CS_HIGH; 68 ret = spi_setup(par->spi); 69 if (ret) { 70 dev_err(par->info->device, "Could not set SPI_CS_HIGH\n"); 71 return ret; 72 } 73 74 /* Reset controller */ 75 write_reg(par, 0xE2); 76 77 /* Set bias ratio */ 78 write_reg(par, 0xE8 | (ratio & 0x03)); 79 80 /* Set bias gain and potentiometer */ 81 write_reg(par, 0x81); 82 write_reg(par, (gain & 0x03) << 6 | (pot & 0x3F)); 83 84 /* Set temperature compensation */ 85 write_reg(par, 0x24 | (temp & 0x03)); 86 87 /* Set panel loading */ 88 write_reg(par, 0x28 | (load & 0x03)); 89 90 /* Set pump control */ 91 write_reg(par, 0x2C | (pump & 0x03)); 92 93 /* Set inverse display */ 94 write_reg(par, 0xA6 | (0x01 & 0x01)); 95 96 /* Set 4-bit grayscale mode */ 97 write_reg(par, 0xD0 | (0x02 & 0x03)); 98 99 /* Set Display enable */ 100 write_reg(par, 0xA8 | 0x07); 101 102 return 0; 103 } 104 105 static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 106 { 107 switch (par->info->var.rotate) { 108 case 90: 109 case 270: 110 /* Set column address */ 111 write_reg(par, ys & 0x0F); 112 write_reg(par, 0x10 | (ys >> 4)); 113 114 /* Set page address (divide xs by 2) (not used by driver) */ 115 write_reg(par, 0x60 | ((xs >> 1) & 0x0F)); 116 write_reg(par, 0x70 | (xs >> 5)); 117 break; 118 default: 119 /* Set column address (not used by driver) */ 120 write_reg(par, xs & 0x0F); 121 write_reg(par, 0x10 | (xs >> 4)); 122 123 /* Set page address (divide ys by 2) */ 124 write_reg(par, 0x60 | ((ys >> 1) & 0x0F)); 125 write_reg(par, 0x70 | (ys >> 5)); 126 break; 127 } 128 } 129 130 static int blank(struct fbtft_par *par, bool on) 131 { 132 fbtft_par_dbg(DEBUG_BLANK, par, "(%s=%s)\n", 133 __func__, on ? "true" : "false"); 134 135 if (on) 136 write_reg(par, 0xA8 | 0x00); 137 else 138 write_reg(par, 0xA8 | 0x07); 139 return 0; 140 } 141 142 static int set_var(struct fbtft_par *par) 143 { 144 /* par->info->fix.visual = FB_VISUAL_PSEUDOCOLOR; */ 145 par->info->var.grayscale = 1; 146 par->info->var.red.offset = 0; 147 par->info->var.red.length = 8; 148 par->info->var.green.offset = 0; 149 par->info->var.green.length = 8; 150 par->info->var.blue.offset = 0; 151 par->info->var.blue.length = 8; 152 par->info->var.transp.offset = 0; 153 par->info->var.transp.length = 0; 154 155 switch (par->info->var.rotate) { 156 case 90: 157 /* Set RAM address control */ 158 write_reg(par, 0x88 159 | (0x0 & 0x1) << 2 /* Increment positively */ 160 | (0x1 & 0x1) << 1 /* Increment page first */ 161 | (0x1 & 0x1)); /* Wrap around (default) */ 162 163 /* Set LCD mapping */ 164 write_reg(par, 0xC0 165 | (0x0 & 0x1) << 2 /* Mirror Y OFF */ 166 | (0x0 & 0x1) << 1 /* Mirror X OFF */ 167 | (0x0 & 0x1)); /* MS nibble last (default) */ 168 break; 169 case 180: 170 /* Set RAM address control */ 171 write_reg(par, 0x88 172 | (0x0 & 0x1) << 2 /* Increment positively */ 173 | (0x0 & 0x1) << 1 /* Increment column first */ 174 | (0x1 & 0x1)); /* Wrap around (default) */ 175 176 /* Set LCD mapping */ 177 write_reg(par, 0xC0 178 | (0x1 & 0x1) << 2 /* Mirror Y ON */ 179 | (0x0 & 0x1) << 1 /* Mirror X OFF */ 180 | (0x0 & 0x1)); /* MS nibble last (default) */ 181 break; 182 case 270: 183 /* Set RAM address control */ 184 write_reg(par, 0x88 185 | (0x0 & 0x1) << 2 /* Increment positively */ 186 | (0x1 & 0x1) << 1 /* Increment page first */ 187 | (0x1 & 0x1)); /* Wrap around (default) */ 188 189 /* Set LCD mapping */ 190 write_reg(par, 0xC0 191 | (0x1 & 0x1) << 2 /* Mirror Y ON */ 192 | (0x1 & 0x1) << 1 /* Mirror X ON */ 193 | (0x0 & 0x1)); /* MS nibble last (default) */ 194 break; 195 default: 196 /* Set RAM address control */ 197 write_reg(par, 0x88 198 | (0x0 & 0x1) << 2 /* Increment positively */ 199 | (0x0 & 0x1) << 1 /* Increment column first */ 200 | (0x1 & 0x1)); /* Wrap around (default) */ 201 202 /* Set LCD mapping */ 203 write_reg(par, 0xC0 204 | (0x0 & 0x1) << 2 /* Mirror Y OFF */ 205 | (0x1 & 0x1) << 1 /* Mirror X ON */ 206 | (0x0 & 0x1)); /* MS nibble last (default) */ 207 break; 208 } 209 210 return 0; 211 } 212 213 static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) 214 { 215 u8 *vmem8 = (u8 *)(par->info->screen_buffer); 216 u8 *buf8 = par->txbuf.buf; 217 u16 *buf16 = par->txbuf.buf; 218 int line_length = par->info->fix.line_length; 219 int y_start = offset / line_length; 220 int y_end = (offset + len - 1) / line_length; 221 int x, y, i; 222 int ret = 0; 223 224 switch (par->pdata->display.buswidth) { 225 case 8: 226 switch (par->info->var.rotate) { 227 case 90: 228 case 270: 229 i = y_start * line_length; 230 for (y = y_start; y <= y_end; y++) { 231 for (x = 0; x < line_length; x += 2) { 232 *buf8 = vmem8[i] >> 4; 233 *buf8 |= vmem8[i + 1] & 0xF0; 234 buf8++; 235 i += 2; 236 } 237 } 238 break; 239 default: 240 /* Must be even because pages are two lines */ 241 y_start &= 0xFE; 242 i = y_start * line_length; 243 for (y = y_start; y <= y_end; y += 2) { 244 for (x = 0; x < line_length; x++) { 245 *buf8 = vmem8[i] >> 4; 246 *buf8 |= vmem8[i + line_length] & 0xF0; 247 buf8++; 248 i++; 249 } 250 i += line_length; 251 } 252 break; 253 } 254 gpio_set_value(par->gpio.dc, 1); 255 256 /* Write data */ 257 ret = par->fbtftops.write(par, par->txbuf.buf, len / 2); 258 break; 259 case 9: 260 switch (par->info->var.rotate) { 261 case 90: 262 case 270: 263 i = y_start * line_length; 264 for (y = y_start; y <= y_end; y++) { 265 for (x = 0; x < line_length; x += 2) { 266 *buf16 = 0x100; 267 *buf16 |= vmem8[i] >> 4; 268 *buf16 |= vmem8[i + 1] & 0xF0; 269 buf16++; 270 i += 2; 271 } 272 } 273 break; 274 default: 275 /* Must be even because pages are two lines */ 276 y_start &= 0xFE; 277 i = y_start * line_length; 278 for (y = y_start; y <= y_end; y += 2) { 279 for (x = 0; x < line_length; x++) { 280 *buf16 = 0x100; 281 *buf16 |= vmem8[i] >> 4; 282 *buf16 |= vmem8[i + line_length] & 0xF0; 283 buf16++; 284 i++; 285 } 286 i += line_length; 287 } 288 break; 289 } 290 291 /* Write data */ 292 ret = par->fbtftops.write(par, par->txbuf.buf, len); 293 break; 294 default: 295 dev_err(par->info->device, "unsupported buswidth %d\n", 296 par->pdata->display.buswidth); 297 } 298 299 if (ret < 0) 300 dev_err(par->info->device, "write failed and returned: %d\n", 301 ret); 302 303 return ret; 304 } 305 306 static struct fbtft_display display = { 307 .txbuflen = -1, 308 .regwidth = 8, 309 .width = WIDTH, 310 .height = HEIGHT, 311 .bpp = BPP, 312 .fps = FPS, 313 .fbtftops = { 314 .write_vmem = write_vmem, 315 .init_display = init_display, 316 .set_addr_win = set_addr_win, 317 .set_var = set_var, 318 .blank = blank, 319 }, 320 }; 321 322 FBTFT_REGISTER_DRIVER(DRVNAME, "ultrachip,uc1611", &display); 323 324 MODULE_ALIAS("spi:" DRVNAME); 325 MODULE_ALIAS("platform:" DRVNAME); 326 MODULE_ALIAS("spi:uc1611"); 327 MODULE_ALIAS("platform:uc1611"); 328 329 MODULE_DESCRIPTION("FB driver for the UC1611 LCD controller"); 330 MODULE_AUTHOR("Henri Chain"); 331 MODULE_LICENSE("GPL"); 332