blob: dd87fc461b95417fe3d14b7dd5029b55392c7408 [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>
Simon Glassf7ae49f2020-05-10 11:40:05 -060012#include <log.h>
Heiko Schocher79db1b42019-12-01 11:23:05 +010013#include <dm/read.h>
14#include <dm/uclass-internal.h>
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020015#include <errno.h>
16#include <spi.h>
Heiko Schocher79db1b42019-12-01 11:23:05 +010017#include <asm/gpio.h>
Simon Glassc05ed002020-05-10 11:40:11 -060018#include <linux/delay.h>
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020019
20#define PWR_ON_DELAY_MSECS 120
21
Heiko Schocher79db1b42019-12-01 11:23:05 +010022static int lb043wv_spi_write_u16(struct spi_slave *slave, u16 val)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020023{
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020024 unsigned short buf16 = htons(val);
25 int ret = 0;
26
Heiko Schocher79db1b42019-12-01 11:23:05 +010027 ret = spi_xfer(slave, 16, &buf16, NULL,
28 SPI_XFER_BEGIN | SPI_XFER_END);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020029 if (ret)
30 debug("%s: Failed to send: %d\n", __func__, ret);
31
32 return ret;
33}
34
Heiko Schocher79db1b42019-12-01 11:23:05 +010035static void lb043wv_spi_write_u16_array(struct spi_slave *slave, u16 *buff,
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020036 int size)
37{
38 int i;
39
40 for (i = 0; i < size; i++)
Heiko Schocher79db1b42019-12-01 11:23:05 +010041 lb043wv_spi_write_u16(slave, buff[i]);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020042}
43
Heiko Schocher79db1b42019-12-01 11:23:05 +010044static void lb043wv_display_mode_settings(struct spi_slave *slave)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020045{
46 static u16 display_mode_settings[] = {
47 0x703A,
48 0x7270,
49 0x70B1,
50 0x7208,
51 0x723B,
52 0x720F,
53 0x70B2,
54 0x7200,
55 0x72C8,
56 0x70B3,
57 0x7200,
58 0x70B4,
59 0x7200,
60 0x70B5,
61 0x7242,
62 0x7210,
63 0x7210,
64 0x7200,
65 0x7220,
66 0x70B6,
67 0x720B,
68 0x720F,
69 0x723C,
70 0x7213,
71 0x7213,
72 0x72E8,
73 0x70B7,
74 0x7246,
75 0x7206,
76 0x720C,
77 0x7200,
78 0x7200,
79 };
80
81 debug("transfer display mode settings\n");
Heiko Schocher79db1b42019-12-01 11:23:05 +010082 lb043wv_spi_write_u16_array(slave, display_mode_settings,
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020083 ARRAY_SIZE(display_mode_settings));
84}
85
Heiko Schocher79db1b42019-12-01 11:23:05 +010086static void lb043wv_power_settings(struct spi_slave *slave)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +020087{
88 static u16 power_settings[] = {
89 0x70C0,
90 0x7201,
91 0x7211,
92 0x70C3,
93 0x7207,
94 0x7203,
95 0x7204,
96 0x7204,
97 0x7204,
98 0x70C4,
99 0x7212,
100 0x7224,
101 0x7218,
102 0x7218,
103 0x7202,
104 0x7249,
105 0x70C5,
106 0x726F,
107 0x70C6,
108 0x7241,
109 0x7263,
110 };
111
112 debug("transfer power settings\n");
Heiko Schocher79db1b42019-12-01 11:23:05 +0100113 lb043wv_spi_write_u16_array(slave, power_settings,
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200114 ARRAY_SIZE(power_settings));
115}
116
Heiko Schocher79db1b42019-12-01 11:23:05 +0100117static void lb043wv_gamma_settings(struct spi_slave *slave)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200118{
119 static u16 gamma_settings[] = {
120 0x70D0,
121 0x7203,
122 0x7207,
123 0x7273,
124 0x7235,
125 0x7200,
126 0x7201,
127 0x7220,
128 0x7200,
129 0x7203,
130 0x70D1,
131 0x7203,
132 0x7207,
133 0x7273,
134 0x7235,
135 0x7200,
136 0x7201,
137 0x7220,
138 0x7200,
139 0x7203,
140 0x70D2,
141 0x7203,
142 0x7207,
143 0x7273,
144 0x7235,
145 0x7200,
146 0x7201,
147 0x7220,
148 0x7200,
149 0x7203,
150 0x70D3,
151 0x7203,
152 0x7207,
153 0x7273,
154 0x7235,
155 0x7200,
156 0x7201,
157 0x7220,
158 0x7200,
159 0x7203,
160 0x70D4,
161 0x7203,
162 0x7207,
163 0x7273,
164 0x7235,
165 0x7200,
166 0x7201,
167 0x7220,
168 0x7200,
169 0x7203,
170 0x70D5,
171 0x7203,
172 0x7207,
173 0x7273,
174 0x7235,
175 0x7200,
176 0x7201,
177 0x7220,
178 0x7200,
179 0x7203,
180 };
181
182 debug("transfer gamma settings\n");
Heiko Schocher79db1b42019-12-01 11:23:05 +0100183 lb043wv_spi_write_u16_array(slave, gamma_settings,
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200184 ARRAY_SIZE(gamma_settings));
185}
186
Heiko Schocher79db1b42019-12-01 11:23:05 +0100187static void lb043wv_display_on(struct spi_slave *slave)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200188{
189 static u16 sleep_out = 0x7011;
190 static u16 display_on = 0x7029;
191
Heiko Schocher79db1b42019-12-01 11:23:05 +0100192 lb043wv_spi_write_u16(slave, sleep_out);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200193 mdelay(PWR_ON_DELAY_MSECS);
Heiko Schocher79db1b42019-12-01 11:23:05 +0100194 lb043wv_spi_write_u16(slave, display_on);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200195}
196
Heiko Schocher79db1b42019-12-01 11:23:05 +0100197static int lg4573_spi_startup(struct spi_slave *slave)
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200198{
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200199 int ret;
200
Heiko Schocher79db1b42019-12-01 11:23:05 +0100201 ret = spi_claim_bus(slave);
202 if (ret)
203 return ret;
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200204
Heiko Schocher79db1b42019-12-01 11:23:05 +0100205 lb043wv_display_mode_settings(slave);
206 lb043wv_power_settings(slave);
207 lb043wv_gamma_settings(slave);
208 lb043wv_display_on(slave);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200209
Heiko Schocher79db1b42019-12-01 11:23:05 +0100210 spi_release_bus(slave);
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200211 return 0;
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200212}
213
Simon Glass09140112020-05-10 11:40:03 -0600214static int do_lgset(struct cmd_tbl *cmdtp, int flag, int argc,
215 char *const argv[])
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200216{
Heiko Schocher79db1b42019-12-01 11:23:05 +0100217 struct spi_slave *slave;
218 struct udevice *dev;
219 int ret;
220
221 ret = uclass_get_device_by_driver(UCLASS_DISPLAY,
Simon Glass65e25be2020-12-28 20:34:56 -0700222 DM_DRIVER_GET(lg4573_lcd), &dev);
Heiko Schocher79db1b42019-12-01 11:23:05 +0100223 if (ret) {
224 printf("%s: Could not get lg4573 device\n", __func__);
225 return ret;
226 }
227 slave = dev_get_parent_priv(dev);
228 if (!slave) {
229 printf("%s: No slave data\n", __func__);
230 return -ENODEV;
231 }
232 lg4573_spi_startup(slave);
233
Heiko Schocherfc1a79d2015-04-12 10:20:19 +0200234 return 0;
235}
236
237U_BOOT_CMD(
238 lgset, 2, 1, do_lgset,
239 "set lgdisplay",
240 ""
241);
Heiko Schocher79db1b42019-12-01 11:23:05 +0100242
243static int lg4573_bind(struct udevice *dev)
244{
245 return 0;
246}
247
248static int lg4573_probe(struct udevice *dev)
249{
250 return 0;
251}
252
253static const struct udevice_id lg4573_ids[] = {
254 { .compatible = "lg,lg4573" },
255 { }
256};
257
258struct lg4573_lcd_priv {
259 struct display_timing timing;
260 struct udevice *backlight;
261 struct gpio_desc enable;
262 int panel_bpp;
263 u32 power_on_delay;
264};
265
266static int lg4573_lcd_read_timing(struct udevice *dev,
267 struct display_timing *timing)
268{
269 struct lg4573_lcd_priv *priv = dev_get_priv(dev);
270
271 memcpy(timing, &priv->timing, sizeof(struct display_timing));
272
273 return 0;
274}
275
276static int lg4573_lcd_enable(struct udevice *dev, int bpp,
277 const struct display_timing *edid)
278{
279 struct spi_slave *slave = dev_get_parent_priv(dev);
280 struct lg4573_lcd_priv *priv = dev_get_priv(dev);
281 int ret = 0;
282
283 dm_gpio_set_value(&priv->enable, 1);
284 ret = backlight_enable(priv->backlight);
285
286 mdelay(priv->power_on_delay);
287 lg4573_spi_startup(slave);
288
289 return ret;
290};
291
292static const struct dm_display_ops lg4573_lcd_ops = {
293 .read_timing = lg4573_lcd_read_timing,
294 .enable = lg4573_lcd_enable,
295};
296
Simon Glassd1998a92020-12-03 16:55:21 -0700297static int lg4573_of_to_plat(struct udevice *dev)
Heiko Schocher79db1b42019-12-01 11:23:05 +0100298{
299 struct lg4573_lcd_priv *priv = dev_get_priv(dev);
300 int ret;
301
302 ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
303 "backlight", &priv->backlight);
304 if (ret) {
305 debug("%s: Cannot get backlight: ret=%d\n", __func__, ret);
306 return log_ret(ret);
307 }
308 ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable,
309 GPIOD_IS_OUT);
310 if (ret) {
311 debug("%s: Warning: cannot get enable GPIO: ret=%d\n",
312 __func__, ret);
313 if (ret != -ENOENT)
314 return log_ret(ret);
315 }
316
317 priv->power_on_delay = dev_read_u32_default(dev, "power-on-delay", 10);
318
319 return 0;
320}
321
322U_BOOT_DRIVER(lg4573_lcd) = {
323 .name = "lg4573",
324 .id = UCLASS_DISPLAY,
325 .ops = &lg4573_lcd_ops,
Simon Glassd1998a92020-12-03 16:55:21 -0700326 .of_to_plat = lg4573_of_to_plat,
Heiko Schocher79db1b42019-12-01 11:23:05 +0100327 .of_match = lg4573_ids,
328 .bind = lg4573_bind,
329 .probe = lg4573_probe,
Simon Glass41575d82020-12-03 16:55:17 -0700330 .priv_auto = sizeof(struct lg4573_lcd_priv),
Heiko Schocher79db1b42019-12-01 11:23:05 +0100331};