blob: 9950ff8bb05e1a45effdb8739bcb68f06b0cf070 [file] [log] [blame]
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
4 */
5
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +03006#include <backlight.h>
7#include <dm.h>
8#include <panel.h>
9#include <log.h>
10#include <misc.h>
11#include <mipi_display.h>
12#include <mipi_dsi.h>
13#include <asm/gpio.h>
14#include <linux/delay.h>
15#include <linux/err.h>
16#include <power/regulator.h>
17
18struct endeavoru_panel_priv {
19 struct udevice *vdd;
20 struct udevice *vddio;
21
22 struct udevice *backlight;
23
24 struct gpio_desc reset_gpio;
25};
26
27static struct display_timing default_timing = {
28 .pixelclock.typ = 63200000,
29 .hactive.typ = 720,
30 .hfront_porch.typ = 55,
31 .hback_porch.typ = 29,
32 .hsync_len.typ = 16,
33 .vactive.typ = 1280,
34 .vfront_porch.typ = 2,
35 .vback_porch.typ = 1,
36 .vsync_len.typ = 1,
37};
38
39static void dcs_write_one(struct mipi_dsi_device *dsi, u8 cmd, u8 data)
40{
41 mipi_dsi_dcs_write(dsi, cmd, &data, 1);
42}
43
44/*
45 * This panel is not able to auto-increment all cmd addresses so for some of
46 * them, we need to send them one by one...
47 */
48#define dcs_write_seq(dsi, cmd, seq...) \
49({ \
50 static const u8 d[] = { seq }; \
51 unsigned int i; \
52 \
53 for (i = 0; i < ARRAY_SIZE(d) ; i++) \
54 dcs_write_one(dsi, cmd + i, d[i]); \
55})
56
57static int endeavoru_panel_enable_backlight(struct udevice *dev)
58{
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +030059 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
60 struct mipi_dsi_device *dsi = plat->device;
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +030061
62 dcs_write_one(dsi, 0xc2, 0x08);
63
64 /* color enhancement 2.2 */
65 dcs_write_one(dsi, 0xff, 0x03);
66 dcs_write_one(dsi, 0xfe, 0x08);
67 dcs_write_one(dsi, 0x18, 0x00);
68 dcs_write_one(dsi, 0x19, 0x00);
69 dcs_write_one(dsi, 0x1a, 0x00);
70 dcs_write_one(dsi, 0x25, 0x26);
71
72 dcs_write_seq(dsi, 0x00, 0x00, 0x05, 0x10, 0x17,
73 0x22, 0x26, 0x29, 0x29, 0x26, 0x23,
74 0x17, 0x12, 0x06, 0x02, 0x01, 0x00);
75
76 dcs_write_one(dsi, 0xfb, 0x01);
77 dcs_write_one(dsi, 0xff, 0x00);
78 dcs_write_one(dsi, 0xfe, 0x01);
79
80 mipi_dsi_dcs_exit_sleep_mode(dsi);
81
82 mdelay(105);
83
84 dcs_write_one(dsi, 0x35, 0x00);
85
86 /* PWM frequency adjust */
87 dcs_write_one(dsi, 0xff, 0x04);
88 dcs_write_one(dsi, 0x0a, 0x07);
89 dcs_write_one(dsi, 0x09, 0x20);
90 dcs_write_one(dsi, 0xff, 0x00);
91
92 dcs_write_one(dsi, 0xff, 0xee);
93 dcs_write_one(dsi, 0x12, 0x50);
94 dcs_write_one(dsi, 0x13, 0x02);
95 dcs_write_one(dsi, 0x6a, 0x60);
96 dcs_write_one(dsi, 0xfb, 0x01);
97 dcs_write_one(dsi, 0xff, 0x00);
98
99 mipi_dsi_dcs_set_display_on(dsi);
100
101 mdelay(42);
102
103 dcs_write_one(dsi, 0xba, 0x01);
104
105 dcs_write_one(dsi, 0x53, 0x24);
106 dcs_write_one(dsi, 0x55, 0x80);
107 dcs_write_one(dsi, 0x5e, 0x06);
108
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +0300109 /* Set backlight */
110 dcs_write_one(dsi, 0x51, 0x96);
111
Svyatoslav Ryhel665b3ac2024-01-31 08:57:19 +0200112 return 0;
113}
114
115static int endeavoru_panel_set_backlight(struct udevice *dev, int percent)
116{
117 struct endeavoru_panel_priv *priv = dev_get_priv(dev);
118 int ret;
119
Svyatoslav Ryhel6f69a0f2025-03-01 14:37:59 +0200120 /*
121 * Due to the use of the Tegra DC backlight feature, backlight
122 * requests MUST NOT be made during probe or earlier. This is
123 * because it creates a loop, as the backlight is a DC child.
124 */
125 ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
126 "backlight", &priv->backlight);
127 if (ret) {
128 log_err("cannot get backlight: ret = %d\n", ret);
129 return ret;
130 }
131
Svyatoslav Ryhel665b3ac2024-01-31 08:57:19 +0200132 ret = backlight_enable(priv->backlight);
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +0300133 if (ret)
134 return ret;
135
Svyatoslav Ryhel665b3ac2024-01-31 08:57:19 +0200136 return backlight_set_brightness(priv->backlight, percent);
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +0300137}
138
139static int endeavoru_panel_timings(struct udevice *dev,
140 struct display_timing *timing)
141{
142 memcpy(timing, &default_timing, sizeof(*timing));
143 return 0;
144}
145
146static int endeavoru_panel_of_to_plat(struct udevice *dev)
147{
148 struct endeavoru_panel_priv *priv = dev_get_priv(dev);
149 int ret;
150
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +0300151 ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
152 "vdd-supply", &priv->vdd);
153 if (ret) {
154 log_err("cannot get vdd-supply: ret = %d\n", ret);
155 return ret;
156 }
157
158 ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
159 "vddio-supply", &priv->vddio);
160 if (ret) {
161 log_err("cannot get vddio-supply: ret = %d\n", ret);
162 return ret;
163 }
164
165 ret = gpio_request_by_name(dev, "reset-gpios", 0,
166 &priv->reset_gpio, GPIOD_IS_OUT);
167 if (ret) {
168 log_err("could not decode reser-gpios (%d)\n", ret);
169 return ret;
170 }
171
172 return 0;
173}
174
Svyatoslav Ryhel665b3ac2024-01-31 08:57:19 +0200175static int endeavoru_panel_hw_init(struct udevice *dev)
176{
177 struct endeavoru_panel_priv *priv = dev_get_priv(dev);
178 int ret;
179
180 ret = dm_gpio_set_value(&priv->reset_gpio, 1);
181 if (ret) {
182 log_debug("%s: error changing reset-gpios (%d)\n",
183 __func__, ret);
184 return ret;
185 }
186 mdelay(5);
187
188 ret = regulator_set_enable_if_allowed(priv->vddio, 1);
189 if (ret) {
190 log_debug("%s: error enabling iovcc-supply (%d)\n",
191 __func__, ret);
192 return ret;
193 }
194 mdelay(1);
195
196 ret = regulator_set_enable_if_allowed(priv->vdd, 1);
197 if (ret) {
198 log_debug("%s: error enabling vcc-supply (%d)\n",
199 __func__, ret);
200 return ret;
201 }
202 mdelay(20);
203
204 ret = dm_gpio_set_value(&priv->reset_gpio, 0);
205 if (ret) {
206 log_debug("%s: error changing reset-gpios (%d)\n",
207 __func__, ret);
208 return ret;
209 }
210 mdelay(2);
211
212 /* Reset panel */
213 ret = dm_gpio_set_value(&priv->reset_gpio, 1);
214 if (ret) {
215 log_debug("%s: error changing reset-gpios (%d)\n",
216 __func__, ret);
217 return ret;
218 }
219 mdelay(1);
220
221 ret = dm_gpio_set_value(&priv->reset_gpio, 0);
222 if (ret) {
223 log_debug("%s: error changing reset-gpios (%d)\n",
224 __func__, ret);
225 return ret;
226 }
227 mdelay(25);
228
229 return 0;
230}
231
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +0300232static int endeavoru_panel_probe(struct udevice *dev)
233{
234 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
235
236 /* fill characteristics of DSI data link */
237 plat->lanes = 2;
238 plat->format = MIPI_DSI_FMT_RGB888;
Svyatoslav Ryhela7516a42025-02-21 16:01:07 +0200239 plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +0300240
Svyatoslav Ryhel665b3ac2024-01-31 08:57:19 +0200241 return endeavoru_panel_hw_init(dev);
Svyatoslav Ryhelbd932b72023-04-25 10:51:47 +0300242}
243
244static const struct panel_ops endeavoru_panel_ops = {
245 .enable_backlight = endeavoru_panel_enable_backlight,
246 .set_backlight = endeavoru_panel_set_backlight,
247 .get_display_timing = endeavoru_panel_timings,
248};
249
250static const struct udevice_id endeavoru_panel_ids[] = {
251 { .compatible = "htc,edge-panel" },
252 { }
253};
254
255U_BOOT_DRIVER(endeavoru_panel) = {
256 .name = "endeavoru_panel",
257 .id = UCLASS_PANEL,
258 .of_match = endeavoru_panel_ids,
259 .ops = &endeavoru_panel_ops,
260 .of_to_plat = endeavoru_panel_of_to_plat,
261 .probe = endeavoru_panel_probe,
262 .plat_auto = sizeof(struct mipi_dsi_panel_plat),
263 .priv_auto = sizeof(struct endeavoru_panel_priv),
264};