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