Linux Audio

Check our new training course

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