projects/linux·blob master · refs

365 lines · 9.5 KiB

1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * DRM driver for Ilitek ILI9341 panels
4 *
5 * Copyright 2018 David Lechner <[email protected]>
6 *
7 * Based on mi0283qt.c:
8 * Copyright 2016 Noralf Trønnes
9 */
10
11#include <linux/backlight.h>
12#include <linux/delay.h>
13#include <linux/gpio/consumer.h>
14#include <linux/module.h>
15#include <linux/property.h>
16#include <linux/spi/spi.h>
17
18#include <drm/clients/drm_client_setup.h>
19#include <drm/drm_atomic_helper.h>
20#include <drm/drm_drv.h>
21#include <drm/drm_fbdev_dma.h>
22#include <drm/drm_gem_atomic_helper.h>
23#include <drm/drm_gem_dma_helper.h>
24#include <drm/drm_managed.h>
25#include <drm/drm_mipi_dbi.h>
26#include <drm/drm_modeset_helper.h>
27#include <drm/drm_print.h>
28#include <video/mipi_display.h>
29
30#define ILI9341_FRMCTR1 0xb1
31#define ILI9341_DISCTRL 0xb6
32#define ILI9341_ETMOD 0xb7
33
34#define ILI9341_PWCTRL1 0xc0
35#define ILI9341_PWCTRL2 0xc1
36#define ILI9341_VMCTRL1 0xc5
37#define ILI9341_VMCTRL2 0xc7
38#define ILI9341_PWCTRLA 0xcb
39#define ILI9341_PWCTRLB 0xcf
40
41#define ILI9341_PGAMCTRL 0xe0
42#define ILI9341_NGAMCTRL 0xe1
43#define ILI9341_DTCTRLA 0xe8
44#define ILI9341_DTCTRLB 0xea
45#define ILI9341_PWRSEQ 0xed
46
47#define ILI9341_EN3GAM 0xf2
48#define ILI9341_PUMPCTRL 0xf7
49
50#define ILI9341_MADCTL_BGR BIT(3)
51#define ILI9341_MADCTL_MV BIT(5)
52#define ILI9341_MADCTL_MX BIT(6)
53#define ILI9341_MADCTL_MY BIT(7)
54
55struct ili9341_device {
56 struct mipi_dbi_dev dbidev;
57
58 struct drm_plane plane;
59 struct drm_crtc crtc;
60 struct drm_encoder encoder;
61 struct drm_connector connector;
62};
63
64static struct ili9341_device *to_ili9341_device(struct drm_device *dev)
65{
66 return container_of(drm_to_mipi_dbi_dev(dev), struct ili9341_device, dbidev);
67}
68
69static const u32 ili9341_plane_formats[] = {
70 DRM_MIPI_DBI_PLANE_FORMATS,
71};
72
73static const u64 ili9341_plane_format_modifiers[] = {
74 DRM_MIPI_DBI_PLANE_FORMAT_MODIFIERS,
75};
76
77static const struct drm_plane_helper_funcs ili9341_plane_helper_funcs = {
78 DRM_MIPI_DBI_PLANE_HELPER_FUNCS,
79};
80
81static const struct drm_plane_funcs ili9341_plane_funcs = {
82 DRM_MIPI_DBI_PLANE_FUNCS,
83 .destroy = drm_plane_cleanup,
84};
85
86static void ili9341_crtc_helper_atomic_enable(struct drm_crtc *crtc,
87 struct drm_atomic_commit *state)
88{
89 struct drm_device *drm = crtc->dev;
90 struct ili9341_device *ili9341 = to_ili9341_device(drm);
91 struct mipi_dbi_dev *dbidev = &ili9341->dbidev;
92 struct mipi_dbi *dbi = &dbidev->dbi;
93 u8 addr_mode;
94 int ret, idx;
95
96 if (!drm_dev_enter(drm, &idx))
97 return;
98
99 DRM_DEBUG_KMS("\n");
100
101 ret = mipi_dbi_poweron_conditional_reset(dbidev);
102 if (ret < 0)
103 goto out_exit;
104 if (ret == 1)
105 goto out_enable;
106
107 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
108
109 mipi_dbi_command(dbi, ILI9341_PWCTRLB, 0x00, 0xc1, 0x30);
110 mipi_dbi_command(dbi, ILI9341_PWRSEQ, 0x64, 0x03, 0x12, 0x81);
111 mipi_dbi_command(dbi, ILI9341_DTCTRLA, 0x85, 0x00, 0x78);
112 mipi_dbi_command(dbi, ILI9341_PWCTRLA, 0x39, 0x2c, 0x00, 0x34, 0x02);
113 mipi_dbi_command(dbi, ILI9341_PUMPCTRL, 0x20);
114 mipi_dbi_command(dbi, ILI9341_DTCTRLB, 0x00, 0x00);
115
116 /* Power Control */
117 mipi_dbi_command(dbi, ILI9341_PWCTRL1, 0x23);
118 mipi_dbi_command(dbi, ILI9341_PWCTRL2, 0x10);
119 /* VCOM */
120 mipi_dbi_command(dbi, ILI9341_VMCTRL1, 0x3e, 0x28);
121 mipi_dbi_command(dbi, ILI9341_VMCTRL2, 0x86);
122
123 /* Memory Access Control */
124 mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);
125
126 /* Frame Rate */
127 mipi_dbi_command(dbi, ILI9341_FRMCTR1, 0x00, 0x1b);
128
129 /* Gamma */
130 mipi_dbi_command(dbi, ILI9341_EN3GAM, 0x00);
131 mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01);
132 mipi_dbi_command(dbi, ILI9341_PGAMCTRL,
133 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1,
134 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00);
135 mipi_dbi_command(dbi, ILI9341_NGAMCTRL,
136 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1,
137 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f);
138
139 /* DDRAM */
140 mipi_dbi_command(dbi, ILI9341_ETMOD, 0x07);
141
142 /* Display */
143 mipi_dbi_command(dbi, ILI9341_DISCTRL, 0x08, 0x82, 0x27, 0x00);
144 mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
145 msleep(100);
146
147 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
148 msleep(100);
149
150out_enable:
151 switch (dbidev->rotation) {
152 default:
153 addr_mode = ILI9341_MADCTL_MX;
154 break;
155 case 90:
156 addr_mode = ILI9341_MADCTL_MV;
157 break;
158 case 180:
159 addr_mode = ILI9341_MADCTL_MY;
160 break;
161 case 270:
162 addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY |
163 ILI9341_MADCTL_MX;
164 break;
165 }
166 addr_mode |= ILI9341_MADCTL_BGR;
167 mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
168
169 backlight_enable(dbidev->backlight);
170out_exit:
171 drm_dev_exit(idx);
172}
173
174static const struct drm_crtc_helper_funcs ili9341_crtc_helper_funcs = {
175 DRM_MIPI_DBI_CRTC_HELPER_FUNCS,
176 .atomic_enable = ili9341_crtc_helper_atomic_enable,
177};
178
179static const struct drm_crtc_funcs ili9341_crtc_funcs = {
180 DRM_MIPI_DBI_CRTC_FUNCS,
181 .destroy = drm_crtc_cleanup,
182};
183
184static const struct drm_encoder_funcs ili9341_encoder_funcs = {
185 .destroy = drm_encoder_cleanup,
186};
187
188static const struct drm_connector_helper_funcs ili9341_connector_helper_funcs = {
189 DRM_MIPI_DBI_CONNECTOR_HELPER_FUNCS,
190};
191
192static const struct drm_connector_funcs ili9341_connector_funcs = {
193 DRM_MIPI_DBI_CONNECTOR_FUNCS,
194 .destroy = drm_connector_cleanup,
195};
196
197static const struct drm_mode_config_helper_funcs ili9341_mode_config_helper_funcs = {
198 DRM_MIPI_DBI_MODE_CONFIG_HELPER_FUNCS,
199};
200
201static const struct drm_mode_config_funcs ili9341_mode_config_funcs = {
202 DRM_MIPI_DBI_MODE_CONFIG_FUNCS,
203};
204
205static const struct drm_display_mode yx240qv29_mode = {
206 DRM_SIMPLE_MODE(240, 320, 37, 49),
207};
208
209DEFINE_DRM_GEM_DMA_FOPS(ili9341_fops);
210
211static const struct drm_driver ili9341_driver = {
212 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
213 .fops = &ili9341_fops,
214 DRM_GEM_DMA_DRIVER_OPS_VMAP,
215 DRM_FBDEV_DMA_DRIVER_OPS,
216 .debugfs_init = mipi_dbi_debugfs_init,
217 .name = "ili9341",
218 .desc = "Ilitek ILI9341",
219 .major = 1,
220 .minor = 0,
221};
222
223static const struct of_device_id ili9341_of_match[] = {
224 { .compatible = "adafruit,yx240qv29" },
225 { }
226};
227MODULE_DEVICE_TABLE(of, ili9341_of_match);
228
229static const struct spi_device_id ili9341_id[] = {
230 { "yx240qv29", 0 },
231 { }
232};
233MODULE_DEVICE_TABLE(spi, ili9341_id);
234
235static int ili9341_probe(struct spi_device *spi)
236{
237 struct device *dev = &spi->dev;
238 struct ili9341_device *ili9341;
239 struct mipi_dbi_dev *dbidev;
240 struct drm_device *drm;
241 struct mipi_dbi *dbi;
242 struct gpio_desc *dc;
243 struct drm_plane *plane;
244 struct drm_crtc *crtc;
245 struct drm_encoder *encoder;
246 struct drm_connector *connector;
247 u32 rotation = 0;
248 int ret;
249
250 ili9341 = devm_drm_dev_alloc(dev, &ili9341_driver, struct ili9341_device, dbidev.drm);
251 if (IS_ERR(ili9341))
252 return PTR_ERR(ili9341);
253 dbidev = &ili9341->dbidev;
254 dbi = &dbidev->dbi;
255 drm = &dbidev->drm;
256
257 dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
258 if (IS_ERR(dbi->reset))
259 return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n");
260
261 dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW);
262 if (IS_ERR(dc))
263 return dev_err_probe(dev, PTR_ERR(dc), "Failed to get GPIO 'dc'\n");
264
265 dbidev->backlight = devm_of_find_backlight(dev);
266 if (IS_ERR(dbidev->backlight))
267 return PTR_ERR(dbidev->backlight);
268
269 device_property_read_u32(dev, "rotation", &rotation);
270
271 ret = mipi_dbi_spi_init(spi, dbi, dc);
272 if (ret)
273 return ret;
274
275 ret = drm_mipi_dbi_dev_init(dbidev, &yx240qv29_mode, ili9341_plane_formats[0],
276 rotation, 0);
277 if (ret)
278 return ret;
279
280 ret = drmm_mode_config_init(drm);
281 if (ret)
282 return ret;
283
284 drm->mode_config.min_width = dbidev->mode.hdisplay;
285 drm->mode_config.max_width = dbidev->mode.hdisplay;
286 drm->mode_config.min_height = dbidev->mode.vdisplay;
287 drm->mode_config.max_height = dbidev->mode.vdisplay;
288 drm->mode_config.funcs = &ili9341_mode_config_funcs;
289 drm->mode_config.preferred_depth = 16;
290 drm->mode_config.helper_private = &ili9341_mode_config_helper_funcs;
291
292 plane = &ili9341->plane;
293 ret = drm_universal_plane_init(drm, plane, 0, &ili9341_plane_funcs,
294 ili9341_plane_formats, ARRAY_SIZE(ili9341_plane_formats),
295 ili9341_plane_format_modifiers,
296 DRM_PLANE_TYPE_PRIMARY, NULL);
297 if (ret)
298 return ret;
299 drm_plane_helper_add(plane, &ili9341_plane_helper_funcs);
300 drm_plane_enable_fb_damage_clips(plane);
301
302 crtc = &ili9341->crtc;
303 ret = drm_crtc_init_with_planes(drm, crtc, plane, NULL, &ili9341_crtc_funcs, NULL);
304 if (ret)
305 return ret;
306 drm_crtc_helper_add(crtc, &ili9341_crtc_helper_funcs);
307
308 encoder = &ili9341->encoder;
309 ret = drm_encoder_init(drm, encoder, &ili9341_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
310 if (ret)
311 return ret;
312 encoder->possible_crtcs = drm_crtc_mask(crtc);
313
314 connector = &ili9341->connector;
315 ret = drm_connector_init(drm, connector, &ili9341_connector_funcs,
316 DRM_MODE_CONNECTOR_SPI);
317 if (ret)
318 return ret;
319 drm_connector_helper_add(connector, &ili9341_connector_helper_funcs);
320
321 ret = drm_connector_attach_encoder(connector, encoder);
322 if (ret)
323 return ret;
324
325 drm_mode_config_reset(drm);
326
327 ret = drm_dev_register(drm, 0);
328 if (ret)
329 return ret;
330
331 spi_set_drvdata(spi, drm);
332
333 drm_client_setup(drm, NULL);
334
335 return 0;
336}
337
338static void ili9341_remove(struct spi_device *spi)
339{
340 struct drm_device *drm = spi_get_drvdata(spi);
341
342 drm_dev_unplug(drm);
343 drm_atomic_helper_shutdown(drm);
344}
345
346static void ili9341_shutdown(struct spi_device *spi)
347{
348 drm_atomic_helper_shutdown(spi_get_drvdata(spi));
349}
350
351static struct spi_driver ili9341_spi_driver = {
352 .driver = {
353 .name = "ili9341",
354 .of_match_table = ili9341_of_match,
355 },
356 .id_table = ili9341_id,
357 .probe = ili9341_probe,
358 .remove = ili9341_remove,
359 .shutdown = ili9341_shutdown,
360};
361module_spi_driver(ili9341_spi_driver);
362
363MODULE_DESCRIPTION("Ilitek ILI9341 DRM driver");
364MODULE_AUTHOR("David Lechner <[email protected]>");
365MODULE_LICENSE("GPL");