]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/gpu/drm/panel/panel-lg-lg4573.c
treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500
[mirror_ubuntu-hirsute-kernel.git] / drivers / gpu / drm / panel / panel-lg-lg4573.c
CommitLineData
d2912cb1 1// SPDX-License-Identifier: GPL-2.0-only
58c467ec
HS
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>
58c467ec
HS
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
26struct lg4573 {
27 struct drm_panel panel;
28 struct spi_device *spi;
29 struct videomode vm;
30};
31
32static inline struct lg4573 *panel_to_lg4573(struct drm_panel *panel)
33{
34 return container_of(panel, struct lg4573, panel);
35}
36
37static 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
53static 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
68static int lg4573_spi_write_dcs(struct lg4573 *ctx, u8 dcs)
69{
70 return lg4573_spi_write_u16(ctx, (0x70 << 8 | dcs));
71}
72
73static 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
86static 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
99static 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
117static 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
133static 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
158static 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
175static int lg4573_power_on(struct lg4573 *ctx)
176{
177 return lg4573_display_on(ctx);
178}
179
180static 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
187static 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
196static 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
209static 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
233static const struct drm_panel_funcs lg4573_drm_funcs = {
234 .disable = lg4573_disable,
235 .enable = lg4573_enable,
236 .get_modes = lg4573_get_modes,
237};
238
239static 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
266static 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
276static const struct of_device_id lg4573_of_match[] = {
277 { .compatible = "lg,lg4573" },
278 { }
279};
280MODULE_DEVICE_TABLE(of, lg4573_of_match);
281
282static struct spi_driver lg4573_driver = {
283 .probe = lg4573_probe,
284 .remove = lg4573_remove,
285 .driver = {
286 .name = "lg4573",
58c467ec
HS
287 .of_match_table = lg4573_of_match,
288 },
289};
290module_spi_driver(lg4573_driver);
291
292MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
293MODULE_DESCRIPTION("lg4573 LCD Driver");
294MODULE_LICENSE("GPL v2");