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
14#include <linux/backlight.h>
15#include <linux/delay.h>
16#include <linux/gpio/consumer.h>
17#include <linux/module.h>
18#include <linux/regulator/consumer.h>
19
20#include <video/mipi_display.h>
21
22#include "panel-samsung-s6e63m0.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_ERROR_CHECK		0xd5
29#define MCS_READ_ID1		0xda
30#define MCS_READ_ID2		0xdb
31#define MCS_READ_ID3		0xdc
32#define MCS_LEVEL_2_KEY		0xf0
33#define MCS_MTP_KEY		0xf1
34#define MCS_DISCTL   0xf2
35#define MCS_SRCCTL           0xf6
36#define MCS_IFCTL                       0xf7
37#define MCS_PANELCTL         0xF8
38#define MCS_PGAMMACTL                   0xfa
39
40#define S6E63M0_LCD_ID_VALUE_M2		0xA4
41#define S6E63M0_LCD_ID_VALUE_SM2	0xB4
42#define S6E63M0_LCD_ID_VALUE_SM2_1	0xB6
43
44#define NUM_GAMMA_LEVELS             11
45#define GAMMA_TABLE_COUNT           23
46
47#define MAX_BRIGHTNESS              (NUM_GAMMA_LEVELS - 1)
48
49/* array of gamma tables for gamma value 2.2 */
50static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
51	{ MCS_PGAMMACTL, 0x00,
52	  0x18, 0x08, 0x24, 0x78, 0xEC, 0x3D, 0xC8,
53	  0xC2, 0xB6, 0xC4, 0xC7, 0xB6, 0xD5, 0xD7,
54	  0xCC, 0x00, 0x39, 0x00, 0x36, 0x00, 0x51 },
55	{ MCS_PGAMMACTL, 0x00,
56	  0x18, 0x08, 0x24, 0x73, 0x4A, 0x3D, 0xC0,
57	  0xC2, 0xB1, 0xBB, 0xBE, 0xAC, 0xCE, 0xCF,
58	  0xC5, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x82 },
59	{ MCS_PGAMMACTL, 0x00,
60	  0x18, 0x08, 0x24, 0x70, 0x51, 0x3E, 0xBF,
61	  0xC1, 0xAF, 0xB9, 0xBC, 0xAB, 0xCC, 0xCC,
62	  0xC2, 0x00, 0x65, 0x00, 0x67, 0x00, 0x8D },
63	{ MCS_PGAMMACTL, 0x00,
64	  0x18, 0x08, 0x24, 0x6C, 0x54, 0x3A, 0xBC,
65	  0xBF, 0xAC, 0xB7, 0xBB, 0xA9, 0xC9, 0xC9,
66	  0xBE, 0x00, 0x71, 0x00, 0x73, 0x00, 0x9E },
67	{ MCS_PGAMMACTL, 0x00,
68	  0x18, 0x08, 0x24, 0x69, 0x54, 0x37, 0xBB,
69	  0xBE, 0xAC, 0xB4, 0xB7, 0xA6, 0xC7, 0xC8,
70	  0xBC, 0x00, 0x7B, 0x00, 0x7E, 0x00, 0xAB },
71	{ MCS_PGAMMACTL, 0x00,
72	  0x18, 0x08, 0x24, 0x66, 0x55, 0x34, 0xBA,
73	  0xBD, 0xAB, 0xB1, 0xB5, 0xA3, 0xC5, 0xC6,
74	  0xB9, 0x00, 0x85, 0x00, 0x88, 0x00, 0xBA },
75	{ MCS_PGAMMACTL, 0x00,
76	  0x18, 0x08, 0x24, 0x63, 0x53, 0x31, 0xB8,
77	  0xBC, 0xA9, 0xB0, 0xB5, 0xA2, 0xC4, 0xC4,
78	  0xB8, 0x00, 0x8B, 0x00, 0x8E, 0x00, 0xC2 },
79	{ MCS_PGAMMACTL, 0x00,
80	  0x18, 0x08, 0x24, 0x62, 0x54, 0x30, 0xB9,
81	  0xBB, 0xA9, 0xB0, 0xB3, 0xA1, 0xC1, 0xC3,
82	  0xB7, 0x00, 0x91, 0x00, 0x95, 0x00, 0xDA },
83	{ MCS_PGAMMACTL, 0x00,
84	  0x18, 0x08, 0x24, 0x66, 0x58, 0x34, 0xB6,
85	  0xBA, 0xA7, 0xAF, 0xB3, 0xA0, 0xC1, 0xC2,
86	  0xB7, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xD1 },
87	{ MCS_PGAMMACTL, 0x00,
88	  0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 0xB6,
89	  0xBA, 0xA8, 0xAC, 0xB1, 0x9D, 0xC1, 0xC1,
90	  0xB7, 0x00, 0x9C, 0x00, 0x9F, 0x00, 0xD6 },
91	{ MCS_PGAMMACTL, 0x00,
92	  0x18, 0x08, 0x24, 0x5f, 0x50, 0x2d, 0xB6,
93	  0xB9, 0xA7, 0xAd, 0xB1, 0x9f, 0xbe, 0xC0,
94	  0xB5, 0x00, 0xa0, 0x00, 0xa4, 0x00, 0xdb },
95};
96
97struct s6e63m0 {
98	struct device *dev;
99	int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val);
100	int (*dcs_write)(struct device *dev, const u8 *data, size_t len);
101	struct drm_panel panel;
102	struct backlight_device *bl_dev;
103	u8 lcd_type;
104
105	struct regulator_bulk_data supplies[2];
106	struct gpio_desc *reset_gpio;
107
108	bool prepared;
109	bool enabled;
110
111	/*
112	 * This field is tested by functions directly accessing bus before
113	 * transfer, transfer is skipped if it is set. In case of transfer
114	 * failure or unexpected response the field is set to error value.
115	 * Such construct allows to eliminate many checks in higher level
116	 * functions.
117	 */
118	int error;
119};
120
121static const struct drm_display_mode default_mode = {
122	.clock		= 25628,
123	.hdisplay	= 480,
124	.hsync_start	= 480 + 16,
125	.hsync_end	= 480 + 16 + 2,
126	.htotal		= 480 + 16 + 2 + 16,
127	.vdisplay	= 800,
128	.vsync_start	= 800 + 28,
129	.vsync_end	= 800 + 28 + 2,
130	.vtotal		= 800 + 28 + 2 + 1,
131	.width_mm	= 53,
132	.height_mm	= 89,
133	.flags		= DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
134};
135
136static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
137{
138	return container_of(panel, struct s6e63m0, panel);
139}
140
141static int s6e63m0_clear_error(struct s6e63m0 *ctx)
142{
143	int ret = ctx->error;
144
145	ctx->error = 0;
146	return ret;
147}
148
149static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
150{
151	if (ctx->error < 0)
152		return;
153
154	ctx->error = ctx->dcs_read(ctx->dev, cmd, data);
155}
156
157static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
158{
159	if (ctx->error < 0 || len == 0)
160		return;
161
162	ctx->error = ctx->dcs_write(ctx->dev, data, len);
163}
164
165#define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
166	({ \
167		static const u8 d[] = { seq }; \
168		s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
169	})
170
171static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
172{
173	u8 id1, id2, id3;
174	int ret;
175
176	s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);
177	s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);
178	s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);
179
180	ret = s6e63m0_clear_error(ctx);
181	if (ret) {
182		dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
183		ctx->lcd_type = 0x00;
184		return ret;
185	}
186
187	dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
188
189	/* We attempt to detect what panel is mounted on the controller */
190	switch (id2) {
191	case S6E63M0_LCD_ID_VALUE_M2:
192		dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
193		break;
194	case S6E63M0_LCD_ID_VALUE_SM2:
195	case S6E63M0_LCD_ID_VALUE_SM2_1:
196		dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
197		break;
198	default:
199		dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
200		break;
201	}
202
203	ctx->lcd_type = id2;
204
205	return 0;
206}
207
208static void s6e63m0_init(struct s6e63m0 *ctx)
209{
210	s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
211				     0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
212				     0x63, 0x86, 0x1a, 0x33, 0x0d, 0x00, 0x00);
213
214	s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
215				     0x02, 0x03, 0x1c, 0x10, 0x10);
216	s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
217				     0x03, 0x00, 0x00);
218
219	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
220				     0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
221				     0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
222				     0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
223				     0xd6);
224	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
225				     0x01);
226
227	s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
228				     0x00, 0x8c, 0x07);
229	s6e63m0_dcs_write_seq_static(ctx, 0xb3,
230				     0xc);
231
232	s6e63m0_dcs_write_seq_static(ctx, 0xb5,
233				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
234				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
235				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
236				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
237				     0x21, 0x20, 0x1e, 0x1e);
238
239	s6e63m0_dcs_write_seq_static(ctx, 0xb6,
240				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
241				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
242				     0x66, 0x66);
243
244	s6e63m0_dcs_write_seq_static(ctx, 0xb7,
245				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
246				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
247				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
248				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
249				     0x21, 0x20, 0x1e, 0x1e, 0x00, 0x00, 0x11,
250				     0x22, 0x33, 0x44, 0x44, 0x44, 0x55, 0x55,
251				     0x66, 0x66, 0x66, 0x66, 0x66, 0x66);
252
253	s6e63m0_dcs_write_seq_static(ctx, 0xb9,
254				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
255				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
256				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
257				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
258				     0x21, 0x20, 0x1e, 0x1e);
259
260	s6e63m0_dcs_write_seq_static(ctx, 0xba,
261				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
262				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
263				     0x66, 0x66);
264
265	s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
266				     0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
267				     0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
268				     0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
269				     0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
270
271	s6e63m0_dcs_write_seq_static(ctx, 0xb2,
272				     0x10, 0x10, 0x0b, 0x05);
273
274	s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
275				     0x01);
276
277	s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
278				     0x0b);
279}
280
281static int s6e63m0_power_on(struct s6e63m0 *ctx)
282{
283	int ret;
284
285	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
286	if (ret < 0)
287		return ret;
288
289	msleep(25);
290
291	/* Be sure to send a reset pulse */
292	gpiod_set_value(ctx->reset_gpio, 1);
293	msleep(5);
294	gpiod_set_value(ctx->reset_gpio, 0);
295	msleep(120);
296
297	return 0;
298}
299
300static int s6e63m0_power_off(struct s6e63m0 *ctx)
301{
302	int ret;
303
304	gpiod_set_value(ctx->reset_gpio, 1);
305	msleep(120);
306
307	ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
308	if (ret < 0)
309		return ret;
310
311	return 0;
312}
313
314static int s6e63m0_disable(struct drm_panel *panel)
315{
316	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
317
318	if (!ctx->enabled)
319		return 0;
320
321	backlight_disable(ctx->bl_dev);
322
323	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
324	msleep(10);
325	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
326	msleep(120);
327
328	ctx->enabled = false;
329
330	return 0;
331}
332
333static int s6e63m0_unprepare(struct drm_panel *panel)
334{
335	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
336	int ret;
337
338	if (!ctx->prepared)
339		return 0;
340
341	s6e63m0_clear_error(ctx);
342
343	ret = s6e63m0_power_off(ctx);
344	if (ret < 0)
345		return ret;
346
347	ctx->prepared = false;
348
349	return 0;
350}
351
352static int s6e63m0_prepare(struct drm_panel *panel)
353{
354	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
355	int ret;
356
357	if (ctx->prepared)
358		return 0;
359
360	ret = s6e63m0_power_on(ctx);
361	if (ret < 0)
362		return ret;
363
364	/* Magic to unlock level 2 control of the display */
365	s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);
366	/* Magic to unlock MTP reading */
367	s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);
368
369	ret = s6e63m0_check_lcd_type(ctx);
370	if (ret < 0)
371		return ret;
372
373	s6e63m0_init(ctx);
374
375	ret = s6e63m0_clear_error(ctx);
376
377	if (ret < 0)
378		s6e63m0_unprepare(panel);
379
380	ctx->prepared = true;
381
382	return ret;
383}
384
385static int s6e63m0_enable(struct drm_panel *panel)
386{
387	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
388
389	if (ctx->enabled)
390		return 0;
391
392	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
393	msleep(120);
394	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
395	msleep(10);
396
397	s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,
398				     0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
399				     0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
400				     0x0F, 0x00);
401
402	backlight_enable(ctx->bl_dev);
403
404	ctx->enabled = true;
405
406	return 0;
407}
408
409static int s6e63m0_get_modes(struct drm_panel *panel,
410			     struct drm_connector *connector)
411{
412	struct drm_display_mode *mode;
413
414	mode = drm_mode_duplicate(connector->dev, &default_mode);
415	if (!mode) {
416		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
417			default_mode.hdisplay, default_mode.vdisplay,
418			drm_mode_vrefresh(&default_mode));
419		return -ENOMEM;
420	}
421
422	drm_mode_set_name(mode);
423
424	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
425	drm_mode_probed_add(connector, mode);
426
427	return 1;
428}
429
430static const struct drm_panel_funcs s6e63m0_drm_funcs = {
431	.disable	= s6e63m0_disable,
432	.unprepare	= s6e63m0_unprepare,
433	.prepare	= s6e63m0_prepare,
434	.enable		= s6e63m0_enable,
435	.get_modes	= s6e63m0_get_modes,
436};
437
438static int s6e63m0_set_brightness(struct backlight_device *bd)
439{
440	struct s6e63m0 *ctx = bl_get_data(bd);
441
442	int brightness = bd->props.brightness;
443
444	/* disable and set new gamma */
445	s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
446			  ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
447
448	/* update gamma table. */
449	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x01);
450
451	return s6e63m0_clear_error(ctx);
452}
453
454static const struct backlight_ops s6e63m0_backlight_ops = {
455	.update_status	= s6e63m0_set_brightness,
456};
457
458static int s6e63m0_backlight_register(struct s6e63m0 *ctx)
459{
460	struct backlight_properties props = {
461		.type		= BACKLIGHT_RAW,
462		.brightness	= MAX_BRIGHTNESS,
463		.max_brightness = MAX_BRIGHTNESS
464	};
465	struct device *dev = ctx->dev;
466	int ret = 0;
467
468	ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
469						     &s6e63m0_backlight_ops,
470						     &props);
471	if (IS_ERR(ctx->bl_dev)) {
472		ret = PTR_ERR(ctx->bl_dev);
473		dev_err(dev, "error registering backlight device (%d)\n", ret);
474	}
475
476	return ret;
477}
478
479int s6e63m0_probe(struct device *dev,
480		  int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val),
481		  int (*dcs_write)(struct device *dev, const u8 *data, size_t len),
482		  bool dsi_mode)
483{
484	struct s6e63m0 *ctx;
485	int ret;
486
487	ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
488	if (!ctx)
489		return -ENOMEM;
490
491	ctx->dcs_read = dcs_read;
492	ctx->dcs_write = dcs_write;
493	dev_set_drvdata(dev, ctx);
494
495	ctx->dev = dev;
496	ctx->enabled = false;
497	ctx->prepared = false;
498
499	ctx->supplies[0].supply = "vdd3";
500	ctx->supplies[1].supply = "vci";
501	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
502				      ctx->supplies);
503	if (ret < 0) {
504		dev_err(dev, "failed to get regulators: %d\n", ret);
505		return ret;
506	}
507
508	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
509	if (IS_ERR(ctx->reset_gpio)) {
510		dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio));
511		return PTR_ERR(ctx->reset_gpio);
512	}
513
514	drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
515		       dsi_mode ? DRM_MODE_CONNECTOR_DSI :
516		       DRM_MODE_CONNECTOR_DPI);
517
518	ret = s6e63m0_backlight_register(ctx);
519	if (ret < 0)
520		return ret;
521
522	drm_panel_add(&ctx->panel);
523
524	return 0;
525}
526EXPORT_SYMBOL_GPL(s6e63m0_probe);
527
528int s6e63m0_remove(struct device *dev)
529{
530	struct s6e63m0 *ctx = dev_get_drvdata(dev);
531
532	drm_panel_remove(&ctx->panel);
533
534	return 0;
535}
536EXPORT_SYMBOL_GPL(s6e63m0_remove);
537
538MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
539MODULE_DESCRIPTION("s6e63m0 LCD Driver");
540MODULE_LICENSE("GPL v2");
541