1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2015 Heiko Schocher <hs@denx.de>
6 * drivers/gpu/drm/panel/panel-ld9040.c
7 * ld9040 AMOLED LCD drm_panel driver.
9 * Copyright (c) 2014 Samsung Electronics Co., Ltd
10 * Derived from drivers/video/backlight/ld9040.c
12 * Andrzej Hajda <a.hajda@samsung.com>
16 #include <drm/drm_panel.h>
18 #include <linux/gpio/consumer.h>
19 #include <linux/regulator/consumer.h>
20 #include <linux/spi/spi.h>
22 #include <video/mipi_display.h>
23 #include <video/of_videomode.h>
24 #include <video/videomode.h>
27 struct drm_panel panel;
28 struct spi_device *spi;
32 static inline struct lg4573 *panel_to_lg4573(struct drm_panel *panel)
34 return container_of(panel, struct lg4573, panel);
37 static int lg4573_spi_write_u16(struct lg4573 *ctx, u16 data)
39 struct spi_transfer xfer = {
42 u16 temp = cpu_to_be16(data);
43 struct spi_message msg;
45 dev_dbg(ctx->panel.dev, "writing data: %x\n", data);
47 spi_message_init(&msg);
48 spi_message_add_tail(&xfer, &msg);
50 return spi_sync(ctx->spi, &msg);
53 static int lg4573_spi_write_u16_array(struct lg4573 *ctx, const u16 *buffer,
59 for (i = 0; i < count; i++) {
60 ret = lg4573_spi_write_u16(ctx, buffer[i]);
68 static int lg4573_spi_write_dcs(struct lg4573 *ctx, u8 dcs)
70 return lg4573_spi_write_u16(ctx, (0x70 << 8 | dcs));
73 static int lg4573_display_on(struct lg4573 *ctx)
77 ret = lg4573_spi_write_dcs(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
83 return lg4573_spi_write_dcs(ctx, MIPI_DCS_SET_DISPLAY_ON);
86 static int lg4573_display_off(struct lg4573 *ctx)
90 ret = lg4573_spi_write_dcs(ctx, MIPI_DCS_SET_DISPLAY_OFF);
96 return lg4573_spi_write_dcs(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
99 static int lg4573_display_mode_settings(struct lg4573 *ctx)
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,
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));
117 static int lg4573_power_settings(struct lg4573 *ctx)
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,
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));
133 static int lg4573_gamma_settings(struct lg4573 *ctx)
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,
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));
158 static int lg4573_init(struct lg4573 *ctx)
162 dev_dbg(ctx->panel.dev, "initializing LCD\n");
164 ret = lg4573_display_mode_settings(ctx);
168 ret = lg4573_power_settings(ctx);
172 return lg4573_gamma_settings(ctx);
175 static int lg4573_power_on(struct lg4573 *ctx)
177 return lg4573_display_on(ctx);
180 static int lg4573_disable(struct drm_panel *panel)
182 struct lg4573 *ctx = panel_to_lg4573(panel);
184 return lg4573_display_off(ctx);
187 static int lg4573_enable(struct drm_panel *panel)
189 struct lg4573 *ctx = panel_to_lg4573(panel);
193 return lg4573_power_on(ctx);
196 static const struct drm_display_mode default_mode = {
199 .hsync_start = 480 + 10,
200 .hsync_end = 480 + 10 + 59,
201 .htotal = 480 + 10 + 59 + 10,
203 .vsync_start = 800 + 15,
204 .vsync_end = 800 + 15 + 15,
205 .vtotal = 800 + 15 + 15 + 15,
209 static int lg4573_get_modes(struct drm_panel *panel)
211 struct drm_connector *connector = panel->connector;
212 struct drm_display_mode *mode;
214 mode = drm_mode_duplicate(panel->drm, &default_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);
222 drm_mode_set_name(mode);
224 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
225 drm_mode_probed_add(connector, mode);
227 panel->connector->display_info.width_mm = 61;
228 panel->connector->display_info.height_mm = 103;
233 static const struct drm_panel_funcs lg4573_drm_funcs = {
234 .disable = lg4573_disable,
235 .enable = lg4573_enable,
236 .get_modes = lg4573_get_modes,
239 static int lg4573_probe(struct spi_device *spi)
244 ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL);
250 spi_set_drvdata(spi, ctx);
251 spi->bits_per_word = 8;
253 ret = spi_setup(spi);
255 dev_err(&spi->dev, "SPI setup failed: %d\n", ret);
259 drm_panel_init(&ctx->panel);
260 ctx->panel.dev = &spi->dev;
261 ctx->panel.funcs = &lg4573_drm_funcs;
263 return drm_panel_add(&ctx->panel);
266 static int lg4573_remove(struct spi_device *spi)
268 struct lg4573 *ctx = spi_get_drvdata(spi);
270 lg4573_display_off(ctx);
271 drm_panel_remove(&ctx->panel);
276 static const struct of_device_id lg4573_of_match[] = {
277 { .compatible = "lg,lg4573" },
280 MODULE_DEVICE_TABLE(of, lg4573_of_match);
282 static struct spi_driver lg4573_driver = {
283 .probe = lg4573_probe,
284 .remove = lg4573_remove,
287 .of_match_table = lg4573_of_match,
290 module_spi_driver(lg4573_driver);
292 MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
293 MODULE_DESCRIPTION("lg4573 LCD Driver");
294 MODULE_LICENSE("GPL v2");