1 // SPDX-License-Identifier: GPL-2.0
3 * S6E63M0 AMOLED LCD drm_panel driver.
6 * Derived from drivers/gpu/drm/panel-samsung-ld9040.c
11 #include <drm/drm_modes.h>
12 #include <drm/drm_panel.h>
13 #include <drm/drm_print.h>
15 #include <linux/backlight.h>
16 #include <linux/delay.h>
17 #include <linux/gpio/consumer.h>
18 #include <linux/module.h>
19 #include <linux/regulator/consumer.h>
20 #include <linux/spi/spi.h>
22 #include <video/mipi_display.h>
24 /* Manufacturer Command Set */
25 #define MCS_ELVSS_ON 0xb1
26 #define MCS_MIECTL1 0xc0
27 #define MCS_BCMODE 0xc1
28 #define MCS_DISCTL 0xf2
29 #define MCS_SRCCTL 0xf6
30 #define MCS_IFCTL 0xf7
31 #define MCS_PANELCTL 0xF8
32 #define MCS_PGAMMACTL 0xfa
34 #define NUM_GAMMA_LEVELS 11
35 #define GAMMA_TABLE_COUNT 23
37 #define DATA_MASK 0x100
39 #define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
41 /* array of gamma tables for gamma value 2.2 */
42 static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
43 { MCS_PGAMMACTL, 0x00,
44 0x18, 0x08, 0x24, 0x78, 0xEC, 0x3D, 0xC8,
45 0xC2, 0xB6, 0xC4, 0xC7, 0xB6, 0xD5, 0xD7,
46 0xCC, 0x00, 0x39, 0x00, 0x36, 0x00, 0x51 },
47 { MCS_PGAMMACTL, 0x00,
48 0x18, 0x08, 0x24, 0x73, 0x4A, 0x3D, 0xC0,
49 0xC2, 0xB1, 0xBB, 0xBE, 0xAC, 0xCE, 0xCF,
50 0xC5, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x82 },
51 { MCS_PGAMMACTL, 0x00,
52 0x18, 0x08, 0x24, 0x70, 0x51, 0x3E, 0xBF,
53 0xC1, 0xAF, 0xB9, 0xBC, 0xAB, 0xCC, 0xCC,
54 0xC2, 0x00, 0x65, 0x00, 0x67, 0x00, 0x8D },
55 { MCS_PGAMMACTL, 0x00,
56 0x18, 0x08, 0x24, 0x6C, 0x54, 0x3A, 0xBC,
57 0xBF, 0xAC, 0xB7, 0xBB, 0xA9, 0xC9, 0xC9,
58 0xBE, 0x00, 0x71, 0x00, 0x73, 0x00, 0x9E },
59 { MCS_PGAMMACTL, 0x00,
60 0x18, 0x08, 0x24, 0x69, 0x54, 0x37, 0xBB,
61 0xBE, 0xAC, 0xB4, 0xB7, 0xA6, 0xC7, 0xC8,
62 0xBC, 0x00, 0x7B, 0x00, 0x7E, 0x00, 0xAB },
63 { MCS_PGAMMACTL, 0x00,
64 0x18, 0x08, 0x24, 0x66, 0x55, 0x34, 0xBA,
65 0xBD, 0xAB, 0xB1, 0xB5, 0xA3, 0xC5, 0xC6,
66 0xB9, 0x00, 0x85, 0x00, 0x88, 0x00, 0xBA },
67 { MCS_PGAMMACTL, 0x00,
68 0x18, 0x08, 0x24, 0x63, 0x53, 0x31, 0xB8,
69 0xBC, 0xA9, 0xB0, 0xB5, 0xA2, 0xC4, 0xC4,
70 0xB8, 0x00, 0x8B, 0x00, 0x8E, 0x00, 0xC2 },
71 { MCS_PGAMMACTL, 0x00,
72 0x18, 0x08, 0x24, 0x62, 0x54, 0x30, 0xB9,
73 0xBB, 0xA9, 0xB0, 0xB3, 0xA1, 0xC1, 0xC3,
74 0xB7, 0x00, 0x91, 0x00, 0x95, 0x00, 0xDA },
75 { MCS_PGAMMACTL, 0x00,
76 0x18, 0x08, 0x24, 0x66, 0x58, 0x34, 0xB6,
77 0xBA, 0xA7, 0xAF, 0xB3, 0xA0, 0xC1, 0xC2,
78 0xB7, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xD1 },
79 { MCS_PGAMMACTL, 0x00,
80 0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 0xB6,
81 0xBA, 0xA8, 0xAC, 0xB1, 0x9D, 0xC1, 0xC1,
82 0xB7, 0x00, 0x9C, 0x00, 0x9F, 0x00, 0xD6 },
83 { MCS_PGAMMACTL, 0x00,
84 0x18, 0x08, 0x24, 0x5f, 0x50, 0x2d, 0xB6,
85 0xB9, 0xA7, 0xAd, 0xB1, 0x9f, 0xbe, 0xC0,
86 0xB5, 0x00, 0xa0, 0x00, 0xa4, 0x00, 0xdb },
91 struct drm_panel panel;
92 struct backlight_device *bl_dev;
94 struct regulator_bulk_data supplies[2];
95 struct gpio_desc *reset_gpio;
101 * This field is tested by functions directly accessing bus before
102 * transfer, transfer is skipped if it is set. In case of transfer
103 * failure or unexpected response the field is set to error value.
104 * Such construct allows to eliminate many checks in higher level
110 static const struct drm_display_mode default_mode = {
113 .hsync_start = 480 + 16,
114 .hsync_end = 480 + 16 + 2,
115 .htotal = 480 + 16 + 2 + 16,
117 .vsync_start = 800 + 28,
118 .vsync_end = 800 + 28 + 2,
119 .vtotal = 800 + 28 + 2 + 1,
122 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
125 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
127 return container_of(panel, struct s6e63m0, panel);
130 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
132 int ret = ctx->error;
138 static int s6e63m0_spi_write_word(struct s6e63m0 *ctx, u16 data)
140 struct spi_device *spi = to_spi_device(ctx->dev);
141 struct spi_transfer xfer = {
145 struct spi_message msg;
147 spi_message_init(&msg);
148 spi_message_add_tail(&xfer, &msg);
150 return spi_sync(spi, &msg);
153 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
157 if (ctx->error < 0 || len == 0)
160 DRM_DEV_DEBUG(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data);
161 ret = s6e63m0_spi_write_word(ctx, *data);
163 while (!ret && --len) {
165 ret = s6e63m0_spi_write_word(ctx, *data | DATA_MASK);
169 DRM_DEV_ERROR(ctx->dev, "error %d writing dcs seq: %*ph\n", ret,
174 usleep_range(300, 310);
177 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
179 static const u8 d[] = { seq }; \
180 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
183 static void s6e63m0_init(struct s6e63m0 *ctx)
185 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
186 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
187 0x63, 0x86, 0x1a, 0x33, 0x0d, 0x00, 0x00);
189 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
190 0x02, 0x03, 0x1c, 0x10, 0x10);
191 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
194 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
195 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
196 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
197 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
199 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
202 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
204 s6e63m0_dcs_write_seq_static(ctx, 0xb3,
207 s6e63m0_dcs_write_seq_static(ctx, 0xb5,
208 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
209 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
210 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
211 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
212 0x21, 0x20, 0x1e, 0x1e);
214 s6e63m0_dcs_write_seq_static(ctx, 0xb6,
215 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
216 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
219 s6e63m0_dcs_write_seq_static(ctx, 0xb7,
220 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
221 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
222 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
223 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
224 0x21, 0x20, 0x1e, 0x1e, 0x00, 0x00, 0x11,
225 0x22, 0x33, 0x44, 0x44, 0x44, 0x55, 0x55,
226 0x66, 0x66, 0x66, 0x66, 0x66, 0x66);
228 s6e63m0_dcs_write_seq_static(ctx, 0xb9,
229 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
230 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
231 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
232 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
233 0x21, 0x20, 0x1e, 0x1e);
235 s6e63m0_dcs_write_seq_static(ctx, 0xba,
236 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
237 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
240 s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
241 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
242 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
244 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
246 s6e63m0_dcs_write_seq_static(ctx, 0xb2,
247 0x10, 0x10, 0x0b, 0x05);
249 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
252 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
255 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
258 static int s6e63m0_power_on(struct s6e63m0 *ctx)
262 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
268 gpiod_set_value(ctx->reset_gpio, 0);
274 static int s6e63m0_power_off(struct s6e63m0 *ctx)
278 gpiod_set_value(ctx->reset_gpio, 1);
281 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
288 static int s6e63m0_disable(struct drm_panel *panel)
290 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
295 backlight_disable(ctx->bl_dev);
297 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
300 ctx->enabled = false;
305 static int s6e63m0_unprepare(struct drm_panel *panel)
307 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
313 s6e63m0_clear_error(ctx);
315 ret = s6e63m0_power_off(ctx);
319 ctx->prepared = false;
324 static int s6e63m0_prepare(struct drm_panel *panel)
326 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
332 ret = s6e63m0_power_on(ctx);
338 ret = s6e63m0_clear_error(ctx);
341 s6e63m0_unprepare(panel);
343 ctx->prepared = true;
348 static int s6e63m0_enable(struct drm_panel *panel)
350 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
355 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
357 backlight_enable(ctx->bl_dev);
364 static int s6e63m0_get_modes(struct drm_panel *panel,
365 struct drm_connector *connector)
367 struct drm_display_mode *mode;
369 mode = drm_mode_duplicate(connector->dev, &default_mode);
371 DRM_ERROR("failed to add mode %ux%ux@%u\n",
372 default_mode.hdisplay, default_mode.vdisplay,
373 drm_mode_vrefresh(&default_mode));
377 drm_mode_set_name(mode);
379 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
380 drm_mode_probed_add(connector, mode);
385 static const struct drm_panel_funcs s6e63m0_drm_funcs = {
386 .disable = s6e63m0_disable,
387 .unprepare = s6e63m0_unprepare,
388 .prepare = s6e63m0_prepare,
389 .enable = s6e63m0_enable,
390 .get_modes = s6e63m0_get_modes,
393 static int s6e63m0_set_brightness(struct backlight_device *bd)
395 struct s6e63m0 *ctx = bl_get_data(bd);
397 int brightness = bd->props.brightness;
399 /* disable and set new gamma */
400 s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
401 ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
403 /* update gamma table. */
404 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x01);
406 return s6e63m0_clear_error(ctx);
409 static const struct backlight_ops s6e63m0_backlight_ops = {
410 .update_status = s6e63m0_set_brightness,
413 static int s6e63m0_backlight_register(struct s6e63m0 *ctx)
415 struct backlight_properties props = {
416 .type = BACKLIGHT_RAW,
417 .brightness = MAX_BRIGHTNESS,
418 .max_brightness = MAX_BRIGHTNESS
420 struct device *dev = ctx->dev;
423 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
424 &s6e63m0_backlight_ops,
426 if (IS_ERR(ctx->bl_dev)) {
427 ret = PTR_ERR(ctx->bl_dev);
428 DRM_DEV_ERROR(dev, "error registering backlight device (%d)\n",
435 static int s6e63m0_probe(struct spi_device *spi)
437 struct device *dev = &spi->dev;
441 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
445 spi_set_drvdata(spi, ctx);
448 ctx->enabled = false;
449 ctx->prepared = false;
451 ctx->supplies[0].supply = "vdd3";
452 ctx->supplies[1].supply = "vci";
453 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
456 DRM_DEV_ERROR(dev, "failed to get regulators: %d\n", ret);
460 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
461 if (IS_ERR(ctx->reset_gpio)) {
462 DRM_DEV_ERROR(dev, "cannot get reset-gpios %ld\n",
463 PTR_ERR(ctx->reset_gpio));
464 return PTR_ERR(ctx->reset_gpio);
467 spi->bits_per_word = 9;
468 spi->mode = SPI_MODE_3;
469 ret = spi_setup(spi);
471 DRM_DEV_ERROR(dev, "spi setup failed.\n");
475 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
476 DRM_MODE_CONNECTOR_DPI);
478 ret = s6e63m0_backlight_register(ctx);
482 return drm_panel_add(&ctx->panel);
485 static int s6e63m0_remove(struct spi_device *spi)
487 struct s6e63m0 *ctx = spi_get_drvdata(spi);
489 drm_panel_remove(&ctx->panel);
494 static const struct of_device_id s6e63m0_of_match[] = {
495 { .compatible = "samsung,s6e63m0" },
498 MODULE_DEVICE_TABLE(of, s6e63m0_of_match);
500 static struct spi_driver s6e63m0_driver = {
501 .probe = s6e63m0_probe,
502 .remove = s6e63m0_remove,
504 .name = "panel-samsung-s6e63m0",
505 .of_match_table = s6e63m0_of_match,
508 module_spi_driver(s6e63m0_driver);
511 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
512 MODULE_LICENSE("GPL v2");