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