drm/msm/hdmi: Enable HPD after HDMI IRQ is set up
[linux/fpc-iii.git] / drivers / gpu / drm / panel / panel-samsung-s6e63j0x03.c
blobaeb32aa588991cfc08d1a1cf6d4578f908135932
1 /*
2 * MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver.
4 * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd
6 * Inki Dae <inki.dae@samsung.com>
7 * Hoegeun Kwon <hoegeun.kwon@samsung.com>
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.
14 #include <drm/drmP.h>
15 #include <drm/drm_mipi_dsi.h>
16 #include <drm/drm_panel.h>
17 #include <linux/backlight.h>
18 #include <linux/gpio/consumer.h>
19 #include <linux/regulator/consumer.h>
20 #include <video/mipi_display.h>
22 #define MCS_LEVEL2_KEY 0xf0
23 #define MCS_MTP_KEY 0xf1
24 #define MCS_MTP_SET3 0xd4
26 #define MAX_BRIGHTNESS 100
27 #define DEFAULT_BRIGHTNESS 80
29 #define NUM_GAMMA_STEPS 9
30 #define GAMMA_CMD_CNT 28
32 #define FIRST_COLUMN 20
34 struct s6e63j0x03 {
35 struct device *dev;
36 struct drm_panel panel;
37 struct backlight_device *bl_dev;
39 struct regulator_bulk_data supplies[2];
40 struct gpio_desc *reset_gpio;
43 static const struct drm_display_mode default_mode = {
44 .clock = 4649,
45 .hdisplay = 320,
46 .hsync_start = 320 + 1,
47 .hsync_end = 320 + 1 + 1,
48 .htotal = 320 + 1 + 1 + 1,
49 .vdisplay = 320,
50 .vsync_start = 320 + 150,
51 .vsync_end = 320 + 150 + 1,
52 .vtotal = 320 + 150 + 1 + 2,
53 .vrefresh = 30,
54 .flags = 0,
57 static 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
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
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
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
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
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
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
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
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
114 static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
116 return container_of(panel, struct s6e63j0x03, panel);
119 static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx,
120 const void *seq, size_t len)
122 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
124 return mipi_dsi_dcs_write_buffer(dsi, seq, len);
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)); \
133 static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx)
135 return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a);
138 static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on)
140 if (on)
141 return s6e63j0x03_dcs_write_seq_static(ctx,
142 MCS_MTP_KEY, 0x5a, 0x5a);
144 return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5);
147 static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx)
149 int ret;
151 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
152 if (ret < 0)
153 return ret;
155 msleep(30);
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);
162 return 0;
165 static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
167 return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
170 static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness)
172 unsigned int index;
174 index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS);
176 if (index >= NUM_GAMMA_STEPS)
177 index = NUM_GAMMA_STEPS - 1;
179 return index;
182 static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx,
183 unsigned int brightness)
185 struct backlight_device *bl_dev = ctx->bl_dev;
186 unsigned int index = s6e63j0x03_get_brightness_index(brightness);
187 int ret;
189 ret = s6e63j0x03_apply_mtp_key(ctx, true);
190 if (ret < 0)
191 return ret;
193 ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT);
194 if (ret < 0)
195 return ret;
197 ret = s6e63j0x03_apply_mtp_key(ctx, false);
198 if (ret < 0)
199 return ret;
201 bl_dev->props.brightness = brightness;
203 return 0;
206 static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev)
208 struct s6e63j0x03 *ctx = bl_get_data(bl_dev);
209 unsigned int brightness = bl_dev->props.brightness;
211 return s6e63j0x03_update_gamma(ctx, brightness);
214 static const struct backlight_ops s6e63j0x03_bl_ops = {
215 .update_status = s6e63j0x03_set_brightness,
218 static int s6e63j0x03_disable(struct drm_panel *panel)
220 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
221 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
222 int ret;
224 ret = mipi_dsi_dcs_set_display_off(dsi);
225 if (ret < 0)
226 return ret;
228 ctx->bl_dev->props.power = FB_BLANK_NORMAL;
230 ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
231 if (ret < 0)
232 return ret;
234 msleep(120);
236 return 0;
239 static int s6e63j0x03_unprepare(struct drm_panel *panel)
241 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
242 int ret;
244 ret = s6e63j0x03_power_off(ctx);
245 if (ret < 0)
246 return ret;
248 ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
250 return 0;
253 static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx)
255 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
256 int ret;
258 ret = s6e63j0x03_enable_lv2_command(ctx);
259 if (ret < 0)
260 return ret;
262 ret = s6e63j0x03_apply_mtp_key(ctx, true);
263 if (ret < 0)
264 return ret;
266 /* set porch adjustment */
267 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28);
268 if (ret < 0)
269 return ret;
271 /* set frame freq */
272 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00);
273 if (ret < 0)
274 return ret;
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;
282 ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
283 if (ret < 0)
284 return ret;
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;
292 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02);
293 if (ret < 0)
294 return ret;
296 /* set param pos te_edge */
297 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01);
298 if (ret < 0)
299 return ret;
301 /* set te rising edge */
302 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f);
303 if (ret < 0)
304 return ret;
306 /* set param pos default */
307 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00);
308 if (ret < 0)
309 return ret;
311 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
312 if (ret < 0)
313 return ret;
315 ret = s6e63j0x03_apply_mtp_key(ctx, false);
316 if (ret < 0)
317 return ret;
319 return 0;
322 static int s6e63j0x03_prepare(struct drm_panel *panel)
324 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
325 int ret;
327 ret = s6e63j0x03_power_on(ctx);
328 if (ret < 0)
329 return ret;
331 ret = s6e63j0x03_panel_init(ctx);
332 if (ret < 0)
333 goto err;
335 ctx->bl_dev->props.power = FB_BLANK_NORMAL;
337 return 0;
339 err:
340 s6e63j0x03_power_off(ctx);
341 return ret;
344 static int s6e63j0x03_enable(struct drm_panel *panel)
346 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
347 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
348 int ret;
350 msleep(120);
352 ret = s6e63j0x03_apply_mtp_key(ctx, true);
353 if (ret < 0)
354 return ret;
356 /* set elvss_cond */
357 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09);
358 if (ret < 0)
359 return ret;
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;
367 /* set default white brightness */
368 ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
369 if (ret < 0)
370 return ret;
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;
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;
384 ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
385 if (ret < 0)
386 return ret;
388 ret = s6e63j0x03_apply_mtp_key(ctx, false);
389 if (ret < 0)
390 return ret;
392 ret = mipi_dsi_dcs_set_display_on(dsi);
393 if (ret < 0)
394 return ret;
396 ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
398 return 0;
401 static int s6e63j0x03_get_modes(struct drm_panel *panel)
403 struct drm_connector *connector = panel->connector;
404 struct drm_display_mode *mode;
406 mode = drm_mode_duplicate(panel->drm, &default_mode);
407 if (!mode) {
408 DRM_ERROR("failed to add mode %ux%ux@%u\n",
409 default_mode.hdisplay, default_mode.vdisplay,
410 default_mode.vrefresh);
411 return -ENOMEM;
414 drm_mode_set_name(mode);
416 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
417 drm_mode_probed_add(connector, mode);
419 connector->display_info.width_mm = 29;
420 connector->display_info.height_mm = 29;
422 return 1;
425 static 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,
433 static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
435 struct device *dev = &dsi->dev;
436 struct s6e63j0x03 *ctx;
437 int ret;
439 ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL);
440 if (!ctx)
441 return -ENOMEM;
443 mipi_dsi_set_drvdata(dsi, ctx);
445 ctx->dev = dev;
447 dsi->lanes = 1;
448 dsi->format = MIPI_DSI_FMT_RGB888;
449 dsi->mode_flags = MIPI_DSI_MODE_EOT_PACKET;
451 ctx->supplies[0].supply = "vdd3";
452 ctx->supplies[1].supply = "vci";
453 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
454 ctx->supplies);
455 if (ret < 0) {
456 dev_err(dev, "failed to get regulators: %d\n", ret);
457 return ret;
460 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
461 if (IS_ERR(ctx->reset_gpio)) {
462 dev_err(dev, "cannot get reset-gpio: %ld\n",
463 PTR_ERR(ctx->reset_gpio));
464 return PTR_ERR(ctx->reset_gpio);
467 drm_panel_init(&ctx->panel);
468 ctx->panel.dev = dev;
469 ctx->panel.funcs = &s6e63j0x03_funcs;
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);
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;
482 ret = drm_panel_add(&ctx->panel);
483 if (ret < 0)
484 goto unregister_backlight;
486 ret = mipi_dsi_attach(dsi);
487 if (ret < 0)
488 goto remove_panel;
490 return ret;
492 remove_panel:
493 drm_panel_remove(&ctx->panel);
495 unregister_backlight:
496 backlight_device_unregister(ctx->bl_dev);
498 return ret;
501 static int s6e63j0x03_remove(struct mipi_dsi_device *dsi)
503 struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);
505 mipi_dsi_detach(dsi);
506 drm_panel_remove(&ctx->panel);
508 backlight_device_unregister(ctx->bl_dev);
510 return 0;
513 static const struct of_device_id s6e63j0x03_of_match[] = {
514 { .compatible = "samsung,s6e63j0x03" },
517 MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
519 static 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,
527 module_mipi_dsi_driver(s6e63j0x03_driver);
529 MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
530 MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
531 MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
532 MODULE_LICENSE("GPL v2");