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 transferring 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 
22 #include <linux/module.h>
23 #include <linux/kernel.h>
24 #include <linux/init.h>
25 #include <linux/vmalloc.h>
26 #include <linux/spi/spi.h>
27 #include <linux/delay.h>
28 
29 #include "fbtft.h"
30 
31 #define DRVNAME		"fb_hx8340bn"
32 #define WIDTH		176
33 #define HEIGHT		220
34 #define TXBUFLEN	(4 * PAGE_SIZE)
35 #define DEFAULT_GAMMA	"1 3 0E 5 0 2 09 0 6 1 7 1 0 2 2\n" \
36 			"3 3 17 8 4 7 05 7 6 0 3 1 6 0 0 "
37 
38 static bool emulate;
39 module_param(emulate, bool, 0);
40 MODULE_PARM_DESC(emulate, "Force emulation in 9-bit mode");
41 
init_display(struct fbtft_par * par)42 static int init_display(struct fbtft_par *par)
43 {
44 	par->fbtftops.reset(par);
45 
46 	/* BTL221722-276L startup sequence, from datasheet */
47 
48 	/* SETEXTCOM: Set extended command set (C1h)
49 	   This command is used to set extended command set access enable.
50 	   Enable: After command (C1h), must write: ffh,83h,40h */
51 	write_reg(par, 0xC1, 0xFF, 0x83, 0x40);
52 
53 	/* Sleep out
54 	   This command turns off sleep mode.
55 	   In this mode the DC/DC converter is enabled, Internal oscillator
56 	   is started, and panel scanning is started. */
57 	write_reg(par, 0x11);
58 	mdelay(150);
59 
60 	/* Undoc'd register? */
61 	write_reg(par, 0xCA, 0x70, 0x00, 0xD9);
62 
63 	/* SETOSC: Set Internal Oscillator (B0h)
64 	   This command is used to set internal oscillator related settings */
65 	/*	OSC_EN: Enable internal oscillator */
66 	/*	Internal oscillator frequency: 125% x 2.52MHz */
67 	write_reg(par, 0xB0, 0x01, 0x11);
68 
69 	/* Drive ability setting */
70 	write_reg(par, 0xC9, 0x90, 0x49, 0x10, 0x28, 0x28, 0x10, 0x00, 0x06);
71 	mdelay(20);
72 
73 	/* SETPWCTR5: Set Power Control 5(B5h)
74 	   This command is used to set VCOM Low and VCOM High Voltage */
75 	/* VCOMH 0110101 :  3.925 */
76 	/* VCOML 0100000 : -1.700 */
77 	/* 45h=69  VCOMH: "VMH" + 5d   VCOML: "VMH" + 5d */
78 	write_reg(par, 0xB5, 0x35, 0x20, 0x45);
79 
80 	/* SETPWCTR4: Set Power Control 4(B4h)
81 		VRH[4:0]:	Specify the VREG1 voltage adjusting.
82 				VREG1 voltage is for gamma voltage setting.
83 		BT[2:0]:	Switch the output factor of step-up circuit 2
84 				for VGH and VGL voltage generation. */
85 	write_reg(par, 0xB4, 0x33, 0x25, 0x4C);
86 	mdelay(10);
87 
88 	/* Interface Pixel Format (3Ah)
89 	   This command is used to define the format of RGB picture data,
90 	   which is to be transfer via the system and RGB interface. */
91 	/* RGB interface: 16 Bit/Pixel	*/
92 	write_reg(par, 0x3A, 0x05);
93 
94 	/* Display on (29h)
95 	   This command is used to recover from DISPLAY OFF mode.
96 	   Output from the Frame Memory is enabled. */
97 	write_reg(par, 0x29);
98 	mdelay(10);
99 
100 	return 0;
101 }
102 
set_addr_win(struct fbtft_par * par,int xs,int ys,int xe,int ye)103 static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
104 {
105 	write_reg(par, FBTFT_CASET, 0x00, xs, 0x00, xe);
106 	write_reg(par, FBTFT_RASET, 0x00, ys, 0x00, ye);
107 	write_reg(par, FBTFT_RAMWR);
108 }
109 
set_var(struct fbtft_par * par)110 static int set_var(struct fbtft_par *par)
111 {
112 	/* MADCTL - Memory data access control */
113 	/* RGB/BGR can be set with H/W pin SRGB and MADCTL BGR bit */
114 #define MY BIT(7)
115 #define MX BIT(6)
116 #define MV BIT(5)
117 	switch (par->info->var.rotate) {
118 	case 0:
119 		write_reg(par, 0x36, par->bgr << 3);
120 		break;
121 	case 270:
122 		write_reg(par, 0x36, MX | MV | (par->bgr << 3));
123 		break;
124 	case 180:
125 		write_reg(par, 0x36, MX | MY | (par->bgr << 3));
126 		break;
127 	case 90:
128 		write_reg(par, 0x36, MY | MV | (par->bgr << 3));
129 		break;
130 	}
131 
132 	return 0;
133 }
134 
135 /*
136   Gamma Curve selection, GC (only GC0 can be customized):
137     0 = 2.2, 1 = 1.8, 2 = 2.5, 3 = 1.0
138   Gamma string format:
139     OP0 OP1 CP0 CP1 CP2 CP3 CP4 MP0 MP1 MP2 MP3 MP4 MP5 CGM0 CGM1
140     ON0 ON1 CN0 CN1 CN2 CN3 CN4 MN0 MN1 MN2 MN3 MN4 MN5 XXXX  GC
141 */
142 #define CURVE(num, idx)  curves[num * par->gamma.num_values + idx]
set_gamma(struct fbtft_par * par,unsigned long * curves)143 static int set_gamma(struct fbtft_par *par, unsigned long *curves)
144 {
145 	unsigned long mask[] = {
146 		0x0f, 0x0f, 0x1f, 0x0f, 0x0f, 0x0f, 0x1f, 0x07, 0x07, 0x07,
147 		0x07, 0x07, 0x07, 0x03, 0x03, 0x0f, 0x0f, 0x1f, 0x0f, 0x0f,
148 		0x0f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x00, 0x00,
149 	};
150 	int i, j;
151 
152 	/* apply mask */
153 	for (i = 0; i < par->gamma.num_curves; i++)
154 		for (j = 0; j < par->gamma.num_values; j++)
155 			CURVE(i, j) &= mask[i * par->gamma.num_values + j];
156 
157 	write_reg(par, 0x26, 1 << CURVE(1, 14)); /* Gamma Set (26h) */
158 
159 	if (CURVE(1, 14))
160 		return 0; /* only GC0 can be customized */
161 
162 	write_reg(par, 0xC2,
163 		(CURVE(0, 8) << 4) | CURVE(0, 7),
164 		(CURVE(0, 10) << 4) | CURVE(0, 9),
165 		(CURVE(0, 12) << 4) | CURVE(0, 11),
166 		CURVE(0, 2),
167 		(CURVE(0, 4) << 4) | CURVE(0, 3),
168 		CURVE(0, 5),
169 		CURVE(0, 6),
170 		(CURVE(0, 1) << 4) | CURVE(0, 0),
171 		(CURVE(0, 14) << 2) | CURVE(0, 13));
172 
173 	write_reg(par, 0xC3,
174 		(CURVE(1, 8) << 4) | CURVE(1, 7),
175 		(CURVE(1, 10) << 4) | CURVE(1, 9),
176 		(CURVE(1, 12) << 4) | CURVE(1, 11),
177 		CURVE(1, 2),
178 		(CURVE(1, 4) << 4) | CURVE(1, 3),
179 		CURVE(1, 5),
180 		CURVE(1, 6),
181 		(CURVE(1, 1) << 4) | CURVE(1, 0));
182 
183 	mdelay(10);
184 
185 	return 0;
186 }
187 #undef CURVE
188 
189 static struct fbtft_display display = {
190 	.regwidth = 8,
191 	.width = WIDTH,
192 	.height = HEIGHT,
193 	.txbuflen = TXBUFLEN,
194 	.gamma_num = 2,
195 	.gamma_len = 15,
196 	.gamma = DEFAULT_GAMMA,
197 	.fbtftops = {
198 		.init_display = init_display,
199 		.set_addr_win = set_addr_win,
200 		.set_var = set_var,
201 		.set_gamma = set_gamma,
202 	},
203 };
204 
205 FBTFT_REGISTER_DRIVER(DRVNAME, "himax,hx8340bn", &display);
206 
207 MODULE_ALIAS("spi:" DRVNAME);
208 MODULE_ALIAS("platform:" DRVNAME);
209 MODULE_ALIAS("spi:hx8340bn");
210 MODULE_ALIAS("platform:hx8340bn");
211 
212 MODULE_DESCRIPTION("FB driver for the HX8340BN LCD Controller");
213 MODULE_AUTHOR("Noralf Tronnes");
214 MODULE_LICENSE("GPL");
215