File Index
Files and directories
Sign in
FILE

panel-jdi-lpm102a188a.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/panel/panel-jdi-lpm102a188a.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2014 Google, Inc.
  4 *
  5 * Copyright (C) 2022 Diogo Ivo <diogo.ivo@tecnico.ulisboa.pt>
  6 *
  7 * Adapted from the downstream Pixel C driver written by Sean Paul
  8 */
  9
 10#include <linux/backlight.h>
 11#include <linux/delay.h>
 12#include <linux/gpio/consumer.h>
 13#include <linux/module.h>
 14#include <linux/of.h>
 15#include <linux/regulator/consumer.h>
 16
 17#include <video/mipi_display.h>
 18
 19#include <drm/drm_crtc.h>
 20#include <drm/drm_mipi_dsi.h>
 21#include <drm/drm_panel.h>
 22
 23#define MCS_CMD_ACS_PROT			0xB0
 24#define MCS_CMD_ACS_PROT_OFF			(0 << 0)
 25
 26#define MCS_PWR_CTRL_FUNC			0xD0
 27#define MCS_PWR_CTRL_PARAM1_DEFAULT		(2 << 0)
 28#define MCS_PWR_CTRL_PARAM1_VGH_210_DIV		(1 << 4)
 29#define MCS_PWR_CTRL_PARAM1_VGH_240_DIV		(2 << 4)
 30#define MCS_PWR_CTRL_PARAM1_VGH_280_DIV		(3 << 4)
 31#define MCS_PWR_CTRL_PARAM1_VGH_330_DIV		(4 << 4)
 32#define MCS_PWR_CTRL_PARAM1_VGH_410_DIV		(5 << 4)
 33#define MCS_PWR_CTRL_PARAM2_DEFAULT		(9 << 4)
 34#define MCS_PWR_CTRL_PARAM2_VGL_210_DIV		(1 << 0)
 35#define MCS_PWR_CTRL_PARAM2_VGL_240_DIV		(2 << 0)
 36#define MCS_PWR_CTRL_PARAM2_VGL_280_DIV		(3 << 0)
 37#define MCS_PWR_CTRL_PARAM2_VGL_330_DIV		(4 << 0)
 38#define MCS_PWR_CTRL_PARAM2_VGL_410_DIV		(5 << 0)
 39
 40struct jdi_panel {
 41	struct drm_panel base;
 42	struct mipi_dsi_device *link1;
 43	struct mipi_dsi_device *link2;
 44
 45	struct regulator *supply;
 46	struct regulator *ddi_supply;
 47	struct backlight_device *backlight;
 48
 49	struct gpio_desc *enable_gpio;
 50	struct gpio_desc *reset_gpio;
 51
 52	const struct drm_display_mode *mode;
 53};
 54
 55static inline struct jdi_panel *to_panel_jdi(struct drm_panel *panel)
 56{
 57	return container_of(panel, struct jdi_panel, base);
 58}
 59
 60static void jdi_wait_frames(struct jdi_panel *jdi, unsigned int frames)
 61{
 62	unsigned int refresh = drm_mode_vrefresh(jdi->mode);
 63
 64	if (WARN_ON(frames > refresh))
 65		return;
 66
 67	msleep(1000 / (refresh / frames));
 68}
 69
 70static int jdi_panel_disable(struct drm_panel *panel)
 71{
 72	struct jdi_panel *jdi = to_panel_jdi(panel);
 73
 74	backlight_disable(jdi->backlight);
 75
 76	jdi_wait_frames(jdi, 2);
 77
 78	return 0;
 79}
 80
 81static int jdi_panel_unprepare(struct drm_panel *panel)
 82{
 83	struct jdi_panel *jdi = to_panel_jdi(panel);
 84
 85	/*
 86	 * One context per panel since we'll continue trying to shut down the
 87	 * other panel even if one isn't responding.
 88	 */
 89	struct mipi_dsi_multi_context dsi_ctx1 = { .dsi = jdi->link1 };
 90	struct mipi_dsi_multi_context dsi_ctx2 = { .dsi = jdi->link2 };
 91
 92	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx1);
 93	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx2);
 94
 95	/* Specified by JDI @ 50ms, subject to change */
 96	msleep(50);
 97
 98	/* Doesn't hurt to try sleep mode even if display off fails */
 99	dsi_ctx1.accum_err = 0;
100	dsi_ctx2.accum_err = 0;
101	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx1);
102	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx2);
103
104	/* Specified by JDI @ 150ms, subject to change */
105	msleep(150);
106
107	gpiod_set_value(jdi->reset_gpio, 1);
108
109	/* T4 = 1ms */
110	usleep_range(1000, 3000);
111
112	gpiod_set_value(jdi->enable_gpio, 0);
113
114	/* T5 = 2ms */
115	usleep_range(2000, 4000);
116
117	regulator_disable(jdi->ddi_supply);
118
119	/* T6 = 2ms plus some time to discharge capacitors */
120	usleep_range(7000, 9000);
121
122	regulator_disable(jdi->supply);
123	/* Specified by JDI @ 20ms, subject to change */
124	msleep(20);
125
126	return 0;
127}
128
129static void jdi_setup_symmetrical_split(struct mipi_dsi_multi_context *dsi_ctx,
130					struct mipi_dsi_device *left,
131					struct mipi_dsi_device *right,
132					const struct drm_display_mode *mode)
133{
134	mipi_dsi_dual(mipi_dsi_dcs_set_column_address_multi,
135		      dsi_ctx, left, right,
136		      0, mode->hdisplay / 2 - 1);
137	mipi_dsi_dual(mipi_dsi_dcs_set_page_address_multi,
138		      dsi_ctx, left, right,
139		      0, mode->vdisplay - 1);
140}
141
142static void jdi_write_dcdc_registers(struct mipi_dsi_multi_context *dsi_ctx,
143				     struct jdi_panel *jdi)
144{
145	/* Clear the manufacturer command access protection */
146	mipi_dsi_dual_generic_write_seq_multi(dsi_ctx, jdi->link1, jdi->link2,
147					      MCS_CMD_ACS_PROT,
148					      MCS_CMD_ACS_PROT_OFF);
149	/*
150	 * Change the VGH/VGL divide ratios to move the noise generated by the
151	 * TCONN. This should hopefully avoid interaction with the backlight
152	 * controller.
153	 */
154	mipi_dsi_dual_generic_write_seq_multi(dsi_ctx, jdi->link1, jdi->link2,
155					      MCS_PWR_CTRL_FUNC,
156					      MCS_PWR_CTRL_PARAM1_VGH_330_DIV |
157					      MCS_PWR_CTRL_PARAM1_DEFAULT,
158					      MCS_PWR_CTRL_PARAM2_VGL_410_DIV |
159					      MCS_PWR_CTRL_PARAM2_DEFAULT);
160}
161
162static int jdi_panel_prepare(struct drm_panel *panel)
163{
164	struct jdi_panel *jdi = to_panel_jdi(panel);
165	struct mipi_dsi_multi_context dsi_ctx = {};
166	int err;
167
168	/* Disable backlight to avoid showing random pixels
169	 * with a conservative delay for it to take effect.
170	 */
171	backlight_disable(jdi->backlight);
172	jdi_wait_frames(jdi, 3);
173
174	jdi->link1->mode_flags |= MIPI_DSI_MODE_LPM;
175	jdi->link2->mode_flags |= MIPI_DSI_MODE_LPM;
176
177	err = regulator_enable(jdi->supply);
178	if (err < 0) {
179		dev_err(panel->dev, "failed to enable supply: %d\n", err);
180		return err;
181	}
182	/* T1 = 2ms */
183	usleep_range(2000, 4000);
184
185	err = regulator_enable(jdi->ddi_supply);
186	if (err < 0) {
187		dev_err(panel->dev, "failed to enable ddi_supply: %d\n", err);
188		goto supply_off;
189	}
190	/* T2 = 1ms */
191	usleep_range(1000, 3000);
192
193	gpiod_set_value(jdi->enable_gpio, 1);
194	/* T3 = 10ms */
195	usleep_range(10000, 15000);
196
197	gpiod_set_value(jdi->reset_gpio, 0);
198	/* Specified by JDI @ 3ms, subject to change */
199	usleep_range(3000, 5000);
200
201	/*
202	 * TODO: The device supports both left-right and even-odd split
203	 * configurations, but this driver currently supports only the left-
204	 * right split. To support a different mode a mechanism needs to be
205	 * put in place to communicate the configuration back to the DSI host
206	 * controller.
207	 */
208	jdi_setup_symmetrical_split(&dsi_ctx, jdi->link1, jdi->link2,
209				    jdi->mode);
210
211	mipi_dsi_dual(mipi_dsi_dcs_set_tear_scanline_multi,
212		      &dsi_ctx, jdi->link1, jdi->link2,
213		      jdi->mode->vdisplay - 16);
214
215	mipi_dsi_dual(mipi_dsi_dcs_set_tear_on_multi,
216		      &dsi_ctx, jdi->link1, jdi->link2,
217		      MIPI_DSI_DCS_TEAR_MODE_VBLANK);
218
219	mipi_dsi_dual(mipi_dsi_dcs_set_pixel_format_multi,
220		      &dsi_ctx, jdi->link1, jdi->link2,
221		      MIPI_DCS_PIXEL_FMT_24BIT);
222
223	mipi_dsi_dual(mipi_dsi_dcs_exit_sleep_mode_multi,
224		      &dsi_ctx, jdi->link1, jdi->link2);
225
226	jdi_write_dcdc_registers(&dsi_ctx, jdi);
227	/*
228	 * We need to wait 150ms between mipi_dsi_dcs_exit_sleep_mode_multi()
229	 * and mipi_dsi_dcs_set_display_on_multi().
230	 */
231	mipi_dsi_msleep(&dsi_ctx, 150);
232
233	mipi_dsi_dual(mipi_dsi_dcs_set_display_on_multi,
234		      &dsi_ctx, jdi->link1, jdi->link2);
235
236	if (dsi_ctx.accum_err < 0) {
237		err = dsi_ctx.accum_err;
238		goto poweroff;
239	}
240
241	jdi->link1->mode_flags &= ~MIPI_DSI_MODE_LPM;
242	jdi->link2->mode_flags &= ~MIPI_DSI_MODE_LPM;
243
244	return 0;
245
246poweroff:
247	regulator_disable(jdi->ddi_supply);
248
249	/* T6 = 2ms plus some time to discharge capacitors */
250	usleep_range(7000, 9000);
251supply_off:
252	regulator_disable(jdi->supply);
253	/* Specified by JDI @ 20ms, subject to change */
254	msleep(20);
255
256	return err;
257}
258
259static int jdi_panel_enable(struct drm_panel *panel)
260{
261	struct jdi_panel *jdi = to_panel_jdi(panel);
262
263	/*
264	 * Ensure we send image data before turning the backlight
265	 * on, to avoid the display showing random pixels.
266	 */
267	jdi_wait_frames(jdi, 3);
268
269	backlight_enable(jdi->backlight);
270
271	return 0;
272}
273
274static const struct drm_display_mode default_mode = {
275	.clock = (2560 + 80 + 80 + 80) * (1800 + 4 + 4 + 4) * 60 / 1000,
276	.hdisplay = 2560,
277	.hsync_start = 2560 + 80,
278	.hsync_end = 2560 + 80 + 80,
279	.htotal = 2560 + 80 + 80 + 80,
280	.vdisplay = 1800,
281	.vsync_start = 1800 + 4,
282	.vsync_end = 1800 + 4 + 4,
283	.vtotal = 1800 + 4 + 4 + 4,
284	.flags = 0,
285};
286
287static int jdi_panel_get_modes(struct drm_panel *panel,
288			       struct drm_connector *connector)
289{
290	struct drm_display_mode *mode;
291	struct jdi_panel *jdi = to_panel_jdi(panel);
292	struct device *dev = &jdi->link1->dev;
293
294	mode = drm_mode_duplicate(connector->dev, &default_mode);
295	if (!mode) {
296		dev_err(dev, "failed to add mode %ux%ux@%u\n",
297			default_mode.hdisplay, default_mode.vdisplay,
298			drm_mode_vrefresh(&default_mode));
299		return -ENOMEM;
300	}
301
302	drm_mode_set_name(mode);
303
304	drm_mode_probed_add(connector, mode);
305
306	connector->display_info.width_mm = 211;
307	connector->display_info.height_mm = 148;
308	connector->display_info.bpc = 8;
309
310	return 1;
311}
312
313static const struct drm_panel_funcs jdi_panel_funcs = {
314	.prepare = jdi_panel_prepare,
315	.enable = jdi_panel_enable,
316	.disable = jdi_panel_disable,
317	.unprepare = jdi_panel_unprepare,
318	.get_modes = jdi_panel_get_modes,
319};
320
321static const struct of_device_id jdi_of_match[] = {
322	{ .compatible = "jdi,lpm102a188a", },
323	{ }
324};
325MODULE_DEVICE_TABLE(of, jdi_of_match);
326
327static int jdi_panel_add(struct jdi_panel *jdi)
328{
329	struct device *dev = &jdi->link1->dev;
330
331	jdi->mode = &default_mode;
332
333	jdi->supply = devm_regulator_get(dev, "power");
334	if (IS_ERR(jdi->supply))
335		return dev_err_probe(dev, PTR_ERR(jdi->supply),
336				     "failed to get power regulator\n");
337
338	jdi->ddi_supply = devm_regulator_get(dev, "ddi");
339	if (IS_ERR(jdi->ddi_supply))
340		return dev_err_probe(dev, PTR_ERR(jdi->ddi_supply),
341				     "failed to get ddi regulator\n");
342
343	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
344	if (IS_ERR(jdi->reset_gpio))
345		return dev_err_probe(dev, PTR_ERR(jdi->reset_gpio),
346				     "failed to get reset gpio\n");
347	/* T4 = 1ms */
348	usleep_range(1000, 3000);
349
350	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
351	if (IS_ERR(jdi->enable_gpio))
352		return dev_err_probe(dev, PTR_ERR(jdi->enable_gpio),
353				     "failed to get enable gpio\n");
354	/* T5 = 2ms */
355	usleep_range(2000, 4000);
356
357	jdi->backlight = devm_of_find_backlight(dev);
358	if (IS_ERR(jdi->backlight))
359		return dev_err_probe(dev, PTR_ERR(jdi->backlight),
360				     "failed to create backlight\n");
361
362	drm_panel_add(&jdi->base);
363
364	return 0;
365}
366
367static void jdi_panel_del(struct jdi_panel *jdi)
368{
369	if (jdi->base.dev)
370		drm_panel_remove(&jdi->base);
371
372	if (jdi->link2)
373		put_device(&jdi->link2->dev);
374}
375
376static int jdi_panel_dsi_probe(struct mipi_dsi_device *dsi)
377{
378	struct mipi_dsi_device *secondary = NULL;
379	struct jdi_panel *jdi;
380	struct device_node *np;
381	int err;
382
383	dsi->lanes = 4;
384	dsi->format = MIPI_DSI_FMT_RGB888;
385	dsi->mode_flags = 0;
386
387	/* Find DSI-LINK1 */
388	np = of_parse_phandle(dsi->dev.of_node, "link2", 0);
389	if (np) {
390		secondary = of_find_mipi_dsi_device_by_node(np);
391		of_node_put(np);
392
393		if (!secondary)
394			return -EPROBE_DEFER;
395	}
396
397	/* register a panel for only the DSI-LINK1 interface */
398	if (secondary) {
399		jdi = devm_drm_panel_alloc(&dsi->dev, __typeof(*jdi),
400					   base, &jdi_panel_funcs,
401					   DRM_MODE_CONNECTOR_DSI);
402
403		if (IS_ERR(jdi)) {
404			put_device(&secondary->dev);
405			return PTR_ERR(jdi);
406		}
407
408		mipi_dsi_set_drvdata(dsi, jdi);
409
410		jdi->link1 = dsi;
411		jdi->link2 = secondary;
412
413		err = jdi_panel_add(jdi);
414		if (err < 0) {
415			put_device(&secondary->dev);
416			return err;
417		}
418	}
419
420	err = mipi_dsi_attach(dsi);
421	if (err < 0) {
422		if (secondary)
423			jdi_panel_del(jdi);
424
425		return err;
426	}
427
428	return 0;
429}
430
431static void jdi_panel_dsi_remove(struct mipi_dsi_device *dsi)
432{
433	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
434	int err;
435
436	/* only detach from host for the DSI-LINK2 interface */
437	if (!jdi) {
438		mipi_dsi_detach(dsi);
439		return;
440	}
441
442	err = jdi_panel_disable(&jdi->base);
443	if (err < 0)
444		dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
445
446	err = mipi_dsi_detach(dsi);
447	if (err < 0)
448		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
449
450	jdi_panel_del(jdi);
451}
452
453static void jdi_panel_dsi_shutdown(struct mipi_dsi_device *dsi)
454{
455	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
456
457	if (!jdi)
458		return;
459
460	jdi_panel_disable(&jdi->base);
461}
462
463static struct mipi_dsi_driver jdi_panel_dsi_driver = {
464	.driver = {
465		.name = "panel-jdi-lpm102a188a",
466		.of_match_table = jdi_of_match,
467	},
468	.probe = jdi_panel_dsi_probe,
469	.remove = jdi_panel_dsi_remove,
470	.shutdown = jdi_panel_dsi_shutdown,
471};
472module_mipi_dsi_driver(jdi_panel_dsi_driver);
473
474MODULE_AUTHOR("Sean Paul <seanpaul@chromium.org>");
475MODULE_AUTHOR("Diogo Ivo <diogo.ivo@tecnico.ulisboa.pt>");
476MODULE_DESCRIPTION("DRM Driver for JDI LPM102A188A DSI panel, command mode");
477MODULE_LICENSE("GPL");
~/ Read-only file preview.