Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 // Copyright (C) 2019, Michael Srba
0003 
0004 #include <linux/delay.h>
0005 #include <linux/gpio/consumer.h>
0006 #include <linux/module.h>
0007 #include <linux/of.h>
0008 #include <linux/regulator/consumer.h>
0009 
0010 #include <video/mipi_display.h>
0011 
0012 #include <drm/drm_mipi_dsi.h>
0013 #include <drm/drm_modes.h>
0014 #include <drm/drm_panel.h>
0015 
0016 struct s6e88a0_ams452ef01 {
0017     struct drm_panel panel;
0018     struct mipi_dsi_device *dsi;
0019     struct regulator_bulk_data supplies[2];
0020     struct gpio_desc *reset_gpio;
0021 
0022     bool prepared;
0023 };
0024 
0025 static inline struct
0026 s6e88a0_ams452ef01 *to_s6e88a0_ams452ef01(struct drm_panel *panel)
0027 {
0028     return container_of(panel, struct s6e88a0_ams452ef01, panel);
0029 }
0030 
0031 #define dsi_dcs_write_seq(dsi, seq...) do {             \
0032         static const u8 d[] = { seq };              \
0033         int ret;                        \
0034         ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \
0035         if (ret < 0)                        \
0036             return ret;                 \
0037     } while (0)
0038 
0039 static void s6e88a0_ams452ef01_reset(struct s6e88a0_ams452ef01 *ctx)
0040 {
0041     gpiod_set_value_cansleep(ctx->reset_gpio, 1);
0042     usleep_range(5000, 6000);
0043     gpiod_set_value_cansleep(ctx->reset_gpio, 0);
0044     usleep_range(1000, 2000);
0045     gpiod_set_value_cansleep(ctx->reset_gpio, 1);
0046     usleep_range(10000, 11000);
0047 }
0048 
0049 static int s6e88a0_ams452ef01_on(struct s6e88a0_ams452ef01 *ctx)
0050 {
0051     struct mipi_dsi_device *dsi = ctx->dsi;
0052     struct device *dev = &dsi->dev;
0053     int ret;
0054 
0055     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
0056 
0057     dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a); // enable LEVEL2 commands
0058     dsi_dcs_write_seq(dsi, 0xcc, 0x4c); // set Pixel Clock Divider polarity
0059 
0060     ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
0061     if (ret < 0) {
0062         dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
0063         return ret;
0064     }
0065     msleep(120);
0066 
0067     // set default brightness/gama
0068     dsi_dcs_write_seq(dsi, 0xca,
0069               0x01, 0x00, 0x01, 0x00, 0x01, 0x00,   // V255 RR,GG,BB
0070               0x80, 0x80, 0x80,         // V203 R,G,B
0071               0x80, 0x80, 0x80,         // V151 R,G,B
0072               0x80, 0x80, 0x80,         // V87  R,G,B
0073               0x80, 0x80, 0x80,         // V51  R,G,B
0074               0x80, 0x80, 0x80,         // V35  R,G,B
0075               0x80, 0x80, 0x80,         // V23  R,G,B
0076               0x80, 0x80, 0x80,         // V11  R,G,B
0077               0x6b, 0x68, 0x71,         // V3   R,G,B
0078               0x00, 0x00, 0x00);            // V1   R,G,B
0079     // set default Amoled Off Ratio
0080     dsi_dcs_write_seq(dsi, 0xb2, 0x40, 0x0a, 0x17, 0x00, 0x0a);
0081     dsi_dcs_write_seq(dsi, 0xb6, 0x2c, 0x0b); // set default elvss voltage
0082     dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
0083     dsi_dcs_write_seq(dsi, 0xf7, 0x03); // gamma/aor update
0084     dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5); // disable LEVEL2 commands
0085 
0086     ret = mipi_dsi_dcs_set_display_on(dsi);
0087     if (ret < 0) {
0088         dev_err(dev, "Failed to set display on: %d\n", ret);
0089         return ret;
0090     }
0091 
0092     return 0;
0093 }
0094 
0095 static int s6e88a0_ams452ef01_off(struct s6e88a0_ams452ef01 *ctx)
0096 {
0097     struct mipi_dsi_device *dsi = ctx->dsi;
0098     struct device *dev = &dsi->dev;
0099     int ret;
0100 
0101     dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
0102 
0103     ret = mipi_dsi_dcs_set_display_off(dsi);
0104     if (ret < 0) {
0105         dev_err(dev, "Failed to set display off: %d\n", ret);
0106         return ret;
0107     }
0108     msleep(35);
0109 
0110     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
0111     if (ret < 0) {
0112         dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
0113         return ret;
0114     }
0115     msleep(120);
0116 
0117     return 0;
0118 }
0119 
0120 static int s6e88a0_ams452ef01_prepare(struct drm_panel *panel)
0121 {
0122     struct s6e88a0_ams452ef01 *ctx = to_s6e88a0_ams452ef01(panel);
0123     struct device *dev = &ctx->dsi->dev;
0124     int ret;
0125 
0126     if (ctx->prepared)
0127         return 0;
0128 
0129     ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0130     if (ret < 0) {
0131         dev_err(dev, "Failed to enable regulators: %d\n", ret);
0132         return ret;
0133     }
0134 
0135     s6e88a0_ams452ef01_reset(ctx);
0136 
0137     ret = s6e88a0_ams452ef01_on(ctx);
0138     if (ret < 0) {
0139         dev_err(dev, "Failed to initialize panel: %d\n", ret);
0140         gpiod_set_value_cansleep(ctx->reset_gpio, 0);
0141         regulator_bulk_disable(ARRAY_SIZE(ctx->supplies),
0142                        ctx->supplies);
0143         return ret;
0144     }
0145 
0146     ctx->prepared = true;
0147     return 0;
0148 }
0149 
0150 static int s6e88a0_ams452ef01_unprepare(struct drm_panel *panel)
0151 {
0152     struct s6e88a0_ams452ef01 *ctx = to_s6e88a0_ams452ef01(panel);
0153     struct device *dev = &ctx->dsi->dev;
0154     int ret;
0155 
0156     if (!ctx->prepared)
0157         return 0;
0158 
0159     ret = s6e88a0_ams452ef01_off(ctx);
0160     if (ret < 0)
0161         dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
0162 
0163     gpiod_set_value_cansleep(ctx->reset_gpio, 0);
0164     regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0165 
0166     ctx->prepared = false;
0167     return 0;
0168 }
0169 
0170 static const struct drm_display_mode s6e88a0_ams452ef01_mode = {
0171     .clock = (540 + 88 + 4 + 20) * (960 + 14 + 2 + 8) * 60 / 1000,
0172     .hdisplay = 540,
0173     .hsync_start = 540 + 88,
0174     .hsync_end = 540 + 88 + 4,
0175     .htotal = 540 + 88 + 4 + 20,
0176     .vdisplay = 960,
0177     .vsync_start = 960 + 14,
0178     .vsync_end = 960 + 14 + 2,
0179     .vtotal = 960 + 14 + 2 + 8,
0180     .width_mm = 56,
0181     .height_mm = 100,
0182 };
0183 
0184 static int s6e88a0_ams452ef01_get_modes(struct drm_panel *panel,
0185                     struct drm_connector *connector)
0186 {
0187     struct drm_display_mode *mode;
0188 
0189     mode = drm_mode_duplicate(connector->dev, &s6e88a0_ams452ef01_mode);
0190     if (!mode)
0191         return -ENOMEM;
0192 
0193     drm_mode_set_name(mode);
0194 
0195     mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0196     connector->display_info.width_mm = mode->width_mm;
0197     connector->display_info.height_mm = mode->height_mm;
0198     drm_mode_probed_add(connector, mode);
0199 
0200     return 1;
0201 }
0202 
0203 static const struct drm_panel_funcs s6e88a0_ams452ef01_panel_funcs = {
0204     .unprepare = s6e88a0_ams452ef01_unprepare,
0205     .prepare = s6e88a0_ams452ef01_prepare,
0206     .get_modes = s6e88a0_ams452ef01_get_modes,
0207 };
0208 
0209 static int s6e88a0_ams452ef01_probe(struct mipi_dsi_device *dsi)
0210 {
0211     struct device *dev = &dsi->dev;
0212     struct s6e88a0_ams452ef01 *ctx;
0213     int ret;
0214 
0215     ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
0216     if (!ctx)
0217         return -ENOMEM;
0218 
0219     ctx->supplies[0].supply = "vdd3";
0220     ctx->supplies[1].supply = "vci";
0221     ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
0222                       ctx->supplies);
0223     if (ret < 0) {
0224         dev_err(dev, "Failed to get regulators: %d\n", ret);
0225         return ret;
0226     }
0227 
0228     ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
0229     if (IS_ERR(ctx->reset_gpio)) {
0230         ret = PTR_ERR(ctx->reset_gpio);
0231         dev_err(dev, "Failed to get reset-gpios: %d\n", ret);
0232         return ret;
0233     }
0234 
0235     ctx->dsi = dsi;
0236     mipi_dsi_set_drvdata(dsi, ctx);
0237 
0238     dsi->lanes = 2;
0239     dsi->format = MIPI_DSI_FMT_RGB888;
0240     dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST;
0241 
0242     drm_panel_init(&ctx->panel, dev, &s6e88a0_ams452ef01_panel_funcs,
0243                DRM_MODE_CONNECTOR_DSI);
0244 
0245     drm_panel_add(&ctx->panel);
0246 
0247     ret = mipi_dsi_attach(dsi);
0248     if (ret < 0) {
0249         dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
0250         drm_panel_remove(&ctx->panel);
0251         return ret;
0252     }
0253 
0254     return 0;
0255 }
0256 
0257 static int s6e88a0_ams452ef01_remove(struct mipi_dsi_device *dsi)
0258 {
0259     struct s6e88a0_ams452ef01 *ctx = mipi_dsi_get_drvdata(dsi);
0260     int ret;
0261 
0262     ret = mipi_dsi_detach(dsi);
0263     if (ret < 0)
0264         dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
0265 
0266     drm_panel_remove(&ctx->panel);
0267 
0268     return 0;
0269 }
0270 
0271 static const struct of_device_id s6e88a0_ams452ef01_of_match[] = {
0272     { .compatible = "samsung,s6e88a0-ams452ef01" },
0273     { /* sentinel */ },
0274 };
0275 MODULE_DEVICE_TABLE(of, s6e88a0_ams452ef01_of_match);
0276 
0277 static struct mipi_dsi_driver s6e88a0_ams452ef01_driver = {
0278     .probe = s6e88a0_ams452ef01_probe,
0279     .remove = s6e88a0_ams452ef01_remove,
0280     .driver = {
0281         .name = "panel-s6e88a0-ams452ef01",
0282         .of_match_table = s6e88a0_ams452ef01_of_match,
0283     },
0284 };
0285 module_mipi_dsi_driver(s6e88a0_ams452ef01_driver);
0286 
0287 MODULE_AUTHOR("Michael Srba <Michael.Srba@seznam.cz>");
0288 MODULE_DESCRIPTION("MIPI-DSI based Panel Driver for AMS452EF01 AMOLED LCD with a S6E88A0 controller");
0289 MODULE_LICENSE("GPL v2");