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