treewide: remove redundant IS_ERR() before error code check
[linux/fpc-iii.git] / drivers / gpu / drm / panel / panel-samsung-s6e63m0.c
bloba5f76eb4fa25b205ad40c6289da7588c62d105e8
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * S6E63M0 AMOLED LCD drm_panel driver.
5 * Copyright (C) 2019 Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>
6 * Derived from drivers/gpu/drm/panel-samsung-ld9040.c
8 * Andrzej Hajda <a.hajda@samsung.com>
9 */
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 },
89 struct s6e63m0 {
90 struct device *dev;
91 struct drm_panel panel;
92 struct backlight_device *bl_dev;
94 struct regulator_bulk_data supplies[2];
95 struct gpio_desc *reset_gpio;
97 bool prepared;
98 bool enabled;
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.
107 int error;
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 .vrefresh = 60,
121 .width_mm = 53,
122 .height_mm = 89,
123 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
126 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
128 return container_of(panel, struct s6e63m0, panel);
131 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
133 int ret = ctx->error;
135 ctx->error = 0;
136 return ret;
139 static int s6e63m0_spi_write_word(struct s6e63m0 *ctx, u16 data)
141 struct spi_device *spi = to_spi_device(ctx->dev);
142 struct spi_transfer xfer = {
143 .len = 2,
144 .tx_buf = &data,
146 struct spi_message msg;
148 spi_message_init(&msg);
149 spi_message_add_tail(&xfer, &msg);
151 return spi_sync(spi, &msg);
154 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
156 int ret = 0;
158 if (ctx->error < 0 || len == 0)
159 return;
161 DRM_DEV_DEBUG(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data);
162 ret = s6e63m0_spi_write_word(ctx, *data);
164 while (!ret && --len) {
165 ++data;
166 ret = s6e63m0_spi_write_word(ctx, *data | DATA_MASK);
169 if (ret) {
170 DRM_DEV_ERROR(ctx->dev, "error %d writing dcs seq: %*ph\n", ret,
171 (int)len, data);
172 ctx->error = ret;
175 usleep_range(300, 310);
178 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
179 ({ \
180 static const u8 d[] = { seq }; \
181 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
184 static void s6e63m0_init(struct s6e63m0 *ctx)
186 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
187 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
188 0x63, 0x86, 0x1a, 0x33, 0x0d, 0x00, 0x00);
190 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
191 0x02, 0x03, 0x1c, 0x10, 0x10);
192 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
193 0x03, 0x00, 0x00);
195 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
196 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
197 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
198 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
199 0xd6);
200 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
201 0x01);
203 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
204 0x00, 0x8c, 0x07);
205 s6e63m0_dcs_write_seq_static(ctx, 0xb3,
206 0xc);
208 s6e63m0_dcs_write_seq_static(ctx, 0xb5,
209 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
210 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
211 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
212 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
213 0x21, 0x20, 0x1e, 0x1e);
215 s6e63m0_dcs_write_seq_static(ctx, 0xb6,
216 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
217 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
218 0x66, 0x66);
220 s6e63m0_dcs_write_seq_static(ctx, 0xb7,
221 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
222 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
223 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
224 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
225 0x21, 0x20, 0x1e, 0x1e, 0x00, 0x00, 0x11,
226 0x22, 0x33, 0x44, 0x44, 0x44, 0x55, 0x55,
227 0x66, 0x66, 0x66, 0x66, 0x66, 0x66);
229 s6e63m0_dcs_write_seq_static(ctx, 0xb9,
230 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
231 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
232 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
233 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
234 0x21, 0x20, 0x1e, 0x1e);
236 s6e63m0_dcs_write_seq_static(ctx, 0xba,
237 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
238 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
239 0x66, 0x66);
241 s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
242 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
243 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
245 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
247 s6e63m0_dcs_write_seq_static(ctx, 0xb2,
248 0x10, 0x10, 0x0b, 0x05);
250 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
251 0x01);
253 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
254 0x0b);
256 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
259 static int s6e63m0_power_on(struct s6e63m0 *ctx)
261 int ret;
263 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
264 if (ret < 0)
265 return ret;
267 msleep(25);
269 gpiod_set_value(ctx->reset_gpio, 0);
270 msleep(120);
272 return 0;
275 static int s6e63m0_power_off(struct s6e63m0 *ctx)
277 int ret;
279 gpiod_set_value(ctx->reset_gpio, 1);
280 msleep(120);
282 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
283 if (ret < 0)
284 return ret;
286 return 0;
289 static int s6e63m0_disable(struct drm_panel *panel)
291 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
293 if (!ctx->enabled)
294 return 0;
296 backlight_disable(ctx->bl_dev);
298 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
299 msleep(200);
301 ctx->enabled = false;
303 return 0;
306 static int s6e63m0_unprepare(struct drm_panel *panel)
308 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
309 int ret;
311 if (!ctx->prepared)
312 return 0;
314 s6e63m0_clear_error(ctx);
316 ret = s6e63m0_power_off(ctx);
317 if (ret < 0)
318 return ret;
320 ctx->prepared = false;
322 return 0;
325 static int s6e63m0_prepare(struct drm_panel *panel)
327 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
328 int ret;
330 if (ctx->prepared)
331 return 0;
333 ret = s6e63m0_power_on(ctx);
334 if (ret < 0)
335 return ret;
337 s6e63m0_init(ctx);
339 ret = s6e63m0_clear_error(ctx);
341 if (ret < 0)
342 s6e63m0_unprepare(panel);
344 ctx->prepared = true;
346 return ret;
349 static int s6e63m0_enable(struct drm_panel *panel)
351 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
353 if (ctx->enabled)
354 return 0;
356 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
358 backlight_enable(ctx->bl_dev);
360 ctx->enabled = true;
362 return 0;
365 static int s6e63m0_get_modes(struct drm_panel *panel,
366 struct drm_connector *connector)
368 struct drm_display_mode *mode;
370 mode = drm_mode_duplicate(connector->dev, &default_mode);
371 if (!mode) {
372 DRM_ERROR("failed to add mode %ux%ux@%u\n",
373 default_mode.hdisplay, default_mode.vdisplay,
374 default_mode.vrefresh);
375 return -ENOMEM;
378 drm_mode_set_name(mode);
380 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
381 drm_mode_probed_add(connector, mode);
383 return 1;
386 static const struct drm_panel_funcs s6e63m0_drm_funcs = {
387 .disable = s6e63m0_disable,
388 .unprepare = s6e63m0_unprepare,
389 .prepare = s6e63m0_prepare,
390 .enable = s6e63m0_enable,
391 .get_modes = s6e63m0_get_modes,
394 static int s6e63m0_set_brightness(struct backlight_device *bd)
396 struct s6e63m0 *ctx = bl_get_data(bd);
398 int brightness = bd->props.brightness;
400 /* disable and set new gamma */
401 s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
402 ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
404 /* update gamma table. */
405 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x01);
407 return s6e63m0_clear_error(ctx);
410 static const struct backlight_ops s6e63m0_backlight_ops = {
411 .update_status = s6e63m0_set_brightness,
414 static int s6e63m0_backlight_register(struct s6e63m0 *ctx)
416 struct backlight_properties props = {
417 .type = BACKLIGHT_RAW,
418 .brightness = MAX_BRIGHTNESS,
419 .max_brightness = MAX_BRIGHTNESS
421 struct device *dev = ctx->dev;
422 int ret = 0;
424 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
425 &s6e63m0_backlight_ops,
426 &props);
427 if (IS_ERR(ctx->bl_dev)) {
428 ret = PTR_ERR(ctx->bl_dev);
429 DRM_DEV_ERROR(dev, "error registering backlight device (%d)\n",
430 ret);
433 return ret;
436 static int s6e63m0_probe(struct spi_device *spi)
438 struct device *dev = &spi->dev;
439 struct s6e63m0 *ctx;
440 int ret;
442 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
443 if (!ctx)
444 return -ENOMEM;
446 spi_set_drvdata(spi, ctx);
448 ctx->dev = dev;
449 ctx->enabled = false;
450 ctx->prepared = false;
452 ctx->supplies[0].supply = "vdd3";
453 ctx->supplies[1].supply = "vci";
454 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
455 ctx->supplies);
456 if (ret < 0) {
457 DRM_DEV_ERROR(dev, "failed to get regulators: %d\n", ret);
458 return ret;
461 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
462 if (IS_ERR(ctx->reset_gpio)) {
463 DRM_DEV_ERROR(dev, "cannot get reset-gpios %ld\n",
464 PTR_ERR(ctx->reset_gpio));
465 return PTR_ERR(ctx->reset_gpio);
468 spi->bits_per_word = 9;
469 spi->mode = SPI_MODE_3;
470 ret = spi_setup(spi);
471 if (ret < 0) {
472 DRM_DEV_ERROR(dev, "spi setup failed.\n");
473 return ret;
476 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
477 DRM_MODE_CONNECTOR_DPI);
479 ret = s6e63m0_backlight_register(ctx);
480 if (ret < 0)
481 return ret;
483 return drm_panel_add(&ctx->panel);
486 static int s6e63m0_remove(struct spi_device *spi)
488 struct s6e63m0 *ctx = spi_get_drvdata(spi);
490 drm_panel_remove(&ctx->panel);
492 return 0;
495 static const struct of_device_id s6e63m0_of_match[] = {
496 { .compatible = "samsung,s6e63m0" },
497 { /* sentinel */ }
499 MODULE_DEVICE_TABLE(of, s6e63m0_of_match);
501 static struct spi_driver s6e63m0_driver = {
502 .probe = s6e63m0_probe,
503 .remove = s6e63m0_remove,
504 .driver = {
505 .name = "panel-samsung-s6e63m0",
506 .of_match_table = s6e63m0_of_match,
509 module_spi_driver(s6e63m0_driver);
511 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
512 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
513 MODULE_LICENSE("GPL v2");