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