blob: a911495786708851f02ce9567539855d8878b468 [file] [log] [blame]
Svyatoslav Ryhel93ceb7d2025-03-19 10:15:29 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Motorola ATRIX 4G and DROID X2 DSI panel driver
4 * Exact manufacturer and model unknown
5 *
6 * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
7 */
8
9#include <backlight.h>
10#include <dm.h>
11#include <panel.h>
12#include <log.h>
13#include <misc.h>
14#include <mipi_display.h>
15#include <mipi_dsi.h>
16#include <asm/gpio.h>
17#include <linux/delay.h>
18#include <linux/err.h>
19#include <power/regulator.h>
20
21struct mot_panel_priv {
22 struct udevice *vdd;
23 struct udevice *vddio;
24
25 struct udevice *backlight;
26
27 struct gpio_desc reset_gpio;
28};
29
30#define dsi_generic_write_seq(dsi, cmd, seq...) do { \
31 static const u8 b[] = { cmd, seq }; \
32 int ret; \
33 ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
34 if (ret < 0) \
35 return ret; \
36 } while (0)
37
38static struct display_timing default_timing = {
39 .pixelclock.typ = 38250000,
40 .hactive.typ = 540,
41 .hfront_porch.typ = 32,
42 .hback_porch.typ = 32,
43 .hsync_len.typ = 16,
44 .vactive.typ = 960,
45 .vfront_porch.typ = 12,
46 .vback_porch.typ = 12,
47 .vsync_len.typ = 8,
48};
49
50static int mot_es2(struct mipi_dsi_device *dsi)
51{
52 int ret;
53
54 dsi_generic_write_seq(dsi, 0x55, 0x01);
55
56 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
57 if (ret < 0) {
58 log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret);
59 return ret;
60 }
61 mdelay(120);
62
63 dsi_generic_write_seq(dsi, 0xf4, 0x00, 0xbb, 0x46, 0x53, 0x0c, 0x49,
64 0x74, 0x29, 0x12, 0x15, 0x2f, 0x2f, 0x04);
65 dsi_generic_write_seq(dsi, 0xf8, 0x4b, 0x04, 0x10, 0x1a, 0x2c, 0x2c,
66 0x2c, 0x2c, 0x14, 0x12);
67
68 dsi_generic_write_seq(dsi, 0xb5, 0x03, 0x7f, 0x00, 0x80, 0xc7, 0x00);
69 dsi_generic_write_seq(dsi, 0xb7, 0x66, 0xf6, 0x46, 0x9f, 0x90, 0x99,
70 0xff, 0x80, 0x6d, 0x01);
71
72 /* Gamma R */
73 dsi_generic_write_seq(dsi, 0xf9, 0x04);
74 dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x30, 0x12, 0x0e, 0x0c,
75 0x22, 0x27, 0x31, 0x2e, 0x07, 0x0f);
76 dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x30, 0x12, 0x0e, 0x0c,
77 0x22, 0x27, 0x31, 0x2e, 0x07, 0x0f);
78
79 /* Gamma G */
80 dsi_generic_write_seq(dsi, 0xf9, 0x02);
81 dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x37, 0x15, 0x15, 0x11,
82 0x1f, 0x25, 0x2d, 0x2a, 0x05, 0x0f);
83 dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x37, 0x15, 0x15, 0x11,
84 0x1f, 0x25, 0x2d, 0x2a, 0x05, 0x0f);
85
86 /* Gamma B */
87 dsi_generic_write_seq(dsi, 0xf9, 0x01);
88 dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x3f, 0x16, 0x1f, 0x15,
89 0x1f, 0x25, 0x2d, 0x2b, 0x06, 0x0b);
90 dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x3f, 0x16, 0x1f, 0x15,
91 0x1f, 0x25, 0x2d, 0x2b, 0x06, 0x0b);
92
93 /* Gamma W */
94 dsi_generic_write_seq(dsi, 0xf9, 0x20);
95 dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x34, 0x15, 0x1a, 0x11,
96 0x1f, 0x23, 0x2d, 0x29, 0x02, 0x08);
97 dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x34, 0x15, 0x1a, 0x11,
98 0x1f, 0x23, 0x2d, 0x29, 0x02, 0x08);
99
100 dsi_generic_write_seq(dsi, 0x53, 0x2c);
101 dsi_generic_write_seq(dsi, 0x35, 0x00);
102
103 return 0;
104}
105
106static int __maybe_unused mot_es4(struct mipi_dsi_device *dsi)
107{
108 int ret;
109
110 dsi_generic_write_seq(dsi, 0xd2, 0x04, 0x53);
111 dsi_generic_write_seq(dsi, 0xd2, 0x05, 0x53);
112 dsi_generic_write_seq(dsi, 0x55, 0x01);
113
114 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
115 if (ret < 0) {
116 log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret);
117 return ret;
118 }
119 mdelay(120);
120
121 dsi_generic_write_seq(dsi, 0xb5, 0x03, 0x7f, 0x0a, 0x80, 0xff, 0x00);
122 dsi_generic_write_seq(dsi, 0xb7, 0x7a, 0xf7, 0x4d, 0x91, 0x90, 0xb3,
123 0xff, 0x80, 0x6d, 0x01);
124 dsi_generic_write_seq(dsi, 0xf4, 0x00, 0xbb, 0x46, 0x53, 0x0c, 0x49,
125 0x74, 0x29, 0x12, 0x15, 0x37, 0x37, 0x04);
126 dsi_generic_write_seq(dsi, 0xf8, 0x0a, 0x04, 0x10, 0x2a, 0x35, 0x35,
127 0x35, 0x35, 0x21, 0x1a);
128
129 /* Gamma R */
130 dsi_generic_write_seq(dsi, 0xf9, 0x04);
131 dsi_generic_write_seq(dsi, 0xfa, 0x08, 0x1c, 0x1b, 0x0f, 0x0f, 0x0a,
132 0x1e, 0x22, 0x27, 0x26, 0x07, 0x0d);
133 dsi_generic_write_seq(dsi, 0xfb, 0x08, 0x3c, 0x27, 0x0f, 0x0f, 0x0a,
134 0x1e, 0x26, 0x31, 0x2f, 0x07, 0x0b);
135
136 /* Gamma G */
137 dsi_generic_write_seq(dsi, 0xf9, 0x02);
138 dsi_generic_write_seq(dsi, 0xfa, 0x30, 0x14, 0x0f, 0x00, 0x06, 0x02,
139 0x1e, 0x22, 0x27, 0x27, 0x08, 0x10);
140 dsi_generic_write_seq(dsi, 0xfb, 0x30, 0x35, 0x0f, 0x00, 0x0a, 0x02,
141 0x1c, 0x23, 0x31, 0x2f, 0x08, 0x0e);
142
143 /* Gamma B */
144 dsi_generic_write_seq(dsi, 0xf9, 0x01);
145 dsi_generic_write_seq(dsi, 0xfa, 0x12, 0x1b, 0x26, 0x0e, 0x12, 0x0b,
146 0x1e, 0x22, 0x27, 0x27, 0x06, 0x0c);
147 dsi_generic_write_seq(dsi, 0xfb, 0x12, 0x3b, 0x2c, 0x12, 0x12, 0x0e,
148 0x1e, 0x26, 0x31, 0x2f, 0x06, 0x0d);
149
150 /* Gamma W */
151 dsi_generic_write_seq(dsi, 0xf9, 0x20);
152 dsi_generic_write_seq(dsi, 0xfa, 0x37, 0x1b, 0x09, 0x01, 0x06, 0x04,
153 0x19, 0x19, 0x22, 0x24, 0x04, 0x15);
154 dsi_generic_write_seq(dsi, 0xfb, 0x37, 0x3b, 0x17, 0x01, 0x0a, 0x04,
155 0x19, 0x1d, 0x2c, 0x2c, 0x04, 0x13);
156
157 dsi_generic_write_seq(dsi, 0x53, 0x2c);
158 dsi_generic_write_seq(dsi, 0x35, 0x00);
159 dsi_generic_write_seq(dsi, 0xc3, 0x01, 0x4e);
160
161 return 0;
162}
163
164static int mot_panel_enable_backlight(struct udevice *dev)
165{
166 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
167 struct mipi_dsi_device *dsi = plat->device;
168 int ret;
169
170 dsi_generic_write_seq(dsi, 0xf0, 0x5a, 0x5a);
171 dsi_generic_write_seq(dsi, 0xf1, 0x5a, 0x5a);
172 dsi_generic_write_seq(dsi, 0xd0, 0x8e);
173
174 ret = mot_es2(dsi);
175 if (ret)
176 return ret;
177
178 ret = mipi_dsi_dcs_set_display_on(dsi);
179 if (ret < 0) {
180 log_debug("%s: failed to set display on: %d\n", __func__, ret);
181 return ret;
182 }
183 mdelay(20);
184
185 return 0;
186}
187
188static int mot_panel_set_backlight(struct udevice *dev, int percent)
189{
190 struct mot_panel_priv *priv = dev_get_priv(dev);
191 int ret;
192
193 ret = backlight_enable(priv->backlight);
194 if (ret)
195 return ret;
196
197 mdelay(5);
198
199 return backlight_set_brightness(priv->backlight, percent);
200}
201
202static int mot_panel_timings(struct udevice *dev, struct display_timing *timing)
203{
204 memcpy(timing, &default_timing, sizeof(*timing));
205 return 0;
206}
207
208static int mot_panel_of_to_plat(struct udevice *dev)
209{
210 struct mot_panel_priv *priv = dev_get_priv(dev);
211 int ret;
212
213 ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
214 "backlight", &priv->backlight);
215 if (ret) {
216 log_debug("%s: cannot get backlight: ret = %d\n", __func__, ret);
217 return ret;
218 }
219
220 ret = device_get_supply_regulator(dev, "vdd-supply", &priv->vdd);
221 if (ret) {
222 log_debug("%s: cannot get vdd-supply: ret = %d\n", __func__, ret);
223 return ret;
224 }
225
226 ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio);
227 if (ret) {
228 log_debug("%s: cannot get vddio-supply: ret = %d\n", __func__, ret);
229 return ret;
230 }
231
232 ret = gpio_request_by_name(dev, "reset-gpios", 0,
233 &priv->reset_gpio, GPIOD_IS_OUT);
234 if (ret) {
235 log_debug("%s: could not decode reser-gpios (%d)\n", __func__, ret);
236 return ret;
237 }
238
239 return 0;
240}
241
242static int mot_panel_hw_init(struct udevice *dev)
243{
244 struct mot_panel_priv *priv = dev_get_priv(dev);
245 int ret;
246
247 ret = regulator_set_enable_if_allowed(priv->vddio, 1);
248 if (ret) {
249 log_debug("%s: enabling vddio-supply failed (%d)\n", __func__, ret);
250 return ret;
251 }
252
253 ret = regulator_set_enable_if_allowed(priv->vdd, 1);
254 if (ret) {
255 log_debug("%s: enabling vdd-supply failed (%d)\n", __func__, ret);
256 return ret;
257 }
258
259 ret = dm_gpio_set_value(&priv->reset_gpio, 1);
260 if (ret) {
261 log_debug("%s: error entering reset (%d)\n", __func__, ret);
262 return ret;
263 }
264 mdelay(50);
265
266 ret = dm_gpio_set_value(&priv->reset_gpio, 0);
267 if (ret) {
268 log_debug("%s: error exiting reset (%d)\n", __func__, ret);
269 return ret;
270 }
271 mdelay(10);
272
273 return 0;
274}
275
276static int mot_panel_probe(struct udevice *dev)
277{
278 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
279
280 /* fill characteristics of DSI data link */
281 plat->lanes = 2;
282 plat->format = MIPI_DSI_FMT_RGB888;
283 plat->mode_flags = MIPI_DSI_MODE_LPM;
284
285 return mot_panel_hw_init(dev);
286}
287
288static const struct panel_ops mot_panel_ops = {
289 .enable_backlight = mot_panel_enable_backlight,
290 .set_backlight = mot_panel_set_backlight,
291 .get_display_timing = mot_panel_timings,
292};
293
294static const struct udevice_id mot_panel_ids[] = {
295 { .compatible = "motorola,mot-panel" },
296 { }
297};
298
299U_BOOT_DRIVER(mot_panel) = {
300 .name = "mot_panel",
301 .id = UCLASS_PANEL,
302 .of_match = mot_panel_ids,
303 .ops = &mot_panel_ops,
304 .of_to_plat = mot_panel_of_to_plat,
305 .probe = mot_panel_probe,
306 .plat_auto = sizeof(struct mipi_dsi_panel_plat),
307 .priv_auto = sizeof(struct mot_panel_priv),
308};