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