1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Panel driver for the Samsung S6D27A1 480x800 DPI RGB panel.
4 * Found in the Samsung Galaxy Ace 2 GT-I8160 mobile phone.
5 */
6
7#include <drm/drm_mipi_dbi.h>
8#include <drm/drm_modes.h>
9#include <drm/drm_panel.h>
10
11#include <linux/delay.h>
12#include <linux/gpio/consumer.h>
13#include <linux/init.h>
14#include <linux/kernel.h>
15#include <linux/media-bus-format.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/regulator/consumer.h>
19#include <linux/spi/spi.h>
20
21#include <video/mipi_display.h>
22
23#define S6D27A1_PASSWD_L2	0xF0	/* Password Command for Level 2 Control */
24#define S6D27A1_RESCTL		0xB3	/* Resolution Select Control */
25#define S6D27A1_PANELCTL2	0xB4	/* ASG Signal Control */
26#define S6D27A1_READID1		0xDA	/* Read panel ID 1 */
27#define S6D27A1_READID2		0xDB	/* Read panel ID 2 */
28#define S6D27A1_READID3		0xDC	/* Read panel ID 3 */
29#define S6D27A1_DISPCTL		0xF2	/* Display Control */
30#define S6D27A1_MANPWR		0xF3	/* Manual Control */
31#define S6D27A1_PWRCTL1		0xF4	/* Power Control */
32#define S6D27A1_SRCCTL		0xF6	/* Source Control */
33#define S6D27A1_PANELCTL	0xF7	/* Panel Control*/
34
35static const u8 s6d27a1_dbi_read_commands[] = {
36	S6D27A1_READID1,
37	S6D27A1_READID2,
38	S6D27A1_READID3,
39	0, /* sentinel */
40};
41
42struct s6d27a1 {
43	struct device *dev;
44	struct mipi_dbi dbi;
45	struct drm_panel panel;
46	struct gpio_desc *reset;
47	struct regulator_bulk_data regulators[2];
48};
49
50static const struct drm_display_mode s6d27a1_480_800_mode = {
51	/*
52	 * The vendor driver states that the S6D27A1 panel
53	 * has a pixel clock frequency of 49920000 Hz / 2 = 24960000 Hz.
54	 */
55	.clock = 24960,
56	.hdisplay = 480,
57	.hsync_start = 480 + 63,
58	.hsync_end = 480 + 63 + 2,
59	.htotal = 480 + 63 + 2 + 63,
60	.vdisplay = 800,
61	.vsync_start = 800 + 11,
62	.vsync_end = 800 + 11 + 2,
63	.vtotal = 800 + 11 + 2 + 10,
64	.width_mm = 50,
65	.height_mm = 84,
66	.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
67};
68
69static inline struct s6d27a1 *to_s6d27a1(struct drm_panel *panel)
70{
71	return container_of(panel, struct s6d27a1, panel);
72}
73
74static void s6d27a1_read_mtp_id(struct s6d27a1 *ctx)
75{
76	struct mipi_dbi *dbi = &ctx->dbi;
77	u8 id1, id2, id3;
78	int ret;
79
80	ret = mipi_dbi_command_read(dbi, S6D27A1_READID1, &id1);
81	if (ret) {
82		dev_err(ctx->dev, "unable to read MTP ID 1\n");
83		return;
84	}
85	ret = mipi_dbi_command_read(dbi, S6D27A1_READID2, &id2);
86	if (ret) {
87		dev_err(ctx->dev, "unable to read MTP ID 2\n");
88		return;
89	}
90	ret = mipi_dbi_command_read(dbi, S6D27A1_READID3, &id3);
91	if (ret) {
92		dev_err(ctx->dev, "unable to read MTP ID 3\n");
93		return;
94	}
95	dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
96}
97
98static int s6d27a1_power_on(struct s6d27a1 *ctx)
99{
100	struct mipi_dbi *dbi = &ctx->dbi;
101	int ret;
102
103	/* Power up */
104	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->regulators),
105				    ctx->regulators);
106	if (ret) {
107		dev_err(ctx->dev, "failed to enable regulators: %d\n", ret);
108		return ret;
109	}
110
111	msleep(20);
112
113	/* Assert reset >=1 ms */
114	gpiod_set_value_cansleep(ctx->reset, 1);
115	usleep_range(1000, 5000);
116	/* De-assert reset */
117	gpiod_set_value_cansleep(ctx->reset, 0);
118	/* Wait >= 10 ms */
119	msleep(20);
120
121	/*
122	 * Exit sleep mode and initialize display - some hammering is
123	 * necessary.
124	 */
125	mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
126	mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
127	msleep(120);
128
129	/* Magic to unlock level 2 control of the display */
130	mipi_dbi_command(dbi, S6D27A1_PASSWD_L2, 0x5A, 0x5A);
131
132	/* Configure resolution to 480RGBx800 */
133	mipi_dbi_command(dbi, S6D27A1_RESCTL, 0x22);
134
135	mipi_dbi_command(dbi, S6D27A1_PANELCTL2, 0x00, 0x02, 0x03, 0x04, 0x05, 0x08, 0x00, 0x0c);
136
137	mipi_dbi_command(dbi, S6D27A1_MANPWR, 0x01, 0x00, 0x00, 0x08, 0x08, 0x02, 0x00);
138
139	mipi_dbi_command(dbi, S6D27A1_DISPCTL, 0x19, 0x00, 0x08, 0x0D, 0x03, 0x41, 0x3F);
140
141	mipi_dbi_command(dbi, S6D27A1_PWRCTL1, 0x00, 0x00, 0x00, 0x00, 0x55,
142					0x44, 0x05, 0x88, 0x4B, 0x50);
143
144	mipi_dbi_command(dbi, S6D27A1_SRCCTL, 0x03, 0x09, 0x8A, 0x00, 0x01, 0x16);
145
146	mipi_dbi_command(dbi, S6D27A1_PANELCTL, 0x00, 0x05, 0x06, 0x07, 0x08,
147					0x01, 0x09, 0x0D, 0x0A, 0x0E,
148					0x0B, 0x0F, 0x0C, 0x10, 0x01,
149					0x11, 0x12, 0x13, 0x14, 0x05,
150					0x06, 0x07, 0x08, 0x01, 0x09,
151					0x0D, 0x0A, 0x0E, 0x0B, 0x0F,
152					0x0C, 0x10, 0x01, 0x11, 0x12,
153					0x13, 0x14);
154
155	/* lock the level 2 control */
156	mipi_dbi_command(dbi, S6D27A1_PASSWD_L2, 0xA5, 0xA5);
157
158	s6d27a1_read_mtp_id(ctx);
159
160	return 0;
161}
162
163static int s6d27a1_power_off(struct s6d27a1 *ctx)
164{
165	/* Go into RESET and disable regulators */
166	gpiod_set_value_cansleep(ctx->reset, 1);
167	return regulator_bulk_disable(ARRAY_SIZE(ctx->regulators),
168				      ctx->regulators);
169}
170
171static int s6d27a1_unprepare(struct drm_panel *panel)
172{
173	struct s6d27a1 *ctx = to_s6d27a1(panel);
174	struct mipi_dbi *dbi = &ctx->dbi;
175
176	mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
177	msleep(120);
178	return s6d27a1_power_off(to_s6d27a1(panel));
179}
180
181static int s6d27a1_disable(struct drm_panel *panel)
182{
183	struct s6d27a1 *ctx = to_s6d27a1(panel);
184	struct mipi_dbi *dbi = &ctx->dbi;
185
186	mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
187	msleep(25);
188
189	return 0;
190}
191
192static int s6d27a1_prepare(struct drm_panel *panel)
193{
194	return s6d27a1_power_on(to_s6d27a1(panel));
195}
196
197static int s6d27a1_enable(struct drm_panel *panel)
198{
199	struct s6d27a1 *ctx = to_s6d27a1(panel);
200	struct mipi_dbi *dbi = &ctx->dbi;
201
202	mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
203
204	return 0;
205}
206
207static int s6d27a1_get_modes(struct drm_panel *panel,
208			    struct drm_connector *connector)
209{
210	struct s6d27a1 *ctx = to_s6d27a1(panel);
211	struct drm_display_mode *mode;
212	static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
213
214	mode = drm_mode_duplicate(connector->dev, &s6d27a1_480_800_mode);
215	if (!mode) {
216		dev_err(ctx->dev, "failed to add mode\n");
217		return -ENOMEM;
218	}
219
220	connector->display_info.bpc = 8;
221	connector->display_info.width_mm = mode->width_mm;
222	connector->display_info.height_mm = mode->height_mm;
223	connector->display_info.bus_flags =
224		DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
225	drm_display_info_set_bus_formats(&connector->display_info,
226					 &bus_format, 1);
227
228	drm_mode_set_name(mode);
229	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
230
231	drm_mode_probed_add(connector, mode);
232
233	return 1;
234}
235
236static const struct drm_panel_funcs s6d27a1_drm_funcs = {
237	.disable = s6d27a1_disable,
238	.unprepare = s6d27a1_unprepare,
239	.prepare = s6d27a1_prepare,
240	.enable = s6d27a1_enable,
241	.get_modes = s6d27a1_get_modes,
242};
243
244static int s6d27a1_probe(struct spi_device *spi)
245{
246	struct device *dev = &spi->dev;
247	struct s6d27a1 *ctx;
248	int ret;
249
250	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
251	if (!ctx)
252		return -ENOMEM;
253
254	ctx->dev = dev;
255
256	/*
257	 * VCI   is the analog voltage supply
258	 * VCCIO is the digital I/O voltage supply
259	 */
260	ctx->regulators[0].supply = "vci";
261	ctx->regulators[1].supply = "vccio";
262	ret = devm_regulator_bulk_get(dev,
263				      ARRAY_SIZE(ctx->regulators),
264				      ctx->regulators);
265	if (ret)
266		return dev_err_probe(dev, ret, "failed to get regulators\n");
267
268	ctx->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
269	if (IS_ERR(ctx->reset)) {
270		ret = PTR_ERR(ctx->reset);
271		return dev_err_probe(dev, ret, "no RESET GPIO\n");
272	}
273
274	ret = mipi_dbi_spi_init(spi, &ctx->dbi, NULL);
275	if (ret)
276		return dev_err_probe(dev, ret, "MIPI DBI init failed\n");
277
278	ctx->dbi.read_commands = s6d27a1_dbi_read_commands;
279
280	drm_panel_init(&ctx->panel, dev, &s6d27a1_drm_funcs,
281		       DRM_MODE_CONNECTOR_DPI);
282
283	ret = drm_panel_of_backlight(&ctx->panel);
284	if (ret)
285		return dev_err_probe(dev, ret, "failed to add backlight\n");
286
287	spi_set_drvdata(spi, ctx);
288
289	drm_panel_add(&ctx->panel);
290
291	return 0;
292}
293
294static void s6d27a1_remove(struct spi_device *spi)
295{
296	struct s6d27a1 *ctx = spi_get_drvdata(spi);
297
298	drm_panel_remove(&ctx->panel);
299}
300
301static const struct of_device_id s6d27a1_match[] = {
302	{ .compatible = "samsung,s6d27a1", },
303	{ /* sentinel */ },
304};
305MODULE_DEVICE_TABLE(of, s6d27a1_match);
306
307static struct spi_driver s6d27a1_driver = {
308	.probe		= s6d27a1_probe,
309	.remove		= s6d27a1_remove,
310	.driver		= {
311		.name	= "s6d27a1-panel",
312		.of_match_table = s6d27a1_match,
313	},
314};
315module_spi_driver(s6d27a1_driver);
316
317MODULE_AUTHOR("Markuss Broks <markuss.broks@gmail.com>");
318MODULE_DESCRIPTION("Samsung S6D27A1 panel driver");
319MODULE_LICENSE("GPL v2");
320