Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2022 Joel Selvaraj <jo@jsfamily.in>
0004  * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
0005  * Copyright (c) 2013, The Linux Foundation. All rights reserved.
0006  */
0007 
0008 #include <linux/delay.h>
0009 #include <linux/gpio/consumer.h>
0010 #include <linux/regulator/consumer.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 
0014 #include <video/mipi_display.h>
0015 
0016 #include <drm/drm_mipi_dsi.h>
0017 #include <drm/drm_modes.h>
0018 #include <drm/drm_panel.h>
0019 
0020 static const char * const regulator_names[] = {
0021     "vddio",
0022     "vddpos",
0023     "vddneg",
0024 };
0025 
0026 static const unsigned long regulator_enable_loads[] = {
0027     62000,
0028     100000,
0029     100000
0030 };
0031 
0032 struct ebbg_ft8719 {
0033     struct drm_panel panel;
0034     struct mipi_dsi_device *dsi;
0035 
0036     struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
0037 
0038     struct gpio_desc *reset_gpio;
0039 };
0040 
0041 static inline
0042 struct ebbg_ft8719 *to_ebbg_ft8719(struct drm_panel *panel)
0043 {
0044     return container_of(panel, struct ebbg_ft8719, panel);
0045 }
0046 
0047 static void ebbg_ft8719_reset(struct ebbg_ft8719 *ctx)
0048 {
0049     gpiod_set_value_cansleep(ctx->reset_gpio, 0);
0050     usleep_range(4000, 5000);
0051     gpiod_set_value_cansleep(ctx->reset_gpio, 1);
0052     usleep_range(1000, 2000);
0053     gpiod_set_value_cansleep(ctx->reset_gpio, 0);
0054     usleep_range(15000, 16000);
0055 }
0056 
0057 static int ebbg_ft8719_on(struct ebbg_ft8719 *ctx)
0058 {
0059     struct mipi_dsi_device *dsi = ctx->dsi;
0060     struct device *dev = &dsi->dev;
0061     int ret;
0062 
0063     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
0064 
0065     ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
0066     if (ret < 0) {
0067         dev_err(dev, "Failed to set display brightness: %d\n", ret);
0068         return ret;
0069     }
0070 
0071     mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
0072     mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
0073 
0074     ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
0075     if (ret < 0) {
0076         dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
0077         return ret;
0078     }
0079     msleep(90);
0080 
0081     ret = mipi_dsi_dcs_set_display_on(dsi);
0082     if (ret < 0) {
0083         dev_err(dev, "Failed to set display on: %d\n", ret);
0084         return ret;
0085     }
0086 
0087     return 0;
0088 }
0089 
0090 static int ebbg_ft8719_off(struct ebbg_ft8719 *ctx)
0091 {
0092     struct mipi_dsi_device *dsi = ctx->dsi;
0093     struct device *dev = &dsi->dev;
0094     int ret;
0095 
0096     dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
0097 
0098     ret = mipi_dsi_dcs_set_display_off(dsi);
0099     if (ret < 0) {
0100         dev_err(dev, "Failed to set display off: %d\n", ret);
0101         return ret;
0102     }
0103     usleep_range(10000, 11000);
0104 
0105     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
0106     if (ret < 0) {
0107         dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
0108         return ret;
0109     }
0110     msleep(90);
0111 
0112     return 0;
0113 }
0114 
0115 static int ebbg_ft8719_prepare(struct drm_panel *panel)
0116 {
0117     struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel);
0118     struct device *dev = &ctx->dsi->dev;
0119     int ret;
0120 
0121     ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0122     if (ret < 0)
0123         return ret;
0124 
0125     ebbg_ft8719_reset(ctx);
0126 
0127     ret = ebbg_ft8719_on(ctx);
0128     if (ret < 0) {
0129         dev_err(dev, "Failed to initialize panel: %d\n", ret);
0130         gpiod_set_value_cansleep(ctx->reset_gpio, 1);
0131         return ret;
0132     }
0133 
0134     return 0;
0135 }
0136 
0137 static int ebbg_ft8719_unprepare(struct drm_panel *panel)
0138 {
0139     struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel);
0140     struct device *dev = &ctx->dsi->dev;
0141     int ret;
0142 
0143     ret = ebbg_ft8719_off(ctx);
0144     if (ret < 0)
0145         dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
0146 
0147     gpiod_set_value_cansleep(ctx->reset_gpio, 1);
0148 
0149     ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0150     if (ret)
0151         dev_err(panel->dev, "Failed to disable regulators: %d\n", ret);
0152 
0153     return 0;
0154 }
0155 
0156 static const struct drm_display_mode ebbg_ft8719_mode = {
0157     .clock = (1080 + 28 + 4 + 16) * (2246 + 120 + 4 + 12) * 60 / 1000,
0158     .hdisplay = 1080,
0159     .hsync_start = 1080 + 28,
0160     .hsync_end = 1080 + 28 + 4,
0161     .htotal = 1080 + 28 + 4 + 16,
0162     .vdisplay = 2246,
0163     .vsync_start = 2246 + 120,
0164     .vsync_end = 2246 + 120 + 4,
0165     .vtotal = 2246 + 120 + 4 + 12,
0166     .width_mm = 68,
0167     .height_mm = 141,
0168 };
0169 
0170 static int ebbg_ft8719_get_modes(struct drm_panel *panel,
0171                      struct drm_connector *connector)
0172 {
0173     struct drm_display_mode *mode;
0174 
0175     mode = drm_mode_duplicate(connector->dev, &ebbg_ft8719_mode);
0176     if (!mode)
0177         return -ENOMEM;
0178 
0179     drm_mode_set_name(mode);
0180 
0181     mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0182     connector->display_info.width_mm = mode->width_mm;
0183     connector->display_info.height_mm = mode->height_mm;
0184     drm_mode_probed_add(connector, mode);
0185 
0186     return 1;
0187 }
0188 
0189 static const struct drm_panel_funcs ebbg_ft8719_panel_funcs = {
0190     .prepare = ebbg_ft8719_prepare,
0191     .unprepare = ebbg_ft8719_unprepare,
0192     .get_modes = ebbg_ft8719_get_modes,
0193 };
0194 
0195 static int ebbg_ft8719_probe(struct mipi_dsi_device *dsi)
0196 {
0197     struct device *dev = &dsi->dev;
0198     struct ebbg_ft8719 *ctx;
0199     int i, ret;
0200 
0201     ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
0202     if (!ctx)
0203         return -ENOMEM;
0204 
0205     for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
0206         ctx->supplies[i].supply = regulator_names[i];
0207 
0208     ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
0209                       ctx->supplies);
0210     if (ret < 0)
0211         return dev_err_probe(dev, ret, "Failed to get regulators\n");
0212 
0213     for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
0214         ret = regulator_set_load(ctx->supplies[i].consumer,
0215                         regulator_enable_loads[i]);
0216         if (ret)
0217             return dev_err_probe(dev, ret,
0218                          "Failed to set regulator load\n");
0219     }
0220 
0221     ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
0222     if (IS_ERR(ctx->reset_gpio))
0223         return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
0224                      "Failed to get reset-gpios\n");
0225 
0226     ctx->dsi = dsi;
0227     mipi_dsi_set_drvdata(dsi, ctx);
0228 
0229     dsi->lanes = 4;
0230     dsi->format = MIPI_DSI_FMT_RGB888;
0231     dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
0232               MIPI_DSI_CLOCK_NON_CONTINUOUS;
0233 
0234     drm_panel_init(&ctx->panel, dev, &ebbg_ft8719_panel_funcs,
0235                DRM_MODE_CONNECTOR_DSI);
0236 
0237     ret = drm_panel_of_backlight(&ctx->panel);
0238     if (ret)
0239         return dev_err_probe(dev, ret, "Failed to get backlight\n");
0240 
0241     drm_panel_add(&ctx->panel);
0242 
0243     ret = mipi_dsi_attach(dsi);
0244     if (ret < 0) {
0245         dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
0246         drm_panel_remove(&ctx->panel);
0247         return ret;
0248     }
0249 
0250     return 0;
0251 }
0252 
0253 static int ebbg_ft8719_remove(struct mipi_dsi_device *dsi)
0254 {
0255     struct ebbg_ft8719 *ctx = mipi_dsi_get_drvdata(dsi);
0256     int ret;
0257 
0258     ret = mipi_dsi_detach(dsi);
0259     if (ret < 0)
0260         dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
0261 
0262     drm_panel_remove(&ctx->panel);
0263 
0264     return 0;
0265 }
0266 
0267 static const struct of_device_id ebbg_ft8719_of_match[] = {
0268     { .compatible = "ebbg,ft8719" },
0269     { /* sentinel */ }
0270 };
0271 MODULE_DEVICE_TABLE(of, ebbg_ft8719_of_match);
0272 
0273 static struct mipi_dsi_driver ebbg_ft8719_driver = {
0274     .probe = ebbg_ft8719_probe,
0275     .remove = ebbg_ft8719_remove,
0276     .driver = {
0277         .name = "panel-ebbg-ft8719",
0278         .of_match_table = ebbg_ft8719_of_match,
0279     },
0280 };
0281 module_mipi_dsi_driver(ebbg_ft8719_driver);
0282 
0283 MODULE_AUTHOR("Joel Selvaraj <jo@jsfamily.in>");
0284 MODULE_DESCRIPTION("DRM driver for EBBG FT8719 video dsi panel");
0285 MODULE_LICENSE("GPL v2");