blob: 74a23889242fbc99b1974bebe9976df9bb9286ae [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0
Heiko Schocherfc1a79d2015-04-12 10:20:19 +02002/*
3 * LCD: LG4573, TFT 4.3", 480x800, RGB24
4 * LCD initialization via SPI
5 *
Heiko Schocherfc1a79d2015-04-12 10:20:19 +02006 */
7#include <common.h>
Heiko Schocher79db1b42019-12-01 11:23:05 +01008#include <backlight.h>
Simon Glass09140112020-05-10 11:40:03 -06009#include <command.h>
Heiko Schocher79db1b42019-12-01 11:23:05 +010010#include <display.h>
11#include <dm.h>
12#include <dm/read.h>
13#include <dm/uclass-internal.h>
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020014#include <errno.h>
15#include <spi.h>
Heiko Schocher79db1b42019-12-01 11:23:05 +010016#include <asm/gpio.h>
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020017
18#define PWR_ON_DELAY_MSECS 120
19
Heiko Schocher79db1b42019-12-01 11:23:05 +010020static int lb043wv_spi_write_u16(struct spi_slave *slave, u16 val)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020021{
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020022 unsigned short buf16 = htons(val);
23 int ret = 0;
24
Heiko Schocher79db1b42019-12-01 11:23:05 +010025 ret = spi_xfer(slave, 16, &buf16, NULL,
26 SPI_XFER_BEGIN | SPI_XFER_END);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020027 if (ret)
28 debug("%s: Failed to send: %d\n", __func__, ret);
29
30 return ret;
31}
32
Heiko Schocher79db1b42019-12-01 11:23:05 +010033static void lb043wv_spi_write_u16_array(struct spi_slave *slave, u16 *buff,
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020034 int size)
35{
36 int i;
37
38 for (i = 0; i < size; i++)
Heiko Schocher79db1b42019-12-01 11:23:05 +010039 lb043wv_spi_write_u16(slave, buff[i]);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020040}
41
Heiko Schocher79db1b42019-12-01 11:23:05 +010042static void lb043wv_display_mode_settings(struct spi_slave *slave)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020043{
44 static u16 display_mode_settings[] = {
45 0x703A,
46 0x7270,
47 0x70B1,
48 0x7208,
49 0x723B,
50 0x720F,
51 0x70B2,
52 0x7200,
53 0x72C8,
54 0x70B3,
55 0x7200,
56 0x70B4,
57 0x7200,
58 0x70B5,
59 0x7242,
60 0x7210,
61 0x7210,
62 0x7200,
63 0x7220,
64 0x70B6,
65 0x720B,
66 0x720F,
67 0x723C,
68 0x7213,
69 0x7213,
70 0x72E8,
71 0x70B7,
72 0x7246,
73 0x7206,
74 0x720C,
75 0x7200,
76 0x7200,
77 };
78
79 debug("transfer display mode settings\n");
Heiko Schocher79db1b42019-12-01 11:23:05 +010080 lb043wv_spi_write_u16_array(slave, display_mode_settings,
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020081 ARRAY_SIZE(display_mode_settings));
82}
83
Heiko Schocher79db1b42019-12-01 11:23:05 +010084static void lb043wv_power_settings(struct spi_slave *slave)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020085{
86 static u16 power_settings[] = {
87 0x70C0,
88 0x7201,
89 0x7211,
90 0x70C3,
91 0x7207,
92 0x7203,
93 0x7204,
94 0x7204,
95 0x7204,
96 0x70C4,
97 0x7212,
98 0x7224,
99 0x7218,
100 0x7218,
101 0x7202,
102 0x7249,
103 0x70C5,
104 0x726F,
105 0x70C6,
106 0x7241,
107 0x7263,
108 };
109
110 debug("transfer power settings\n");
Heiko Schocher79db1b42019-12-01 11:23:05 +0100111 lb043wv_spi_write_u16_array(slave, power_settings,
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200112 ARRAY_SIZE(power_settings));
113}
114
Heiko Schocher79db1b42019-12-01 11:23:05 +0100115static void lb043wv_gamma_settings(struct spi_slave *slave)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200116{
117 static u16 gamma_settings[] = {
118 0x70D0,
119 0x7203,
120 0x7207,
121 0x7273,
122 0x7235,
123 0x7200,
124 0x7201,
125 0x7220,
126 0x7200,
127 0x7203,
128 0x70D1,
129 0x7203,
130 0x7207,
131 0x7273,
132 0x7235,
133 0x7200,
134 0x7201,
135 0x7220,
136 0x7200,
137 0x7203,
138 0x70D2,
139 0x7203,
140 0x7207,
141 0x7273,
142 0x7235,
143 0x7200,
144 0x7201,
145 0x7220,
146 0x7200,
147 0x7203,
148 0x70D3,
149 0x7203,
150 0x7207,
151 0x7273,
152 0x7235,
153 0x7200,
154 0x7201,
155 0x7220,
156 0x7200,
157 0x7203,
158 0x70D4,
159 0x7203,
160 0x7207,
161 0x7273,
162 0x7235,
163 0x7200,
164 0x7201,
165 0x7220,
166 0x7200,
167 0x7203,
168 0x70D5,
169 0x7203,
170 0x7207,
171 0x7273,
172 0x7235,
173 0x7200,
174 0x7201,
175 0x7220,
176 0x7200,
177 0x7203,
178 };
179
180 debug("transfer gamma settings\n");
Heiko Schocher79db1b42019-12-01 11:23:05 +0100181 lb043wv_spi_write_u16_array(slave, gamma_settings,
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200182 ARRAY_SIZE(gamma_settings));
183}
184
Heiko Schocher79db1b42019-12-01 11:23:05 +0100185static void lb043wv_display_on(struct spi_slave *slave)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200186{
187 static u16 sleep_out = 0x7011;
188 static u16 display_on = 0x7029;
189
Heiko Schocher79db1b42019-12-01 11:23:05 +0100190 lb043wv_spi_write_u16(slave, sleep_out);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200191 mdelay(PWR_ON_DELAY_MSECS);
Heiko Schocher79db1b42019-12-01 11:23:05 +0100192 lb043wv_spi_write_u16(slave, display_on);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200193}
194
Heiko Schocher79db1b42019-12-01 11:23:05 +0100195static int lg4573_spi_startup(struct spi_slave *slave)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200196{
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200197 int ret;
198
Heiko Schocher79db1b42019-12-01 11:23:05 +0100199 ret = spi_claim_bus(slave);
200 if (ret)
201 return ret;
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200202
Heiko Schocher79db1b42019-12-01 11:23:05 +0100203 lb043wv_display_mode_settings(slave);
204 lb043wv_power_settings(slave);
205 lb043wv_gamma_settings(slave);
206 lb043wv_display_on(slave);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200207
Heiko Schocher79db1b42019-12-01 11:23:05 +0100208 spi_release_bus(slave);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200209 return 0;
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200210}
211
Simon Glass09140112020-05-10 11:40:03 -0600212static int do_lgset(struct cmd_tbl *cmdtp, int flag, int argc,
213 char *const argv[])
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200214{
Heiko Schocher79db1b42019-12-01 11:23:05 +0100215 struct spi_slave *slave;
216 struct udevice *dev;
217 int ret;
218
219 ret = uclass_get_device_by_driver(UCLASS_DISPLAY,
220 DM_GET_DRIVER(lg4573_lcd), &dev);
221 if (ret) {
222 printf("%s: Could not get lg4573 device\n", __func__);
223 return ret;
224 }
225 slave = dev_get_parent_priv(dev);
226 if (!slave) {
227 printf("%s: No slave data\n", __func__);
228 return -ENODEV;
229 }
230 lg4573_spi_startup(slave);
231
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200232 return 0;
233}
234
235U_BOOT_CMD(
236 lgset, 2, 1, do_lgset,
237 "set lgdisplay",
238 ""
239);
Heiko Schocher79db1b42019-12-01 11:23:05 +0100240
241static int lg4573_bind(struct udevice *dev)
242{
243 return 0;
244}
245
246static int lg4573_probe(struct udevice *dev)
247{
248 return 0;
249}
250
251static const struct udevice_id lg4573_ids[] = {
252 { .compatible = "lg,lg4573" },
253 { }
254};
255
256struct lg4573_lcd_priv {
257 struct display_timing timing;
258 struct udevice *backlight;
259 struct gpio_desc enable;
260 int panel_bpp;
261 u32 power_on_delay;
262};
263
264static int lg4573_lcd_read_timing(struct udevice *dev,
265 struct display_timing *timing)
266{
267 struct lg4573_lcd_priv *priv = dev_get_priv(dev);
268
269 memcpy(timing, &priv->timing, sizeof(struct display_timing));
270
271 return 0;
272}
273
274static int lg4573_lcd_enable(struct udevice *dev, int bpp,
275 const struct display_timing *edid)
276{
277 struct spi_slave *slave = dev_get_parent_priv(dev);
278 struct lg4573_lcd_priv *priv = dev_get_priv(dev);
279 int ret = 0;
280
281 dm_gpio_set_value(&priv->enable, 1);
282 ret = backlight_enable(priv->backlight);
283
284 mdelay(priv->power_on_delay);
285 lg4573_spi_startup(slave);
286
287 return ret;
288};
289
290static const struct dm_display_ops lg4573_lcd_ops = {
291 .read_timing = lg4573_lcd_read_timing,
292 .enable = lg4573_lcd_enable,
293};
294
295static int lg4573_ofdata_to_platdata(struct udevice *dev)
296{
297 struct lg4573_lcd_priv *priv = dev_get_priv(dev);
298 int ret;
299
300 ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
301 "backlight", &priv->backlight);
302 if (ret) {
303 debug("%s: Cannot get backlight: ret=%d\n", __func__, ret);
304 return log_ret(ret);
305 }
306 ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable,
307 GPIOD_IS_OUT);
308 if (ret) {
309 debug("%s: Warning: cannot get enable GPIO: ret=%d\n",
310 __func__, ret);
311 if (ret != -ENOENT)
312 return log_ret(ret);
313 }
314
315 priv->power_on_delay = dev_read_u32_default(dev, "power-on-delay", 10);
316
317 return 0;
318}
319
320U_BOOT_DRIVER(lg4573_lcd) = {
321 .name = "lg4573",
322 .id = UCLASS_DISPLAY,
323 .ops = &lg4573_lcd_ops,
324 .ofdata_to_platdata = lg4573_ofdata_to_platdata,
325 .of_match = lg4573_ids,
326 .bind = lg4573_bind,
327 .probe = lg4573_probe,
328 .priv_auto_alloc_size = sizeof(struct lg4573_lcd_priv),
329};