xref: /linux/drivers/gpu/drm/panel/panel-chipone-icna35xx.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1f747473aSTeguh Sobirin // SPDX-License-Identifier: GPL-2.0-only
2f747473aSTeguh Sobirin /*
3f747473aSTeguh Sobirin  * Chipone ICNA35XX Driver IC panels driver
4f747473aSTeguh Sobirin  *
5f747473aSTeguh Sobirin  * Copyright (c) 2025 Teguh Sobirin <teguh@sobir.in>
6f747473aSTeguh Sobirin  */
7f747473aSTeguh Sobirin 
8f747473aSTeguh Sobirin #include <linux/backlight.h>
9f747473aSTeguh Sobirin #include <linux/delay.h>
10f747473aSTeguh Sobirin #include <linux/gpio/consumer.h>
11f747473aSTeguh Sobirin #include <linux/module.h>
12f747473aSTeguh Sobirin #include <linux/of.h>
13f747473aSTeguh Sobirin #include <linux/of_graph.h>
14f747473aSTeguh Sobirin #include <linux/regulator/consumer.h>
15f747473aSTeguh Sobirin 
16f747473aSTeguh Sobirin #include <video/mipi_display.h>
17f747473aSTeguh Sobirin 
18f747473aSTeguh Sobirin #include <drm/display/drm_dsc.h>
19f747473aSTeguh Sobirin #include <drm/display/drm_dsc_helper.h>
20f747473aSTeguh Sobirin #include <drm/drm_connector.h>
21f747473aSTeguh Sobirin #include <drm/drm_crtc.h>
22f747473aSTeguh Sobirin #include <drm/drm_mipi_dsi.h>
23f747473aSTeguh Sobirin #include <drm/drm_modes.h>
24*b5a8b1f2SThierry Reding #include <drm/drm_of.h>
25f747473aSTeguh Sobirin #include <drm/drm_panel.h>
26f747473aSTeguh Sobirin #include <drm/drm_probe_helper.h>
27f747473aSTeguh Sobirin 
28f747473aSTeguh Sobirin struct panel_info {
29f747473aSTeguh Sobirin 	struct drm_panel panel;
30f747473aSTeguh Sobirin 	struct drm_connector *connector;
31f747473aSTeguh Sobirin 	struct mipi_dsi_device *dsi;
32f747473aSTeguh Sobirin 	struct panel_desc *desc;
33f747473aSTeguh Sobirin 	enum drm_panel_orientation orientation;
34f747473aSTeguh Sobirin 
35f747473aSTeguh Sobirin 	struct gpio_desc *reset_gpio;
36f747473aSTeguh Sobirin 	struct regulator_bulk_data *supplies;
37f747473aSTeguh Sobirin };
38f747473aSTeguh Sobirin 
39f747473aSTeguh Sobirin struct panel_desc {
40f747473aSTeguh Sobirin 	unsigned int width_mm;
41f747473aSTeguh Sobirin 	unsigned int height_mm;
42f747473aSTeguh Sobirin 
43f747473aSTeguh Sobirin 	unsigned int bpc;
44f747473aSTeguh Sobirin 	unsigned int lanes;
45f747473aSTeguh Sobirin 	unsigned long mode_flags;
46f747473aSTeguh Sobirin 	enum mipi_dsi_pixel_format format;
47f747473aSTeguh Sobirin 
48f747473aSTeguh Sobirin 	const struct drm_display_mode *modes;
49f747473aSTeguh Sobirin 	unsigned int num_modes;
50f747473aSTeguh Sobirin 	int (*init_sequence)(struct panel_info *pinfo);
51f747473aSTeguh Sobirin 
52f747473aSTeguh Sobirin 	struct drm_dsc_config dsc;
53f747473aSTeguh Sobirin };
54f747473aSTeguh Sobirin 
55f747473aSTeguh Sobirin static const struct regulator_bulk_data panel_supplies[] = {
56f747473aSTeguh Sobirin 	{ .supply = "vdd" },
57f747473aSTeguh Sobirin 	{ .supply = "vddio" },
58f747473aSTeguh Sobirin 	{ .supply = "vci" },
59f747473aSTeguh Sobirin 	{ .supply = "disp" },
60f747473aSTeguh Sobirin 	{ .supply = "blvdd" },
61f747473aSTeguh Sobirin };
62f747473aSTeguh Sobirin 
63f747473aSTeguh Sobirin static inline struct panel_info *to_panel_info(struct drm_panel *panel)
64f747473aSTeguh Sobirin {
65f747473aSTeguh Sobirin 	return container_of(panel, struct panel_info, panel);
66f747473aSTeguh Sobirin }
67f747473aSTeguh Sobirin 
68f747473aSTeguh Sobirin static int icna3512_init_sequence(struct panel_info *pinfo)
69f747473aSTeguh Sobirin {
70f747473aSTeguh Sobirin 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi };
71f747473aSTeguh Sobirin 	struct drm_dsc_picture_parameter_set pps;
72f747473aSTeguh Sobirin 
73f747473aSTeguh Sobirin 	pinfo->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
74f747473aSTeguh Sobirin 
75f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9C, 0xA5, 0xA5);
76f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xFD, 0x5A, 0x5A);
77f747473aSTeguh Sobirin 
78f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x53, 0xE0);
79f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x35, 0x00);
80f747473aSTeguh Sobirin 
81f747473aSTeguh Sobirin 	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
82f747473aSTeguh Sobirin 
83f747473aSTeguh Sobirin 	mipi_dsi_msleep(&dsi_ctx, 120);
84f747473aSTeguh Sobirin 
85f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x0F);
86f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xCE, 0x22);
87f747473aSTeguh Sobirin 
88f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x01);
89f747473aSTeguh Sobirin 
90f747473aSTeguh Sobirin 	/* 165 hz */
91f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x48, 0x20);
92f747473aSTeguh Sobirin 
93f747473aSTeguh Sobirin 	drm_dsc_pps_payload_pack(&pps, &pinfo->desc->dsc);
94f747473aSTeguh Sobirin 	mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
95f747473aSTeguh Sobirin 
96f747473aSTeguh Sobirin 	mipi_dsi_msleep(&dsi_ctx, 20);
97f747473aSTeguh Sobirin 
98f747473aSTeguh Sobirin 	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
99f747473aSTeguh Sobirin 
100f747473aSTeguh Sobirin 	return dsi_ctx.accum_err;
101f747473aSTeguh Sobirin }
102f747473aSTeguh Sobirin 
103f747473aSTeguh Sobirin static int icna3520_init_sequence(struct panel_info *pinfo)
104f747473aSTeguh Sobirin {
105f747473aSTeguh Sobirin 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi };
106f747473aSTeguh Sobirin 	struct drm_dsc_picture_parameter_set pps;
107f747473aSTeguh Sobirin 
108f747473aSTeguh Sobirin 	pinfo->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
109f747473aSTeguh Sobirin 
110f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9C, 0xA5, 0xA5);
111f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xFD, 0x5A, 0x5A);
112f747473aSTeguh Sobirin 
113f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x53, 0xE0);
114f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x35, 0x00);
115f747473aSTeguh Sobirin 
116f747473aSTeguh Sobirin 	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
117f747473aSTeguh Sobirin 
118f747473aSTeguh Sobirin 	mipi_dsi_msleep(&dsi_ctx, 120);
119f747473aSTeguh Sobirin 
120f747473aSTeguh Sobirin 	/* 120 hz */
121f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x48, 0x00);
122f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x00);
123f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB3,
124f747473aSTeguh Sobirin 		0x00, 0xD8, 0x00, 0x1C, 0x00, 0x4C);
125f747473aSTeguh Sobirin 
126f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x01);
127f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB2, 0x00);
128f747473aSTeguh Sobirin 
129f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x0D);
130f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB2, 0x27);
131f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB6, 0x03);
132f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xBB, 0x01);
133f747473aSTeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB2, 0x24);
134f747473aSTeguh Sobirin 
135f747473aSTeguh Sobirin 	drm_dsc_pps_payload_pack(&pps, &pinfo->desc->dsc);
136f747473aSTeguh Sobirin 	mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
137f747473aSTeguh Sobirin 
138f747473aSTeguh Sobirin 	mipi_dsi_msleep(&dsi_ctx, 20);
139f747473aSTeguh Sobirin 
140f747473aSTeguh Sobirin 	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
141f747473aSTeguh Sobirin 
142f747473aSTeguh Sobirin 	return dsi_ctx.accum_err;
143f747473aSTeguh Sobirin }
144f747473aSTeguh Sobirin 
145f747473aSTeguh Sobirin static const struct drm_display_mode odin2portal_modes[] = {
146f747473aSTeguh Sobirin 	{
147f747473aSTeguh Sobirin 		/* 165Hz */
148f747473aSTeguh Sobirin 		.clock = (1080 + 98 + 1 + 23) * (1920 + 20 + 1 + 15) * 165 / 1000,
149f747473aSTeguh Sobirin 		.hdisplay = 1080,
150f747473aSTeguh Sobirin 		.hsync_start = 1080 + 98,
151f747473aSTeguh Sobirin 		.hsync_end = 1080 + 98 + 1,
152f747473aSTeguh Sobirin 		.htotal = 1080 + 98 + 1 + 23,
153f747473aSTeguh Sobirin 		.vdisplay = 1920,
154f747473aSTeguh Sobirin 		.vsync_start = 1920 + 20,
155f747473aSTeguh Sobirin 		.vsync_end = 1920 + 20 + 1,
156f747473aSTeguh Sobirin 		.vtotal = 1920 + 20 + 1 + 15,
157f747473aSTeguh Sobirin 	}
158f747473aSTeguh Sobirin };
159f747473aSTeguh Sobirin 
160f747473aSTeguh Sobirin static const struct drm_display_mode thor_top_modes[] = {
161f747473aSTeguh Sobirin 	{
162f747473aSTeguh Sobirin 		/* 120Hz */
163f747473aSTeguh Sobirin 		.clock = (1080 + 24 + 1 + 24) * (1920 + 28 + 1 + 28) * 120 / 1000,
164f747473aSTeguh Sobirin 		.hdisplay = 1080,
165f747473aSTeguh Sobirin 		.hsync_start = 1080 + 24,
166f747473aSTeguh Sobirin 		.hsync_end = 1080 + 24 + 1,
167f747473aSTeguh Sobirin 		.htotal = 1080 + 24 + 1 + 24,
168f747473aSTeguh Sobirin 		.vdisplay = 1920,
169f747473aSTeguh Sobirin 		.vsync_start = 1920 + 28,
170f747473aSTeguh Sobirin 		.vsync_end = 1920 + 28 + 1,
171f747473aSTeguh Sobirin 		.vtotal = 1920 + 28 + 1 + 28,
172f747473aSTeguh Sobirin 	}
173f747473aSTeguh Sobirin };
174f747473aSTeguh Sobirin 
175f747473aSTeguh Sobirin static struct panel_desc odin2portal_desc = {
176f747473aSTeguh Sobirin 	.modes = odin2portal_modes,
177f747473aSTeguh Sobirin 	.num_modes = ARRAY_SIZE(odin2portal_modes),
178f747473aSTeguh Sobirin 	.width_mm = 160,
179f747473aSTeguh Sobirin 	.height_mm = 89,
180f747473aSTeguh Sobirin 	.bpc = 8,
181f747473aSTeguh Sobirin 	.lanes = 4,
182f747473aSTeguh Sobirin 	.format = MIPI_DSI_FMT_RGB888,
183f747473aSTeguh Sobirin 	.mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS |
184f747473aSTeguh Sobirin 			MIPI_DSI_MODE_LPM,
185f747473aSTeguh Sobirin 	.init_sequence = icna3512_init_sequence,
186f747473aSTeguh Sobirin 	.dsc = {
187f747473aSTeguh Sobirin 		.dsc_version_major = 0x1,
188f747473aSTeguh Sobirin 		.dsc_version_minor = 0x1,
189f747473aSTeguh Sobirin 		.slice_height = 20,
190f747473aSTeguh Sobirin 		.slice_width = 540,
191f747473aSTeguh Sobirin 		.slice_count = 2,
192f747473aSTeguh Sobirin 		.bits_per_component = 8,
193f747473aSTeguh Sobirin 		.bits_per_pixel = 8 << 4,
194f747473aSTeguh Sobirin 		.block_pred_enable = true,
195f747473aSTeguh Sobirin 	},
196f747473aSTeguh Sobirin };
197f747473aSTeguh Sobirin 
198f747473aSTeguh Sobirin static struct panel_desc thor_top_desc = {
199f747473aSTeguh Sobirin 	.modes = thor_top_modes,
200f747473aSTeguh Sobirin 	.num_modes = ARRAY_SIZE(thor_top_modes),
201f747473aSTeguh Sobirin 	.width_mm = 136,
202f747473aSTeguh Sobirin 	.height_mm = 68,
203f747473aSTeguh Sobirin 	.bpc = 8,
204f747473aSTeguh Sobirin 	.lanes = 4,
205f747473aSTeguh Sobirin 	.format = MIPI_DSI_FMT_RGB888,
206f747473aSTeguh Sobirin 	.mode_flags =  MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS |
207f747473aSTeguh Sobirin 			MIPI_DSI_MODE_LPM,
208f747473aSTeguh Sobirin 	.init_sequence = icna3520_init_sequence,
209f747473aSTeguh Sobirin 	.dsc = {
210f747473aSTeguh Sobirin 		.dsc_version_major = 0x1,
211f747473aSTeguh Sobirin 		.dsc_version_minor = 0x1,
212f747473aSTeguh Sobirin 		.slice_height = 12,
213f747473aSTeguh Sobirin 		.slice_width = 540,
214f747473aSTeguh Sobirin 		.slice_count = 2,
215f747473aSTeguh Sobirin 		.bits_per_component = 8,
216f747473aSTeguh Sobirin 		.bits_per_pixel = 8 << 4,
217f747473aSTeguh Sobirin 		.block_pred_enable = true,
218f747473aSTeguh Sobirin 	},
219f747473aSTeguh Sobirin };
220f747473aSTeguh Sobirin 
221f747473aSTeguh Sobirin static void icna35xx_reset(struct panel_info *pinfo)
222f747473aSTeguh Sobirin {
223f747473aSTeguh Sobirin 	gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
224f747473aSTeguh Sobirin 	usleep_range(20000, 21000);
225f747473aSTeguh Sobirin 	gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
226f747473aSTeguh Sobirin 	usleep_range(20000, 21000);
227f747473aSTeguh Sobirin 	gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
228f747473aSTeguh Sobirin 	usleep_range(20000, 21000);
229f747473aSTeguh Sobirin }
230f747473aSTeguh Sobirin 
231f747473aSTeguh Sobirin static int icna35xx_prepare(struct drm_panel *panel)
232f747473aSTeguh Sobirin {
233f747473aSTeguh Sobirin 	struct panel_info *pinfo = to_panel_info(panel);
234f747473aSTeguh Sobirin 	int ret;
235f747473aSTeguh Sobirin 
236f747473aSTeguh Sobirin 	ret = regulator_bulk_enable(ARRAY_SIZE(panel_supplies), pinfo->supplies);
237f747473aSTeguh Sobirin 	if (ret < 0) {
238f747473aSTeguh Sobirin 		dev_err(panel->dev, "failed to enable regulators: %d\n", ret);
239f747473aSTeguh Sobirin 		return ret;
240f747473aSTeguh Sobirin 	}
241f747473aSTeguh Sobirin 
242f747473aSTeguh Sobirin 	icna35xx_reset(pinfo);
243f747473aSTeguh Sobirin 
244f747473aSTeguh Sobirin 	ret = pinfo->desc->init_sequence(pinfo);
245f747473aSTeguh Sobirin 	if (ret < 0) {
246f747473aSTeguh Sobirin 		regulator_bulk_disable(ARRAY_SIZE(panel_supplies), pinfo->supplies);
247f747473aSTeguh Sobirin 		dev_err(panel->dev, "failed to initialize panel: %d\n", ret);
248f747473aSTeguh Sobirin 		return ret;
249f747473aSTeguh Sobirin 	}
250f747473aSTeguh Sobirin 
251f747473aSTeguh Sobirin 	return 0;
252f747473aSTeguh Sobirin }
253f747473aSTeguh Sobirin 
254f747473aSTeguh Sobirin static int icna35xx_disable(struct drm_panel *panel)
255f747473aSTeguh Sobirin {
256f747473aSTeguh Sobirin 	struct panel_info *pinfo = to_panel_info(panel);
257f747473aSTeguh Sobirin 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi };
258f747473aSTeguh Sobirin 
259f747473aSTeguh Sobirin 	pinfo->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
260f747473aSTeguh Sobirin 
261f747473aSTeguh Sobirin 	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
262f747473aSTeguh Sobirin 	mipi_dsi_msleep(&dsi_ctx, 50);
263f747473aSTeguh Sobirin 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
264f747473aSTeguh Sobirin 	mipi_dsi_msleep(&dsi_ctx, 120);
265f747473aSTeguh Sobirin 
266f747473aSTeguh Sobirin 	return dsi_ctx.accum_err;
267f747473aSTeguh Sobirin }
268f747473aSTeguh Sobirin 
269f747473aSTeguh Sobirin static int icna35xx_unprepare(struct drm_panel *panel)
270f747473aSTeguh Sobirin {
271f747473aSTeguh Sobirin 	struct panel_info *pinfo = to_panel_info(panel);
272f747473aSTeguh Sobirin 
273f747473aSTeguh Sobirin 	gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
274f747473aSTeguh Sobirin 	regulator_bulk_disable(ARRAY_SIZE(panel_supplies), pinfo->supplies);
275f747473aSTeguh Sobirin 
276f747473aSTeguh Sobirin 	return 0;
277f747473aSTeguh Sobirin }
278f747473aSTeguh Sobirin 
279f747473aSTeguh Sobirin static int icna35xx_get_modes(struct drm_panel *panel,
280f747473aSTeguh Sobirin 			       struct drm_connector *connector)
281f747473aSTeguh Sobirin {
282f747473aSTeguh Sobirin 	struct panel_info *pinfo = to_panel_info(panel);
283f747473aSTeguh Sobirin 
284f747473aSTeguh Sobirin 	return drm_connector_helper_get_modes_fixed(connector, pinfo->desc->modes);
285f747473aSTeguh Sobirin }
286f747473aSTeguh Sobirin 
287f747473aSTeguh Sobirin static enum drm_panel_orientation icna35xx_get_orientation(struct drm_panel *panel)
288f747473aSTeguh Sobirin {
289f747473aSTeguh Sobirin 	struct panel_info *pinfo = to_panel_info(panel);
290f747473aSTeguh Sobirin 
291f747473aSTeguh Sobirin 	return pinfo->orientation;
292f747473aSTeguh Sobirin }
293f747473aSTeguh Sobirin 
294f747473aSTeguh Sobirin static const struct drm_panel_funcs icna35xx_panel_funcs = {
295f747473aSTeguh Sobirin 	.disable = icna35xx_disable,
296f747473aSTeguh Sobirin 	.prepare = icna35xx_prepare,
297f747473aSTeguh Sobirin 	.unprepare = icna35xx_unprepare,
298f747473aSTeguh Sobirin 	.get_modes = icna35xx_get_modes,
299f747473aSTeguh Sobirin 	.get_orientation = icna35xx_get_orientation,
300f747473aSTeguh Sobirin };
301f747473aSTeguh Sobirin 
302f747473aSTeguh Sobirin static int icna35xx_bl_update_status(struct backlight_device *bl)
303f747473aSTeguh Sobirin {
304f747473aSTeguh Sobirin 	struct mipi_dsi_device *dsi = bl_get_data(bl);
305f747473aSTeguh Sobirin 	u16 brightness = backlight_get_brightness(bl);
306f747473aSTeguh Sobirin 	int ret;
307f747473aSTeguh Sobirin 
308f747473aSTeguh Sobirin 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
309f747473aSTeguh Sobirin 
310f747473aSTeguh Sobirin 	ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
311f747473aSTeguh Sobirin 
312f747473aSTeguh Sobirin 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
313f747473aSTeguh Sobirin 
314f747473aSTeguh Sobirin 	return ret;
315f747473aSTeguh Sobirin }
316f747473aSTeguh Sobirin 
317f747473aSTeguh Sobirin static int icna35xx_bl_get_brightness(struct backlight_device *bl)
318f747473aSTeguh Sobirin {
319f747473aSTeguh Sobirin 	struct mipi_dsi_device *dsi = bl_get_data(bl);
320f747473aSTeguh Sobirin 	u16 brightness;
321f747473aSTeguh Sobirin 	int ret;
322f747473aSTeguh Sobirin 
323f747473aSTeguh Sobirin 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
324f747473aSTeguh Sobirin 
325f747473aSTeguh Sobirin 	ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
326f747473aSTeguh Sobirin 
327f747473aSTeguh Sobirin 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
328f747473aSTeguh Sobirin 
329f747473aSTeguh Sobirin 	return ret < 0 ? ret : brightness;
330f747473aSTeguh Sobirin }
331f747473aSTeguh Sobirin 
332f747473aSTeguh Sobirin static const struct backlight_ops icna35xx_bl_ops = {
333f747473aSTeguh Sobirin 	.update_status = icna35xx_bl_update_status,
334f747473aSTeguh Sobirin 	.get_brightness = icna35xx_bl_get_brightness,
335f747473aSTeguh Sobirin };
336f747473aSTeguh Sobirin 
337f747473aSTeguh Sobirin static struct backlight_device *icna35xx_create_backlight(struct mipi_dsi_device *dsi)
338f747473aSTeguh Sobirin {
339f747473aSTeguh Sobirin 	struct device *dev = &dsi->dev;
340f747473aSTeguh Sobirin 	const struct backlight_properties props = {
341f747473aSTeguh Sobirin 		.type = BACKLIGHT_RAW,
342f747473aSTeguh Sobirin 		.brightness = 4096,
343f747473aSTeguh Sobirin 		.max_brightness = 4096,
344f747473aSTeguh Sobirin 	};
345f747473aSTeguh Sobirin 
346f747473aSTeguh Sobirin 	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
347f747473aSTeguh Sobirin 					      &icna35xx_bl_ops, &props);
348f747473aSTeguh Sobirin }
349f747473aSTeguh Sobirin 
350f747473aSTeguh Sobirin static int icna35xx_probe(struct mipi_dsi_device *dsi)
351f747473aSTeguh Sobirin {
352f747473aSTeguh Sobirin 	struct device *dev = &dsi->dev;
353f747473aSTeguh Sobirin 	struct panel_info *pinfo;
354f747473aSTeguh Sobirin 	int ret;
355f747473aSTeguh Sobirin 
356f747473aSTeguh Sobirin 	pinfo = devm_drm_panel_alloc(dev, __typeof(*pinfo), panel,
357f747473aSTeguh Sobirin 				     &icna35xx_panel_funcs,
358f747473aSTeguh Sobirin 				     DRM_MODE_CONNECTOR_DSI);
359f747473aSTeguh Sobirin 	if (IS_ERR(pinfo))
360f747473aSTeguh Sobirin 		return PTR_ERR(pinfo);
361f747473aSTeguh Sobirin 
362f747473aSTeguh Sobirin 	ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(panel_supplies),
363f747473aSTeguh Sobirin 					    panel_supplies, &pinfo->supplies);
364f747473aSTeguh Sobirin 	if (ret < 0)
365f747473aSTeguh Sobirin 		return dev_err_probe(dev, ret, "Failed to get regulators\n");
366f747473aSTeguh Sobirin 
367f747473aSTeguh Sobirin 	pinfo->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
368f747473aSTeguh Sobirin 	if (IS_ERR(pinfo->reset_gpio))
369f747473aSTeguh Sobirin 		return dev_err_probe(dev, PTR_ERR(pinfo->reset_gpio), "failed to get reset gpio\n");
370f747473aSTeguh Sobirin 
371f747473aSTeguh Sobirin 	pinfo->desc = (struct panel_desc *)of_device_get_match_data(dev);
372f747473aSTeguh Sobirin 	if (!pinfo->desc)
373f747473aSTeguh Sobirin 		return -ENODEV;
374f747473aSTeguh Sobirin 
375f747473aSTeguh Sobirin 	pinfo->dsi = dsi;
376f747473aSTeguh Sobirin 	mipi_dsi_set_drvdata(dsi, pinfo);
377f747473aSTeguh Sobirin 
378*b5a8b1f2SThierry Reding 	ret = drm_of_get_panel_orientation(dev->of_node, &pinfo->orientation);
379f747473aSTeguh Sobirin 	if (ret < 0) {
380f747473aSTeguh Sobirin 		dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
381f747473aSTeguh Sobirin 		return ret;
382f747473aSTeguh Sobirin 	}
383f747473aSTeguh Sobirin 
384f747473aSTeguh Sobirin 	pinfo->panel.prepare_prev_first = true;
385f747473aSTeguh Sobirin 
386f747473aSTeguh Sobirin 	pinfo->panel.backlight = icna35xx_create_backlight(dsi);
387f747473aSTeguh Sobirin 	if (IS_ERR(pinfo->panel.backlight))
388f747473aSTeguh Sobirin 		return dev_err_probe(dev, PTR_ERR(pinfo->panel.backlight),
389f747473aSTeguh Sobirin 				     "Failed to create backlight\n");
390f747473aSTeguh Sobirin 
391f747473aSTeguh Sobirin 	ret = devm_drm_panel_add(dev, &pinfo->panel);
392f747473aSTeguh Sobirin 	if (ret)
393f747473aSTeguh Sobirin 		return ret;
394f747473aSTeguh Sobirin 
395f747473aSTeguh Sobirin 	pinfo->dsi->lanes = pinfo->desc->lanes;
396f747473aSTeguh Sobirin 	pinfo->dsi->format = pinfo->desc->format;
397f747473aSTeguh Sobirin 	pinfo->dsi->mode_flags = pinfo->desc->mode_flags;
398f747473aSTeguh Sobirin 	pinfo->dsi->dsc = &pinfo->desc->dsc;
399f747473aSTeguh Sobirin 
400f747473aSTeguh Sobirin 	return devm_mipi_dsi_attach(dev, dsi);
401f747473aSTeguh Sobirin }
402f747473aSTeguh Sobirin 
403f747473aSTeguh Sobirin static const struct of_device_id icna35xx_of_match[] = {
404f747473aSTeguh Sobirin 	{ .compatible = "ayaneo,pocketds-panel-top", .data = &odin2portal_desc },
405f747473aSTeguh Sobirin 	{ .compatible = "ayntec,odin2portal-panel", .data = &odin2portal_desc },
406f747473aSTeguh Sobirin 	{ .compatible = "ayntec,odin3-panel", .data = &thor_top_desc },
407f747473aSTeguh Sobirin 	{ .compatible = "ayntec,thor-panel-top", .data = &thor_top_desc },
408f747473aSTeguh Sobirin 	{ /* sentinel */ }
409f747473aSTeguh Sobirin };
410f747473aSTeguh Sobirin MODULE_DEVICE_TABLE(of, icna35xx_of_match);
411f747473aSTeguh Sobirin 
412f747473aSTeguh Sobirin static struct mipi_dsi_driver icna35xx_driver = {
413f747473aSTeguh Sobirin 	.probe = icna35xx_probe,
414f747473aSTeguh Sobirin 	.driver = {
415f747473aSTeguh Sobirin 		.name = "panel-chipone-icna35xx",
416f747473aSTeguh Sobirin 		.of_match_table = icna35xx_of_match,
417f747473aSTeguh Sobirin 	},
418f747473aSTeguh Sobirin };
419f747473aSTeguh Sobirin module_mipi_dsi_driver(icna35xx_driver);
420f747473aSTeguh Sobirin 
421f747473aSTeguh Sobirin MODULE_AUTHOR("Teguh Sobirin <teguh@sobir.in>");
422f747473aSTeguh Sobirin MODULE_DESCRIPTION("DRM driver for Chipone ICNA35XX based MIPI DSI panels");
423f747473aSTeguh Sobirin MODULE_LICENSE("GPL");
424