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