Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver.
0004  *
0005  * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd
0006  *
0007  * Inki Dae <inki.dae@samsung.com>
0008  * Hoegeun Kwon <hoegeun.kwon@samsung.com>
0009  */
0010 
0011 #include <linux/backlight.h>
0012 #include <linux/delay.h>
0013 #include <linux/gpio/consumer.h>
0014 #include <linux/module.h>
0015 #include <linux/regulator/consumer.h>
0016 
0017 #include <video/mipi_display.h>
0018 
0019 #include <drm/drm_mipi_dsi.h>
0020 #include <drm/drm_modes.h>
0021 #include <drm/drm_panel.h>
0022 
0023 #define MCS_LEVEL2_KEY      0xf0
0024 #define MCS_MTP_KEY     0xf1
0025 #define MCS_MTP_SET3        0xd4
0026 
0027 #define MAX_BRIGHTNESS      100
0028 #define DEFAULT_BRIGHTNESS  80
0029 
0030 #define NUM_GAMMA_STEPS     9
0031 #define GAMMA_CMD_CNT       28
0032 
0033 #define FIRST_COLUMN 20
0034 
0035 struct s6e63j0x03 {
0036     struct device *dev;
0037     struct drm_panel panel;
0038     struct backlight_device *bl_dev;
0039 
0040     struct regulator_bulk_data supplies[2];
0041     struct gpio_desc *reset_gpio;
0042 };
0043 
0044 static const struct drm_display_mode default_mode = {
0045     .clock = 4649,
0046     .hdisplay = 320,
0047     .hsync_start = 320 + 1,
0048     .hsync_end = 320 + 1 + 1,
0049     .htotal = 320 + 1 + 1 + 1,
0050     .vdisplay = 320,
0051     .vsync_start = 320 + 150,
0052     .vsync_end = 320 + 150 + 1,
0053     .vtotal = 320 + 150 + 1 + 2,
0054     .flags = 0,
0055 };
0056 
0057 static const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = {
0058     {   /* Gamma 10 */
0059         MCS_MTP_SET3,
0060         0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26,
0061         0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36,
0062         0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf
0063     },
0064     {   /* gamma 30 */
0065         MCS_MTP_SET3,
0066         0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26,
0067         0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34,
0068         0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc
0069     },
0070     {   /* gamma 60 */
0071         MCS_MTP_SET3,
0072         0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a,
0073         0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33,
0074         0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5
0075     },
0076     {   /* gamma 90 */
0077         MCS_MTP_SET3,
0078         0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29,
0079         0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31,
0080         0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09
0081     },
0082     {   /* gamma 120 */
0083         MCS_MTP_SET3,
0084         0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28,
0085         0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31,
0086         0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b
0087     },
0088     {   /* gamma 150 */
0089         MCS_MTP_SET3,
0090         0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28,
0091         0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31,
0092         0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29
0093     },
0094     {   /* gamma 200 */
0095         MCS_MTP_SET3,
0096         0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29,
0097         0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30,
0098         0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b
0099     },
0100     {   /* gamma 240 */
0101         MCS_MTP_SET3,
0102         0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a,
0103         0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30,
0104         0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47
0105     },
0106     {   /* gamma 300 */
0107         MCS_MTP_SET3,
0108         0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28,
0109         0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f,
0110         0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57
0111     }
0112 };
0113 
0114 static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
0115 {
0116     return container_of(panel, struct s6e63j0x03, panel);
0117 }
0118 
0119 static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx,
0120                     const void *seq, size_t len)
0121 {
0122     struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0123 
0124     return mipi_dsi_dcs_write_buffer(dsi, seq, len);
0125 }
0126 
0127 #define s6e63j0x03_dcs_write_seq_static(ctx, seq...)            \
0128     ({                              \
0129         static const u8 d[] = { seq };              \
0130         s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d));    \
0131     })
0132 
0133 static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx)
0134 {
0135     return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a);
0136 }
0137 
0138 static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on)
0139 {
0140     if (on)
0141         return s6e63j0x03_dcs_write_seq_static(ctx,
0142                 MCS_MTP_KEY, 0x5a, 0x5a);
0143 
0144     return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5);
0145 }
0146 
0147 static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx)
0148 {
0149     int ret;
0150 
0151     ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0152     if (ret < 0)
0153         return ret;
0154 
0155     msleep(30);
0156 
0157     gpiod_set_value(ctx->reset_gpio, 1);
0158     usleep_range(1000, 2000);
0159     gpiod_set_value(ctx->reset_gpio, 0);
0160     usleep_range(5000, 6000);
0161 
0162     return 0;
0163 }
0164 
0165 static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
0166 {
0167     return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0168 }
0169 
0170 static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness)
0171 {
0172     unsigned int index;
0173 
0174     index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS);
0175 
0176     if (index >= NUM_GAMMA_STEPS)
0177         index = NUM_GAMMA_STEPS - 1;
0178 
0179     return index;
0180 }
0181 
0182 static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx,
0183                     unsigned int brightness)
0184 {
0185     struct backlight_device *bl_dev = ctx->bl_dev;
0186     unsigned int index = s6e63j0x03_get_brightness_index(brightness);
0187     int ret;
0188 
0189     ret = s6e63j0x03_apply_mtp_key(ctx, true);
0190     if (ret < 0)
0191         return ret;
0192 
0193     ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT);
0194     if (ret < 0)
0195         return ret;
0196 
0197     ret = s6e63j0x03_apply_mtp_key(ctx, false);
0198     if (ret < 0)
0199         return ret;
0200 
0201     bl_dev->props.brightness = brightness;
0202 
0203     return 0;
0204 }
0205 
0206 static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev)
0207 {
0208     struct s6e63j0x03 *ctx = bl_get_data(bl_dev);
0209     unsigned int brightness = bl_dev->props.brightness;
0210 
0211     return s6e63j0x03_update_gamma(ctx, brightness);
0212 }
0213 
0214 static const struct backlight_ops s6e63j0x03_bl_ops = {
0215     .update_status = s6e63j0x03_set_brightness,
0216 };
0217 
0218 static int s6e63j0x03_disable(struct drm_panel *panel)
0219 {
0220     struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
0221     struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0222     int ret;
0223 
0224     ret = mipi_dsi_dcs_set_display_off(dsi);
0225     if (ret < 0)
0226         return ret;
0227 
0228     ctx->bl_dev->props.power = FB_BLANK_NORMAL;
0229 
0230     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
0231     if (ret < 0)
0232         return ret;
0233 
0234     msleep(120);
0235 
0236     return 0;
0237 }
0238 
0239 static int s6e63j0x03_unprepare(struct drm_panel *panel)
0240 {
0241     struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
0242     int ret;
0243 
0244     ret = s6e63j0x03_power_off(ctx);
0245     if (ret < 0)
0246         return ret;
0247 
0248     ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
0249 
0250     return 0;
0251 }
0252 
0253 static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx)
0254 {
0255     struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0256     int ret;
0257 
0258     ret = s6e63j0x03_enable_lv2_command(ctx);
0259     if (ret < 0)
0260         return ret;
0261 
0262     ret = s6e63j0x03_apply_mtp_key(ctx, true);
0263     if (ret < 0)
0264         return ret;
0265 
0266     /* set porch adjustment */
0267     ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28);
0268     if (ret < 0)
0269         return ret;
0270 
0271     /* set frame freq */
0272     ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00);
0273     if (ret < 0)
0274         return ret;
0275 
0276     /* set caset, paset */
0277     ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN,
0278         default_mode.hdisplay - 1 + FIRST_COLUMN);
0279     if (ret < 0)
0280         return ret;
0281 
0282     ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
0283     if (ret < 0)
0284         return ret;
0285 
0286     /* set ltps timming 0, 1 */
0287     ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17,
0288         0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00);
0289     if (ret < 0)
0290         return ret;
0291 
0292     ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02);
0293     if (ret < 0)
0294         return ret;
0295 
0296     /* set param pos te_edge */
0297     ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01);
0298     if (ret < 0)
0299         return ret;
0300 
0301     /* set te rising edge */
0302     ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f);
0303     if (ret < 0)
0304         return ret;
0305 
0306     /* set param pos default */
0307     ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00);
0308     if (ret < 0)
0309         return ret;
0310 
0311     ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
0312     if (ret < 0)
0313         return ret;
0314 
0315     ret = s6e63j0x03_apply_mtp_key(ctx, false);
0316     if (ret < 0)
0317         return ret;
0318 
0319     return 0;
0320 }
0321 
0322 static int s6e63j0x03_prepare(struct drm_panel *panel)
0323 {
0324     struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
0325     int ret;
0326 
0327     ret = s6e63j0x03_power_on(ctx);
0328     if (ret < 0)
0329         return ret;
0330 
0331     ret = s6e63j0x03_panel_init(ctx);
0332     if (ret < 0)
0333         goto err;
0334 
0335     ctx->bl_dev->props.power = FB_BLANK_NORMAL;
0336 
0337     return 0;
0338 
0339 err:
0340     s6e63j0x03_power_off(ctx);
0341     return ret;
0342 }
0343 
0344 static int s6e63j0x03_enable(struct drm_panel *panel)
0345 {
0346     struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
0347     struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0348     int ret;
0349 
0350     msleep(120);
0351 
0352     ret = s6e63j0x03_apply_mtp_key(ctx, true);
0353     if (ret < 0)
0354         return ret;
0355 
0356     /* set elvss_cond */
0357     ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09);
0358     if (ret < 0)
0359         return ret;
0360 
0361     /* set pos */
0362     ret = s6e63j0x03_dcs_write_seq_static(ctx,
0363         MIPI_DCS_SET_ADDRESS_MODE, 0x40);
0364     if (ret < 0)
0365         return ret;
0366 
0367     /* set default white brightness */
0368     ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
0369     if (ret < 0)
0370         return ret;
0371 
0372     /* set white ctrl */
0373     ret = s6e63j0x03_dcs_write_seq_static(ctx,
0374         MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
0375     if (ret < 0)
0376         return ret;
0377 
0378     /* set acl off */
0379     ret = s6e63j0x03_dcs_write_seq_static(ctx,
0380         MIPI_DCS_WRITE_POWER_SAVE, 0x00);
0381     if (ret < 0)
0382         return ret;
0383 
0384     ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
0385     if (ret < 0)
0386         return ret;
0387 
0388     ret = s6e63j0x03_apply_mtp_key(ctx, false);
0389     if (ret < 0)
0390         return ret;
0391 
0392     ret = mipi_dsi_dcs_set_display_on(dsi);
0393     if (ret < 0)
0394         return ret;
0395 
0396     ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
0397 
0398     return 0;
0399 }
0400 
0401 static int s6e63j0x03_get_modes(struct drm_panel *panel,
0402                 struct drm_connector *connector)
0403 {
0404     struct drm_display_mode *mode;
0405 
0406     mode = drm_mode_duplicate(connector->dev, &default_mode);
0407     if (!mode) {
0408         dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
0409             default_mode.hdisplay, default_mode.vdisplay,
0410             drm_mode_vrefresh(&default_mode));
0411         return -ENOMEM;
0412     }
0413 
0414     drm_mode_set_name(mode);
0415 
0416     mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0417     drm_mode_probed_add(connector, mode);
0418 
0419     connector->display_info.width_mm = 29;
0420     connector->display_info.height_mm = 29;
0421 
0422     return 1;
0423 }
0424 
0425 static const struct drm_panel_funcs s6e63j0x03_funcs = {
0426     .disable = s6e63j0x03_disable,
0427     .unprepare = s6e63j0x03_unprepare,
0428     .prepare = s6e63j0x03_prepare,
0429     .enable = s6e63j0x03_enable,
0430     .get_modes = s6e63j0x03_get_modes,
0431 };
0432 
0433 static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
0434 {
0435     struct device *dev = &dsi->dev;
0436     struct s6e63j0x03 *ctx;
0437     int ret;
0438 
0439     ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL);
0440     if (!ctx)
0441         return -ENOMEM;
0442 
0443     mipi_dsi_set_drvdata(dsi, ctx);
0444 
0445     ctx->dev = dev;
0446 
0447     dsi->lanes = 1;
0448     dsi->format = MIPI_DSI_FMT_RGB888;
0449     dsi->mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET;
0450 
0451     ctx->supplies[0].supply = "vdd3";
0452     ctx->supplies[1].supply = "vci";
0453     ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
0454                       ctx->supplies);
0455     if (ret < 0)
0456         return dev_err_probe(dev, ret, "failed to get regulators\n");
0457 
0458     ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
0459     if (IS_ERR(ctx->reset_gpio))
0460         return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
0461                      "cannot get reset-gpio\n");
0462 
0463     drm_panel_init(&ctx->panel, dev, &s6e63j0x03_funcs,
0464                DRM_MODE_CONNECTOR_DSI);
0465 
0466     ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,
0467                         &s6e63j0x03_bl_ops, NULL);
0468     if (IS_ERR(ctx->bl_dev))
0469         return dev_err_probe(dev, PTR_ERR(ctx->bl_dev),
0470                      "failed to register backlight device\n");
0471 
0472     ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS;
0473     ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS;
0474     ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
0475 
0476     drm_panel_add(&ctx->panel);
0477 
0478     ret = mipi_dsi_attach(dsi);
0479     if (ret < 0)
0480         goto remove_panel;
0481 
0482     return ret;
0483 
0484 remove_panel:
0485     drm_panel_remove(&ctx->panel);
0486     backlight_device_unregister(ctx->bl_dev);
0487 
0488     return ret;
0489 }
0490 
0491 static int s6e63j0x03_remove(struct mipi_dsi_device *dsi)
0492 {
0493     struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);
0494 
0495     mipi_dsi_detach(dsi);
0496     drm_panel_remove(&ctx->panel);
0497 
0498     backlight_device_unregister(ctx->bl_dev);
0499 
0500     return 0;
0501 }
0502 
0503 static const struct of_device_id s6e63j0x03_of_match[] = {
0504     { .compatible = "samsung,s6e63j0x03" },
0505     { }
0506 };
0507 MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
0508 
0509 static struct mipi_dsi_driver s6e63j0x03_driver = {
0510     .probe = s6e63j0x03_probe,
0511     .remove = s6e63j0x03_remove,
0512     .driver = {
0513         .name = "panel_samsung_s6e63j0x03",
0514         .of_match_table = s6e63j0x03_of_match,
0515     },
0516 };
0517 module_mipi_dsi_driver(s6e63j0x03_driver);
0518 
0519 MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
0520 MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
0521 MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
0522 MODULE_LICENSE("GPL v2");