1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver.
4  *
5  * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd
6  *
7  * Inki Dae <inki.dae@samsung.com>
8  * Hoegeun Kwon <hoegeun.kwon@samsung.com>
9  */
10 
11 #include <drm/drmP.h>
12 #include <drm/drm_mipi_dsi.h>
13 #include <drm/drm_panel.h>
14 #include <linux/backlight.h>
15 #include <linux/gpio/consumer.h>
16 #include <linux/regulator/consumer.h>
17 #include <video/mipi_display.h>
18 
19 #define MCS_LEVEL2_KEY		0xf0
20 #define MCS_MTP_KEY		0xf1
21 #define MCS_MTP_SET3		0xd4
22 
23 #define MAX_BRIGHTNESS		100
24 #define DEFAULT_BRIGHTNESS	80
25 
26 #define NUM_GAMMA_STEPS		9
27 #define GAMMA_CMD_CNT		28
28 
29 #define FIRST_COLUMN 20
30 
31 struct s6e63j0x03 {
32 	struct device *dev;
33 	struct drm_panel panel;
34 	struct backlight_device *bl_dev;
35 
36 	struct regulator_bulk_data supplies[2];
37 	struct gpio_desc *reset_gpio;
38 };
39 
40 static const struct drm_display_mode default_mode = {
41 	.clock = 4649,
42 	.hdisplay = 320,
43 	.hsync_start = 320 + 1,
44 	.hsync_end = 320 + 1 + 1,
45 	.htotal = 320 + 1 + 1 + 1,
46 	.vdisplay = 320,
47 	.vsync_start = 320 + 150,
48 	.vsync_end = 320 + 150 + 1,
49 	.vtotal = 320 + 150 + 1 + 2,
50 	.vrefresh = 30,
51 	.flags = 0,
52 };
53 
54 static const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = {
55 	{	/* Gamma 10 */
56 		MCS_MTP_SET3,
57 		0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26,
58 		0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36,
59 		0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf
60 	},
61 	{	/* gamma 30 */
62 		MCS_MTP_SET3,
63 		0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26,
64 		0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34,
65 		0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc
66 	},
67 	{	/* gamma 60 */
68 		MCS_MTP_SET3,
69 		0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a,
70 		0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33,
71 		0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5
72 	},
73 	{	/* gamma 90 */
74 		MCS_MTP_SET3,
75 		0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29,
76 		0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31,
77 		0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09
78 	},
79 	{	/* gamma 120 */
80 		MCS_MTP_SET3,
81 		0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28,
82 		0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31,
83 		0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b
84 	},
85 	{	/* gamma 150 */
86 		MCS_MTP_SET3,
87 		0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28,
88 		0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31,
89 		0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29
90 	},
91 	{	/* gamma 200 */
92 		MCS_MTP_SET3,
93 		0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29,
94 		0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30,
95 		0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b
96 	},
97 	{	/* gamma 240 */
98 		MCS_MTP_SET3,
99 		0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a,
100 		0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30,
101 		0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47
102 	},
103 	{	/* gamma 300 */
104 		MCS_MTP_SET3,
105 		0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28,
106 		0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f,
107 		0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57
108 	}
109 };
110 
111 static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
112 {
113 	return container_of(panel, struct s6e63j0x03, panel);
114 }
115 
116 static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx,
117 					const void *seq, size_t len)
118 {
119 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
120 
121 	return mipi_dsi_dcs_write_buffer(dsi, seq, len);
122 }
123 
124 #define s6e63j0x03_dcs_write_seq_static(ctx, seq...)			\
125 	({								\
126 		static const u8 d[] = { seq };				\
127 		s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d));	\
128 	})
129 
130 static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx)
131 {
132 	return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a);
133 }
134 
135 static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on)
136 {
137 	if (on)
138 		return s6e63j0x03_dcs_write_seq_static(ctx,
139 				MCS_MTP_KEY, 0x5a, 0x5a);
140 
141 	return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5);
142 }
143 
144 static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx)
145 {
146 	int ret;
147 
148 	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
149 	if (ret < 0)
150 		return ret;
151 
152 	msleep(30);
153 
154 	gpiod_set_value(ctx->reset_gpio, 1);
155 	usleep_range(1000, 2000);
156 	gpiod_set_value(ctx->reset_gpio, 0);
157 	usleep_range(5000, 6000);
158 
159 	return 0;
160 }
161 
162 static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
163 {
164 	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
165 }
166 
167 static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness)
168 {
169 	unsigned int index;
170 
171 	index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS);
172 
173 	if (index >= NUM_GAMMA_STEPS)
174 		index = NUM_GAMMA_STEPS - 1;
175 
176 	return index;
177 }
178 
179 static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx,
180 					unsigned int brightness)
181 {
182 	struct backlight_device *bl_dev = ctx->bl_dev;
183 	unsigned int index = s6e63j0x03_get_brightness_index(brightness);
184 	int ret;
185 
186 	ret = s6e63j0x03_apply_mtp_key(ctx, true);
187 	if (ret < 0)
188 		return ret;
189 
190 	ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT);
191 	if (ret < 0)
192 		return ret;
193 
194 	ret = s6e63j0x03_apply_mtp_key(ctx, false);
195 	if (ret < 0)
196 		return ret;
197 
198 	bl_dev->props.brightness = brightness;
199 
200 	return 0;
201 }
202 
203 static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev)
204 {
205 	struct s6e63j0x03 *ctx = bl_get_data(bl_dev);
206 	unsigned int brightness = bl_dev->props.brightness;
207 
208 	return s6e63j0x03_update_gamma(ctx, brightness);
209 }
210 
211 static const struct backlight_ops s6e63j0x03_bl_ops = {
212 	.update_status = s6e63j0x03_set_brightness,
213 };
214 
215 static int s6e63j0x03_disable(struct drm_panel *panel)
216 {
217 	struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
218 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
219 	int ret;
220 
221 	ret = mipi_dsi_dcs_set_display_off(dsi);
222 	if (ret < 0)
223 		return ret;
224 
225 	ctx->bl_dev->props.power = FB_BLANK_NORMAL;
226 
227 	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
228 	if (ret < 0)
229 		return ret;
230 
231 	msleep(120);
232 
233 	return 0;
234 }
235 
236 static int s6e63j0x03_unprepare(struct drm_panel *panel)
237 {
238 	struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
239 	int ret;
240 
241 	ret = s6e63j0x03_power_off(ctx);
242 	if (ret < 0)
243 		return ret;
244 
245 	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
246 
247 	return 0;
248 }
249 
250 static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx)
251 {
252 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
253 	int ret;
254 
255 	ret = s6e63j0x03_enable_lv2_command(ctx);
256 	if (ret < 0)
257 		return ret;
258 
259 	ret = s6e63j0x03_apply_mtp_key(ctx, true);
260 	if (ret < 0)
261 		return ret;
262 
263 	/* set porch adjustment */
264 	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28);
265 	if (ret < 0)
266 		return ret;
267 
268 	/* set frame freq */
269 	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00);
270 	if (ret < 0)
271 		return ret;
272 
273 	/* set caset, paset */
274 	ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN,
275 		default_mode.hdisplay - 1 + FIRST_COLUMN);
276 	if (ret < 0)
277 		return ret;
278 
279 	ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
280 	if (ret < 0)
281 		return ret;
282 
283 	/* set ltps timming 0, 1 */
284 	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17,
285 		0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00);
286 	if (ret < 0)
287 		return ret;
288 
289 	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02);
290 	if (ret < 0)
291 		return ret;
292 
293 	/* set param pos te_edge */
294 	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01);
295 	if (ret < 0)
296 		return ret;
297 
298 	/* set te rising edge */
299 	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f);
300 	if (ret < 0)
301 		return ret;
302 
303 	/* set param pos default */
304 	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00);
305 	if (ret < 0)
306 		return ret;
307 
308 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
309 	if (ret < 0)
310 		return ret;
311 
312 	ret = s6e63j0x03_apply_mtp_key(ctx, false);
313 	if (ret < 0)
314 		return ret;
315 
316 	return 0;
317 }
318 
319 static int s6e63j0x03_prepare(struct drm_panel *panel)
320 {
321 	struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
322 	int ret;
323 
324 	ret = s6e63j0x03_power_on(ctx);
325 	if (ret < 0)
326 		return ret;
327 
328 	ret = s6e63j0x03_panel_init(ctx);
329 	if (ret < 0)
330 		goto err;
331 
332 	ctx->bl_dev->props.power = FB_BLANK_NORMAL;
333 
334 	return 0;
335 
336 err:
337 	s6e63j0x03_power_off(ctx);
338 	return ret;
339 }
340 
341 static int s6e63j0x03_enable(struct drm_panel *panel)
342 {
343 	struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
344 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
345 	int ret;
346 
347 	msleep(120);
348 
349 	ret = s6e63j0x03_apply_mtp_key(ctx, true);
350 	if (ret < 0)
351 		return ret;
352 
353 	/* set elvss_cond */
354 	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09);
355 	if (ret < 0)
356 		return ret;
357 
358 	/* set pos */
359 	ret = s6e63j0x03_dcs_write_seq_static(ctx,
360 		MIPI_DCS_SET_ADDRESS_MODE, 0x40);
361 	if (ret < 0)
362 		return ret;
363 
364 	/* set default white brightness */
365 	ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
366 	if (ret < 0)
367 		return ret;
368 
369 	/* set white ctrl */
370 	ret = s6e63j0x03_dcs_write_seq_static(ctx,
371 		MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
372 	if (ret < 0)
373 		return ret;
374 
375 	/* set acl off */
376 	ret = s6e63j0x03_dcs_write_seq_static(ctx,
377 		MIPI_DCS_WRITE_POWER_SAVE, 0x00);
378 	if (ret < 0)
379 		return ret;
380 
381 	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
382 	if (ret < 0)
383 		return ret;
384 
385 	ret = s6e63j0x03_apply_mtp_key(ctx, false);
386 	if (ret < 0)
387 		return ret;
388 
389 	ret = mipi_dsi_dcs_set_display_on(dsi);
390 	if (ret < 0)
391 		return ret;
392 
393 	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
394 
395 	return 0;
396 }
397 
398 static int s6e63j0x03_get_modes(struct drm_panel *panel)
399 {
400 	struct drm_connector *connector = panel->connector;
401 	struct drm_display_mode *mode;
402 
403 	mode = drm_mode_duplicate(panel->drm, &default_mode);
404 	if (!mode) {
405 		DRM_ERROR("failed to add mode %ux%ux@%u\n",
406 			default_mode.hdisplay, default_mode.vdisplay,
407 			default_mode.vrefresh);
408 		return -ENOMEM;
409 	}
410 
411 	drm_mode_set_name(mode);
412 
413 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
414 	drm_mode_probed_add(connector, mode);
415 
416 	connector->display_info.width_mm = 29;
417 	connector->display_info.height_mm = 29;
418 
419 	return 1;
420 }
421 
422 static const struct drm_panel_funcs s6e63j0x03_funcs = {
423 	.disable = s6e63j0x03_disable,
424 	.unprepare = s6e63j0x03_unprepare,
425 	.prepare = s6e63j0x03_prepare,
426 	.enable = s6e63j0x03_enable,
427 	.get_modes = s6e63j0x03_get_modes,
428 };
429 
430 static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
431 {
432 	struct device *dev = &dsi->dev;
433 	struct s6e63j0x03 *ctx;
434 	int ret;
435 
436 	ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL);
437 	if (!ctx)
438 		return -ENOMEM;
439 
440 	mipi_dsi_set_drvdata(dsi, ctx);
441 
442 	ctx->dev = dev;
443 
444 	dsi->lanes = 1;
445 	dsi->format = MIPI_DSI_FMT_RGB888;
446 	dsi->mode_flags = MIPI_DSI_MODE_EOT_PACKET;
447 
448 	ctx->supplies[0].supply = "vdd3";
449 	ctx->supplies[1].supply = "vci";
450 	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
451 				      ctx->supplies);
452 	if (ret < 0) {
453 		dev_err(dev, "failed to get regulators: %d\n", ret);
454 		return ret;
455 	}
456 
457 	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
458 	if (IS_ERR(ctx->reset_gpio)) {
459 		dev_err(dev, "cannot get reset-gpio: %ld\n",
460 				PTR_ERR(ctx->reset_gpio));
461 		return PTR_ERR(ctx->reset_gpio);
462 	}
463 
464 	drm_panel_init(&ctx->panel);
465 	ctx->panel.dev = dev;
466 	ctx->panel.funcs = &s6e63j0x03_funcs;
467 
468 	ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,
469 						&s6e63j0x03_bl_ops, NULL);
470 	if (IS_ERR(ctx->bl_dev)) {
471 		dev_err(dev, "failed to register backlight device\n");
472 		return PTR_ERR(ctx->bl_dev);
473 	}
474 
475 	ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS;
476 	ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS;
477 	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
478 
479 	ret = drm_panel_add(&ctx->panel);
480 	if (ret < 0)
481 		goto unregister_backlight;
482 
483 	ret = mipi_dsi_attach(dsi);
484 	if (ret < 0)
485 		goto remove_panel;
486 
487 	return ret;
488 
489 remove_panel:
490 	drm_panel_remove(&ctx->panel);
491 
492 unregister_backlight:
493 	backlight_device_unregister(ctx->bl_dev);
494 
495 	return ret;
496 }
497 
498 static int s6e63j0x03_remove(struct mipi_dsi_device *dsi)
499 {
500 	struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);
501 
502 	mipi_dsi_detach(dsi);
503 	drm_panel_remove(&ctx->panel);
504 
505 	backlight_device_unregister(ctx->bl_dev);
506 
507 	return 0;
508 }
509 
510 static const struct of_device_id s6e63j0x03_of_match[] = {
511 	{ .compatible = "samsung,s6e63j0x03" },
512 	{ }
513 };
514 MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
515 
516 static struct mipi_dsi_driver s6e63j0x03_driver = {
517 	.probe = s6e63j0x03_probe,
518 	.remove = s6e63j0x03_remove,
519 	.driver = {
520 		.name = "panel_samsung_s6e63j0x03",
521 		.of_match_table = s6e63j0x03_of_match,
522 	},
523 };
524 module_mipi_dsi_driver(s6e63j0x03_driver);
525 
526 MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
527 MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
528 MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
529 MODULE_LICENSE("GPL v2");
530