]> Git Repo - linux.git/blob - drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
ASoC: simple-card: Use snd_soc_of_parse_aux_devs()
[linux.git] / drivers / gpu / drm / panel / panel-samsung-s6e63m0.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * S6E63M0 AMOLED LCD drm_panel driver.
4  *
5  * Copyright (C) 2019 PaweÅ‚ Chmiel <[email protected]>
6  * Derived from drivers/gpu/drm/panel-samsung-ld9040.c
7  *
8  * Andrzej Hajda <[email protected]>
9  */
10
11 #include <drm/drm_modes.h>
12 #include <drm/drm_panel.h>
13 #include <drm/drm_print.h>
14
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>
21
22 #include <video/mipi_display.h>
23
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
33
34 #define NUM_GAMMA_LEVELS             11
35 #define GAMMA_TABLE_COUNT           23
36
37 #define DATA_MASK                                       0x100
38
39 #define MAX_BRIGHTNESS              (NUM_GAMMA_LEVELS - 1)
40
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 },
87 };
88
89 struct s6e63m0 {
90         struct device *dev;
91         struct drm_panel panel;
92         struct backlight_device *bl_dev;
93
94         struct regulator_bulk_data supplies[2];
95         struct gpio_desc *reset_gpio;
96
97         bool prepared;
98         bool enabled;
99
100         /*
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
105          * functions.
106          */
107         int error;
108 };
109
110 static const struct drm_display_mode default_mode = {
111         .clock          = 25628,
112         .hdisplay       = 480,
113         .hsync_start    = 480 + 16,
114         .hsync_end      = 480 + 16 + 2,
115         .htotal         = 480 + 16 + 2 + 16,
116         .vdisplay       = 800,
117         .vsync_start    = 800 + 28,
118         .vsync_end      = 800 + 28 + 2,
119         .vtotal         = 800 + 28 + 2 + 1,
120         .width_mm       = 53,
121         .height_mm      = 89,
122         .flags          = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
123 };
124
125 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
126 {
127         return container_of(panel, struct s6e63m0, panel);
128 }
129
130 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
131 {
132         int ret = ctx->error;
133
134         ctx->error = 0;
135         return ret;
136 }
137
138 static int s6e63m0_spi_write_word(struct s6e63m0 *ctx, u16 data)
139 {
140         struct spi_device *spi = to_spi_device(ctx->dev);
141         struct spi_transfer xfer = {
142                 .len    = 2,
143                 .tx_buf = &data,
144         };
145         struct spi_message msg;
146
147         spi_message_init(&msg);
148         spi_message_add_tail(&xfer, &msg);
149
150         return spi_sync(spi, &msg);
151 }
152
153 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
154 {
155         int ret = 0;
156
157         if (ctx->error < 0 || len == 0)
158                 return;
159
160         DRM_DEV_DEBUG(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data);
161         ret = s6e63m0_spi_write_word(ctx, *data);
162
163         while (!ret && --len) {
164                 ++data;
165                 ret = s6e63m0_spi_write_word(ctx, *data | DATA_MASK);
166         }
167
168         if (ret) {
169                 DRM_DEV_ERROR(ctx->dev, "error %d writing dcs seq: %*ph\n", ret,
170                               (int)len, data);
171                 ctx->error = ret;
172         }
173
174         usleep_range(300, 310);
175 }
176
177 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
178         ({ \
179                 static const u8 d[] = { seq }; \
180                 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
181         })
182
183 static void s6e63m0_init(struct s6e63m0 *ctx)
184 {
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);
188
189         s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
190                                      0x02, 0x03, 0x1c, 0x10, 0x10);
191         s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
192                                      0x03, 0x00, 0x00);
193
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,
198                                      0xd6);
199         s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
200                                      0x01);
201
202         s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
203                                      0x00, 0x8c, 0x07);
204         s6e63m0_dcs_write_seq_static(ctx, 0xb3,
205                                      0xc);
206
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);
213
214         s6e63m0_dcs_write_seq_static(ctx, 0xb6,
215                                      0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
216                                      0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
217                                      0x66, 0x66);
218
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);
227
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);
234
235         s6e63m0_dcs_write_seq_static(ctx, 0xba,
236                                      0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
237                                      0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
238                                      0x66, 0x66);
239
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);
245
246         s6e63m0_dcs_write_seq_static(ctx, 0xb2,
247                                      0x10, 0x10, 0x0b, 0x05);
248
249         s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
250                                      0x01);
251
252         s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
253                                      0x0b);
254
255         s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
256 }
257
258 static int s6e63m0_power_on(struct s6e63m0 *ctx)
259 {
260         int ret;
261
262         ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
263         if (ret < 0)
264                 return ret;
265
266         msleep(25);
267
268         gpiod_set_value(ctx->reset_gpio, 0);
269         msleep(120);
270
271         return 0;
272 }
273
274 static int s6e63m0_power_off(struct s6e63m0 *ctx)
275 {
276         int ret;
277
278         gpiod_set_value(ctx->reset_gpio, 1);
279         msleep(120);
280
281         ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
282         if (ret < 0)
283                 return ret;
284
285         return 0;
286 }
287
288 static int s6e63m0_disable(struct drm_panel *panel)
289 {
290         struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
291
292         if (!ctx->enabled)
293                 return 0;
294
295         backlight_disable(ctx->bl_dev);
296
297         s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
298         msleep(200);
299
300         ctx->enabled = false;
301
302         return 0;
303 }
304
305 static int s6e63m0_unprepare(struct drm_panel *panel)
306 {
307         struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
308         int ret;
309
310         if (!ctx->prepared)
311                 return 0;
312
313         s6e63m0_clear_error(ctx);
314
315         ret = s6e63m0_power_off(ctx);
316         if (ret < 0)
317                 return ret;
318
319         ctx->prepared = false;
320
321         return 0;
322 }
323
324 static int s6e63m0_prepare(struct drm_panel *panel)
325 {
326         struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
327         int ret;
328
329         if (ctx->prepared)
330                 return 0;
331
332         ret = s6e63m0_power_on(ctx);
333         if (ret < 0)
334                 return ret;
335
336         s6e63m0_init(ctx);
337
338         ret = s6e63m0_clear_error(ctx);
339
340         if (ret < 0)
341                 s6e63m0_unprepare(panel);
342
343         ctx->prepared = true;
344
345         return ret;
346 }
347
348 static int s6e63m0_enable(struct drm_panel *panel)
349 {
350         struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
351
352         if (ctx->enabled)
353                 return 0;
354
355         s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
356
357         backlight_enable(ctx->bl_dev);
358
359         ctx->enabled = true;
360
361         return 0;
362 }
363
364 static int s6e63m0_get_modes(struct drm_panel *panel,
365                              struct drm_connector *connector)
366 {
367         struct drm_display_mode *mode;
368
369         mode = drm_mode_duplicate(connector->dev, &default_mode);
370         if (!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));
374                 return -ENOMEM;
375         }
376
377         drm_mode_set_name(mode);
378
379         mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
380         drm_mode_probed_add(connector, mode);
381
382         return 1;
383 }
384
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,
391 };
392
393 static int s6e63m0_set_brightness(struct backlight_device *bd)
394 {
395         struct s6e63m0 *ctx = bl_get_data(bd);
396
397         int brightness = bd->props.brightness;
398
399         /* disable and set new gamma */
400         s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
401                           ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
402
403         /* update gamma table. */
404         s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x01);
405
406         return s6e63m0_clear_error(ctx);
407 }
408
409 static const struct backlight_ops s6e63m0_backlight_ops = {
410         .update_status  = s6e63m0_set_brightness,
411 };
412
413 static int s6e63m0_backlight_register(struct s6e63m0 *ctx)
414 {
415         struct backlight_properties props = {
416                 .type           = BACKLIGHT_RAW,
417                 .brightness     = MAX_BRIGHTNESS,
418                 .max_brightness = MAX_BRIGHTNESS
419         };
420         struct device *dev = ctx->dev;
421         int ret = 0;
422
423         ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
424                                                      &s6e63m0_backlight_ops,
425                                                      &props);
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",
429                               ret);
430         }
431
432         return ret;
433 }
434
435 static int s6e63m0_probe(struct spi_device *spi)
436 {
437         struct device *dev = &spi->dev;
438         struct s6e63m0 *ctx;
439         int ret;
440
441         ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
442         if (!ctx)
443                 return -ENOMEM;
444
445         spi_set_drvdata(spi, ctx);
446
447         ctx->dev = dev;
448         ctx->enabled = false;
449         ctx->prepared = false;
450
451         ctx->supplies[0].supply = "vdd3";
452         ctx->supplies[1].supply = "vci";
453         ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
454                                       ctx->supplies);
455         if (ret < 0) {
456                 DRM_DEV_ERROR(dev, "failed to get regulators: %d\n", ret);
457                 return ret;
458         }
459
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);
465         }
466
467         spi->bits_per_word = 9;
468         spi->mode = SPI_MODE_3;
469         ret = spi_setup(spi);
470         if (ret < 0) {
471                 DRM_DEV_ERROR(dev, "spi setup failed.\n");
472                 return ret;
473         }
474
475         drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
476                        DRM_MODE_CONNECTOR_DPI);
477
478         ret = s6e63m0_backlight_register(ctx);
479         if (ret < 0)
480                 return ret;
481
482         return drm_panel_add(&ctx->panel);
483 }
484
485 static int s6e63m0_remove(struct spi_device *spi)
486 {
487         struct s6e63m0 *ctx = spi_get_drvdata(spi);
488
489         drm_panel_remove(&ctx->panel);
490
491         return 0;
492 }
493
494 static const struct of_device_id s6e63m0_of_match[] = {
495         { .compatible = "samsung,s6e63m0" },
496         { /* sentinel */ }
497 };
498 MODULE_DEVICE_TABLE(of, s6e63m0_of_match);
499
500 static struct spi_driver s6e63m0_driver = {
501         .probe                  = s6e63m0_probe,
502         .remove                 = s6e63m0_remove,
503         .driver                 = {
504                 .name           = "panel-samsung-s6e63m0",
505                 .of_match_table = s6e63m0_of_match,
506         },
507 };
508 module_spi_driver(s6e63m0_driver);
509
510 MODULE_AUTHOR("PaweÅ‚ Chmiel <[email protected]>");
511 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
512 MODULE_LICENSE("GPL v2");
This page took 0.057245 seconds and 4 git commands to generate.