1 /*
2  * FB driver for the TLS8204 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  * Copyright (C) 2014 Michael Hope (adapted for the TLS8204)
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 
21 #include <linux/module.h>
22 #include <linux/kernel.h>
23 #include <linux/init.h>
24 #include <linux/gpio.h>
25 #include <linux/spi/spi.h>
26 #include <linux/delay.h>
27 
28 #include "fbtft.h"
29 
30 #define DRVNAME		"fb_tls8204"
31 #define WIDTH		84
32 #define HEIGHT		48
33 #define TXBUFLEN	WIDTH
34 
35 /* gamma is used to control contrast in this driver */
36 #define DEFAULT_GAMMA	"40"
37 
38 static unsigned bs = 4;
39 module_param(bs, uint, 0);
40 MODULE_PARM_DESC(bs, "BS[2:0] Bias voltage level: 0-7 (default: 4)");
41 
42 static int init_display(struct fbtft_par *par)
43 {
44 	par->fbtftops.reset(par);
45 
46 	/* Enter extended command mode */
47 	write_reg(par, 0x21); /* 5:1  1
48 				 2:0  PD - Powerdown control: chip is active
49 				 1:0  V  - Entry mode: horizontal addressing
50 				 0:1  H  - Extended instruction set control:
51 						extended
52 			      */
53 
54 	/* H=1 Bias system */
55 	write_reg(par, 0x10 | (bs & 0x7)); /*
56 				 4:1  1
57 				 3:0  0
58 				 2:x  BS2 - Bias System
59 				 1:x  BS1
60 				 0:x  BS0
61 			      */
62 
63 	/* Set the address of the first display line. */
64 	write_reg(par, 0x04 | (64 >> 6));
65 	write_reg(par, 0x40 | (64 & 0x3F));
66 
67 	/* Enter H=0 standard command mode */
68 	write_reg(par, 0x20);
69 
70 	/* H=0 Display control */
71 	write_reg(par, 0x08 | 4); /*
72 				 3:1  1
73 				 2:1  D  - DE: 10=normal mode
74 				 1:0  0
75 				 0:0  E
76 			      */
77 
78 	return 0;
79 }
80 
81 static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
82 {
83 	/* H=0 Set X address of RAM */
84 	write_reg(par, 0x80); /* 7:1  1
85 				 6-0: X[6:0] - 0x00
86 			      */
87 
88 	/* H=0 Set Y address of RAM */
89 	write_reg(par, 0x40); /* 7:0  0
90 				 6:1  1
91 				 2-0: Y[2:0] - 0x0
92 			      */
93 }
94 
95 static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
96 {
97 	u16 *vmem16 = (u16 *)par->info->screen_buffer;
98 	int x, y, i;
99 	int ret = 0;
100 
101 	for (y = 0; y < HEIGHT / 8; y++) {
102 		u8 *buf = par->txbuf.buf;
103 		/* The display is 102x68 but the LCD is 84x48.  Set
104 		   the write pointer at the start of each row. */
105 		gpio_set_value(par->gpio.dc, 0);
106 		write_reg(par, 0x80 | 0);
107 		write_reg(par, 0x40 | y);
108 
109 		for (x = 0; x < WIDTH; x++) {
110 			u8 ch = 0;
111 
112 			for (i = 0; i < 8 * WIDTH; i += WIDTH) {
113 				ch >>= 1;
114 				if (vmem16[(y * 8 * WIDTH) + i + x])
115 					ch |= 0x80;
116 			}
117 			*buf++ = ch;
118 		}
119 		/* Write the row */
120 		gpio_set_value(par->gpio.dc, 1);
121 		ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH);
122 		if (ret < 0) {
123 			dev_err(par->info->device,
124 				"write failed and returned: %d\n", ret);
125 			break;
126 		}
127 	}
128 
129 	return ret;
130 }
131 
132 static int set_gamma(struct fbtft_par *par, unsigned long *curves)
133 {
134 	/* apply mask */
135 	curves[0] &= 0x7F;
136 
137 	write_reg(par, 0x21); /* turn on extended instruction set */
138 	write_reg(par, 0x80 | curves[0]);
139 	write_reg(par, 0x20); /* turn off extended instruction set */
140 
141 	return 0;
142 }
143 
144 static struct fbtft_display display = {
145 	.regwidth = 8,
146 	.width = WIDTH,
147 	.height = HEIGHT,
148 	.txbuflen = TXBUFLEN,
149 	.gamma_num = 1,
150 	.gamma_len = 1,
151 	.gamma = DEFAULT_GAMMA,
152 	.fbtftops = {
153 		.init_display = init_display,
154 		.set_addr_win = set_addr_win,
155 		.write_vmem = write_vmem,
156 		.set_gamma = set_gamma,
157 	},
158 	.backlight = 1,
159 };
160 
161 FBTFT_REGISTER_DRIVER(DRVNAME, "teralane,tls8204", &display);
162 
163 MODULE_ALIAS("spi:" DRVNAME);
164 MODULE_ALIAS("spi:tls8204");
165 
166 MODULE_DESCRIPTION("FB driver for the TLS8204 LCD Controller");
167 MODULE_AUTHOR("Michael Hope");
168 MODULE_LICENSE("GPL");
169