linux/drivers/gpu/drm/panel/panel-innolux-p079zca.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 */
   9
  10#include <linux/backlight.h>
  11#include <linux/gpio/consumer.h>
  12#include <linux/module.h>
  13#include <linux/of.h>
  14#include <linux/regulator/consumer.h>
  15
  16#include <drm/drmP.h>
  17#include <drm/drm_crtc.h>
  18#include <drm/drm_mipi_dsi.h>
  19#include <drm/drm_panel.h>
  20
  21#include <video/mipi_display.h>
  22
  23struct innolux_panel {
  24        struct drm_panel base;
  25        struct mipi_dsi_device *link;
  26
  27        struct backlight_device *backlight;
  28        struct regulator *supply;
  29        struct gpio_desc *enable_gpio;
  30
  31        bool prepared;
  32        bool enabled;
  33};
  34
  35static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel)
  36{
  37        return container_of(panel, struct innolux_panel, base);
  38}
  39
  40static int innolux_panel_disable(struct drm_panel *panel)
  41{
  42        struct innolux_panel *innolux = to_innolux_panel(panel);
  43        int err;
  44
  45        if (!innolux->enabled)
  46                return 0;
  47
  48        backlight_disable(innolux->backlight);
  49
  50        err = mipi_dsi_dcs_set_display_off(innolux->link);
  51        if (err < 0)
  52                DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
  53                              err);
  54
  55        innolux->enabled = false;
  56
  57        return 0;
  58}
  59
  60static int innolux_panel_unprepare(struct drm_panel *panel)
  61{
  62        struct innolux_panel *innolux = to_innolux_panel(panel);
  63        int err;
  64
  65        if (!innolux->prepared)
  66                return 0;
  67
  68        err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);
  69        if (err < 0) {
  70                DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
  71                              err);
  72                return err;
  73        }
  74
  75        gpiod_set_value_cansleep(innolux->enable_gpio, 0);
  76
  77        /* T8: 80ms - 1000ms */
  78        msleep(80);
  79
  80        err = regulator_disable(innolux->supply);
  81        if (err < 0)
  82                return err;
  83
  84        innolux->prepared = false;
  85
  86        return 0;
  87}
  88
  89static int innolux_panel_prepare(struct drm_panel *panel)
  90{
  91        struct innolux_panel *innolux = to_innolux_panel(panel);
  92        int err, regulator_err;
  93
  94        if (innolux->prepared)
  95                return 0;
  96
  97        gpiod_set_value_cansleep(innolux->enable_gpio, 0);
  98
  99        err = regulator_enable(innolux->supply);
 100        if (err < 0)
 101                return err;
 102
 103        /* T2: 15ms - 1000ms */
 104        usleep_range(15000, 16000);
 105
 106        gpiod_set_value_cansleep(innolux->enable_gpio, 1);
 107
 108        /* T4: 15ms - 1000ms */
 109        usleep_range(15000, 16000);
 110
 111        err = mipi_dsi_dcs_exit_sleep_mode(innolux->link);
 112        if (err < 0) {
 113                DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
 114                              err);
 115                goto poweroff;
 116        }
 117
 118        /* T6: 120ms - 1000ms*/
 119        msleep(120);
 120
 121        err = mipi_dsi_dcs_set_display_on(innolux->link);
 122        if (err < 0) {
 123                DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
 124                              err);
 125                goto poweroff;
 126        }
 127
 128        /* T7: 5ms */
 129        usleep_range(5000, 6000);
 130
 131        innolux->prepared = true;
 132
 133        return 0;
 134
 135poweroff:
 136        regulator_err = regulator_disable(innolux->supply);
 137        if (regulator_err)
 138                DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n",
 139                              regulator_err);
 140
 141        gpiod_set_value_cansleep(innolux->enable_gpio, 0);
 142        return err;
 143}
 144
 145static int innolux_panel_enable(struct drm_panel *panel)
 146{
 147        struct innolux_panel *innolux = to_innolux_panel(panel);
 148        int ret;
 149
 150        if (innolux->enabled)
 151                return 0;
 152
 153        ret = backlight_enable(innolux->backlight);
 154        if (ret) {
 155                DRM_DEV_ERROR(panel->drm->dev,
 156                              "Failed to enable backlight %d\n", ret);
 157                return ret;
 158        }
 159
 160        innolux->enabled = true;
 161
 162        return 0;
 163}
 164
 165static const struct drm_display_mode default_mode = {
 166        .clock = 56900,
 167        .hdisplay = 768,
 168        .hsync_start = 768 + 40,
 169        .hsync_end = 768 + 40 + 40,
 170        .htotal = 768 + 40 + 40 + 40,
 171        .vdisplay = 1024,
 172        .vsync_start = 1024 + 20,
 173        .vsync_end = 1024 + 20 + 4,
 174        .vtotal = 1024 + 20 + 4 + 20,
 175        .vrefresh = 60,
 176};
 177
 178static int innolux_panel_get_modes(struct drm_panel *panel)
 179{
 180        struct drm_display_mode *mode;
 181
 182        mode = drm_mode_duplicate(panel->drm, &default_mode);
 183        if (!mode) {
 184                DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
 185                              default_mode.hdisplay, default_mode.vdisplay,
 186                              default_mode.vrefresh);
 187                return -ENOMEM;
 188        }
 189
 190        drm_mode_set_name(mode);
 191
 192        drm_mode_probed_add(panel->connector, mode);
 193
 194        panel->connector->display_info.width_mm = 120;
 195        panel->connector->display_info.height_mm = 160;
 196        panel->connector->display_info.bpc = 8;
 197
 198        return 1;
 199}
 200
 201static const struct drm_panel_funcs innolux_panel_funcs = {
 202        .disable = innolux_panel_disable,
 203        .unprepare = innolux_panel_unprepare,
 204        .prepare = innolux_panel_prepare,
 205        .enable = innolux_panel_enable,
 206        .get_modes = innolux_panel_get_modes,
 207};
 208
 209static const struct of_device_id innolux_of_match[] = {
 210        { .compatible = "innolux,p079zca", },
 211        { }
 212};
 213MODULE_DEVICE_TABLE(of, innolux_of_match);
 214
 215static int innolux_panel_add(struct innolux_panel *innolux)
 216{
 217        struct device *dev = &innolux->link->dev;
 218        int err;
 219
 220        innolux->supply = devm_regulator_get(dev, "power");
 221        if (IS_ERR(innolux->supply))
 222                return PTR_ERR(innolux->supply);
 223
 224        innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
 225                                                       GPIOD_OUT_HIGH);
 226        if (IS_ERR(innolux->enable_gpio)) {
 227                err = PTR_ERR(innolux->enable_gpio);
 228                dev_dbg(dev, "failed to get enable gpio: %d\n", err);
 229                innolux->enable_gpio = NULL;
 230        }
 231
 232        innolux->backlight = devm_of_find_backlight(dev);
 233
 234        if (IS_ERR(innolux->backlight))
 235                return PTR_ERR(innolux->backlight);
 236
 237        drm_panel_init(&innolux->base);
 238        innolux->base.funcs = &innolux_panel_funcs;
 239        innolux->base.dev = &innolux->link->dev;
 240
 241        return drm_panel_add(&innolux->base);
 242}
 243
 244static void innolux_panel_del(struct innolux_panel *innolux)
 245{
 246        if (innolux->base.dev)
 247                drm_panel_remove(&innolux->base);
 248}
 249
 250static int innolux_panel_probe(struct mipi_dsi_device *dsi)
 251{
 252        struct innolux_panel *innolux;
 253        int err;
 254
 255        dsi->lanes = 4;
 256        dsi->format = MIPI_DSI_FMT_RGB888;
 257        dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
 258                          MIPI_DSI_MODE_LPM;
 259
 260        innolux = devm_kzalloc(&dsi->dev, sizeof(*innolux), GFP_KERNEL);
 261        if (!innolux)
 262                return -ENOMEM;
 263
 264        mipi_dsi_set_drvdata(dsi, innolux);
 265
 266        innolux->link = dsi;
 267
 268        err = innolux_panel_add(innolux);
 269        if (err < 0)
 270                return err;
 271
 272        err = mipi_dsi_attach(dsi);
 273        return err;
 274}
 275
 276static int innolux_panel_remove(struct mipi_dsi_device *dsi)
 277{
 278        struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
 279        int err;
 280
 281        err = innolux_panel_unprepare(&innolux->base);
 282        if (err < 0)
 283                DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
 284                              err);
 285
 286        err = innolux_panel_disable(&innolux->base);
 287        if (err < 0)
 288                DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
 289
 290        err = mipi_dsi_detach(dsi);
 291        if (err < 0)
 292                DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n",
 293                              err);
 294
 295        drm_panel_detach(&innolux->base);
 296        innolux_panel_del(innolux);
 297
 298        return 0;
 299}
 300
 301static void innolux_panel_shutdown(struct mipi_dsi_device *dsi)
 302{
 303        struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
 304
 305        innolux_panel_unprepare(&innolux->base);
 306        innolux_panel_disable(&innolux->base);
 307}
 308
 309static struct mipi_dsi_driver innolux_panel_driver = {
 310        .driver = {
 311                .name = "panel-innolux-p079zca",
 312                .of_match_table = innolux_of_match,
 313        },
 314        .probe = innolux_panel_probe,
 315        .remove = innolux_panel_remove,
 316        .shutdown = innolux_panel_shutdown,
 317};
 318module_mipi_dsi_driver(innolux_panel_driver);
 319
 320MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
 321MODULE_DESCRIPTION("Innolux P079ZCA panel driver");
 322MODULE_LICENSE("GPL v2");
 323