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