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 	.vrefresh	= 60,
121 	.width_mm	= 53,
122 	.height_mm	= 89,
123 	.flags		= DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
124 };
125 
126 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
127 {
128 	return container_of(panel, struct s6e63m0, panel);
129 }
130 
131 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
132 {
133 	int ret = ctx->error;
134 
135 	ctx->error = 0;
136 	return ret;
137 }
138 
139 static int s6e63m0_spi_write_word(struct s6e63m0 *ctx, u16 data)
140 {
141 	struct spi_device *spi = to_spi_device(ctx->dev);
142 	struct spi_transfer xfer = {
143 		.len	= 2,
144 		.tx_buf = &data,
145 	};
146 	struct spi_message msg;
147 
148 	spi_message_init(&msg);
149 	spi_message_add_tail(&xfer, &msg);
150 
151 	return spi_sync(spi, &msg);
152 }
153 
154 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
155 {
156 	int ret = 0;
157 
158 	if (ctx->error < 0 || len == 0)
159 		return;
160 
161 	DRM_DEV_DEBUG(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data);
162 	ret = s6e63m0_spi_write_word(ctx, *data);
163 
164 	while (!ret && --len) {
165 		++data;
166 		ret = s6e63m0_spi_write_word(ctx, *data | DATA_MASK);
167 	}
168 
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;
173 	}
174 
175 	usleep_range(300, 310);
176 }
177 
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)); \
182 	})
183 
184 static void s6e63m0_init(struct s6e63m0 *ctx)
185 {
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);
189 
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);
194 
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);
202 
203 	s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
204 				     0x00, 0x8c, 0x07);
205 	s6e63m0_dcs_write_seq_static(ctx, 0xb3,
206 				     0xc);
207 
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);
214 
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);
219 
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);
228 
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);
235 
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);
240 
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);
246 
247 	s6e63m0_dcs_write_seq_static(ctx, 0xb2,
248 				     0x10, 0x10, 0x0b, 0x05);
249 
250 	s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
251 				     0x01);
252 
253 	s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
254 				     0x0b);
255 
256 	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
257 }
258 
259 static int s6e63m0_power_on(struct s6e63m0 *ctx)
260 {
261 	int ret;
262 
263 	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
264 	if (ret < 0)
265 		return ret;
266 
267 	msleep(25);
268 
269 	gpiod_set_value(ctx->reset_gpio, 0);
270 	msleep(120);
271 
272 	return 0;
273 }
274 
275 static int s6e63m0_power_off(struct s6e63m0 *ctx)
276 {
277 	int ret;
278 
279 	gpiod_set_value(ctx->reset_gpio, 1);
280 	msleep(120);
281 
282 	ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
283 	if (ret < 0)
284 		return ret;
285 
286 	return 0;
287 }
288 
289 static int s6e63m0_disable(struct drm_panel *panel)
290 {
291 	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
292 
293 	if (!ctx->enabled)
294 		return 0;
295 
296 	backlight_disable(ctx->bl_dev);
297 
298 	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
299 	msleep(200);
300 
301 	ctx->enabled = false;
302 
303 	return 0;
304 }
305 
306 static int s6e63m0_unprepare(struct drm_panel *panel)
307 {
308 	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
309 	int ret;
310 
311 	if (!ctx->prepared)
312 		return 0;
313 
314 	s6e63m0_clear_error(ctx);
315 
316 	ret = s6e63m0_power_off(ctx);
317 	if (ret < 0)
318 		return ret;
319 
320 	ctx->prepared = false;
321 
322 	return 0;
323 }
324 
325 static int s6e63m0_prepare(struct drm_panel *panel)
326 {
327 	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
328 	int ret;
329 
330 	if (ctx->prepared)
331 		return 0;
332 
333 	ret = s6e63m0_power_on(ctx);
334 	if (ret < 0)
335 		return ret;
336 
337 	s6e63m0_init(ctx);
338 
339 	ret = s6e63m0_clear_error(ctx);
340 
341 	if (ret < 0)
342 		s6e63m0_unprepare(panel);
343 
344 	ctx->prepared = true;
345 
346 	return ret;
347 }
348 
349 static int s6e63m0_enable(struct drm_panel *panel)
350 {
351 	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
352 
353 	if (ctx->enabled)
354 		return 0;
355 
356 	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
357 
358 	backlight_enable(ctx->bl_dev);
359 
360 	ctx->enabled = true;
361 
362 	return 0;
363 }
364 
365 static int s6e63m0_get_modes(struct drm_panel *panel)
366 {
367 	struct drm_connector *connector = panel->connector;
368 	struct drm_display_mode *mode;
369 
370 	mode = drm_mode_duplicate(panel->drm, &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;
376 	}
377 
378 	drm_mode_set_name(mode);
379 
380 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
381 	drm_mode_probed_add(connector, mode);
382 
383 	return 1;
384 }
385 
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,
392 };
393 
394 static int s6e63m0_set_brightness(struct backlight_device *bd)
395 {
396 	struct s6e63m0 *ctx = bl_get_data(bd);
397 
398 	int brightness = bd->props.brightness;
399 
400 	/* disable and set new gamma */
401 	s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
402 			  ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
403 
404 	/* update gamma table. */
405 	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x01);
406 
407 	return s6e63m0_clear_error(ctx);
408 }
409 
410 static const struct backlight_ops s6e63m0_backlight_ops = {
411 	.update_status	= s6e63m0_set_brightness,
412 };
413 
414 static int s6e63m0_backlight_register(struct s6e63m0 *ctx)
415 {
416 	struct backlight_properties props = {
417 		.type		= BACKLIGHT_RAW,
418 		.brightness	= MAX_BRIGHTNESS,
419 		.max_brightness = MAX_BRIGHTNESS
420 	};
421 	struct device *dev = ctx->dev;
422 	int ret = 0;
423 
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);
431 	}
432 
433 	return ret;
434 }
435 
436 static int s6e63m0_probe(struct spi_device *spi)
437 {
438 	struct device *dev = &spi->dev;
439 	struct s6e63m0 *ctx;
440 	int ret;
441 
442 	ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
443 	if (!ctx)
444 		return -ENOMEM;
445 
446 	spi_set_drvdata(spi, ctx);
447 
448 	ctx->dev = dev;
449 	ctx->enabled = false;
450 	ctx->prepared = false;
451 
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;
459 	}
460 
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);
466 	}
467 
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;
474 	}
475 
476 	drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
477 		       DRM_MODE_CONNECTOR_DPI);
478 
479 	ret = s6e63m0_backlight_register(ctx);
480 	if (ret < 0)
481 		return ret;
482 
483 	return drm_panel_add(&ctx->panel);
484 }
485 
486 static int s6e63m0_remove(struct spi_device *spi)
487 {
488 	struct s6e63m0 *ctx = spi_get_drvdata(spi);
489 
490 	drm_panel_remove(&ctx->panel);
491 
492 	return 0;
493 }
494 
495 static const struct of_device_id s6e63m0_of_match[] = {
496 	{ .compatible = "samsung,s6e63m0" },
497 	{ /* sentinel */ }
498 };
499 MODULE_DEVICE_TABLE(of, s6e63m0_of_match);
500 
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,
507 	},
508 };
509 module_spi_driver(s6e63m0_driver);
510 
511 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
512 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
513 MODULE_LICENSE("GPL v2");
514