Merge tag 'ieee802154-for-davem-2021-06-03' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.git] / drivers / gpu / drm / panel / panel-lg-lg4573.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2015 Heiko Schocher <hs@denx.de>
4  *
5  * from:
6  * drivers/gpu/drm/panel/panel-ld9040.c
7  * ld9040 AMOLED LCD drm_panel driver.
8  *
9  * Copyright (c) 2014 Samsung Electronics Co., Ltd
10  * Derived from drivers/video/backlight/ld9040.c
11  *
12  * Andrzej Hajda <a.hajda@samsung.com>
13 */
14
15 #include <linux/delay.h>
16 #include <linux/gpio/consumer.h>
17 #include <linux/module.h>
18 #include <linux/regulator/consumer.h>
19 #include <linux/spi/spi.h>
20
21 #include <video/mipi_display.h>
22 #include <video/of_videomode.h>
23 #include <video/videomode.h>
24
25 #include <drm/drm_device.h>
26 #include <drm/drm_modes.h>
27 #include <drm/drm_panel.h>
28
29 struct lg4573 {
30         struct drm_panel panel;
31         struct spi_device *spi;
32         struct videomode vm;
33 };
34
35 static inline struct lg4573 *panel_to_lg4573(struct drm_panel *panel)
36 {
37         return container_of(panel, struct lg4573, panel);
38 }
39
40 static int lg4573_spi_write_u16(struct lg4573 *ctx, u16 data)
41 {
42         struct spi_transfer xfer = {
43                 .len = 2,
44         };
45         __be16 temp = cpu_to_be16(data);
46         struct spi_message msg;
47
48         dev_dbg(ctx->panel.dev, "writing data: %x\n", data);
49         xfer.tx_buf = &temp;
50         spi_message_init(&msg);
51         spi_message_add_tail(&xfer, &msg);
52
53         return spi_sync(ctx->spi, &msg);
54 }
55
56 static int lg4573_spi_write_u16_array(struct lg4573 *ctx, const u16 *buffer,
57                                       unsigned int count)
58 {
59         unsigned int i;
60         int ret;
61
62         for (i = 0; i < count; i++) {
63                 ret = lg4573_spi_write_u16(ctx, buffer[i]);
64                 if (ret)
65                         return ret;
66         }
67
68         return 0;
69 }
70
71 static int lg4573_spi_write_dcs(struct lg4573 *ctx, u8 dcs)
72 {
73         return lg4573_spi_write_u16(ctx, (0x70 << 8 | dcs));
74 }
75
76 static int lg4573_display_on(struct lg4573 *ctx)
77 {
78         int ret;
79
80         ret = lg4573_spi_write_dcs(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
81         if (ret)
82                 return ret;
83
84         msleep(5);
85
86         return lg4573_spi_write_dcs(ctx, MIPI_DCS_SET_DISPLAY_ON);
87 }
88
89 static int lg4573_display_off(struct lg4573 *ctx)
90 {
91         int ret;
92
93         ret = lg4573_spi_write_dcs(ctx, MIPI_DCS_SET_DISPLAY_OFF);
94         if (ret)
95                 return ret;
96
97         msleep(120);
98
99         return lg4573_spi_write_dcs(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
100 }
101
102 static int lg4573_display_mode_settings(struct lg4573 *ctx)
103 {
104         static const u16 display_mode_settings[] = {
105                 0x703A, 0x7270, 0x70B1, 0x7208,
106                 0x723B, 0x720F, 0x70B2, 0x7200,
107                 0x72C8, 0x70B3, 0x7200, 0x70B4,
108                 0x7200, 0x70B5, 0x7242, 0x7210,
109                 0x7210, 0x7200, 0x7220, 0x70B6,
110                 0x720B, 0x720F, 0x723C, 0x7213,
111                 0x7213, 0x72E8, 0x70B7, 0x7246,
112                 0x7206, 0x720C, 0x7200, 0x7200,
113         };
114
115         dev_dbg(ctx->panel.dev, "transfer display mode settings\n");
116         return lg4573_spi_write_u16_array(ctx, display_mode_settings,
117                                           ARRAY_SIZE(display_mode_settings));
118 }
119
120 static int lg4573_power_settings(struct lg4573 *ctx)
121 {
122         static const u16 power_settings[] = {
123                 0x70C0, 0x7201, 0x7211, 0x70C3,
124                 0x7207, 0x7203, 0x7204, 0x7204,
125                 0x7204, 0x70C4, 0x7212, 0x7224,
126                 0x7218, 0x7218, 0x7202, 0x7249,
127                 0x70C5, 0x726F, 0x70C6, 0x7241,
128                 0x7263,
129         };
130
131         dev_dbg(ctx->panel.dev, "transfer power settings\n");
132         return lg4573_spi_write_u16_array(ctx, power_settings,
133                                           ARRAY_SIZE(power_settings));
134 }
135
136 static int lg4573_gamma_settings(struct lg4573 *ctx)
137 {
138         static const u16 gamma_settings[] = {
139                 0x70D0, 0x7203, 0x7207, 0x7273,
140                 0x7235, 0x7200, 0x7201, 0x7220,
141                 0x7200, 0x7203, 0x70D1, 0x7203,
142                 0x7207, 0x7273, 0x7235, 0x7200,
143                 0x7201, 0x7220, 0x7200, 0x7203,
144                 0x70D2, 0x7203, 0x7207, 0x7273,
145                 0x7235, 0x7200, 0x7201, 0x7220,
146                 0x7200, 0x7203, 0x70D3, 0x7203,
147                 0x7207, 0x7273, 0x7235, 0x7200,
148                 0x7201, 0x7220, 0x7200, 0x7203,
149                 0x70D4, 0x7203, 0x7207, 0x7273,
150                 0x7235, 0x7200, 0x7201, 0x7220,
151                 0x7200, 0x7203, 0x70D5, 0x7203,
152                 0x7207, 0x7273, 0x7235, 0x7200,
153                 0x7201, 0x7220, 0x7200, 0x7203,
154         };
155
156         dev_dbg(ctx->panel.dev, "transfer gamma settings\n");
157         return lg4573_spi_write_u16_array(ctx, gamma_settings,
158                                           ARRAY_SIZE(gamma_settings));
159 }
160
161 static int lg4573_init(struct lg4573 *ctx)
162 {
163         int ret;
164
165         dev_dbg(ctx->panel.dev, "initializing LCD\n");
166
167         ret = lg4573_display_mode_settings(ctx);
168         if (ret)
169                 return ret;
170
171         ret = lg4573_power_settings(ctx);
172         if (ret)
173                 return ret;
174
175         return lg4573_gamma_settings(ctx);
176 }
177
178 static int lg4573_power_on(struct lg4573 *ctx)
179 {
180         return lg4573_display_on(ctx);
181 }
182
183 static int lg4573_disable(struct drm_panel *panel)
184 {
185         struct lg4573 *ctx = panel_to_lg4573(panel);
186
187         return lg4573_display_off(ctx);
188 }
189
190 static int lg4573_enable(struct drm_panel *panel)
191 {
192         struct lg4573 *ctx = panel_to_lg4573(panel);
193
194         lg4573_init(ctx);
195
196         return lg4573_power_on(ctx);
197 }
198
199 static const struct drm_display_mode default_mode = {
200         .clock = 28341,
201         .hdisplay = 480,
202         .hsync_start = 480 + 10,
203         .hsync_end = 480 + 10 + 59,
204         .htotal = 480 + 10 + 59 + 10,
205         .vdisplay = 800,
206         .vsync_start = 800 + 15,
207         .vsync_end = 800 + 15 + 15,
208         .vtotal = 800 + 15 + 15 + 15,
209 };
210
211 static int lg4573_get_modes(struct drm_panel *panel,
212                             struct drm_connector *connector)
213 {
214         struct drm_display_mode *mode;
215
216         mode = drm_mode_duplicate(connector->dev, &default_mode);
217         if (!mode) {
218                 dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
219                         default_mode.hdisplay, default_mode.vdisplay,
220                         drm_mode_vrefresh(&default_mode));
221                 return -ENOMEM;
222         }
223
224         drm_mode_set_name(mode);
225
226         mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
227         drm_mode_probed_add(connector, mode);
228
229         connector->display_info.width_mm = 61;
230         connector->display_info.height_mm = 103;
231
232         return 1;
233 }
234
235 static const struct drm_panel_funcs lg4573_drm_funcs = {
236         .disable = lg4573_disable,
237         .enable = lg4573_enable,
238         .get_modes = lg4573_get_modes,
239 };
240
241 static int lg4573_probe(struct spi_device *spi)
242 {
243         struct lg4573 *ctx;
244         int ret;
245
246         ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL);
247         if (!ctx)
248                 return -ENOMEM;
249
250         ctx->spi = spi;
251
252         spi_set_drvdata(spi, ctx);
253         spi->bits_per_word = 8;
254
255         ret = spi_setup(spi);
256         if (ret < 0) {
257                 dev_err(&spi->dev, "SPI setup failed: %d\n", ret);
258                 return ret;
259         }
260
261         drm_panel_init(&ctx->panel, &spi->dev, &lg4573_drm_funcs,
262                        DRM_MODE_CONNECTOR_DPI);
263
264         drm_panel_add(&ctx->panel);
265
266         return 0;
267 }
268
269 static int lg4573_remove(struct spi_device *spi)
270 {
271         struct lg4573 *ctx = spi_get_drvdata(spi);
272
273         lg4573_display_off(ctx);
274         drm_panel_remove(&ctx->panel);
275
276         return 0;
277 }
278
279 static const struct of_device_id lg4573_of_match[] = {
280         { .compatible = "lg,lg4573" },
281         { }
282 };
283 MODULE_DEVICE_TABLE(of, lg4573_of_match);
284
285 static struct spi_driver lg4573_driver = {
286         .probe = lg4573_probe,
287         .remove = lg4573_remove,
288         .driver = {
289                 .name = "lg4573",
290                 .of_match_table = lg4573_of_match,
291         },
292 };
293 module_spi_driver(lg4573_driver);
294
295 MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
296 MODULE_DESCRIPTION("lg4573 LCD Driver");
297 MODULE_LICENSE("GPL v2");