linux/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2018 Amarula Solutions
   4 * Author: Jagan Teki <jagan@amarulasolutions.com>
   5 */
   6
   7#include <drm/drm_mipi_dsi.h>
   8#include <drm/drm_modes.h>
   9#include <drm/drm_panel.h>
  10#include <drm/drm_print.h>
  11
  12#include <linux/gpio/consumer.h>
  13#include <linux/delay.h>
  14#include <linux/module.h>
  15#include <linux/of_device.h>
  16#include <linux/regulator/consumer.h>
  17
  18#define FEIYANG_INIT_CMD_LEN    2
  19
  20struct feiyang {
  21        struct drm_panel        panel;
  22        struct mipi_dsi_device  *dsi;
  23
  24        struct regulator        *dvdd;
  25        struct regulator        *avdd;
  26        struct gpio_desc        *reset;
  27};
  28
  29static inline struct feiyang *panel_to_feiyang(struct drm_panel *panel)
  30{
  31        return container_of(panel, struct feiyang, panel);
  32}
  33
  34struct feiyang_init_cmd {
  35        u8 data[FEIYANG_INIT_CMD_LEN];
  36};
  37
  38static const struct feiyang_init_cmd feiyang_init_cmds[] = {
  39        { .data = { 0x80, 0x58 } },
  40        { .data = { 0x81, 0x47 } },
  41        { .data = { 0x82, 0xD4 } },
  42        { .data = { 0x83, 0x88 } },
  43        { .data = { 0x84, 0xA9 } },
  44        { .data = { 0x85, 0xC3 } },
  45        { .data = { 0x86, 0x82 } },
  46};
  47
  48static int feiyang_prepare(struct drm_panel *panel)
  49{
  50        struct feiyang *ctx = panel_to_feiyang(panel);
  51        struct mipi_dsi_device *dsi = ctx->dsi;
  52        unsigned int i;
  53        int ret;
  54
  55        ret = regulator_enable(ctx->dvdd);
  56        if (ret)
  57                return ret;
  58
  59        /* T1 (dvdd start + dvdd rise) 0 < T1 <= 10ms */
  60        msleep(10);
  61
  62        ret = regulator_enable(ctx->avdd);
  63        if (ret)
  64                return ret;
  65
  66        /* T3 (dvdd rise + avdd start + avdd rise) T3 >= 20ms */
  67        msleep(20);
  68
  69        gpiod_set_value(ctx->reset, 0);
  70
  71        /*
  72         * T5 + T6 (avdd rise + video & logic signal rise)
  73         * T5 >= 10ms, 0 < T6 <= 10ms
  74         */
  75        msleep(20);
  76
  77        gpiod_set_value(ctx->reset, 1);
  78
  79        /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */
  80        msleep(200);
  81
  82        for (i = 0; i < ARRAY_SIZE(feiyang_init_cmds); i++) {
  83                const struct feiyang_init_cmd *cmd =
  84                                                &feiyang_init_cmds[i];
  85
  86                ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data,
  87                                                FEIYANG_INIT_CMD_LEN);
  88                if (ret < 0)
  89                        return ret;
  90        }
  91
  92        return 0;
  93}
  94
  95static int feiyang_enable(struct drm_panel *panel)
  96{
  97        struct feiyang *ctx = panel_to_feiyang(panel);
  98
  99        /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */
 100        msleep(200);
 101
 102        mipi_dsi_dcs_set_display_on(ctx->dsi);
 103
 104        return 0;
 105}
 106
 107static int feiyang_disable(struct drm_panel *panel)
 108{
 109        struct feiyang *ctx = panel_to_feiyang(panel);
 110
 111        return mipi_dsi_dcs_set_display_off(ctx->dsi);
 112}
 113
 114static int feiyang_unprepare(struct drm_panel *panel)
 115{
 116        struct feiyang *ctx = panel_to_feiyang(panel);
 117        int ret;
 118
 119        ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
 120        if (ret < 0)
 121                DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
 122                              ret);
 123
 124        ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
 125        if (ret < 0)
 126                DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
 127                              ret);
 128
 129        /* T13 (backlight fall + video & logic signal fall) T13 >= 200ms */
 130        msleep(200);
 131
 132        gpiod_set_value(ctx->reset, 0);
 133
 134        regulator_disable(ctx->avdd);
 135
 136        /* T11 (dvdd rise to fall) 0 < T11 <= 10ms  */
 137        msleep(10);
 138
 139        regulator_disable(ctx->dvdd);
 140
 141        return 0;
 142}
 143
 144static const struct drm_display_mode feiyang_default_mode = {
 145        .clock          = 55000,
 146
 147        .hdisplay       = 1024,
 148        .hsync_start    = 1024 + 310,
 149        .hsync_end      = 1024 + 310 + 20,
 150        .htotal         = 1024 + 310 + 20 + 90,
 151
 152        .vdisplay       = 600,
 153        .vsync_start    = 600 + 12,
 154        .vsync_end      = 600 + 12 + 2,
 155        .vtotal         = 600 + 12 + 2 + 21,
 156        .vrefresh       = 60,
 157
 158        .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
 159};
 160
 161static int feiyang_get_modes(struct drm_panel *panel,
 162                             struct drm_connector *connector)
 163{
 164        struct feiyang *ctx = panel_to_feiyang(panel);
 165        struct drm_display_mode *mode;
 166
 167        mode = drm_mode_duplicate(connector->dev, &feiyang_default_mode);
 168        if (!mode) {
 169                DRM_DEV_ERROR(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n",
 170                              feiyang_default_mode.hdisplay,
 171                              feiyang_default_mode.vdisplay,
 172                              feiyang_default_mode.vrefresh);
 173                return -ENOMEM;
 174        }
 175
 176        drm_mode_set_name(mode);
 177
 178        drm_mode_probed_add(connector, mode);
 179
 180        return 1;
 181}
 182
 183static const struct drm_panel_funcs feiyang_funcs = {
 184        .disable = feiyang_disable,
 185        .unprepare = feiyang_unprepare,
 186        .prepare = feiyang_prepare,
 187        .enable = feiyang_enable,
 188        .get_modes = feiyang_get_modes,
 189};
 190
 191static int feiyang_dsi_probe(struct mipi_dsi_device *dsi)
 192{
 193        struct feiyang *ctx;
 194        int ret;
 195
 196        ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
 197        if (!ctx)
 198                return -ENOMEM;
 199
 200        mipi_dsi_set_drvdata(dsi, ctx);
 201        ctx->dsi = dsi;
 202
 203        drm_panel_init(&ctx->panel, &dsi->dev, &feiyang_funcs,
 204                       DRM_MODE_CONNECTOR_DSI);
 205
 206        ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd");
 207        if (IS_ERR(ctx->dvdd)) {
 208                DRM_DEV_ERROR(&dsi->dev, "Couldn't get dvdd regulator\n");
 209                return PTR_ERR(ctx->dvdd);
 210        }
 211
 212        ctx->avdd = devm_regulator_get(&dsi->dev, "avdd");
 213        if (IS_ERR(ctx->avdd)) {
 214                DRM_DEV_ERROR(&dsi->dev, "Couldn't get avdd regulator\n");
 215                return PTR_ERR(ctx->avdd);
 216        }
 217
 218        ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
 219        if (IS_ERR(ctx->reset)) {
 220                DRM_DEV_ERROR(&dsi->dev, "Couldn't get our reset GPIO\n");
 221                return PTR_ERR(ctx->reset);
 222        }
 223
 224        ret = drm_panel_of_backlight(&ctx->panel);
 225        if (ret)
 226                return ret;
 227
 228        ret = drm_panel_add(&ctx->panel);
 229        if (ret < 0)
 230                return ret;
 231
 232        dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST;
 233        dsi->format = MIPI_DSI_FMT_RGB888;
 234        dsi->lanes = 4;
 235
 236        return mipi_dsi_attach(dsi);
 237}
 238
 239static int feiyang_dsi_remove(struct mipi_dsi_device *dsi)
 240{
 241        struct feiyang *ctx = mipi_dsi_get_drvdata(dsi);
 242
 243        mipi_dsi_detach(dsi);
 244        drm_panel_remove(&ctx->panel);
 245
 246        return 0;
 247}
 248
 249static const struct of_device_id feiyang_of_match[] = {
 250        { .compatible = "feiyang,fy07024di26a30d", },
 251        { /* sentinel */ }
 252};
 253MODULE_DEVICE_TABLE(of, feiyang_of_match);
 254
 255static struct mipi_dsi_driver feiyang_driver = {
 256        .probe = feiyang_dsi_probe,
 257        .remove = feiyang_dsi_remove,
 258        .driver = {
 259                .name = "feiyang-fy07024di26a30d",
 260                .of_match_table = feiyang_of_match,
 261        },
 262};
 263module_mipi_dsi_driver(feiyang_driver);
 264
 265MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
 266MODULE_DESCRIPTION("Feiyang FY07024DI26A30-D MIPI-DSI LCD panel");
 267MODULE_LICENSE("GPL");
 268