File Index
Files and directories
Sign in
FILE

panel-xinpeng-xpp055c272.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c C
  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 void xpp055c272_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
 63{
 64	/*
 65	 * Init sequence was supplied by the panel vendor without much
 66	 * documentation.
 67	 */
 68	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83);
 69	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETMIPI,
 70				     0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00,
 71				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
 72				     0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01,
 73				     0x00, 0x00, 0x37);
 74	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETPOWER_EXT, 0x25);
 75	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00);
 76	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETRGBIF,
 77				     0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00,
 78				     0x00, 0x00);
 79	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETSCR,
 80				     0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
 81				     0x00);
 82	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETVDC, 0x46);
 83	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETPANEL, 0x0b);
 84	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETCYC, 0x80);
 85	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30);
 86	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETEQ,
 87				     0x07, 0x07, 0x0b, 0x0b, 0x03, 0x0b, 0x00, 0x00,
 88				     0x00, 0x00, 0xff, 0x00, 0xC0, 0x10);
 89	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETPOWER,
 90				     0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd,
 91				     0x67, 0x77, 0x33, 0x33);
 92	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff,
 93				     0xff, 0x01, 0xff);
 94	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETBGP, 0x09, 0x09);
 95	mipi_dsi_msleep(dsi_ctx, 20);
 96
 97	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETVCOM, 0x87, 0x95);
 98	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETGIP1,
 99				     0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12,
100				     0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18,
101				     0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
102				     0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42,
103				     0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58,
104				     0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88,
105				     0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
106				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
107	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETGIP2,
108				     0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
109				     0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35,
110				     0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f,
111				     0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88,
112				     0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00,
113				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05,
115				     0xa0, 0x00, 0x00, 0x00, 0x00);
116	mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETGAMMA,
117				     0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36,
118				     0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11,
119				     0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38,
120				     0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13,
121				     0x11, 0x18);
122
123	mipi_dsi_msleep(dsi_ctx, 60);
124}
125
126static int xpp055c272_unprepare(struct drm_panel *panel)
127{
128	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
129	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
130	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
131
132	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
133	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
134	if (dsi_ctx.accum_err)
135		return dsi_ctx.accum_err;
136
137	regulator_disable(ctx->iovcc);
138	regulator_disable(ctx->vci);
139
140	return 0;
141}
142
143static int xpp055c272_prepare(struct drm_panel *panel)
144{
145	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
146	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
147	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
148
149	dev_dbg(ctx->dev, "Resetting the panel\n");
150	dsi_ctx.accum_err = regulator_enable(ctx->vci);
151	if (dsi_ctx.accum_err) {
152		dev_err(ctx->dev, "Failed to enable vci supply: %d\n",
153			dsi_ctx.accum_err);
154		return dsi_ctx.accum_err;
155	}
156	dsi_ctx.accum_err = regulator_enable(ctx->iovcc);
157	if (dsi_ctx.accum_err) {
158		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n",
159			dsi_ctx.accum_err);
160		goto disable_vci;
161	}
162
163	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
164	/* T6: 10us */
165	usleep_range(10, 20);
166	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
167
168	/* T8: 20ms */
169	msleep(20);
170
171	xpp055c272_init_sequence(&dsi_ctx);
172	if (!dsi_ctx.accum_err)
173		dev_dbg(ctx->dev, "Panel init sequence done\n");
174
175	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
176	/* T9: 120ms */
177	mipi_dsi_msleep(&dsi_ctx, 120);
178	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
179
180	if (dsi_ctx.accum_err)
181		goto disable_iovcc;
182
183	msleep(50);
184
185	return 0;
186
187disable_iovcc:
188	regulator_disable(ctx->iovcc);
189disable_vci:
190	regulator_disable(ctx->vci);
191	return dsi_ctx.accum_err;
192}
193
194static const struct drm_display_mode default_mode = {
195	.hdisplay	= 720,
196	.hsync_start	= 720 + 40,
197	.hsync_end	= 720 + 40 + 10,
198	.htotal		= 720 + 40 + 10 + 40,
199	.vdisplay	= 1280,
200	.vsync_start	= 1280 + 22,
201	.vsync_end	= 1280 + 22 + 4,
202	.vtotal		= 1280 + 22 + 4 + 11,
203	.clock		= 64000,
204	.width_mm	= 68,
205	.height_mm	= 121,
206};
207
208static int xpp055c272_get_modes(struct drm_panel *panel,
209				struct drm_connector *connector)
210{
211	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
212	struct drm_display_mode *mode;
213
214	mode = drm_mode_duplicate(connector->dev, &default_mode);
215	if (!mode) {
216		dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
217			default_mode.hdisplay, default_mode.vdisplay,
218			drm_mode_vrefresh(&default_mode));
219		return -ENOMEM;
220	}
221
222	drm_mode_set_name(mode);
223
224	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
225	connector->display_info.width_mm = mode->width_mm;
226	connector->display_info.height_mm = mode->height_mm;
227	drm_mode_probed_add(connector, mode);
228
229	return 1;
230}
231
232static const struct drm_panel_funcs xpp055c272_funcs = {
233	.unprepare	= xpp055c272_unprepare,
234	.prepare	= xpp055c272_prepare,
235	.get_modes	= xpp055c272_get_modes,
236};
237
238static int xpp055c272_probe(struct mipi_dsi_device *dsi)
239{
240	struct device *dev = &dsi->dev;
241	struct xpp055c272 *ctx;
242	int ret;
243
244	ctx = devm_drm_panel_alloc(dev, struct xpp055c272, panel,
245				   &xpp055c272_funcs, DRM_MODE_CONNECTOR_DSI);
246	if (IS_ERR(ctx))
247		return PTR_ERR(ctx);
248
249	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
250	if (IS_ERR(ctx->reset_gpio))
251		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
252				     "cannot get reset gpio\n");
253
254	ctx->vci = devm_regulator_get(dev, "vci");
255	if (IS_ERR(ctx->vci))
256		return dev_err_probe(dev, PTR_ERR(ctx->vci),
257				     "Failed to request vci regulator\n");
258
259	ctx->iovcc = devm_regulator_get(dev, "iovcc");
260	if (IS_ERR(ctx->iovcc))
261		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
262				     "Failed to request iovcc regulator\n");
263
264	mipi_dsi_set_drvdata(dsi, ctx);
265
266	ctx->dev = dev;
267
268	dsi->lanes = 4;
269	dsi->format = MIPI_DSI_FMT_RGB888;
270	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
271			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
272
273	ret = drm_panel_of_backlight(&ctx->panel);
274	if (ret)
275		return ret;
276
277	drm_panel_add(&ctx->panel);
278
279	ret = mipi_dsi_attach(dsi);
280	if (ret < 0) {
281		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
282		drm_panel_remove(&ctx->panel);
283		return ret;
284	}
285
286	return 0;
287}
288
289static void xpp055c272_remove(struct mipi_dsi_device *dsi)
290{
291	struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
292	int ret;
293
294	ret = mipi_dsi_detach(dsi);
295	if (ret < 0)
296		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
297
298	drm_panel_remove(&ctx->panel);
299}
300
301static const struct of_device_id xpp055c272_of_match[] = {
302	{ .compatible = "xinpeng,xpp055c272" },
303	{ /* sentinel */ }
304};
305MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
306
307static struct mipi_dsi_driver xpp055c272_driver = {
308	.driver = {
309		.name = "panel-xinpeng-xpp055c272",
310		.of_match_table = xpp055c272_of_match,
311	},
312	.probe	= xpp055c272_probe,
313	.remove = xpp055c272_remove,
314};
315module_mipi_dsi_driver(xpp055c272_driver);
316
317MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
318MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
319MODULE_LICENSE("GPL v2");
~/ Read-only file preview.