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