1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * AU Optronics A030JTN01.0 TFT LCD panel driver |
4 | * |
5 | * Copyright (C) 2023, Paul Cercueil <paul@crapouillou.net> |
6 | * Copyright (C) 2023, Christophe Branchereau <cbranchereau@gmail.com> |
7 | */ |
8 | |
9 | #include <linux/bitfield.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/device.h> |
12 | #include <linux/gpio/consumer.h> |
13 | #include <linux/media-bus-format.h> |
14 | #include <linux/mod_devicetable.h> |
15 | #include <linux/module.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 | #define REG05 0x05 |
24 | #define REG06 0x06 |
25 | #define REG07 0x07 |
26 | |
27 | #define REG05_STDBY BIT(0) |
28 | #define REG06_VBLK GENMASK(4, 0) |
29 | #define REG07_HBLK GENMASK(7, 0) |
30 | |
31 | |
32 | struct a030jtn01_info { |
33 | const struct drm_display_mode *display_modes; |
34 | unsigned int num_modes; |
35 | u16 width_mm, height_mm; |
36 | u32 bus_format, bus_flags; |
37 | }; |
38 | |
39 | struct a030jtn01 { |
40 | struct drm_panel panel; |
41 | struct spi_device *spi; |
42 | struct regmap *map; |
43 | |
44 | const struct a030jtn01_info *panel_info; |
45 | |
46 | struct regulator *supply; |
47 | struct gpio_desc *reset_gpio; |
48 | }; |
49 | |
50 | static inline struct a030jtn01 *to_a030jtn01(struct drm_panel *panel) |
51 | { |
52 | return container_of(panel, struct a030jtn01, panel); |
53 | } |
54 | |
55 | static int a030jtn01_prepare(struct drm_panel *panel) |
56 | { |
57 | struct a030jtn01 *priv = to_a030jtn01(panel); |
58 | struct device *dev = &priv->spi->dev; |
59 | unsigned int dummy; |
60 | int err; |
61 | |
62 | err = regulator_enable(regulator: priv->supply); |
63 | if (err) { |
64 | dev_err(dev, "Failed to enable power supply: %d\n" , err); |
65 | return err; |
66 | } |
67 | |
68 | usleep_range(min: 1000, max: 8000); |
69 | |
70 | /* Reset the chip */ |
71 | gpiod_set_value_cansleep(desc: priv->reset_gpio, value: 1); |
72 | usleep_range(min: 100, max: 8000); |
73 | gpiod_set_value_cansleep(desc: priv->reset_gpio, value: 0); |
74 | usleep_range(min: 2000, max: 8000); |
75 | |
76 | /* |
77 | * No idea why, but a register read (doesn't matter which) is needed to |
78 | * properly initialize the chip after a reset; otherwise, the colors |
79 | * will be wrong. It doesn't seem to be timing-related as a msleep(200) |
80 | * doesn't fix it. |
81 | */ |
82 | err = regmap_read(map: priv->map, REG05, val: &dummy); |
83 | if (err) |
84 | goto err_disable_regulator; |
85 | |
86 | /* Use (24 + 6) == 0x1e as the vertical back porch */ |
87 | err = regmap_write(map: priv->map, REG06, FIELD_PREP(REG06_VBLK, 0x1e)); |
88 | if (err) |
89 | goto err_disable_regulator; |
90 | |
91 | /* Use (42 + 30) * 3 == 0xd8 as the horizontal back porch */ |
92 | err = regmap_write(map: priv->map, REG07, FIELD_PREP(REG07_HBLK, 0xd8)); |
93 | if (err) |
94 | goto err_disable_regulator; |
95 | |
96 | return 0; |
97 | |
98 | err_disable_regulator: |
99 | gpiod_set_value_cansleep(desc: priv->reset_gpio, value: 1); |
100 | regulator_disable(regulator: priv->supply); |
101 | return err; |
102 | } |
103 | |
104 | static int a030jtn01_unprepare(struct drm_panel *panel) |
105 | { |
106 | struct a030jtn01 *priv = to_a030jtn01(panel); |
107 | |
108 | gpiod_set_value_cansleep(desc: priv->reset_gpio, value: 1); |
109 | regulator_disable(regulator: priv->supply); |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | static int a030jtn01_enable(struct drm_panel *panel) |
115 | { |
116 | struct a030jtn01 *priv = to_a030jtn01(panel); |
117 | int ret; |
118 | |
119 | ret = regmap_set_bits(map: priv->map, REG05, REG05_STDBY); |
120 | if (ret) |
121 | return ret; |
122 | |
123 | /* Wait for the picture to be stable */ |
124 | if (panel->backlight) |
125 | msleep(msecs: 100); |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | static int a030jtn01_disable(struct drm_panel *panel) |
131 | { |
132 | struct a030jtn01 *priv = to_a030jtn01(panel); |
133 | |
134 | return regmap_clear_bits(map: priv->map, REG05, REG05_STDBY); |
135 | } |
136 | |
137 | static int a030jtn01_get_modes(struct drm_panel *panel, |
138 | struct drm_connector *connector) |
139 | { |
140 | struct a030jtn01 *priv = to_a030jtn01(panel); |
141 | const struct a030jtn01_info *panel_info = priv->panel_info; |
142 | struct drm_display_mode *mode; |
143 | unsigned int i; |
144 | |
145 | for (i = 0; i < panel_info->num_modes; i++) { |
146 | mode = drm_mode_duplicate(dev: connector->dev, |
147 | mode: &panel_info->display_modes[i]); |
148 | if (!mode) |
149 | return -ENOMEM; |
150 | |
151 | drm_mode_set_name(mode); |
152 | |
153 | mode->type = DRM_MODE_TYPE_DRIVER; |
154 | if (panel_info->num_modes == 1) |
155 | mode->type |= DRM_MODE_TYPE_PREFERRED; |
156 | |
157 | drm_mode_probed_add(connector, mode); |
158 | } |
159 | |
160 | connector->display_info.bpc = 8; |
161 | connector->display_info.width_mm = panel_info->width_mm; |
162 | connector->display_info.height_mm = panel_info->height_mm; |
163 | |
164 | drm_display_info_set_bus_formats(info: &connector->display_info, |
165 | formats: &panel_info->bus_format, num_formats: 1); |
166 | connector->display_info.bus_flags = panel_info->bus_flags; |
167 | |
168 | return panel_info->num_modes; |
169 | } |
170 | |
171 | static const struct drm_panel_funcs a030jtn01_funcs = { |
172 | .prepare = a030jtn01_prepare, |
173 | .unprepare = a030jtn01_unprepare, |
174 | .enable = a030jtn01_enable, |
175 | .disable = a030jtn01_disable, |
176 | .get_modes = a030jtn01_get_modes, |
177 | }; |
178 | |
179 | static bool a030jtn01_has_reg(struct device *dev, unsigned int reg) |
180 | { |
181 | static const u32 a030jtn01_regs_mask = 0x001823f1fb; |
182 | |
183 | return a030jtn01_regs_mask & BIT(reg); |
184 | }; |
185 | |
186 | static const struct regmap_config a030jtn01_regmap_config = { |
187 | .reg_bits = 8, |
188 | .val_bits = 8, |
189 | .read_flag_mask = 0x40, |
190 | .max_register = 0x1c, |
191 | .readable_reg = a030jtn01_has_reg, |
192 | .writeable_reg = a030jtn01_has_reg, |
193 | }; |
194 | |
195 | static int a030jtn01_probe(struct spi_device *spi) |
196 | { |
197 | struct device *dev = &spi->dev; |
198 | struct a030jtn01 *priv; |
199 | int err; |
200 | |
201 | spi->mode |= SPI_MODE_3 | SPI_3WIRE; |
202 | |
203 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
204 | if (!priv) |
205 | return -ENOMEM; |
206 | |
207 | priv->spi = spi; |
208 | spi_set_drvdata(spi, data: priv); |
209 | |
210 | priv->map = devm_regmap_init_spi(spi, &a030jtn01_regmap_config); |
211 | if (IS_ERR(ptr: priv->map)) |
212 | return dev_err_probe(dev, err: PTR_ERR(ptr: priv->map), fmt: "Unable to init regmap" ); |
213 | |
214 | priv->panel_info = spi_get_device_match_data(sdev: spi); |
215 | if (!priv->panel_info) |
216 | return -EINVAL; |
217 | |
218 | priv->supply = devm_regulator_get(dev, id: "power" ); |
219 | if (IS_ERR(ptr: priv->supply)) |
220 | return dev_err_probe(dev, err: PTR_ERR(ptr: priv->supply), fmt: "Failed to get power supply" ); |
221 | |
222 | priv->reset_gpio = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
223 | if (IS_ERR(ptr: priv->reset_gpio)) |
224 | return dev_err_probe(dev, err: PTR_ERR(ptr: priv->reset_gpio), fmt: "Failed to get reset GPIO" ); |
225 | |
226 | drm_panel_init(panel: &priv->panel, dev, funcs: &a030jtn01_funcs, |
227 | DRM_MODE_CONNECTOR_DPI); |
228 | |
229 | err = drm_panel_of_backlight(panel: &priv->panel); |
230 | if (err) |
231 | return err; |
232 | |
233 | drm_panel_add(panel: &priv->panel); |
234 | |
235 | return 0; |
236 | } |
237 | |
238 | static void a030jtn01_remove(struct spi_device *spi) |
239 | { |
240 | struct a030jtn01 *priv = spi_get_drvdata(spi); |
241 | |
242 | drm_panel_remove(panel: &priv->panel); |
243 | drm_panel_disable(panel: &priv->panel); |
244 | drm_panel_unprepare(panel: &priv->panel); |
245 | } |
246 | |
247 | static const struct drm_display_mode a030jtn01_modes[] = { |
248 | { /* 60 Hz */ |
249 | .clock = 14400, |
250 | .hdisplay = 320, |
251 | .hsync_start = 320 + 8, |
252 | .hsync_end = 320 + 8 + 42, |
253 | .htotal = 320 + 8 + 42 + 30, |
254 | .vdisplay = 480, |
255 | .vsync_start = 480 + 90, |
256 | .vsync_end = 480 + 90 + 24, |
257 | .vtotal = 480 + 90 + 24 + 6, |
258 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
259 | }, |
260 | { /* 50 Hz */ |
261 | .clock = 12000, |
262 | .hdisplay = 320, |
263 | .hsync_start = 320 + 8, |
264 | .hsync_end = 320 + 8 + 42, |
265 | .htotal = 320 + 8 + 42 + 30, |
266 | .vdisplay = 480, |
267 | .vsync_start = 480 + 90, |
268 | .vsync_end = 480 + 90 + 24, |
269 | .vtotal = 480 + 90 + 24 + 6, |
270 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
271 | }, |
272 | }; |
273 | |
274 | static const struct a030jtn01_info a030jtn01_info = { |
275 | .display_modes = a030jtn01_modes, |
276 | .num_modes = ARRAY_SIZE(a030jtn01_modes), |
277 | .width_mm = 70, |
278 | .height_mm = 51, |
279 | .bus_format = MEDIA_BUS_FMT_RGB888_3X8_DELTA, |
280 | .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, |
281 | }; |
282 | |
283 | static const struct spi_device_id a030jtn01_id[] = { |
284 | { "a030jtn01" , (kernel_ulong_t) &a030jtn01_info }, |
285 | { /* sentinel */ } |
286 | }; |
287 | MODULE_DEVICE_TABLE(spi, a030jtn01_id); |
288 | |
289 | static const struct of_device_id a030jtn01_of_match[] = { |
290 | { .compatible = "auo,a030jtn01" }, |
291 | { /* sentinel */ } |
292 | }; |
293 | MODULE_DEVICE_TABLE(of, a030jtn01_of_match); |
294 | |
295 | static struct spi_driver a030jtn01_driver = { |
296 | .driver = { |
297 | .name = "auo-a030jtn01" , |
298 | .of_match_table = a030jtn01_of_match, |
299 | }, |
300 | .id_table = a030jtn01_id, |
301 | .probe = a030jtn01_probe, |
302 | .remove = a030jtn01_remove, |
303 | }; |
304 | module_spi_driver(a030jtn01_driver); |
305 | |
306 | MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>" ); |
307 | MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>" ); |
308 | MODULE_LICENSE("GPL" ); |
309 | |