Linux Audio

Check our new training course

Loading...
v6.13.7
  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 = BACKLIGHT_POWER_REDUCED;
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 = BACKLIGHT_POWER_OFF;
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 = BACKLIGHT_POWER_REDUCED;
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 = BACKLIGHT_POWER_ON;
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 = BACKLIGHT_POWER_OFF;
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");
v5.9
  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
 36struct 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
 45static 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
 58static 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
115static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
116{
117	return container_of(panel, struct s6e63j0x03, panel);
118}
119
120static 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
134static 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
139static 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
148static 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
166static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
167{
168	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
169}
170
171static 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
183static 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
207static 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
215static const struct backlight_ops s6e63j0x03_bl_ops = {
216	.update_status = s6e63j0x03_set_brightness,
217};
218
219static 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
240static 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
254static 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
323static 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
340err:
341	s6e63j0x03_power_off(ctx);
342	return ret;
343}
344
345static 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
402static 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
426static 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
434static 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
492remove_panel:
493	drm_panel_remove(&ctx->panel);
494
495unregister_backlight:
496	backlight_device_unregister(ctx->bl_dev);
497
498	return ret;
499}
500
501static 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
513static const struct of_device_id s6e63j0x03_of_match[] = {
514	{ .compatible = "samsung,s6e63j0x03" },
515	{ }
516};
517MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
518
519static 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};
527module_mipi_dsi_driver(s6e63j0x03_driver);
528
529MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
530MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
531MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
532MODULE_LICENSE("GPL v2");