1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Innolux/Chimei EJ030NA TFT LCD panel driver |
4 | * |
5 | * Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net> |
6 | * Copyright (C) 2020, Christophe Branchereau <cbranchereau@gmail.com> |
7 | */ |
8 | |
9 | #include <linux/delay.h> |
10 | #include <linux/device.h> |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/media-bus-format.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/regulator/consumer.h> |
18 | #include <linux/spi/spi.h> |
19 | |
20 | #include <drm/drm_modes.h> |
21 | #include <drm/drm_panel.h> |
22 | |
23 | struct ej030na_info { |
24 | const struct drm_display_mode *display_modes; |
25 | unsigned int num_modes; |
26 | u16 width_mm, height_mm; |
27 | u32 bus_format, bus_flags; |
28 | }; |
29 | |
30 | struct ej030na { |
31 | struct drm_panel panel; |
32 | struct spi_device *spi; |
33 | struct regmap *map; |
34 | |
35 | const struct ej030na_info *panel_info; |
36 | |
37 | struct regulator *supply; |
38 | struct gpio_desc *reset_gpio; |
39 | }; |
40 | |
41 | static inline struct ej030na *to_ej030na(struct drm_panel *panel) |
42 | { |
43 | return container_of(panel, struct ej030na, panel); |
44 | } |
45 | |
46 | static const struct reg_sequence ej030na_init_sequence[] = { |
47 | { 0x05, 0x1e }, |
48 | { 0x05, 0x5c }, |
49 | { 0x02, 0x14 }, |
50 | { 0x03, 0x40 }, |
51 | { 0x04, 0x07 }, |
52 | { 0x06, 0x12 }, |
53 | { 0x07, 0xd2 }, |
54 | { 0x0c, 0x06 }, |
55 | { 0x0d, 0x40 }, |
56 | { 0x0e, 0x40 }, |
57 | { 0x0f, 0x40 }, |
58 | { 0x10, 0x40 }, |
59 | { 0x11, 0x40 }, |
60 | { 0x2f, 0x40 }, |
61 | { 0x5a, 0x02 }, |
62 | |
63 | { 0x30, 0x07 }, |
64 | { 0x31, 0x57 }, |
65 | { 0x32, 0x53 }, |
66 | { 0x33, 0x77 }, |
67 | { 0x34, 0xb8 }, |
68 | { 0x35, 0xbd }, |
69 | { 0x36, 0xb8 }, |
70 | { 0x37, 0xe7 }, |
71 | { 0x38, 0x04 }, |
72 | { 0x39, 0xff }, |
73 | |
74 | { 0x40, 0x0b }, |
75 | { 0x41, 0xb8 }, |
76 | { 0x42, 0xab }, |
77 | { 0x43, 0xb9 }, |
78 | { 0x44, 0x6a }, |
79 | { 0x45, 0x56 }, |
80 | { 0x46, 0x61 }, |
81 | { 0x47, 0x08 }, |
82 | { 0x48, 0x0f }, |
83 | { 0x49, 0x0f }, |
84 | }; |
85 | |
86 | static int ej030na_prepare(struct drm_panel *panel) |
87 | { |
88 | struct ej030na *priv = to_ej030na(panel); |
89 | struct device *dev = &priv->spi->dev; |
90 | int err; |
91 | |
92 | err = regulator_enable(regulator: priv->supply); |
93 | if (err) { |
94 | dev_err(dev, "Failed to enable power supply: %d\n" , err); |
95 | return err; |
96 | } |
97 | |
98 | /* Reset the chip */ |
99 | gpiod_set_value_cansleep(desc: priv->reset_gpio, value: 1); |
100 | usleep_range(min: 50, max: 150); |
101 | gpiod_set_value_cansleep(desc: priv->reset_gpio, value: 0); |
102 | usleep_range(min: 50, max: 150); |
103 | |
104 | err = regmap_multi_reg_write(map: priv->map, regs: ej030na_init_sequence, |
105 | ARRAY_SIZE(ej030na_init_sequence)); |
106 | if (err) { |
107 | dev_err(dev, "Failed to init registers: %d\n" , err); |
108 | goto err_disable_regulator; |
109 | } |
110 | |
111 | return 0; |
112 | |
113 | err_disable_regulator: |
114 | regulator_disable(regulator: priv->supply); |
115 | return err; |
116 | } |
117 | |
118 | static int ej030na_unprepare(struct drm_panel *panel) |
119 | { |
120 | struct ej030na *priv = to_ej030na(panel); |
121 | |
122 | gpiod_set_value_cansleep(desc: priv->reset_gpio, value: 1); |
123 | regulator_disable(regulator: priv->supply); |
124 | |
125 | return 0; |
126 | } |
127 | |
128 | static int ej030na_enable(struct drm_panel *panel) |
129 | { |
130 | struct ej030na *priv = to_ej030na(panel); |
131 | |
132 | /* standby off */ |
133 | regmap_write(map: priv->map, reg: 0x2b, val: 0x01); |
134 | |
135 | if (panel->backlight) { |
136 | /* Wait for the picture to be ready before enabling backlight */ |
137 | msleep(msecs: 120); |
138 | } |
139 | |
140 | return 0; |
141 | } |
142 | |
143 | static int ej030na_disable(struct drm_panel *panel) |
144 | { |
145 | struct ej030na *priv = to_ej030na(panel); |
146 | |
147 | /* standby on */ |
148 | regmap_write(map: priv->map, reg: 0x2b, val: 0x00); |
149 | |
150 | return 0; |
151 | } |
152 | |
153 | static int ej030na_get_modes(struct drm_panel *panel, |
154 | struct drm_connector *connector) |
155 | { |
156 | struct ej030na *priv = to_ej030na(panel); |
157 | const struct ej030na_info *panel_info = priv->panel_info; |
158 | struct drm_display_mode *mode; |
159 | unsigned int i; |
160 | |
161 | for (i = 0; i < panel_info->num_modes; i++) { |
162 | mode = drm_mode_duplicate(dev: connector->dev, |
163 | mode: &panel_info->display_modes[i]); |
164 | if (!mode) |
165 | return -ENOMEM; |
166 | |
167 | drm_mode_set_name(mode); |
168 | |
169 | mode->type = DRM_MODE_TYPE_DRIVER; |
170 | if (panel_info->num_modes == 1) |
171 | mode->type |= DRM_MODE_TYPE_PREFERRED; |
172 | |
173 | drm_mode_probed_add(connector, mode); |
174 | } |
175 | |
176 | connector->display_info.bpc = 8; |
177 | connector->display_info.width_mm = panel_info->width_mm; |
178 | connector->display_info.height_mm = panel_info->height_mm; |
179 | |
180 | drm_display_info_set_bus_formats(info: &connector->display_info, |
181 | formats: &panel_info->bus_format, num_formats: 1); |
182 | connector->display_info.bus_flags = panel_info->bus_flags; |
183 | |
184 | return panel_info->num_modes; |
185 | } |
186 | |
187 | static const struct drm_panel_funcs ej030na_funcs = { |
188 | .prepare = ej030na_prepare, |
189 | .unprepare = ej030na_unprepare, |
190 | .enable = ej030na_enable, |
191 | .disable = ej030na_disable, |
192 | .get_modes = ej030na_get_modes, |
193 | }; |
194 | |
195 | static const struct regmap_config ej030na_regmap_config = { |
196 | .reg_bits = 8, |
197 | .val_bits = 8, |
198 | .max_register = 0x5a, |
199 | }; |
200 | |
201 | static int ej030na_probe(struct spi_device *spi) |
202 | { |
203 | struct device *dev = &spi->dev; |
204 | struct ej030na *priv; |
205 | int err; |
206 | |
207 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
208 | if (!priv) |
209 | return -ENOMEM; |
210 | |
211 | priv->spi = spi; |
212 | spi_set_drvdata(spi, data: priv); |
213 | |
214 | priv->map = devm_regmap_init_spi(spi, &ej030na_regmap_config); |
215 | if (IS_ERR(ptr: priv->map)) { |
216 | dev_err(dev, "Unable to init regmap\n" ); |
217 | return PTR_ERR(ptr: priv->map); |
218 | } |
219 | |
220 | priv->panel_info = of_device_get_match_data(dev); |
221 | if (!priv->panel_info) |
222 | return -EINVAL; |
223 | |
224 | priv->supply = devm_regulator_get(dev, id: "power" ); |
225 | if (IS_ERR(ptr: priv->supply)) |
226 | return dev_err_probe(dev, err: PTR_ERR(ptr: priv->supply), |
227 | fmt: "Failed to get power supply\n" ); |
228 | |
229 | priv->reset_gpio = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
230 | if (IS_ERR(ptr: priv->reset_gpio)) |
231 | return dev_err_probe(dev, err: PTR_ERR(ptr: priv->reset_gpio), |
232 | fmt: "Failed to get reset GPIO\n" ); |
233 | |
234 | drm_panel_init(panel: &priv->panel, dev, funcs: &ej030na_funcs, |
235 | DRM_MODE_CONNECTOR_DPI); |
236 | |
237 | err = drm_panel_of_backlight(panel: &priv->panel); |
238 | if (err) |
239 | return err; |
240 | |
241 | drm_panel_add(panel: &priv->panel); |
242 | |
243 | return 0; |
244 | } |
245 | |
246 | static void ej030na_remove(struct spi_device *spi) |
247 | { |
248 | struct ej030na *priv = spi_get_drvdata(spi); |
249 | |
250 | drm_panel_remove(panel: &priv->panel); |
251 | drm_panel_disable(panel: &priv->panel); |
252 | drm_panel_unprepare(panel: &priv->panel); |
253 | } |
254 | |
255 | static const struct drm_display_mode ej030na_modes[] = { |
256 | { /* 60 Hz */ |
257 | .clock = 14400, |
258 | .hdisplay = 320, |
259 | .hsync_start = 320 + 10, |
260 | .hsync_end = 320 + 10 + 37, |
261 | .htotal = 320 + 10 + 37 + 33, |
262 | .vdisplay = 480, |
263 | .vsync_start = 480 + 102, |
264 | .vsync_end = 480 + 102 + 9 + 9, |
265 | .vtotal = 480 + 102 + 9 + 9, |
266 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
267 | }, |
268 | { /* 50 Hz */ |
269 | .clock = 12000, |
270 | .hdisplay = 320, |
271 | .hsync_start = 320 + 10, |
272 | .hsync_end = 320 + 10 + 37, |
273 | .htotal = 320 + 10 + 37 + 33, |
274 | .vdisplay = 480, |
275 | .vsync_start = 480 + 102, |
276 | .vsync_end = 480 + 102 + 9, |
277 | .vtotal = 480 + 102 + 9 + 9, |
278 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
279 | }, |
280 | }; |
281 | |
282 | static const struct ej030na_info ej030na_info = { |
283 | .display_modes = ej030na_modes, |
284 | .num_modes = ARRAY_SIZE(ej030na_modes), |
285 | .width_mm = 70, |
286 | .height_mm = 51, |
287 | .bus_format = MEDIA_BUS_FMT_RGB888_3X8_DELTA, |
288 | .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE | DRM_BUS_FLAG_DE_LOW, |
289 | }; |
290 | |
291 | static const struct of_device_id ej030na_of_match[] = { |
292 | { .compatible = "innolux,ej030na" , .data = &ej030na_info }, |
293 | { /* sentinel */ } |
294 | }; |
295 | MODULE_DEVICE_TABLE(of, ej030na_of_match); |
296 | |
297 | static struct spi_driver ej030na_driver = { |
298 | .driver = { |
299 | .name = "panel-innolux-ej030na" , |
300 | .of_match_table = ej030na_of_match, |
301 | }, |
302 | .probe = ej030na_probe, |
303 | .remove = ej030na_remove, |
304 | }; |
305 | module_spi_driver(ej030na_driver); |
306 | |
307 | MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>" ); |
308 | MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>" ); |
309 | MODULE_LICENSE("GPL v2" ); |
310 | |