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
37struct 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
44static inline struct hx83112a_panel *to_hx83112a_panel(struct drm_panel *panel)
45{
46	return container_of(panel, struct hx83112a_panel, panel);
47}
48
49static 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
59static 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
202static 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
228static 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
253static 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
263static 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
278static 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
284static 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
291static 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
343static 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
355static const struct of_device_id hx83112a_of_match[] = {
356	{ .compatible = "djn,9a-3r063-1102b" },
357	{ /* sentinel */ }
358};
359MODULE_DEVICE_TABLE(of, hx83112a_of_match);
360
361static 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};
369module_mipi_dsi_driver(hx83112a_driver);
370
371MODULE_DESCRIPTION("DRM driver for hx83112a-equipped DSI panels");
372MODULE_LICENSE("GPL");
373