1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2020 BayLibre, SAS
4  * Author: Neil Armstrong <narmstrong@baylibre.com>
5  */
6 #include <common.h>
7 #include <backlight.h>
8 #include <dm.h>
9 #include <mipi_dsi.h>
10 #include <panel.h>
11 #include <asm/gpio.h>
12 #include <dm/device_compat.h>
13 #include <linux/delay.h>
14 #include <power/regulator.h>
15 
16 struct tl070wsh30_panel_priv {
17 	struct udevice *reg;
18 	struct udevice *backlight;
19 	struct gpio_desc reset;
20 };
21 
22 static const struct display_timing default_timing = {
23 	.pixelclock.typ		= 47250000,
24 	.hactive.typ		= 1024,
25 	.hfront_porch.typ	= 46,
26 	.hback_porch.typ	= 100,
27 	.hsync_len.typ		= 80,
28 	.vactive.typ		= 600,
29 	.vfront_porch.typ	= 5,
30 	.vback_porch.typ	= 20,
31 	.vsync_len.typ		= 5,
32 	.flags			= DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH,
33 };
34 
tl070wsh30_panel_enable_backlight(struct udevice * dev)35 static int tl070wsh30_panel_enable_backlight(struct udevice *dev)
36 {
37 	struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
38 	struct mipi_dsi_device *device = plat->device;
39 	struct tl070wsh30_panel_priv *priv = dev_get_priv(dev);
40 	int ret;
41 
42 	ret = mipi_dsi_attach(device);
43 	if (ret < 0)
44 		return ret;
45 
46 	ret = mipi_dsi_dcs_exit_sleep_mode(device);
47 	if (ret)
48 		return ret;
49 
50 	mdelay(200);
51 
52 	ret = mipi_dsi_dcs_set_display_on(device);
53 	if (ret)
54 		return ret;
55 
56 	mdelay(20);
57 
58 	ret = backlight_enable(priv->backlight);
59 	if (ret)
60 		return ret;
61 
62 	return 0;
63 }
64 
tl070wsh30_panel_get_display_timing(struct udevice * dev,struct display_timing * timings)65 static int tl070wsh30_panel_get_display_timing(struct udevice *dev,
66 					    struct display_timing *timings)
67 {
68 	memcpy(timings, &default_timing, sizeof(*timings));
69 
70 	return 0;
71 }
72 
tl070wsh30_panel_of_to_plat(struct udevice * dev)73 static int tl070wsh30_panel_of_to_plat(struct udevice *dev)
74 {
75 	struct tl070wsh30_panel_priv *priv = dev_get_priv(dev);
76 	int ret;
77 
78 	if (IS_ENABLED(CONFIG_DM_REGULATOR)) {
79 		ret =  device_get_supply_regulator(dev, "power-supply",
80 						   &priv->reg);
81 		if (ret && ret != -ENOENT) {
82 			dev_err(dev, "Warning: cannot get power supply\n");
83 			return ret;
84 		}
85 	}
86 
87 	ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset,
88 				   GPIOD_IS_OUT);
89 	if (ret) {
90 		dev_err(dev, "Warning: cannot get reset GPIO\n");
91 		if (ret != -ENOENT)
92 			return ret;
93 	}
94 
95 	ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
96 					   "backlight", &priv->backlight);
97 	if (ret) {
98 		dev_err(dev, "Cannot get backlight: ret=%d\n", ret);
99 		return ret;
100 	}
101 
102 	return 0;
103 }
104 
tl070wsh30_panel_probe(struct udevice * dev)105 static int tl070wsh30_panel_probe(struct udevice *dev)
106 {
107 	struct tl070wsh30_panel_priv *priv = dev_get_priv(dev);
108 	struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
109 	int ret;
110 
111 	if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) {
112 		ret = regulator_set_enable(priv->reg, true);
113 		if (ret)
114 			return ret;
115 	}
116 
117 	mdelay(10);
118 
119 	/* reset panel */
120 	dm_gpio_set_value(&priv->reset, true);
121 
122 	mdelay(10);
123 
124 	dm_gpio_set_value(&priv->reset, false);
125 
126 	/* fill characteristics of DSI data link */
127 	plat->lanes = 4;
128 	plat->format = MIPI_DSI_FMT_RGB888;
129 	plat->mode_flags = MIPI_DSI_MODE_VIDEO |
130 			   MIPI_DSI_MODE_VIDEO_BURST |
131 			   MIPI_DSI_MODE_LPM;
132 
133 	return 0;
134 }
135 
136 static const struct panel_ops tl070wsh30_panel_ops = {
137 	.enable_backlight = tl070wsh30_panel_enable_backlight,
138 	.get_display_timing = tl070wsh30_panel_get_display_timing,
139 };
140 
141 static const struct udevice_id tl070wsh30_panel_ids[] = {
142 	{ .compatible = "tdo,tl070wsh30" },
143 	{ }
144 };
145 
146 U_BOOT_DRIVER(tl070wsh30_panel) = {
147 	.name			  = "tl070wsh30_panel",
148 	.id			  = UCLASS_PANEL,
149 	.of_match		  = tl070wsh30_panel_ids,
150 	.ops			  = &tl070wsh30_panel_ops,
151 	.of_to_plat		  = tl070wsh30_panel_of_to_plat,
152 	.probe			  = tl070wsh30_panel_probe,
153 	.plat_auto		  = sizeof(struct mipi_dsi_panel_plat),
154 	.priv_auto		  = sizeof(struct tl070wsh30_panel_priv),
155 };
156