diff options
author | Noralf Trønnes <noralf@tronnes.org> | 2017-01-22 09:23:48 -0500 |
---|---|---|
committer | Noralf Trønnes <noralf@tronnes.org> | 2017-02-18 12:05:02 -0500 |
commit | 1f47e6cbf58d10392a2158b0e33ef72ad681e40f (patch) | |
tree | 56a12cb1d0c8950a4bb1995286d627294d2d9588 | |
parent | 6b4e48b5a99c7fdc6886c4296798922f2545b7cb (diff) |
drm/tinydrm: Add support for Multi-Inno MI0283QT display
Add driver to support the Multi-Inno MI0283QT display panel.
It has an ILI9341 MIPI DBI compatible display controller.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Acked-by: Thierry Reding <treding@nvidia.com>
-rw-r--r-- | MAINTAINERS | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/tinydrm/Kconfig | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/tinydrm/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/tinydrm/mi0283qt.c | 279 | ||||
-rw-r--r-- | include/drm/tinydrm/ili9341.h | 54 |
5 files changed, 350 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index adfc60d9c456..24a4531c8875 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -4245,6 +4245,12 @@ S: Supported | |||
4245 | F: drivers/gpu/drm/mediatek/ | 4245 | F: drivers/gpu/drm/mediatek/ |
4246 | F: Documentation/devicetree/bindings/display/mediatek/ | 4246 | F: Documentation/devicetree/bindings/display/mediatek/ |
4247 | 4247 | ||
4248 | DRM DRIVER FOR MI0283QT | ||
4249 | M: Noralf Trønnes <noralf@tronnes.org> | ||
4250 | S: Maintained | ||
4251 | F: drivers/gpu/drm/tinydrm/mi0283qt.c | ||
4252 | F: Documentation/devicetree/bindings/display/multi-inno,mi0283qt.txt | ||
4253 | |||
4248 | DRM DRIVER FOR MSM ADRENO GPU | 4254 | DRM DRIVER FOR MSM ADRENO GPU |
4249 | M: Rob Clark <robdclark@gmail.com> | 4255 | M: Rob Clark <robdclark@gmail.com> |
4250 | L: linux-arm-msm@vger.kernel.org | 4256 | L: linux-arm-msm@vger.kernel.org |
diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig index e00bcfca3088..3504c53846da 100644 --- a/drivers/gpu/drm/tinydrm/Kconfig +++ b/drivers/gpu/drm/tinydrm/Kconfig | |||
@@ -11,3 +11,11 @@ menuconfig DRM_TINYDRM | |||
11 | 11 | ||
12 | config TINYDRM_MIPI_DBI | 12 | config TINYDRM_MIPI_DBI |
13 | tristate | 13 | tristate |
14 | |||
15 | config TINYDRM_MI0283QT | ||
16 | tristate "DRM support for MI0283QT" | ||
17 | depends on DRM_TINYDRM && SPI | ||
18 | select TINYDRM_MIPI_DBI | ||
19 | help | ||
20 | DRM driver for the Multi-Inno MI0283QT display panel | ||
21 | If M is selected the module will be called mi0283qt. | ||
diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile index fe5d4c619e77..7a3604cf4fc2 100644 --- a/drivers/gpu/drm/tinydrm/Makefile +++ b/drivers/gpu/drm/tinydrm/Makefile | |||
@@ -2,3 +2,6 @@ obj-$(CONFIG_DRM_TINYDRM) += core/ | |||
2 | 2 | ||
3 | # Controllers | 3 | # Controllers |
4 | obj-$(CONFIG_TINYDRM_MIPI_DBI) += mipi-dbi.o | 4 | obj-$(CONFIG_TINYDRM_MIPI_DBI) += mipi-dbi.o |
5 | |||
6 | # Displays | ||
7 | obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o | ||
diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c new file mode 100644 index 000000000000..b29fe86158f7 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c | |||
@@ -0,0 +1,279 @@ | |||
1 | /* | ||
2 | * DRM driver for Multi-Inno MI0283QT panels | ||
3 | * | ||
4 | * Copyright 2016 Noralf Trønnes | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <drm/tinydrm/ili9341.h> | ||
13 | #include <drm/tinydrm/mipi-dbi.h> | ||
14 | #include <drm/tinydrm/tinydrm-helpers.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/gpio/consumer.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/property.h> | ||
19 | #include <linux/regulator/consumer.h> | ||
20 | #include <linux/spi/spi.h> | ||
21 | #include <video/mipi_display.h> | ||
22 | |||
23 | static int mi0283qt_init(struct mipi_dbi *mipi) | ||
24 | { | ||
25 | struct tinydrm_device *tdev = &mipi->tinydrm; | ||
26 | struct device *dev = tdev->drm->dev; | ||
27 | u8 addr_mode; | ||
28 | int ret; | ||
29 | |||
30 | DRM_DEBUG_KMS("\n"); | ||
31 | |||
32 | ret = regulator_enable(mipi->regulator); | ||
33 | if (ret) { | ||
34 | dev_err(dev, "Failed to enable regulator %d\n", ret); | ||
35 | return ret; | ||
36 | } | ||
37 | |||
38 | /* Avoid flicker by skipping setup if the bootloader has done it */ | ||
39 | if (mipi_dbi_display_is_on(mipi)) | ||
40 | return 0; | ||
41 | |||
42 | mipi_dbi_hw_reset(mipi); | ||
43 | ret = mipi_dbi_command(mipi, MIPI_DCS_SOFT_RESET); | ||
44 | if (ret) { | ||
45 | dev_err(dev, "Error sending command %d\n", ret); | ||
46 | regulator_disable(mipi->regulator); | ||
47 | return ret; | ||
48 | } | ||
49 | |||
50 | msleep(20); | ||
51 | |||
52 | mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_OFF); | ||
53 | |||
54 | mipi_dbi_command(mipi, ILI9341_PWCTRLB, 0x00, 0x83, 0x30); | ||
55 | mipi_dbi_command(mipi, ILI9341_PWRSEQ, 0x64, 0x03, 0x12, 0x81); | ||
56 | mipi_dbi_command(mipi, ILI9341_DTCTRLA, 0x85, 0x01, 0x79); | ||
57 | mipi_dbi_command(mipi, ILI9341_PWCTRLA, 0x39, 0x2c, 0x00, 0x34, 0x02); | ||
58 | mipi_dbi_command(mipi, ILI9341_PUMPCTRL, 0x20); | ||
59 | mipi_dbi_command(mipi, ILI9341_DTCTRLB, 0x00, 0x00); | ||
60 | |||
61 | /* Power Control */ | ||
62 | mipi_dbi_command(mipi, ILI9341_PWCTRL1, 0x26); | ||
63 | mipi_dbi_command(mipi, ILI9341_PWCTRL2, 0x11); | ||
64 | /* VCOM */ | ||
65 | mipi_dbi_command(mipi, ILI9341_VMCTRL1, 0x35, 0x3e); | ||
66 | mipi_dbi_command(mipi, ILI9341_VMCTRL2, 0xbe); | ||
67 | |||
68 | /* Memory Access Control */ | ||
69 | mipi_dbi_command(mipi, MIPI_DCS_SET_PIXEL_FORMAT, 0x55); | ||
70 | |||
71 | switch (mipi->rotation) { | ||
72 | default: | ||
73 | addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY | | ||
74 | ILI9341_MADCTL_MX; | ||
75 | break; | ||
76 | case 90: | ||
77 | addr_mode = ILI9341_MADCTL_MY; | ||
78 | break; | ||
79 | case 180: | ||
80 | addr_mode = ILI9341_MADCTL_MV; | ||
81 | break; | ||
82 | case 270: | ||
83 | addr_mode = ILI9341_MADCTL_MX; | ||
84 | break; | ||
85 | } | ||
86 | addr_mode |= ILI9341_MADCTL_BGR; | ||
87 | mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); | ||
88 | |||
89 | /* Frame Rate */ | ||
90 | mipi_dbi_command(mipi, ILI9341_FRMCTR1, 0x00, 0x1b); | ||
91 | |||
92 | /* Gamma */ | ||
93 | mipi_dbi_command(mipi, ILI9341_EN3GAM, 0x08); | ||
94 | mipi_dbi_command(mipi, MIPI_DCS_SET_GAMMA_CURVE, 0x01); | ||
95 | mipi_dbi_command(mipi, ILI9341_PGAMCTRL, | ||
96 | 0x1f, 0x1a, 0x18, 0x0a, 0x0f, 0x06, 0x45, 0x87, | ||
97 | 0x32, 0x0a, 0x07, 0x02, 0x07, 0x05, 0x00); | ||
98 | mipi_dbi_command(mipi, ILI9341_NGAMCTRL, | ||
99 | 0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3a, 0x78, | ||
100 | 0x4d, 0x05, 0x18, 0x0d, 0x38, 0x3a, 0x1f); | ||
101 | |||
102 | /* DDRAM */ | ||
103 | mipi_dbi_command(mipi, ILI9341_ETMOD, 0x07); | ||
104 | |||
105 | /* Display */ | ||
106 | mipi_dbi_command(mipi, ILI9341_DISCTRL, 0x0a, 0x82, 0x27, 0x00); | ||
107 | mipi_dbi_command(mipi, MIPI_DCS_EXIT_SLEEP_MODE); | ||
108 | msleep(100); | ||
109 | |||
110 | mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON); | ||
111 | msleep(100); | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static void mi0283qt_fini(void *data) | ||
117 | { | ||
118 | struct mipi_dbi *mipi = data; | ||
119 | |||
120 | DRM_DEBUG_KMS("\n"); | ||
121 | regulator_disable(mipi->regulator); | ||
122 | } | ||
123 | |||
124 | static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = { | ||
125 | .enable = mipi_dbi_pipe_enable, | ||
126 | .disable = mipi_dbi_pipe_disable, | ||
127 | .update = tinydrm_display_pipe_update, | ||
128 | .prepare_fb = tinydrm_display_pipe_prepare_fb, | ||
129 | }; | ||
130 | |||
131 | static const struct drm_display_mode mi0283qt_mode = { | ||
132 | TINYDRM_MODE(320, 240, 58, 43), | ||
133 | }; | ||
134 | |||
135 | static struct drm_driver mi0283qt_driver = { | ||
136 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | | ||
137 | DRIVER_ATOMIC, | ||
138 | TINYDRM_GEM_DRIVER_OPS, | ||
139 | .lastclose = tinydrm_lastclose, | ||
140 | .debugfs_init = mipi_dbi_debugfs_init, | ||
141 | .name = "mi0283qt", | ||
142 | .desc = "Multi-Inno MI0283QT", | ||
143 | .date = "20160614", | ||
144 | .major = 1, | ||
145 | .minor = 0, | ||
146 | }; | ||
147 | |||
148 | static const struct of_device_id mi0283qt_of_match[] = { | ||
149 | { .compatible = "multi-inno,mi0283qt" }, | ||
150 | {}, | ||
151 | }; | ||
152 | MODULE_DEVICE_TABLE(of, mi0283qt_of_match); | ||
153 | |||
154 | static const struct spi_device_id mi0283qt_id[] = { | ||
155 | { "mi0283qt", 0 }, | ||
156 | { }, | ||
157 | }; | ||
158 | MODULE_DEVICE_TABLE(spi, mi0283qt_id); | ||
159 | |||
160 | static int mi0283qt_probe(struct spi_device *spi) | ||
161 | { | ||
162 | struct device *dev = &spi->dev; | ||
163 | struct tinydrm_device *tdev; | ||
164 | struct mipi_dbi *mipi; | ||
165 | struct gpio_desc *dc; | ||
166 | u32 rotation = 0; | ||
167 | int ret; | ||
168 | |||
169 | mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL); | ||
170 | if (!mipi) | ||
171 | return -ENOMEM; | ||
172 | |||
173 | mipi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); | ||
174 | if (IS_ERR(mipi->reset)) { | ||
175 | dev_err(dev, "Failed to get gpio 'reset'\n"); | ||
176 | return PTR_ERR(mipi->reset); | ||
177 | } | ||
178 | |||
179 | dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); | ||
180 | if (IS_ERR(dc)) { | ||
181 | dev_err(dev, "Failed to get gpio 'dc'\n"); | ||
182 | return PTR_ERR(dc); | ||
183 | } | ||
184 | |||
185 | mipi->regulator = devm_regulator_get(dev, "power"); | ||
186 | if (IS_ERR(mipi->regulator)) | ||
187 | return PTR_ERR(mipi->regulator); | ||
188 | |||
189 | mipi->backlight = tinydrm_of_find_backlight(dev); | ||
190 | if (IS_ERR(mipi->backlight)) | ||
191 | return PTR_ERR(mipi->backlight); | ||
192 | |||
193 | device_property_read_u32(dev, "rotation", &rotation); | ||
194 | |||
195 | ret = mipi_dbi_spi_init(spi, mipi, dc, &mi0283qt_pipe_funcs, | ||
196 | &mi0283qt_driver, &mi0283qt_mode, rotation); | ||
197 | if (ret) | ||
198 | return ret; | ||
199 | |||
200 | ret = mi0283qt_init(mipi); | ||
201 | if (ret) | ||
202 | return ret; | ||
203 | |||
204 | /* use devres to fini after drm unregister (drv->remove is before) */ | ||
205 | ret = devm_add_action(dev, mi0283qt_fini, mipi); | ||
206 | if (ret) { | ||
207 | mi0283qt_fini(mipi); | ||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | tdev = &mipi->tinydrm; | ||
212 | |||
213 | ret = devm_tinydrm_register(tdev); | ||
214 | if (ret) | ||
215 | return ret; | ||
216 | |||
217 | spi_set_drvdata(spi, mipi); | ||
218 | |||
219 | DRM_DEBUG_DRIVER("Initialized %s:%s @%uMHz on minor %d\n", | ||
220 | tdev->drm->driver->name, dev_name(dev), | ||
221 | spi->max_speed_hz / 1000000, | ||
222 | tdev->drm->primary->index); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static void mi0283qt_shutdown(struct spi_device *spi) | ||
228 | { | ||
229 | struct mipi_dbi *mipi = spi_get_drvdata(spi); | ||
230 | |||
231 | tinydrm_shutdown(&mipi->tinydrm); | ||
232 | } | ||
233 | |||
234 | static int __maybe_unused mi0283qt_pm_suspend(struct device *dev) | ||
235 | { | ||
236 | struct mipi_dbi *mipi = dev_get_drvdata(dev); | ||
237 | int ret; | ||
238 | |||
239 | ret = tinydrm_suspend(&mipi->tinydrm); | ||
240 | if (ret) | ||
241 | return ret; | ||
242 | |||
243 | mi0283qt_fini(mipi); | ||
244 | |||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | static int __maybe_unused mi0283qt_pm_resume(struct device *dev) | ||
249 | { | ||
250 | struct mipi_dbi *mipi = dev_get_drvdata(dev); | ||
251 | int ret; | ||
252 | |||
253 | ret = mi0283qt_init(mipi); | ||
254 | if (ret) | ||
255 | return ret; | ||
256 | |||
257 | return tinydrm_resume(&mipi->tinydrm); | ||
258 | } | ||
259 | |||
260 | static const struct dev_pm_ops mi0283qt_pm_ops = { | ||
261 | SET_SYSTEM_SLEEP_PM_OPS(mi0283qt_pm_suspend, mi0283qt_pm_resume) | ||
262 | }; | ||
263 | |||
264 | static struct spi_driver mi0283qt_spi_driver = { | ||
265 | .driver = { | ||
266 | .name = "mi0283qt", | ||
267 | .owner = THIS_MODULE, | ||
268 | .of_match_table = mi0283qt_of_match, | ||
269 | .pm = &mi0283qt_pm_ops, | ||
270 | }, | ||
271 | .id_table = mi0283qt_id, | ||
272 | .probe = mi0283qt_probe, | ||
273 | .shutdown = mi0283qt_shutdown, | ||
274 | }; | ||
275 | module_spi_driver(mi0283qt_spi_driver); | ||
276 | |||
277 | MODULE_DESCRIPTION("Multi-Inno MI0283QT DRM driver"); | ||
278 | MODULE_AUTHOR("Noralf Trønnes"); | ||
279 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/drm/tinydrm/ili9341.h b/include/drm/tinydrm/ili9341.h new file mode 100644 index 000000000000..807a09f43cad --- /dev/null +++ b/include/drm/tinydrm/ili9341.h | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * ILI9341 LCD controller | ||
3 | * | ||
4 | * Copyright 2016 Noralf Trønnes | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #ifndef __LINUX_ILI9341_H | ||
13 | #define __LINUX_ILI9341_H | ||
14 | |||
15 | #define ILI9341_FRMCTR1 0xb1 | ||
16 | #define ILI9341_FRMCTR2 0xb2 | ||
17 | #define ILI9341_FRMCTR3 0xb3 | ||
18 | #define ILI9341_INVTR 0xb4 | ||
19 | #define ILI9341_PRCTR 0xb5 | ||
20 | #define ILI9341_DISCTRL 0xb6 | ||
21 | #define ILI9341_ETMOD 0xb7 | ||
22 | |||
23 | #define ILI9341_PWCTRL1 0xc0 | ||
24 | #define ILI9341_PWCTRL2 0xc1 | ||
25 | #define ILI9341_VMCTRL1 0xc5 | ||
26 | #define ILI9341_VMCTRL2 0xc7 | ||
27 | #define ILI9341_PWCTRLA 0xcb | ||
28 | #define ILI9341_PWCTRLB 0xcf | ||
29 | |||
30 | #define ILI9341_RDID1 0xda | ||
31 | #define ILI9341_RDID2 0xdb | ||
32 | #define ILI9341_RDID3 0xdc | ||
33 | #define ILI9341_RDID4 0xd3 | ||
34 | |||
35 | #define ILI9341_PGAMCTRL 0xe0 | ||
36 | #define ILI9341_NGAMCTRL 0xe1 | ||
37 | #define ILI9341_DGAMCTRL1 0xe2 | ||
38 | #define ILI9341_DGAMCTRL2 0xe3 | ||
39 | #define ILI9341_DTCTRLA 0xe8 | ||
40 | #define ILI9341_DTCTRLB 0xea | ||
41 | #define ILI9341_PWRSEQ 0xed | ||
42 | |||
43 | #define ILI9341_EN3GAM 0xf2 | ||
44 | #define ILI9341_IFCTRL 0xf6 | ||
45 | #define ILI9341_PUMPCTRL 0xf7 | ||
46 | |||
47 | #define ILI9341_MADCTL_MH BIT(2) | ||
48 | #define ILI9341_MADCTL_BGR BIT(3) | ||
49 | #define ILI9341_MADCTL_ML BIT(4) | ||
50 | #define ILI9341_MADCTL_MV BIT(5) | ||
51 | #define ILI9341_MADCTL_MX BIT(6) | ||
52 | #define ILI9341_MADCTL_MY BIT(7) | ||
53 | |||
54 | #endif /* __LINUX_ILI9341_H */ | ||