Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Feb 10-13, 2025
Register
Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Copyright (C) 2013, NVIDIA Corporation.  All rights reserved.
  3 *
  4 * Permission is hereby granted, free of charge, to any person obtaining a
  5 * copy of this software and associated documentation files (the "Software"),
  6 * to deal in the Software without restriction, including without limitation
  7 * the rights to use, copy, modify, merge, publish, distribute, sub license,
  8 * and/or sell copies of the Software, and to permit persons to whom the
  9 * Software is furnished to do so, subject to the following conditions:
 10 *
 11 * The above copyright notice and this permission notice (including the
 12 * next paragraph) shall be included in all copies or substantial portions
 13 * of the Software.
 14 *
 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 21 * DEALINGS IN THE SOFTWARE.
 22 */
 23
 24#include <linux/backlight.h>
 25#include <linux/gpio/consumer.h>
 26#include <linux/module.h>
 27#include <linux/of_platform.h>
 28#include <linux/platform_device.h>
 29#include <linux/regulator/consumer.h>
 30
 31#include <drm/drmP.h>
 32#include <drm/drm_crtc.h>
 33#include <drm/drm_mipi_dsi.h>
 34#include <drm/drm_panel.h>
 35
 36struct panel_desc {
 37	const struct drm_display_mode *modes;
 38	unsigned int num_modes;
 39
 40	struct {
 41		unsigned int width;
 42		unsigned int height;
 43	} size;
 44};
 45
 46struct panel_simple {
 47	struct drm_panel base;
 48	bool enabled;
 49
 50	const struct panel_desc *desc;
 51
 52	struct backlight_device *backlight;
 53	struct regulator *supply;
 54	struct i2c_adapter *ddc;
 55
 56	struct gpio_desc *enable_gpio;
 57};
 58
 59static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
 60{
 61	return container_of(panel, struct panel_simple, base);
 62}
 63
 64static int panel_simple_get_fixed_modes(struct panel_simple *panel)
 65{
 66	struct drm_connector *connector = panel->base.connector;
 67	struct drm_device *drm = panel->base.drm;
 68	struct drm_display_mode *mode;
 69	unsigned int i, num = 0;
 70
 71	if (!panel->desc)
 72		return 0;
 73
 74	for (i = 0; i < panel->desc->num_modes; i++) {
 75		const struct drm_display_mode *m = &panel->desc->modes[i];
 76
 77		mode = drm_mode_duplicate(drm, m);
 78		if (!mode) {
 79			dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
 80				m->hdisplay, m->vdisplay, m->vrefresh);
 81			continue;
 82		}
 83
 84		drm_mode_set_name(mode);
 85
 86		drm_mode_probed_add(connector, mode);
 87		num++;
 88	}
 89
 90	connector->display_info.width_mm = panel->desc->size.width;
 91	connector->display_info.height_mm = panel->desc->size.height;
 92
 93	return num;
 94}
 95
 96static int panel_simple_disable(struct drm_panel *panel)
 97{
 98	struct panel_simple *p = to_panel_simple(panel);
 99
100	if (!p->enabled)
101		return 0;
102
103	if (p->backlight) {
104		p->backlight->props.power = FB_BLANK_POWERDOWN;
105		backlight_update_status(p->backlight);
106	}
107
108	if (p->enable_gpio)
109		gpiod_set_value_cansleep(p->enable_gpio, 0);
110
111	regulator_disable(p->supply);
112	p->enabled = false;
113
114	return 0;
115}
116
117static int panel_simple_enable(struct drm_panel *panel)
118{
119	struct panel_simple *p = to_panel_simple(panel);
120	int err;
121
122	if (p->enabled)
123		return 0;
124
125	err = regulator_enable(p->supply);
126	if (err < 0) {
127		dev_err(panel->dev, "failed to enable supply: %d\n", err);
128		return err;
129	}
130
131	if (p->enable_gpio)
132		gpiod_set_value_cansleep(p->enable_gpio, 1);
133
134	if (p->backlight) {
135		p->backlight->props.power = FB_BLANK_UNBLANK;
136		backlight_update_status(p->backlight);
137	}
138
139	p->enabled = true;
140
141	return 0;
142}
143
144static int panel_simple_get_modes(struct drm_panel *panel)
145{
146	struct panel_simple *p = to_panel_simple(panel);
147	int num = 0;
148
149	/* probe EDID if a DDC bus is available */
150	if (p->ddc) {
151		struct edid *edid = drm_get_edid(panel->connector, p->ddc);
152		drm_mode_connector_update_edid_property(panel->connector, edid);
153		if (edid) {
154			num += drm_add_edid_modes(panel->connector, edid);
155			kfree(edid);
156		}
157	}
158
159	/* add hard-coded panel modes */
160	num += panel_simple_get_fixed_modes(p);
161
162	return num;
163}
164
165static const struct drm_panel_funcs panel_simple_funcs = {
166	.disable = panel_simple_disable,
167	.enable = panel_simple_enable,
168	.get_modes = panel_simple_get_modes,
169};
170
171static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
172{
173	struct device_node *backlight, *ddc;
174	struct panel_simple *panel;
175	int err;
176
177	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
178	if (!panel)
179		return -ENOMEM;
180
181	panel->enabled = false;
182	panel->desc = desc;
183
184	panel->supply = devm_regulator_get(dev, "power");
185	if (IS_ERR(panel->supply))
186		return PTR_ERR(panel->supply);
187
188	panel->enable_gpio = devm_gpiod_get(dev, "enable");
189	if (IS_ERR(panel->enable_gpio)) {
190		err = PTR_ERR(panel->enable_gpio);
191		if (err != -ENOENT) {
192			dev_err(dev, "failed to request GPIO: %d\n", err);
193			return err;
194		}
195
196		panel->enable_gpio = NULL;
197	} else {
198		err = gpiod_direction_output(panel->enable_gpio, 0);
199		if (err < 0) {
200			dev_err(dev, "failed to setup GPIO: %d\n", err);
201			return err;
202		}
203	}
204
205	backlight = of_parse_phandle(dev->of_node, "backlight", 0);
206	if (backlight) {
207		panel->backlight = of_find_backlight_by_node(backlight);
208		of_node_put(backlight);
209
210		if (!panel->backlight)
211			return -EPROBE_DEFER;
212	}
213
214	ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
215	if (ddc) {
216		panel->ddc = of_find_i2c_adapter_by_node(ddc);
217		of_node_put(ddc);
218
219		if (!panel->ddc) {
220			err = -EPROBE_DEFER;
221			goto free_backlight;
222		}
223	}
224
225	drm_panel_init(&panel->base);
226	panel->base.dev = dev;
227	panel->base.funcs = &panel_simple_funcs;
228
229	err = drm_panel_add(&panel->base);
230	if (err < 0)
231		goto free_ddc;
232
233	dev_set_drvdata(dev, panel);
234
235	return 0;
236
237free_ddc:
238	if (panel->ddc)
239		put_device(&panel->ddc->dev);
240free_backlight:
241	if (panel->backlight)
242		put_device(&panel->backlight->dev);
243
244	return err;
245}
246
247static int panel_simple_remove(struct device *dev)
248{
249	struct panel_simple *panel = dev_get_drvdata(dev);
250
251	drm_panel_detach(&panel->base);
252	drm_panel_remove(&panel->base);
253
254	panel_simple_disable(&panel->base);
255
256	if (panel->ddc)
257		put_device(&panel->ddc->dev);
258
259	if (panel->backlight)
260		put_device(&panel->backlight->dev);
261
262	return 0;
263}
264
265static const struct drm_display_mode auo_b101aw03_mode = {
266	.clock = 51450,
267	.hdisplay = 1024,
268	.hsync_start = 1024 + 156,
269	.hsync_end = 1024 + 156 + 8,
270	.htotal = 1024 + 156 + 8 + 156,
271	.vdisplay = 600,
272	.vsync_start = 600 + 16,
273	.vsync_end = 600 + 16 + 6,
274	.vtotal = 600 + 16 + 6 + 16,
275	.vrefresh = 60,
276};
277
278static const struct panel_desc auo_b101aw03 = {
279	.modes = &auo_b101aw03_mode,
280	.num_modes = 1,
281	.size = {
282		.width = 223,
283		.height = 125,
284	},
285};
286
287static const struct drm_display_mode chunghwa_claa101wa01a_mode = {
288	.clock = 72070,
289	.hdisplay = 1366,
290	.hsync_start = 1366 + 58,
291	.hsync_end = 1366 + 58 + 58,
292	.htotal = 1366 + 58 + 58 + 58,
293	.vdisplay = 768,
294	.vsync_start = 768 + 4,
295	.vsync_end = 768 + 4 + 4,
296	.vtotal = 768 + 4 + 4 + 4,
297	.vrefresh = 60,
298};
299
300static const struct panel_desc chunghwa_claa101wa01a = {
301	.modes = &chunghwa_claa101wa01a_mode,
302	.num_modes = 1,
303	.size = {
304		.width = 220,
305		.height = 120,
306	},
307};
308
309static const struct drm_display_mode chunghwa_claa101wb01_mode = {
310	.clock = 69300,
311	.hdisplay = 1366,
312	.hsync_start = 1366 + 48,
313	.hsync_end = 1366 + 48 + 32,
314	.htotal = 1366 + 48 + 32 + 20,
315	.vdisplay = 768,
316	.vsync_start = 768 + 16,
317	.vsync_end = 768 + 16 + 8,
318	.vtotal = 768 + 16 + 8 + 16,
319	.vrefresh = 60,
320};
321
322static const struct panel_desc chunghwa_claa101wb01 = {
323	.modes = &chunghwa_claa101wb01_mode,
324	.num_modes = 1,
325	.size = {
326		.width = 223,
327		.height = 125,
328	},
329};
330
331static const struct drm_display_mode lg_lp129qe_mode = {
332	.clock = 285250,
333	.hdisplay = 2560,
334	.hsync_start = 2560 + 48,
335	.hsync_end = 2560 + 48 + 32,
336	.htotal = 2560 + 48 + 32 + 80,
337	.vdisplay = 1700,
338	.vsync_start = 1700 + 3,
339	.vsync_end = 1700 + 3 + 10,
340	.vtotal = 1700 + 3 + 10 + 36,
341	.vrefresh = 60,
342};
343
344static const struct panel_desc lg_lp129qe = {
345	.modes = &lg_lp129qe_mode,
346	.num_modes = 1,
347	.size = {
348		.width = 272,
349		.height = 181,
350	},
351};
352
353static const struct drm_display_mode samsung_ltn101nt05_mode = {
354	.clock = 54030,
355	.hdisplay = 1024,
356	.hsync_start = 1024 + 24,
357	.hsync_end = 1024 + 24 + 136,
358	.htotal = 1024 + 24 + 136 + 160,
359	.vdisplay = 600,
360	.vsync_start = 600 + 3,
361	.vsync_end = 600 + 3 + 6,
362	.vtotal = 600 + 3 + 6 + 61,
363	.vrefresh = 60,
364};
365
366static const struct panel_desc samsung_ltn101nt05 = {
367	.modes = &samsung_ltn101nt05_mode,
368	.num_modes = 1,
369	.size = {
370		.width = 1024,
371		.height = 600,
372	},
373};
374
375static const struct of_device_id platform_of_match[] = {
376	{
377		.compatible = "auo,b101aw03",
378		.data = &auo_b101aw03,
379	}, {
380		.compatible = "chunghwa,claa101wa01a",
381		.data = &chunghwa_claa101wa01a
382	}, {
383		.compatible = "chunghwa,claa101wb01",
384		.data = &chunghwa_claa101wb01
385	}, {
386		.compatible = "lg,lp129qe",
387		.data = &lg_lp129qe,
388	}, {
389		.compatible = "samsung,ltn101nt05",
390		.data = &samsung_ltn101nt05,
391	}, {
392		.compatible = "simple-panel",
393	}, {
394		/* sentinel */
395	}
396};
397MODULE_DEVICE_TABLE(of, platform_of_match);
398
399static int panel_simple_platform_probe(struct platform_device *pdev)
400{
401	const struct of_device_id *id;
402
403	id = of_match_node(platform_of_match, pdev->dev.of_node);
404	if (!id)
405		return -ENODEV;
406
407	return panel_simple_probe(&pdev->dev, id->data);
408}
409
410static int panel_simple_platform_remove(struct platform_device *pdev)
411{
412	return panel_simple_remove(&pdev->dev);
413}
414
415static struct platform_driver panel_simple_platform_driver = {
416	.driver = {
417		.name = "panel-simple",
418		.owner = THIS_MODULE,
419		.of_match_table = platform_of_match,
420	},
421	.probe = panel_simple_platform_probe,
422	.remove = panel_simple_platform_remove,
423};
424
425struct panel_desc_dsi {
426	struct panel_desc desc;
427
428	unsigned long flags;
429	enum mipi_dsi_pixel_format format;
430	unsigned int lanes;
431};
432
433static const struct drm_display_mode lg_ld070wx3_sl01_mode = {
434	.clock = 71000,
435	.hdisplay = 800,
436	.hsync_start = 800 + 32,
437	.hsync_end = 800 + 32 + 1,
438	.htotal = 800 + 32 + 1 + 57,
439	.vdisplay = 1280,
440	.vsync_start = 1280 + 28,
441	.vsync_end = 1280 + 28 + 1,
442	.vtotal = 1280 + 28 + 1 + 14,
443	.vrefresh = 60,
444};
445
446static const struct panel_desc_dsi lg_ld070wx3_sl01 = {
447	.desc = {
448		.modes = &lg_ld070wx3_sl01_mode,
449		.num_modes = 1,
450		.size = {
451			.width = 94,
452			.height = 151,
453		},
454	},
455	.flags = MIPI_DSI_MODE_VIDEO,
456	.format = MIPI_DSI_FMT_RGB888,
457	.lanes = 4,
458};
459
460static const struct drm_display_mode lg_lh500wx1_sd03_mode = {
461	.clock = 67000,
462	.hdisplay = 720,
463	.hsync_start = 720 + 12,
464	.hsync_end = 720 + 12 + 4,
465	.htotal = 720 + 12 + 4 + 112,
466	.vdisplay = 1280,
467	.vsync_start = 1280 + 8,
468	.vsync_end = 1280 + 8 + 4,
469	.vtotal = 1280 + 8 + 4 + 12,
470	.vrefresh = 60,
471};
472
473static const struct panel_desc_dsi lg_lh500wx1_sd03 = {
474	.desc = {
475		.modes = &lg_lh500wx1_sd03_mode,
476		.num_modes = 1,
477		.size = {
478			.width = 62,
479			.height = 110,
480		},
481	},
482	.flags = MIPI_DSI_MODE_VIDEO,
483	.format = MIPI_DSI_FMT_RGB888,
484	.lanes = 4,
485};
486
487static const struct drm_display_mode panasonic_vvx10f004b00_mode = {
488	.clock = 157200,
489	.hdisplay = 1920,
490	.hsync_start = 1920 + 154,
491	.hsync_end = 1920 + 154 + 16,
492	.htotal = 1920 + 154 + 16 + 32,
493	.vdisplay = 1200,
494	.vsync_start = 1200 + 17,
495	.vsync_end = 1200 + 17 + 2,
496	.vtotal = 1200 + 17 + 2 + 16,
497	.vrefresh = 60,
498};
499
500static const struct panel_desc_dsi panasonic_vvx10f004b00 = {
501	.desc = {
502		.modes = &panasonic_vvx10f004b00_mode,
503		.num_modes = 1,
504		.size = {
505			.width = 217,
506			.height = 136,
507		},
508	},
509	.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
510	.format = MIPI_DSI_FMT_RGB888,
511	.lanes = 4,
512};
513
514static const struct of_device_id dsi_of_match[] = {
515	{
516		.compatible = "lg,ld070wx3-sl01",
517		.data = &lg_ld070wx3_sl01
518	}, {
519		.compatible = "lg,lh500wx1-sd03",
520		.data = &lg_lh500wx1_sd03
521	}, {
522		.compatible = "panasonic,vvx10f004b00",
523		.data = &panasonic_vvx10f004b00
524	}, {
525		/* sentinel */
526	}
527};
528MODULE_DEVICE_TABLE(of, dsi_of_match);
529
530static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
531{
532	const struct panel_desc_dsi *desc;
533	const struct of_device_id *id;
534	int err;
535
536	id = of_match_node(dsi_of_match, dsi->dev.of_node);
537	if (!id)
538		return -ENODEV;
539
540	desc = id->data;
541
542	err = panel_simple_probe(&dsi->dev, &desc->desc);
543	if (err < 0)
544		return err;
545
546	dsi->mode_flags = desc->flags;
547	dsi->format = desc->format;
548	dsi->lanes = desc->lanes;
549
550	return mipi_dsi_attach(dsi);
551}
552
553static int panel_simple_dsi_remove(struct mipi_dsi_device *dsi)
554{
555	int err;
556
557	err = mipi_dsi_detach(dsi);
558	if (err < 0)
559		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
560
561	return panel_simple_remove(&dsi->dev);
562}
563
564static struct mipi_dsi_driver panel_simple_dsi_driver = {
565	.driver = {
566		.name = "panel-simple-dsi",
567		.owner = THIS_MODULE,
568		.of_match_table = dsi_of_match,
569	},
570	.probe = panel_simple_dsi_probe,
571	.remove = panel_simple_dsi_remove,
572};
573
574static int __init panel_simple_init(void)
575{
576	int err;
577
578	err = platform_driver_register(&panel_simple_platform_driver);
579	if (err < 0)
580		return err;
581
582	if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
583		err = mipi_dsi_driver_register(&panel_simple_dsi_driver);
584		if (err < 0)
585			return err;
586	}
587
588	return 0;
589}
590module_init(panel_simple_init);
591
592static void __exit panel_simple_exit(void)
593{
594	if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
595		mipi_dsi_driver_unregister(&panel_simple_dsi_driver);
596
597	platform_driver_unregister(&panel_simple_platform_driver);
598}
599module_exit(panel_simple_exit);
600
601MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
602MODULE_DESCRIPTION("DRM Driver for Simple Panels");
603MODULE_LICENSE("GPL and additional rights");