Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Feb 10-13, 2025
Register
Loading...
v4.6
 
  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 <drm/drmP.h>
 19#include <drm/drm_panel.h>
 20
 21#include <linux/gpio/consumer.h>
 
 22#include <linux/regulator/consumer.h>
 23#include <linux/spi/spi.h>
 24
 25#include <video/mipi_display.h>
 26#include <video/of_videomode.h>
 27#include <video/videomode.h>
 28
 
 
 
 
 29struct lg4573 {
 30	struct drm_panel panel;
 31	struct spi_device *spi;
 32	struct videomode vm;
 33};
 34
 35static inline struct lg4573 *panel_to_lg4573(struct drm_panel *panel)
 36{
 37	return container_of(panel, struct lg4573, panel);
 38}
 39
 40static int lg4573_spi_write_u16(struct lg4573 *ctx, u16 data)
 41{
 42	struct spi_transfer xfer = {
 43		.len = 2,
 44	};
 45	u16 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
 56static 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
 71static int lg4573_spi_write_dcs(struct lg4573 *ctx, u8 dcs)
 72{
 73	return lg4573_spi_write_u16(ctx, (0x70 << 8 | dcs));
 74}
 75
 76static 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
 89static 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
102static 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
120static 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
136static 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
161static 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
178static int lg4573_power_on(struct lg4573 *ctx)
179{
180	return lg4573_display_on(ctx);
181}
182
183static 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
190static 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
199static const struct drm_display_mode default_mode = {
200	.clock = 27000,
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	.vrefresh = 60,
210};
211
212static int lg4573_get_modes(struct drm_panel *panel)
 
213{
214	struct drm_connector *connector = panel->connector;
215	struct drm_display_mode *mode;
216
217	mode = drm_mode_duplicate(panel->drm, &default_mode);
218	if (!mode) {
219		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
220			default_mode.hdisplay, default_mode.vdisplay,
221			default_mode.vrefresh);
222		return -ENOMEM;
223	}
224
225	drm_mode_set_name(mode);
226
227	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
228	drm_mode_probed_add(connector, mode);
229
230	panel->connector->display_info.width_mm = 61;
231	panel->connector->display_info.height_mm = 103;
232
233	return 1;
234}
235
236static const struct drm_panel_funcs lg4573_drm_funcs = {
237	.disable = lg4573_disable,
238	.enable = lg4573_enable,
239	.get_modes = lg4573_get_modes,
240};
241
242static int lg4573_probe(struct spi_device *spi)
243{
244	struct lg4573 *ctx;
245	int ret;
246
247	ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL);
248	if (!ctx)
249		return -ENOMEM;
250
251	ctx->spi = spi;
252
253	spi_set_drvdata(spi, ctx);
254	spi->bits_per_word = 8;
255
256	ret = spi_setup(spi);
257	if (ret < 0) {
258		dev_err(&spi->dev, "SPI setup failed: %d\n", ret);
259		return ret;
260	}
261
262	drm_panel_init(&ctx->panel);
263	ctx->panel.dev = &spi->dev;
264	ctx->panel.funcs = &lg4573_drm_funcs;
265
266	return drm_panel_add(&ctx->panel);
 
 
267}
268
269static 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
279static const struct of_device_id lg4573_of_match[] = {
280	{ .compatible = "lg,lg4573" },
281	{ }
282};
283MODULE_DEVICE_TABLE(of, lg4573_of_match);
284
285static 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};
293module_spi_driver(lg4573_driver);
294
295MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
296MODULE_DESCRIPTION("lg4573 LCD Driver");
297MODULE_LICENSE("GPL v2");
v6.13.7
  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
 29struct lg4573 {
 30	struct drm_panel panel;
 31	struct spi_device *spi;
 32	struct videomode vm;
 33};
 34
 35static inline struct lg4573 *panel_to_lg4573(struct drm_panel *panel)
 36{
 37	return container_of(panel, struct lg4573, panel);
 38}
 39
 40static 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
 56static 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
 71static int lg4573_spi_write_dcs(struct lg4573 *ctx, u8 dcs)
 72{
 73	return lg4573_spi_write_u16(ctx, (0x70 << 8 | dcs));
 74}
 75
 76static 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
 89static 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
102static 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
120static 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
136static 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
161static 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
178static int lg4573_power_on(struct lg4573 *ctx)
179{
180	return lg4573_display_on(ctx);
181}
182
183static 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
190static 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
199static 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
211static 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
235static const struct drm_panel_funcs lg4573_drm_funcs = {
236	.disable = lg4573_disable,
237	.enable = lg4573_enable,
238	.get_modes = lg4573_get_modes,
239};
240
241static 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
269static void 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
277static const struct of_device_id lg4573_of_match[] = {
278	{ .compatible = "lg,lg4573" },
279	{ }
280};
281MODULE_DEVICE_TABLE(of, lg4573_of_match);
282
283static struct spi_driver lg4573_driver = {
284	.probe = lg4573_probe,
285	.remove = lg4573_remove,
286	.driver = {
287		.name = "lg4573",
288		.of_match_table = lg4573_of_match,
289	},
290};
291module_spi_driver(lg4573_driver);
292
293MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
294MODULE_DESCRIPTION("lg4573 LCD Driver");
295MODULE_LICENSE("GPL v2");