1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * S6E63M0 AMOLED LCD drm_panel driver.
4  *
5  * Copyright (C) 2019 Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>
6  * Derived from drivers/gpu/drm/panel-samsung-ld9040.c
7  *
8  * Andrzej Hajda <a.hajda@samsung.com>
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 <pawel.mikolaj.chmiel@gmail.com>");
511 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
512 MODULE_LICENSE("GPL v2");
513