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
13 #include <drm/drm_mipi_dsi.h>
14 #include <drm/drm_modes.h>
15 #include <drm/drm_panel.h>
16 #include <drm/drm_probe_helper.h>
17
18 /* Manufacturer specific DSI commands */
19 #define HX83112A_SETPOWER1 0xb1
20 #define HX83112A_SETDISP 0xb2
21 #define HX83112A_SETDRV 0xb4
22 #define HX83112A_SETEXTC 0xb9
23 #define HX83112A_SETBANK 0xbd
24 #define HX83112A_SETPTBA 0xbf
25 #define HX83112A_SETDGCLUT 0xc1
26 #define HX83112A_SETTCON 0xc7
27 #define HX83112A_SETCLOCK 0xcb
28 #define HX83112A_SETPANEL 0xcc
29 #define HX83112A_SETPOWER2 0xd2
30 #define HX83112A_SETGIP0 0xd3
31 #define HX83112A_SETGIP1 0xd5
32 #define HX83112A_SETGIP2 0xd6
33 #define HX83112A_SETGIP3 0xd8
34 #define HX83112A_SETTP1 0xe7
35 #define HX83112A_UNKNOWN1 0xe9
36
37 struct hx83112a_panel {
38 struct drm_panel panel;
39 struct mipi_dsi_device *dsi;
40 struct regulator_bulk_data supplies[3];
41 struct gpio_desc *reset_gpio;
42 };
43
to_hx83112a_panel(struct drm_panel * panel)44 static inline struct hx83112a_panel *to_hx83112a_panel(struct drm_panel *panel)
45 {
46 return container_of(panel, struct hx83112a_panel, panel);
47 }
48
hx83112a_reset(struct hx83112a_panel * ctx)49 static void hx83112a_reset(struct hx83112a_panel *ctx)
50 {
51 gpiod_set_value_cansleep(ctx->reset_gpio, 0);
52 msleep(20);
53 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
54 msleep(20);
55 gpiod_set_value_cansleep(ctx->reset_gpio, 0);
56 msleep(50);
57 }
58
hx83112a_on(struct hx83112a_panel * ctx)59 static int hx83112a_on(struct hx83112a_panel *ctx)
60 {
61 struct mipi_dsi_device *dsi = ctx->dsi;
62 struct device *dev = &dsi->dev;
63 int ret;
64
65 dsi->mode_flags |= MIPI_DSI_MODE_LPM;
66
67 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETEXTC, 0x83, 0x11, 0x2a);
68 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPOWER1,
69 0x08, 0x28, 0x28, 0x83, 0x83, 0x4c, 0x4f, 0x33);
70 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDISP,
71 0x00, 0x02, 0x00, 0x90, 0x24, 0x00, 0x08, 0x19,
72 0xea, 0x11, 0x11, 0x00, 0x11, 0xa3);
73 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDRV,
74 0x58, 0x68, 0x58, 0x68, 0x0f, 0xef, 0x0b, 0xc0,
75 0x0b, 0xc0, 0x0b, 0xc0, 0x00, 0xff, 0x00, 0xff,
76 0x00, 0x00, 0x14, 0x15, 0x00, 0x29, 0x11, 0x07,
77 0x12, 0x00, 0x29);
78 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
79 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDRV,
80 0x00, 0x12, 0x12, 0x11, 0x88, 0x12, 0x12, 0x00,
81 0x53);
82 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
83 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x03);
84 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT,
85 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
86 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
87 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
88 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
89 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
90 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
91 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
92 0x40);
93 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
94 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT,
95 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
96 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
97 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
98 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
99 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
100 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
101 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
102 0x40);
103 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
104 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT,
105 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
106 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
107 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
108 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
109 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
110 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
111 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
112 0x40);
113 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
114 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT, 0x01);
115 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTCON,
116 0x70, 0x00, 0x04, 0xe0, 0x33, 0x00);
117 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPANEL, 0x08);
118 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPOWER2, 0x2b, 0x2b);
119 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP0,
120 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08,
121 0x08, 0x03, 0x03, 0x22, 0x18, 0x07, 0x07, 0x07,
122 0x07, 0x32, 0x10, 0x06, 0x00, 0x06, 0x32, 0x10,
123 0x07, 0x00, 0x07, 0x32, 0x19, 0x31, 0x09, 0x31,
124 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x08,
125 0x09, 0x30, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x00,
126 0x0f);
127 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
128 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP0,
129 0x00, 0x00, 0x19, 0x10, 0x00, 0x0a, 0x00, 0x81);
130 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
131 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP1,
132 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
133 0xc0, 0xc0, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18,
134 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
135 0x28, 0x28, 0x24, 0x24, 0x02, 0x03, 0x02, 0x03,
136 0x00, 0x01, 0x00, 0x01, 0x31, 0x31, 0x31, 0x31,
137 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
138 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP2,
139 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
140 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19,
141 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
142 0x24, 0x24, 0x28, 0x28, 0x01, 0x00, 0x01, 0x00,
143 0x03, 0x02, 0x03, 0x02, 0x31, 0x31, 0x31, 0x31,
144 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
145 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
146 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea,
147 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa,
148 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa, 0xaa, 0xaa);
149 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
150 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
151 0xaa, 0x2e, 0x28, 0x00, 0x00, 0x00, 0xaa, 0x2e,
152 0x28, 0x00, 0x00, 0x00, 0xaa, 0xee, 0xaa, 0xaa,
153 0xaa, 0xaa, 0xaa, 0xee, 0xaa, 0xaa, 0xaa, 0xaa);
154 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
155 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
156 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xff,
157 0xff, 0xff, 0xff, 0xff);
158 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x03);
159 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
160 0xaa, 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
161 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff,
162 0xff, 0xff, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff);
163 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
164 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTP1,
165 0x0e, 0x0e, 0x1e, 0x65, 0x1c, 0x65, 0x00, 0x50,
166 0x20, 0x20, 0x00, 0x00, 0x02, 0x02, 0x02, 0x05,
167 0x14, 0x14, 0x32, 0xb9, 0x23, 0xb9, 0x08);
168 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
169 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTP1,
170 0x02, 0x00, 0xa8, 0x01, 0xa8, 0x0d, 0xa4, 0x0e);
171 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
172 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTP1,
173 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
176 0x00, 0x00, 0x00, 0x02, 0x00);
177 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
178 mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0xc3);
179 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETCLOCK, 0xd1, 0xd6);
180 mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0x3f);
181 mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0xc6);
182 mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPTBA, 0x37);
183 mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0x3f);
184
185 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
186 if (ret < 0) {
187 dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
188 return ret;
189 }
190 msleep(150);
191
192 ret = mipi_dsi_dcs_set_display_on(dsi);
193 if (ret < 0) {
194 dev_err(dev, "Failed to set display on: %d\n", ret);
195 return ret;
196 }
197 msleep(50);
198
199 return 0;
200 }
201
hx83112a_disable(struct drm_panel * panel)202 static int hx83112a_disable(struct drm_panel *panel)
203 {
204 struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
205 struct mipi_dsi_device *dsi = ctx->dsi;
206 struct device *dev = &dsi->dev;
207 int ret;
208
209 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
210
211 ret = mipi_dsi_dcs_set_display_off(dsi);
212 if (ret < 0) {
213 dev_err(dev, "Failed to set display off: %d\n", ret);
214 return ret;
215 }
216 msleep(20);
217
218 ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
219 if (ret < 0) {
220 dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
221 return ret;
222 }
223 msleep(120);
224
225 return 0;
226 }
227
hx83112a_prepare(struct drm_panel * panel)228 static int hx83112a_prepare(struct drm_panel *panel)
229 {
230 struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
231 struct device *dev = &ctx->dsi->dev;
232 int ret;
233
234 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
235 if (ret < 0) {
236 dev_err(dev, "Failed to enable regulators: %d\n", ret);
237 return ret;
238 }
239
240 hx83112a_reset(ctx);
241
242 ret = hx83112a_on(ctx);
243 if (ret < 0) {
244 dev_err(dev, "Failed to initialize panel: %d\n", ret);
245 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
246 regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
247 return ret;
248 }
249
250 return 0;
251 }
252
hx83112a_unprepare(struct drm_panel * panel)253 static int hx83112a_unprepare(struct drm_panel *panel)
254 {
255 struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
256
257 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
258 regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
259
260 return 0;
261 }
262
263 static const struct drm_display_mode hx83112a_mode = {
264 .clock = (1080 + 28 + 8 + 8) * (2340 + 27 + 5 + 5) * 60 / 1000,
265 .hdisplay = 1080,
266 .hsync_start = 1080 + 28,
267 .hsync_end = 1080 + 28 + 8,
268 .htotal = 1080 + 28 + 8 + 8,
269 .vdisplay = 2340,
270 .vsync_start = 2340 + 27,
271 .vsync_end = 2340 + 27 + 5,
272 .vtotal = 2340 + 27 + 5 + 5,
273 .width_mm = 67,
274 .height_mm = 145,
275 .type = DRM_MODE_TYPE_DRIVER,
276 };
277
hx83112a_get_modes(struct drm_panel * panel,struct drm_connector * connector)278 static int hx83112a_get_modes(struct drm_panel *panel,
279 struct drm_connector *connector)
280 {
281 return drm_connector_helper_get_modes_fixed(connector, &hx83112a_mode);
282 }
283
284 static const struct drm_panel_funcs hx83112a_panel_funcs = {
285 .prepare = hx83112a_prepare,
286 .unprepare = hx83112a_unprepare,
287 .disable = hx83112a_disable,
288 .get_modes = hx83112a_get_modes,
289 };
290
hx83112a_probe(struct mipi_dsi_device * dsi)291 static int hx83112a_probe(struct mipi_dsi_device *dsi)
292 {
293 struct device *dev = &dsi->dev;
294 struct hx83112a_panel *ctx;
295 int ret;
296
297 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
298 if (!ctx)
299 return -ENOMEM;
300
301 ctx->supplies[0].supply = "vdd1";
302 ctx->supplies[1].supply = "vsn";
303 ctx->supplies[2].supply = "vsp";
304 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
305 ctx->supplies);
306 if (ret < 0)
307 return dev_err_probe(dev, ret, "Failed to get regulators\n");
308
309 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
310 if (IS_ERR(ctx->reset_gpio))
311 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
312 "Failed to get reset-gpios\n");
313
314 ctx->dsi = dsi;
315 mipi_dsi_set_drvdata(dsi, ctx);
316
317 dsi->lanes = 4;
318 dsi->format = MIPI_DSI_FMT_RGB888;
319 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
320 MIPI_DSI_MODE_VIDEO_HSE |
321 MIPI_DSI_CLOCK_NON_CONTINUOUS;
322
323 drm_panel_init(&ctx->panel, dev, &hx83112a_panel_funcs,
324 DRM_MODE_CONNECTOR_DSI);
325 ctx->panel.prepare_prev_first = true;
326
327 ret = drm_panel_of_backlight(&ctx->panel);
328 if (ret)
329 return dev_err_probe(dev, ret, "Failed to get backlight\n");
330
331 drm_panel_add(&ctx->panel);
332
333 ret = mipi_dsi_attach(dsi);
334 if (ret < 0) {
335 dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
336 drm_panel_remove(&ctx->panel);
337 return ret;
338 }
339
340 return 0;
341 }
342
hx83112a_remove(struct mipi_dsi_device * dsi)343 static void hx83112a_remove(struct mipi_dsi_device *dsi)
344 {
345 struct hx83112a_panel *ctx = mipi_dsi_get_drvdata(dsi);
346 int ret;
347
348 ret = mipi_dsi_detach(dsi);
349 if (ret < 0)
350 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
351
352 drm_panel_remove(&ctx->panel);
353 }
354
355 static const struct of_device_id hx83112a_of_match[] = {
356 { .compatible = "djn,9a-3r063-1102b" },
357 { /* sentinel */ }
358 };
359 MODULE_DEVICE_TABLE(of, hx83112a_of_match);
360
361 static struct mipi_dsi_driver hx83112a_driver = {
362 .probe = hx83112a_probe,
363 .remove = hx83112a_remove,
364 .driver = {
365 .name = "panel-himax-hx83112a",
366 .of_match_table = hx83112a_of_match,
367 },
368 };
369 module_mipi_dsi_driver(hx83112a_driver);
370
371 MODULE_DESCRIPTION("DRM driver for hx83112a-equipped DSI panels");
372 MODULE_LICENSE("GPL");
373