0001
0002
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);
0058 dsi_dcs_write_seq(dsi, 0xcc, 0x4c);
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
0068 dsi_dcs_write_seq(dsi, 0xca,
0069 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0070 0x80, 0x80, 0x80,
0071 0x80, 0x80, 0x80,
0072 0x80, 0x80, 0x80,
0073 0x80, 0x80, 0x80,
0074 0x80, 0x80, 0x80,
0075 0x80, 0x80, 0x80,
0076 0x80, 0x80, 0x80,
0077 0x6b, 0x68, 0x71,
0078 0x00, 0x00, 0x00);
0079
0080 dsi_dcs_write_seq(dsi, 0xb2, 0x40, 0x0a, 0x17, 0x00, 0x0a);
0081 dsi_dcs_write_seq(dsi, 0xb6, 0x2c, 0x0b);
0082 dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
0083 dsi_dcs_write_seq(dsi, 0xf7, 0x03);
0084 dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
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 { },
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");