1 /*
2  * FB driver for the HX8357D LCD Controller
3  * Copyright (C) 2015 Adafruit Industries
4  *
5  * Based on the HX8347D FB driver
6  * Copyright (C) 2013 Christian Vogelgsang
7  *
8  * Based on driver code found here: https://github.com/watterott/r61505u-Adapter
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/delay.h>
25 
26 #include "fbtft.h"
27 #include "fb_hx8357d.h"
28 
29 #define DRVNAME		"fb_hx8357d"
30 #define WIDTH		320
31 #define HEIGHT		480
32 
init_display(struct fbtft_par * par)33 static int init_display(struct fbtft_par *par)
34 {
35 	par->fbtftops.reset(par);
36 
37 	/* Reset things like Gamma */
38 	write_reg(par, HX8357B_SWRESET);
39 	usleep_range(5000, 7000);
40 
41 	/* setextc */
42 	write_reg(par, HX8357D_SETC, 0xFF, 0x83, 0x57);
43 	msleep(150);
44 
45 	/* setRGB which also enables SDO */
46 	write_reg(par, HX8357_SETRGB, 0x00, 0x00, 0x06, 0x06);
47 
48 	/* -1.52V */
49 	write_reg(par, HX8357D_SETCOM, 0x25);
50 
51 	/* Normal mode 70Hz, Idle mode 55 Hz */
52 	write_reg(par, HX8357_SETOSC, 0x68);
53 
54 	/* Set Panel - BGR, Gate direction swapped */
55 	write_reg(par, HX8357_SETPANEL, 0x05);
56 
57 	write_reg(par, HX8357_SETPWR1,
58 		0x00,  /* Not deep standby */
59 		0x15,  /* BT */
60 		0x1C,  /* VSPR */
61 		0x1C,  /* VSNR */
62 		0x83,  /* AP */
63 		0xAA);  /* FS */
64 
65 	write_reg(par, HX8357D_SETSTBA,
66 		0x50,  /* OPON normal */
67 		0x50,  /* OPON idle */
68 		0x01,  /* STBA */
69 		0x3C,  /* STBA */
70 		0x1E,  /* STBA */
71 		0x08);  /* GEN */
72 
73 	write_reg(par, HX8357D_SETCYC,
74 		0x02,  /* NW 0x02 */
75 		0x40,  /* RTN */
76 		0x00,  /* DIV */
77 		0x2A,  /* DUM */
78 		0x2A,  /* DUM */
79 		0x0D,  /* GDON */
80 		0x78);  /* GDOFF */
81 
82 	write_reg(par, HX8357D_SETGAMMA,
83 		0x02,
84 		0x0A,
85 		0x11,
86 		0x1d,
87 		0x23,
88 		0x35,
89 		0x41,
90 		0x4b,
91 		0x4b,
92 		0x42,
93 		0x3A,
94 		0x27,
95 		0x1B,
96 		0x08,
97 		0x09,
98 		0x03,
99 		0x02,
100 		0x0A,
101 		0x11,
102 		0x1d,
103 		0x23,
104 		0x35,
105 		0x41,
106 		0x4b,
107 		0x4b,
108 		0x42,
109 		0x3A,
110 		0x27,
111 		0x1B,
112 		0x08,
113 		0x09,
114 		0x03,
115 		0x00,
116 		0x01);
117 
118 	/* 16 bit */
119 	write_reg(par, HX8357_COLMOD, 0x55);
120 
121 	write_reg(par, HX8357_MADCTL, 0xC0);
122 
123 	/* TE off */
124 	write_reg(par, HX8357_TEON, 0x00);
125 
126 	/* tear line */
127 	write_reg(par, HX8357_TEARLINE, 0x00, 0x02);
128 
129 	/* Exit Sleep */
130 	write_reg(par, HX8357_SLPOUT);
131 	msleep(150);
132 
133 	/* display on */
134 	write_reg(par, HX8357_DISPON);
135 	usleep_range(5000, 7000);
136 
137 	return 0;
138 }
139 
set_addr_win(struct fbtft_par * par,int xs,int ys,int xe,int ye)140 static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
141 {
142 	/* Column addr set */
143 	write_reg(par, HX8357_CASET,
144 		xs >> 8, xs & 0xff,  /* XSTART */
145 		xe >> 8, xe & 0xff); /* XEND */
146 
147 	/* Row addr set */
148 	write_reg(par, HX8357_PASET,
149 		ys >> 8, ys & 0xff,  /* YSTART */
150 		ye >> 8, ye & 0xff); /* YEND */
151 
152 	/* write to RAM */
153 	write_reg(par, HX8357_RAMWR);
154 }
155 
156 #define HX8357D_MADCTL_MY  0x80
157 #define HX8357D_MADCTL_MX  0x40
158 #define HX8357D_MADCTL_MV  0x20
159 #define HX8357D_MADCTL_ML  0x10
160 #define HX8357D_MADCTL_RGB 0x00
161 #define HX8357D_MADCTL_BGR 0x08
162 #define HX8357D_MADCTL_MH  0x04
set_var(struct fbtft_par * par)163 static int set_var(struct fbtft_par *par)
164 {
165 	u8 val;
166 
167 	switch (par->info->var.rotate) {
168 	case 270:
169 		val = HX8357D_MADCTL_MV | HX8357D_MADCTL_MX;
170 		break;
171 	case 180:
172 		val = 0;
173 		break;
174 	case 90:
175 		val = HX8357D_MADCTL_MV | HX8357D_MADCTL_MY;
176 		break;
177 	default:
178 		val = HX8357D_MADCTL_MX | HX8357D_MADCTL_MY;
179 		break;
180 	}
181 
182 	val |= (par->bgr ? HX8357D_MADCTL_RGB : HX8357D_MADCTL_BGR);
183 
184 	/* Memory Access Control */
185 	write_reg(par, HX8357_MADCTL, val);
186 
187 	return 0;
188 }
189 
190 static struct fbtft_display display = {
191 	.regwidth = 8,
192 	.width = WIDTH,
193 	.height = HEIGHT,
194 	.gamma_num = 2,
195 	.gamma_len = 14,
196 	.fbtftops = {
197 		.init_display = init_display,
198 		.set_addr_win = set_addr_win,
199 		.set_var = set_var,
200 	},
201 };
202 
203 FBTFT_REGISTER_DRIVER(DRVNAME, "himax,hx8357d", &display);
204 
205 MODULE_ALIAS("spi:" DRVNAME);
206 MODULE_ALIAS("platform:" DRVNAME);
207 MODULE_ALIAS("spi:hx8357d");
208 MODULE_ALIAS("platform:hx8357d");
209 
210 MODULE_DESCRIPTION("FB driver for the HX8357D LCD Controller");
211 MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
212 MODULE_LICENSE("GPL");
213