Linux Audio

Check our new training course

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