1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree.
4 * Copyright (c) 2024 Luca Weiss <luca.weiss@fairphone.com>
5 */
6
7 #include <linux/delay.h>
8 #include <linux/gpio/consumer.h>
9 #include <linux/module.h>
10 #include <linux/of.h>
11 #include <linux/regulator/consumer.h>
12 #include <linux/types.h>
13
14 #include <drm/drm_mipi_dsi.h>
15 #include <drm/drm_modes.h>
16 #include <drm/drm_panel.h>
17 #include <drm/drm_probe_helper.h>
18
19 /* Manufacturer specific DSI commands */
20 #define EK79007AD3_GAMMA1 0x80
21 #define EK79007AD3_GAMMA2 0x81
22 #define EK79007AD3_GAMMA3 0x82
23 #define EK79007AD3_GAMMA4 0x83
24 #define EK79007AD3_GAMMA5 0x84
25 #define EK79007AD3_GAMMA6 0x85
26 #define EK79007AD3_GAMMA7 0x86
27 #define EK79007AD3_PANEL_CTRL3 0xB2
28
29 struct m9189_panel {
30 struct drm_panel panel;
31 struct mipi_dsi_device *dsi;
32 struct regulator *supply;
33 struct gpio_desc *reset_gpio;
34 struct gpio_desc *standby_gpio;
35 };
36
to_m9189_panel(struct drm_panel * panel)37 static inline struct m9189_panel *to_m9189_panel(struct drm_panel *panel)
38 {
39 return container_of(panel, struct m9189_panel, panel);
40 }
41
m9189_reset(struct m9189_panel * m9189)42 static void m9189_reset(struct m9189_panel *m9189)
43 {
44 gpiod_set_value_cansleep(m9189->reset_gpio, 0);
45 msleep(20);
46 gpiod_set_value_cansleep(m9189->reset_gpio, 1);
47 msleep(30);
48 gpiod_set_value_cansleep(m9189->reset_gpio, 0);
49 msleep(55);
50 }
51
m9189_on(struct m9189_panel * m9189)52 static int m9189_on(struct m9189_panel *m9189)
53 {
54 struct mipi_dsi_multi_context ctx = { .dsi = m9189->dsi };
55
56 ctx.dsi->mode_flags |= MIPI_DSI_MODE_LPM;
57
58 /* Gamma 2.2 */
59 mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA1, 0x48);
60 mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA2, 0xB8);
61 mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA3, 0x88);
62 mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA4, 0x88);
63 mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA5, 0x58);
64 mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA6, 0xD2);
65 mipi_dsi_dcs_write_seq_multi(&ctx, EK79007AD3_GAMMA7, 0x88);
66 mipi_dsi_msleep(&ctx, 50);
67
68 /* 4 Lanes */
69 mipi_dsi_generic_write_multi(&ctx, (u8[]){ EK79007AD3_PANEL_CTRL3, 0x70 }, 2);
70
71 mipi_dsi_dcs_exit_sleep_mode_multi(&ctx);
72 mipi_dsi_msleep(&ctx, 120);
73
74 mipi_dsi_dcs_set_display_on_multi(&ctx);
75 mipi_dsi_msleep(&ctx, 120);
76
77 return ctx.accum_err;
78 }
79
m9189_disable(struct drm_panel * panel)80 static int m9189_disable(struct drm_panel *panel)
81 {
82 struct m9189_panel *m9189 = to_m9189_panel(panel);
83 struct mipi_dsi_multi_context ctx = { .dsi = m9189->dsi };
84
85 ctx.dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
86
87 mipi_dsi_dcs_enter_sleep_mode_multi(&ctx);
88 mipi_dsi_msleep(&ctx, 120);
89
90 gpiod_set_value_cansleep(m9189->standby_gpio, 1);
91
92 return ctx.accum_err;
93 }
94
m9189_prepare(struct drm_panel * panel)95 static int m9189_prepare(struct drm_panel *panel)
96 {
97 struct m9189_panel *m9189 = to_m9189_panel(panel);
98 struct device *dev = &m9189->dsi->dev;
99 int ret;
100
101 ret = regulator_enable(m9189->supply);
102 if (ret < 0) {
103 dev_err(dev, "Failed to enable regulators: %d\n", ret);
104 return ret;
105 }
106
107 gpiod_set_value_cansleep(m9189->standby_gpio, 0);
108 msleep(20);
109 m9189_reset(m9189);
110
111 ret = m9189_on(m9189);
112 if (ret < 0) {
113 dev_err(dev, "Failed to initialize panel: %d\n", ret);
114 gpiod_set_value_cansleep(m9189->reset_gpio, 1);
115 regulator_disable(m9189->supply);
116 return ret;
117 }
118
119 return 0;
120 }
121
m9189_unprepare(struct drm_panel * panel)122 static int m9189_unprepare(struct drm_panel *panel)
123 {
124 struct m9189_panel *m9189 = to_m9189_panel(panel);
125
126 gpiod_set_value_cansleep(m9189->standby_gpio, 1);
127 msleep(50);
128
129 gpiod_set_value_cansleep(m9189->reset_gpio, 1);
130 regulator_disable(m9189->supply);
131
132 return 0;
133 }
134
135 static const struct drm_display_mode m9189_mode = {
136 .clock = (1024 + 160 + 160 + 10) * (600 + 12 + 23 + 1) * 60 / 1000,
137 .hdisplay = 1024,
138 .hsync_start = 1024 + 160,
139 .hsync_end = 1024 + 160 + 160,
140 .htotal = 1024 + 160 + 160 + 10,
141 .vdisplay = 600,
142 .vsync_start = 600 + 12,
143 .vsync_end = 600 + 12 + 23,
144 .vtotal = 600 + 12 + 23 + 1,
145 .width_mm = 154,
146 .height_mm = 86,
147 };
148
m9189_get_modes(struct drm_panel * panel,struct drm_connector * connector)149 static int m9189_get_modes(struct drm_panel *panel,
150 struct drm_connector *connector)
151 {
152 return drm_connector_helper_get_modes_fixed(connector, &m9189_mode);
153 }
154
155 static const struct drm_panel_funcs m9189_panel_funcs = {
156 .prepare = m9189_prepare,
157 .unprepare = m9189_unprepare,
158 .disable = m9189_disable,
159 .get_modes = m9189_get_modes,
160 };
161
lxd_m9189_probe(struct mipi_dsi_device * dsi)162 static int lxd_m9189_probe(struct mipi_dsi_device *dsi)
163 {
164 struct device *dev = &dsi->dev;
165 struct m9189_panel *m9189;
166 int ret;
167
168 m9189 = devm_kzalloc(dev, sizeof(*m9189), GFP_KERNEL);
169 if (!m9189)
170 return -ENOMEM;
171
172 m9189->supply = devm_regulator_get(dev, "power");
173 if (IS_ERR(m9189->supply))
174 return dev_err_probe(dev, PTR_ERR(m9189->supply),
175 "Failed to get power-supply\n");
176
177 m9189->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
178 if (IS_ERR(m9189->reset_gpio))
179 return dev_err_probe(dev, PTR_ERR(m9189->reset_gpio),
180 "Failed to get reset-gpios\n");
181
182 m9189->standby_gpio = devm_gpiod_get(dev, "standby", GPIOD_OUT_LOW);
183 if (IS_ERR(m9189->standby_gpio))
184 return dev_err_probe(dev, PTR_ERR(m9189->standby_gpio),
185 "Failed to get standby-gpios\n");
186
187 m9189->dsi = dsi;
188 mipi_dsi_set_drvdata(dsi, m9189);
189
190 dsi->lanes = 4;
191 dsi->format = MIPI_DSI_FMT_RGB888;
192 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST;
193
194 drm_panel_init(&m9189->panel, dev, &m9189_panel_funcs,
195 DRM_MODE_CONNECTOR_DSI);
196 m9189->panel.prepare_prev_first = true;
197
198 ret = drm_panel_of_backlight(&m9189->panel);
199 if (ret)
200 return dev_err_probe(dev, ret, "Failed to get backlight\n");
201
202 drm_panel_add(&m9189->panel);
203
204 ret = mipi_dsi_attach(dsi);
205 if (ret < 0) {
206 dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
207 drm_panel_remove(&m9189->panel);
208 return ret;
209 }
210
211 return 0;
212 }
213
lxd_m9189_remove(struct mipi_dsi_device * dsi)214 static void lxd_m9189_remove(struct mipi_dsi_device *dsi)
215 {
216 struct m9189_panel *m9189 = mipi_dsi_get_drvdata(dsi);
217 int ret;
218
219 ret = mipi_dsi_detach(dsi);
220 if (ret < 0)
221 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
222
223 drm_panel_remove(&m9189->panel);
224 }
225
226 static const struct of_device_id lxd_m9189_of_match[] = {
227 { .compatible = "lxd,m9189a" },
228 { /* sentinel */ }
229 };
230 MODULE_DEVICE_TABLE(of, lxd_m9189_of_match);
231
232 static struct mipi_dsi_driver lxd_m9189_driver = {
233 .probe = lxd_m9189_probe,
234 .remove = lxd_m9189_remove,
235 .driver = {
236 .name = "panel-lxd-m9189a",
237 .of_match_table = lxd_m9189_of_match,
238 },
239 };
240 module_mipi_dsi_driver(lxd_m9189_driver);
241
242 MODULE_DESCRIPTION("DRM driver for LXD M9189A MIPI-DSI panels");
243 MODULE_LICENSE("GPL");
244