1d1479d02SHeiko Stuebner // SPDX-License-Identifier: GPL-2.0
2d1479d02SHeiko Stuebner /*
3d1479d02SHeiko Stuebner * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver
4d1479d02SHeiko Stuebner * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH
5d1479d02SHeiko Stuebner *
6d1479d02SHeiko Stuebner * based on
7d1479d02SHeiko Stuebner *
8d1479d02SHeiko Stuebner * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
9d1479d02SHeiko Stuebner * Copyright (C) Purism SPC 2019
10d1479d02SHeiko Stuebner */
11d1479d02SHeiko Stuebner
12d1479d02SHeiko Stuebner #include <drm/drm_mipi_dsi.h>
13d1479d02SHeiko Stuebner #include <drm/drm_modes.h>
14d1479d02SHeiko Stuebner #include <drm/drm_panel.h>
15d1479d02SHeiko Stuebner
16d1479d02SHeiko Stuebner #include <video/display_timing.h>
17d1479d02SHeiko Stuebner #include <video/mipi_display.h>
18d1479d02SHeiko Stuebner
19d1479d02SHeiko Stuebner #include <linux/delay.h>
20d1479d02SHeiko Stuebner #include <linux/gpio/consumer.h>
21d1479d02SHeiko Stuebner #include <linux/media-bus-format.h>
22d1479d02SHeiko Stuebner #include <linux/module.h>
23d1479d02SHeiko Stuebner #include <linux/of.h>
24d1479d02SHeiko Stuebner #include <linux/regulator/consumer.h>
25d1479d02SHeiko Stuebner
26d1479d02SHeiko Stuebner /* Manufacturer specific Commands send via DSI */
27d1479d02SHeiko Stuebner #define XPP055C272_CMD_ALL_PIXEL_OFF 0x22
28d1479d02SHeiko Stuebner #define XPP055C272_CMD_ALL_PIXEL_ON 0x23
29d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETDISP 0xb2
30d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETRGBIF 0xb3
31d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETCYC 0xb4
32d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETBGP 0xb5
33d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETVCOM 0xb6
34d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETOTP 0xb7
35d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETPOWER_EXT 0xb8
36d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETEXTC 0xb9
37d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETMIPI 0xbA
38d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETVDC 0xbc
39d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETPCR 0xbf
40d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETSCR 0xc0
41d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETPOWER 0xc1
42d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETECO 0xc6
43d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETPANEL 0xcc
44d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETGAMMA 0xe0
45d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETEQ 0xe3
46d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETGIP1 0xe9
47d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETGIP2 0xea
48d1479d02SHeiko Stuebner
49d1479d02SHeiko Stuebner struct xpp055c272 {
50d1479d02SHeiko Stuebner struct device *dev;
51d1479d02SHeiko Stuebner struct drm_panel panel;
52d1479d02SHeiko Stuebner struct gpio_desc *reset_gpio;
53d1479d02SHeiko Stuebner struct regulator *vci;
54d1479d02SHeiko Stuebner struct regulator *iovcc;
55d1479d02SHeiko Stuebner bool prepared;
56d1479d02SHeiko Stuebner };
57d1479d02SHeiko Stuebner
panel_to_xpp055c272(struct drm_panel * panel)58d1479d02SHeiko Stuebner static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel)
59d1479d02SHeiko Stuebner {
60d1479d02SHeiko Stuebner return container_of(panel, struct xpp055c272, panel);
61d1479d02SHeiko Stuebner }
62d1479d02SHeiko Stuebner
xpp055c272_init_sequence(struct xpp055c272 * ctx)63d1479d02SHeiko Stuebner static int xpp055c272_init_sequence(struct xpp055c272 *ctx)
64d1479d02SHeiko Stuebner {
65d1479d02SHeiko Stuebner struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
66d1479d02SHeiko Stuebner struct device *dev = ctx->dev;
67d1479d02SHeiko Stuebner
68d1479d02SHeiko Stuebner /*
69d1479d02SHeiko Stuebner * Init sequence was supplied by the panel vendor without much
70d1479d02SHeiko Stuebner * documentation.
71d1479d02SHeiko Stuebner */
72*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83);
73*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETMIPI,
74d1479d02SHeiko Stuebner 0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00,
75d1479d02SHeiko Stuebner 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
76d1479d02SHeiko Stuebner 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01,
77d1479d02SHeiko Stuebner 0x00, 0x00, 0x37);
78*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPOWER_EXT, 0x25);
79*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00);
80*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETRGBIF,
81d1479d02SHeiko Stuebner 0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00,
82d1479d02SHeiko Stuebner 0x00, 0x00);
83*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETSCR,
84d1479d02SHeiko Stuebner 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
85d1479d02SHeiko Stuebner 0x00);
86*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETVDC, 0x46);
87*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPANEL, 0x0b);
88*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETCYC, 0x80);
89*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30);
90*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETEQ,
91d1479d02SHeiko Stuebner 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
92d1479d02SHeiko Stuebner 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
93*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPOWER,
94d1479d02SHeiko Stuebner 0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd,
95d1479d02SHeiko Stuebner 0x67, 0x77, 0x33, 0x33);
96*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff,
97d1479d02SHeiko Stuebner 0xff, 0x01, 0xff);
98*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09);
99d1479d02SHeiko Stuebner msleep(20);
100d1479d02SHeiko Stuebner
101*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETVCOM, 0x87, 0x95);
102*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGIP1,
103d1479d02SHeiko Stuebner 0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12,
104d1479d02SHeiko Stuebner 0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18,
105d1479d02SHeiko Stuebner 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
106d1479d02SHeiko Stuebner 0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42,
107d1479d02SHeiko Stuebner 0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58,
108d1479d02SHeiko Stuebner 0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88,
109d1479d02SHeiko Stuebner 0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
110d1479d02SHeiko Stuebner 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
111*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGIP2,
112d1479d02SHeiko Stuebner 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
113d1479d02SHeiko Stuebner 0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35,
114d1479d02SHeiko Stuebner 0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f,
115d1479d02SHeiko Stuebner 0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88,
116d1479d02SHeiko Stuebner 0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00,
117d1479d02SHeiko Stuebner 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118d1479d02SHeiko Stuebner 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05,
119d1479d02SHeiko Stuebner 0xa0, 0x00, 0x00, 0x00, 0x00);
120*1a4cf058SJavier Martinez Canillas mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGAMMA,
121d1479d02SHeiko Stuebner 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36,
122d1479d02SHeiko Stuebner 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11,
123d1479d02SHeiko Stuebner 0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38,
124d1479d02SHeiko Stuebner 0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13,
125d1479d02SHeiko Stuebner 0x11, 0x18);
126d1479d02SHeiko Stuebner
127d1479d02SHeiko Stuebner msleep(60);
128d1479d02SHeiko Stuebner
129a25b6b27SSam Ravnborg dev_dbg(dev, "Panel init sequence done\n");
130d1479d02SHeiko Stuebner return 0;
131d1479d02SHeiko Stuebner }
132d1479d02SHeiko Stuebner
xpp055c272_unprepare(struct drm_panel * panel)133d1479d02SHeiko Stuebner static int xpp055c272_unprepare(struct drm_panel *panel)
134d1479d02SHeiko Stuebner {
135d1479d02SHeiko Stuebner struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
136d1479d02SHeiko Stuebner struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
137d1479d02SHeiko Stuebner int ret;
138d1479d02SHeiko Stuebner
139d1479d02SHeiko Stuebner if (!ctx->prepared)
140d1479d02SHeiko Stuebner return 0;
141d1479d02SHeiko Stuebner
142d1479d02SHeiko Stuebner ret = mipi_dsi_dcs_set_display_off(dsi);
143d1479d02SHeiko Stuebner if (ret < 0)
144a25b6b27SSam Ravnborg dev_err(ctx->dev, "failed to set display off: %d\n", ret);
145d1479d02SHeiko Stuebner
146d1479d02SHeiko Stuebner mipi_dsi_dcs_enter_sleep_mode(dsi);
147d1479d02SHeiko Stuebner if (ret < 0) {
148a25b6b27SSam Ravnborg dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
149d1479d02SHeiko Stuebner return ret;
150d1479d02SHeiko Stuebner }
151d1479d02SHeiko Stuebner
152d1479d02SHeiko Stuebner regulator_disable(ctx->iovcc);
153d1479d02SHeiko Stuebner regulator_disable(ctx->vci);
154d1479d02SHeiko Stuebner
155d1479d02SHeiko Stuebner ctx->prepared = false;
156d1479d02SHeiko Stuebner
157d1479d02SHeiko Stuebner return 0;
158d1479d02SHeiko Stuebner }
159d1479d02SHeiko Stuebner
xpp055c272_prepare(struct drm_panel * panel)160d1479d02SHeiko Stuebner static int xpp055c272_prepare(struct drm_panel *panel)
161d1479d02SHeiko Stuebner {
162d1479d02SHeiko Stuebner struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
163d1479d02SHeiko Stuebner struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
164d1479d02SHeiko Stuebner int ret;
165d1479d02SHeiko Stuebner
166d1479d02SHeiko Stuebner if (ctx->prepared)
167d1479d02SHeiko Stuebner return 0;
168d1479d02SHeiko Stuebner
169a25b6b27SSam Ravnborg dev_dbg(ctx->dev, "Resetting the panel\n");
170d1479d02SHeiko Stuebner ret = regulator_enable(ctx->vci);
171d1479d02SHeiko Stuebner if (ret < 0) {
172a25b6b27SSam Ravnborg dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
173d1479d02SHeiko Stuebner return ret;
174d1479d02SHeiko Stuebner }
175d1479d02SHeiko Stuebner ret = regulator_enable(ctx->iovcc);
176d1479d02SHeiko Stuebner if (ret < 0) {
177a25b6b27SSam Ravnborg dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
178d1479d02SHeiko Stuebner goto disable_vci;
179d1479d02SHeiko Stuebner }
180d1479d02SHeiko Stuebner
181d1479d02SHeiko Stuebner gpiod_set_value_cansleep(ctx->reset_gpio, 1);
182d1479d02SHeiko Stuebner /* T6: 10us */
183d1479d02SHeiko Stuebner usleep_range(10, 20);
184d1479d02SHeiko Stuebner gpiod_set_value_cansleep(ctx->reset_gpio, 0);
185d1479d02SHeiko Stuebner
186d1479d02SHeiko Stuebner /* T8: 20ms */
187d1479d02SHeiko Stuebner msleep(20);
188d1479d02SHeiko Stuebner
189d1479d02SHeiko Stuebner ret = xpp055c272_init_sequence(ctx);
190d1479d02SHeiko Stuebner if (ret < 0) {
191a25b6b27SSam Ravnborg dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
192d1479d02SHeiko Stuebner goto disable_iovcc;
193d1479d02SHeiko Stuebner }
194d1479d02SHeiko Stuebner
195d1479d02SHeiko Stuebner ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
196d1479d02SHeiko Stuebner if (ret < 0) {
197a25b6b27SSam Ravnborg dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
198d1479d02SHeiko Stuebner goto disable_iovcc;
199d1479d02SHeiko Stuebner }
200d1479d02SHeiko Stuebner
201d1479d02SHeiko Stuebner /* T9: 120ms */
202d1479d02SHeiko Stuebner msleep(120);
203d1479d02SHeiko Stuebner
204d1479d02SHeiko Stuebner ret = mipi_dsi_dcs_set_display_on(dsi);
205d1479d02SHeiko Stuebner if (ret < 0) {
206a25b6b27SSam Ravnborg dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
207d1479d02SHeiko Stuebner goto disable_iovcc;
208d1479d02SHeiko Stuebner }
209d1479d02SHeiko Stuebner
210d1479d02SHeiko Stuebner msleep(50);
211d1479d02SHeiko Stuebner
212d1479d02SHeiko Stuebner ctx->prepared = true;
213d1479d02SHeiko Stuebner
214d1479d02SHeiko Stuebner return 0;
215d1479d02SHeiko Stuebner
216d1479d02SHeiko Stuebner disable_iovcc:
217d1479d02SHeiko Stuebner regulator_disable(ctx->iovcc);
218d1479d02SHeiko Stuebner disable_vci:
219d1479d02SHeiko Stuebner regulator_disable(ctx->vci);
220d1479d02SHeiko Stuebner return ret;
221d1479d02SHeiko Stuebner }
222d1479d02SHeiko Stuebner
223d1479d02SHeiko Stuebner static const struct drm_display_mode default_mode = {
224d1479d02SHeiko Stuebner .hdisplay = 720,
225d1479d02SHeiko Stuebner .hsync_start = 720 + 40,
226d1479d02SHeiko Stuebner .hsync_end = 720 + 40 + 10,
227d1479d02SHeiko Stuebner .htotal = 720 + 40 + 10 + 40,
228d1479d02SHeiko Stuebner .vdisplay = 1280,
229d1479d02SHeiko Stuebner .vsync_start = 1280 + 22,
230d1479d02SHeiko Stuebner .vsync_end = 1280 + 22 + 4,
231d1479d02SHeiko Stuebner .vtotal = 1280 + 22 + 4 + 11,
232d1479d02SHeiko Stuebner .clock = 64000,
233d1479d02SHeiko Stuebner .width_mm = 68,
234d1479d02SHeiko Stuebner .height_mm = 121,
235d1479d02SHeiko Stuebner };
236d1479d02SHeiko Stuebner
xpp055c272_get_modes(struct drm_panel * panel,struct drm_connector * connector)237d1479d02SHeiko Stuebner static int xpp055c272_get_modes(struct drm_panel *panel,
238d1479d02SHeiko Stuebner struct drm_connector *connector)
239d1479d02SHeiko Stuebner {
240d1479d02SHeiko Stuebner struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
241d1479d02SHeiko Stuebner struct drm_display_mode *mode;
242d1479d02SHeiko Stuebner
243d1479d02SHeiko Stuebner mode = drm_mode_duplicate(connector->dev, &default_mode);
244d1479d02SHeiko Stuebner if (!mode) {
245a25b6b27SSam Ravnborg dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
246d1479d02SHeiko Stuebner default_mode.hdisplay, default_mode.vdisplay,
2470425662fSVille Syrjälä drm_mode_vrefresh(&default_mode));
248d1479d02SHeiko Stuebner return -ENOMEM;
249d1479d02SHeiko Stuebner }
250d1479d02SHeiko Stuebner
251d1479d02SHeiko Stuebner drm_mode_set_name(mode);
252d1479d02SHeiko Stuebner
253d1479d02SHeiko Stuebner mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
254d1479d02SHeiko Stuebner connector->display_info.width_mm = mode->width_mm;
255d1479d02SHeiko Stuebner connector->display_info.height_mm = mode->height_mm;
256d1479d02SHeiko Stuebner drm_mode_probed_add(connector, mode);
257d1479d02SHeiko Stuebner
258d1479d02SHeiko Stuebner return 1;
259d1479d02SHeiko Stuebner }
260d1479d02SHeiko Stuebner
261d1479d02SHeiko Stuebner static const struct drm_panel_funcs xpp055c272_funcs = {
262d1479d02SHeiko Stuebner .unprepare = xpp055c272_unprepare,
263d1479d02SHeiko Stuebner .prepare = xpp055c272_prepare,
264d1479d02SHeiko Stuebner .get_modes = xpp055c272_get_modes,
265d1479d02SHeiko Stuebner };
266d1479d02SHeiko Stuebner
xpp055c272_probe(struct mipi_dsi_device * dsi)267d1479d02SHeiko Stuebner static int xpp055c272_probe(struct mipi_dsi_device *dsi)
268d1479d02SHeiko Stuebner {
269d1479d02SHeiko Stuebner struct device *dev = &dsi->dev;
270d1479d02SHeiko Stuebner struct xpp055c272 *ctx;
271d1479d02SHeiko Stuebner int ret;
272d1479d02SHeiko Stuebner
273d1479d02SHeiko Stuebner ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
274d1479d02SHeiko Stuebner if (!ctx)
275d1479d02SHeiko Stuebner return -ENOMEM;
276d1479d02SHeiko Stuebner
277d1479d02SHeiko Stuebner ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
278e82ef424SCai Huoqing if (IS_ERR(ctx->reset_gpio))
279e82ef424SCai Huoqing return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
280e82ef424SCai Huoqing "cannot get reset gpio\n");
281d1479d02SHeiko Stuebner
282d1479d02SHeiko Stuebner ctx->vci = devm_regulator_get(dev, "vci");
283e82ef424SCai Huoqing if (IS_ERR(ctx->vci))
284e82ef424SCai Huoqing return dev_err_probe(dev, PTR_ERR(ctx->vci),
285e82ef424SCai Huoqing "Failed to request vci regulator\n");
286d1479d02SHeiko Stuebner
287d1479d02SHeiko Stuebner ctx->iovcc = devm_regulator_get(dev, "iovcc");
288e82ef424SCai Huoqing if (IS_ERR(ctx->iovcc))
289e82ef424SCai Huoqing return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
290e82ef424SCai Huoqing "Failed to request iovcc regulator\n");
291d1479d02SHeiko Stuebner
292d1479d02SHeiko Stuebner mipi_dsi_set_drvdata(dsi, ctx);
293d1479d02SHeiko Stuebner
294d1479d02SHeiko Stuebner ctx->dev = dev;
295d1479d02SHeiko Stuebner
296d1479d02SHeiko Stuebner dsi->lanes = 4;
297d1479d02SHeiko Stuebner dsi->format = MIPI_DSI_FMT_RGB888;
298d1479d02SHeiko Stuebner dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
2990f3b68b6SNicolas Boichat MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
300d1479d02SHeiko Stuebner
301d1479d02SHeiko Stuebner drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs,
302d1479d02SHeiko Stuebner DRM_MODE_CONNECTOR_DSI);
303d1479d02SHeiko Stuebner
304d1479d02SHeiko Stuebner ret = drm_panel_of_backlight(&ctx->panel);
305d1479d02SHeiko Stuebner if (ret)
306d1479d02SHeiko Stuebner return ret;
307d1479d02SHeiko Stuebner
308d1479d02SHeiko Stuebner drm_panel_add(&ctx->panel);
309d1479d02SHeiko Stuebner
310d1479d02SHeiko Stuebner ret = mipi_dsi_attach(dsi);
311d1479d02SHeiko Stuebner if (ret < 0) {
312a25b6b27SSam Ravnborg dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
313d1479d02SHeiko Stuebner drm_panel_remove(&ctx->panel);
314d1479d02SHeiko Stuebner return ret;
315d1479d02SHeiko Stuebner }
316d1479d02SHeiko Stuebner
317d1479d02SHeiko Stuebner return 0;
318d1479d02SHeiko Stuebner }
319d1479d02SHeiko Stuebner
xpp055c272_shutdown(struct mipi_dsi_device * dsi)320d1479d02SHeiko Stuebner static void xpp055c272_shutdown(struct mipi_dsi_device *dsi)
321d1479d02SHeiko Stuebner {
322d1479d02SHeiko Stuebner struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
323d1479d02SHeiko Stuebner int ret;
324d1479d02SHeiko Stuebner
325d1479d02SHeiko Stuebner ret = drm_panel_unprepare(&ctx->panel);
326d1479d02SHeiko Stuebner if (ret < 0)
327a25b6b27SSam Ravnborg dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
328d1479d02SHeiko Stuebner
329d1479d02SHeiko Stuebner ret = drm_panel_disable(&ctx->panel);
330d1479d02SHeiko Stuebner if (ret < 0)
331a25b6b27SSam Ravnborg dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
332d1479d02SHeiko Stuebner }
333d1479d02SHeiko Stuebner
xpp055c272_remove(struct mipi_dsi_device * dsi)33479abca2bSUwe Kleine-König static void xpp055c272_remove(struct mipi_dsi_device *dsi)
335d1479d02SHeiko Stuebner {
336d1479d02SHeiko Stuebner struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
337d1479d02SHeiko Stuebner int ret;
338d1479d02SHeiko Stuebner
339d1479d02SHeiko Stuebner xpp055c272_shutdown(dsi);
340d1479d02SHeiko Stuebner
341d1479d02SHeiko Stuebner ret = mipi_dsi_detach(dsi);
342d1479d02SHeiko Stuebner if (ret < 0)
343a25b6b27SSam Ravnborg dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
344d1479d02SHeiko Stuebner
345d1479d02SHeiko Stuebner drm_panel_remove(&ctx->panel);
346d1479d02SHeiko Stuebner }
347d1479d02SHeiko Stuebner
348d1479d02SHeiko Stuebner static const struct of_device_id xpp055c272_of_match[] = {
349d1479d02SHeiko Stuebner { .compatible = "xinpeng,xpp055c272" },
350d1479d02SHeiko Stuebner { /* sentinel */ }
351d1479d02SHeiko Stuebner };
352d1479d02SHeiko Stuebner MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
353d1479d02SHeiko Stuebner
354d1479d02SHeiko Stuebner static struct mipi_dsi_driver xpp055c272_driver = {
355d1479d02SHeiko Stuebner .driver = {
356d1479d02SHeiko Stuebner .name = "panel-xinpeng-xpp055c272",
357d1479d02SHeiko Stuebner .of_match_table = xpp055c272_of_match,
358d1479d02SHeiko Stuebner },
359d1479d02SHeiko Stuebner .probe = xpp055c272_probe,
360d1479d02SHeiko Stuebner .remove = xpp055c272_remove,
361d1479d02SHeiko Stuebner .shutdown = xpp055c272_shutdown,
362d1479d02SHeiko Stuebner };
363d1479d02SHeiko Stuebner module_mipi_dsi_driver(xpp055c272_driver);
364d1479d02SHeiko Stuebner
365d1479d02SHeiko Stuebner MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
366d1479d02SHeiko Stuebner MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
367d1479d02SHeiko Stuebner MODULE_LICENSE("GPL v2");
368