Linux Audio

Check our new training course

Loading...
v5.4
  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	.vrefresh = 30,
 56	.flags = 0,
 57};
 58
 59static 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
116static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
117{
118	return container_of(panel, struct s6e63j0x03, panel);
119}
120
121static 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
135static 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
140static 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
149static 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
167static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
168{
169	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
170}
171
172static 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
184static 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
208static 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
216static const struct backlight_ops s6e63j0x03_bl_ops = {
217	.update_status = s6e63j0x03_set_brightness,
218};
219
220static 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
241static 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
255static 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
324static 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
341err:
342	s6e63j0x03_power_off(ctx);
343	return ret;
344}
345
346static 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
403static int s6e63j0x03_get_modes(struct drm_panel *panel)
 
404{
405	struct drm_connector *connector = panel->connector;
406	struct drm_display_mode *mode;
407
408	mode = drm_mode_duplicate(panel->drm, &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
427static 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
435static 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);
470	ctx->panel.dev = dev;
471	ctx->panel.funcs = &s6e63j0x03_funcs;
472
473	ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,
474						&s6e63j0x03_bl_ops, NULL);
475	if (IS_ERR(ctx->bl_dev)) {
476		dev_err(dev, "failed to register backlight device\n");
477		return PTR_ERR(ctx->bl_dev);
478	}
479
480	ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS;
481	ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS;
482	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
483
484	ret = drm_panel_add(&ctx->panel);
485	if (ret < 0)
486		goto unregister_backlight;
487
488	ret = mipi_dsi_attach(dsi);
489	if (ret < 0)
490		goto remove_panel;
491
492	return ret;
493
494remove_panel:
495	drm_panel_remove(&ctx->panel);
496
497unregister_backlight:
498	backlight_device_unregister(ctx->bl_dev);
499
500	return ret;
501}
502
503static int s6e63j0x03_remove(struct mipi_dsi_device *dsi)
504{
505	struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);
506
507	mipi_dsi_detach(dsi);
508	drm_panel_remove(&ctx->panel);
509
510	backlight_device_unregister(ctx->bl_dev);
511
512	return 0;
513}
514
515static const struct of_device_id s6e63j0x03_of_match[] = {
516	{ .compatible = "samsung,s6e63j0x03" },
517	{ }
518};
519MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
520
521static struct mipi_dsi_driver s6e63j0x03_driver = {
522	.probe = s6e63j0x03_probe,
523	.remove = s6e63j0x03_remove,
524	.driver = {
525		.name = "panel_samsung_s6e63j0x03",
526		.of_match_table = s6e63j0x03_of_match,
527	},
528};
529module_mipi_dsi_driver(s6e63j0x03_driver);
530
531MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
532MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
533MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
534MODULE_LICENSE("GPL v2");
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");