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