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, val: &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, val: &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, val: &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 consumers: ctx->regulators);
106 if (ret) {
107 dev_err(ctx->dev, "failed to enable regulators: %d\n", ret);
108 return ret;
109 }
110
111 msleep(msecs: 20);
112
113 /* Assert reset >=1 ms */
114 gpiod_set_value_cansleep(desc: ctx->reset, value: 1);
115 usleep_range(min: 1000, max: 5000);
116 /* De-assert reset */
117 gpiod_set_value_cansleep(desc: ctx->reset, value: 0);
118 /* Wait >= 10 ms */
119 msleep(msecs: 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(msecs: 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(desc: ctx->reset, value: 1);
167 return regulator_bulk_disable(ARRAY_SIZE(ctx->regulators),
168 consumers: 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(msecs: 120);
178 return s6d27a1_power_off(ctx: 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(msecs: 25);
188
189 return 0;
190}
191
192static int s6d27a1_prepare(struct drm_panel *panel)
193{
194 return s6d27a1_power_on(ctx: 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(dev: connector->dev, mode: &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(info: &connector->display_info,
226 formats: &bus_format, num_formats: 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, size: 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 consumers: ctx->regulators);
265 if (ret)
266 return dev_err_probe(dev, err: ret, fmt: "failed to get regulators\n");
267
268 ctx->reset = devm_gpiod_get(dev, con_id: "reset", flags: GPIOD_OUT_HIGH);
269 if (IS_ERR(ptr: ctx->reset)) {
270 ret = PTR_ERR(ptr: ctx->reset);
271 return dev_err_probe(dev, err: ret, fmt: "no RESET GPIO\n");
272 }
273
274 ret = mipi_dbi_spi_init(spi, dbi: &ctx->dbi, NULL);
275 if (ret)
276 return dev_err_probe(dev, err: ret, fmt: "MIPI DBI init failed\n");
277
278 ctx->dbi.read_commands = s6d27a1_dbi_read_commands;
279
280 drm_panel_init(panel: &ctx->panel, dev, funcs: &s6d27a1_drm_funcs,
281 DRM_MODE_CONNECTOR_DPI);
282
283 ret = drm_panel_of_backlight(panel: &ctx->panel);
284 if (ret)
285 return dev_err_probe(dev, err: ret, fmt: "failed to add backlight\n");
286
287 spi_set_drvdata(spi, data: ctx);
288
289 drm_panel_add(panel: &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(panel: &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

source code of linux/drivers/gpu/drm/panel/panel-samsung-s6d27a1.c