Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2018 Amarula Solutions
0004  * Author: Jagan Teki <jagan@amarulasolutions.com>
0005  */
0006 
0007 #include <drm/drm_mipi_dsi.h>
0008 #include <drm/drm_modes.h>
0009 #include <drm/drm_panel.h>
0010 
0011 #include <linux/gpio/consumer.h>
0012 #include <linux/delay.h>
0013 #include <linux/module.h>
0014 #include <linux/of_device.h>
0015 #include <linux/regulator/consumer.h>
0016 
0017 #define FEIYANG_INIT_CMD_LEN    2
0018 
0019 struct feiyang {
0020     struct drm_panel    panel;
0021     struct mipi_dsi_device  *dsi;
0022 
0023     struct regulator    *dvdd;
0024     struct regulator    *avdd;
0025     struct gpio_desc    *reset;
0026 };
0027 
0028 static inline struct feiyang *panel_to_feiyang(struct drm_panel *panel)
0029 {
0030     return container_of(panel, struct feiyang, panel);
0031 }
0032 
0033 struct feiyang_init_cmd {
0034     u8 data[FEIYANG_INIT_CMD_LEN];
0035 };
0036 
0037 static const struct feiyang_init_cmd feiyang_init_cmds[] = {
0038     { .data = { 0x80, 0x58 } },
0039     { .data = { 0x81, 0x47 } },
0040     { .data = { 0x82, 0xD4 } },
0041     { .data = { 0x83, 0x88 } },
0042     { .data = { 0x84, 0xA9 } },
0043     { .data = { 0x85, 0xC3 } },
0044     { .data = { 0x86, 0x82 } },
0045 };
0046 
0047 static int feiyang_prepare(struct drm_panel *panel)
0048 {
0049     struct feiyang *ctx = panel_to_feiyang(panel);
0050     struct mipi_dsi_device *dsi = ctx->dsi;
0051     unsigned int i;
0052     int ret;
0053 
0054     ret = regulator_enable(ctx->dvdd);
0055     if (ret)
0056         return ret;
0057 
0058     /* T1 (dvdd start + dvdd rise) 0 < T1 <= 10ms */
0059     msleep(10);
0060 
0061     ret = regulator_enable(ctx->avdd);
0062     if (ret)
0063         return ret;
0064 
0065     /* T3 (dvdd rise + avdd start + avdd rise) T3 >= 20ms */
0066     msleep(20);
0067 
0068     gpiod_set_value(ctx->reset, 0);
0069 
0070     /*
0071      * T5 + T6 (avdd rise + video & logic signal rise)
0072      * T5 >= 10ms, 0 < T6 <= 10ms
0073      */
0074     msleep(20);
0075 
0076     gpiod_set_value(ctx->reset, 1);
0077 
0078     /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */
0079     msleep(200);
0080 
0081     for (i = 0; i < ARRAY_SIZE(feiyang_init_cmds); i++) {
0082         const struct feiyang_init_cmd *cmd =
0083                         &feiyang_init_cmds[i];
0084 
0085         ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data,
0086                         FEIYANG_INIT_CMD_LEN);
0087         if (ret < 0)
0088             return ret;
0089     }
0090 
0091     return 0;
0092 }
0093 
0094 static int feiyang_enable(struct drm_panel *panel)
0095 {
0096     struct feiyang *ctx = panel_to_feiyang(panel);
0097 
0098     /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */
0099     msleep(200);
0100 
0101     mipi_dsi_dcs_set_display_on(ctx->dsi);
0102 
0103     return 0;
0104 }
0105 
0106 static int feiyang_disable(struct drm_panel *panel)
0107 {
0108     struct feiyang *ctx = panel_to_feiyang(panel);
0109 
0110     return mipi_dsi_dcs_set_display_off(ctx->dsi);
0111 }
0112 
0113 static int feiyang_unprepare(struct drm_panel *panel)
0114 {
0115     struct feiyang *ctx = panel_to_feiyang(panel);
0116     int ret;
0117 
0118     ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
0119     if (ret < 0)
0120         dev_err(panel->dev, "failed to set display off: %d\n", ret);
0121 
0122     ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
0123     if (ret < 0)
0124         dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);
0125 
0126     /* T13 (backlight fall + video & logic signal fall) T13 >= 200ms */
0127     msleep(200);
0128 
0129     gpiod_set_value(ctx->reset, 0);
0130 
0131     regulator_disable(ctx->avdd);
0132 
0133     /* T11 (dvdd rise to fall) 0 < T11 <= 10ms  */
0134     msleep(10);
0135 
0136     regulator_disable(ctx->dvdd);
0137 
0138     return 0;
0139 }
0140 
0141 static const struct drm_display_mode feiyang_default_mode = {
0142     .clock      = 55000,
0143 
0144     .hdisplay   = 1024,
0145     .hsync_start    = 1024 + 310,
0146     .hsync_end  = 1024 + 310 + 20,
0147     .htotal     = 1024 + 310 + 20 + 90,
0148 
0149     .vdisplay   = 600,
0150     .vsync_start    = 600 + 12,
0151     .vsync_end  = 600 + 12 + 2,
0152     .vtotal     = 600 + 12 + 2 + 21,
0153 
0154     .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
0155 };
0156 
0157 static int feiyang_get_modes(struct drm_panel *panel,
0158                  struct drm_connector *connector)
0159 {
0160     struct feiyang *ctx = panel_to_feiyang(panel);
0161     struct drm_display_mode *mode;
0162 
0163     mode = drm_mode_duplicate(connector->dev, &feiyang_default_mode);
0164     if (!mode) {
0165         dev_err(&ctx->dsi->dev, "failed to add mode %ux%u@%u\n",
0166             feiyang_default_mode.hdisplay,
0167             feiyang_default_mode.vdisplay,
0168             drm_mode_vrefresh(&feiyang_default_mode));
0169         return -ENOMEM;
0170     }
0171 
0172     drm_mode_set_name(mode);
0173 
0174     drm_mode_probed_add(connector, mode);
0175 
0176     return 1;
0177 }
0178 
0179 static const struct drm_panel_funcs feiyang_funcs = {
0180     .disable = feiyang_disable,
0181     .unprepare = feiyang_unprepare,
0182     .prepare = feiyang_prepare,
0183     .enable = feiyang_enable,
0184     .get_modes = feiyang_get_modes,
0185 };
0186 
0187 static int feiyang_dsi_probe(struct mipi_dsi_device *dsi)
0188 {
0189     struct feiyang *ctx;
0190     int ret;
0191 
0192     ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
0193     if (!ctx)
0194         return -ENOMEM;
0195 
0196     mipi_dsi_set_drvdata(dsi, ctx);
0197     ctx->dsi = dsi;
0198 
0199     drm_panel_init(&ctx->panel, &dsi->dev, &feiyang_funcs,
0200                DRM_MODE_CONNECTOR_DSI);
0201 
0202     ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd");
0203     if (IS_ERR(ctx->dvdd))
0204         return dev_err_probe(&dsi->dev, PTR_ERR(ctx->dvdd),
0205                      "Couldn't get dvdd regulator\n");
0206 
0207     ctx->avdd = devm_regulator_get(&dsi->dev, "avdd");
0208     if (IS_ERR(ctx->avdd))
0209         return dev_err_probe(&dsi->dev, PTR_ERR(ctx->avdd),
0210                      "Couldn't get avdd regulator\n");
0211 
0212     ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW);
0213     if (IS_ERR(ctx->reset))
0214         return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
0215                      "Couldn't get our reset GPIO\n");
0216 
0217     ret = drm_panel_of_backlight(&ctx->panel);
0218     if (ret)
0219         return ret;
0220 
0221     drm_panel_add(&ctx->panel);
0222 
0223     dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST;
0224     dsi->format = MIPI_DSI_FMT_RGB888;
0225     dsi->lanes = 4;
0226 
0227     ret = mipi_dsi_attach(dsi);
0228     if (ret < 0) {
0229         drm_panel_remove(&ctx->panel);
0230         return ret;
0231     }
0232 
0233     return 0;
0234 }
0235 
0236 static int feiyang_dsi_remove(struct mipi_dsi_device *dsi)
0237 {
0238     struct feiyang *ctx = mipi_dsi_get_drvdata(dsi);
0239 
0240     mipi_dsi_detach(dsi);
0241     drm_panel_remove(&ctx->panel);
0242 
0243     return 0;
0244 }
0245 
0246 static const struct of_device_id feiyang_of_match[] = {
0247     { .compatible = "feiyang,fy07024di26a30d", },
0248     { /* sentinel */ }
0249 };
0250 MODULE_DEVICE_TABLE(of, feiyang_of_match);
0251 
0252 static struct mipi_dsi_driver feiyang_driver = {
0253     .probe = feiyang_dsi_probe,
0254     .remove = feiyang_dsi_remove,
0255     .driver = {
0256         .name = "feiyang-fy07024di26a30d",
0257         .of_match_table = feiyang_of_match,
0258     },
0259 };
0260 module_mipi_dsi_driver(feiyang_driver);
0261 
0262 MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
0263 MODULE_DESCRIPTION("Feiyang FY07024DI26A30-D MIPI-DSI LCD panel");
0264 MODULE_LICENSE("GPL");