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