linux/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2015 Red Hat
   4 * Copyright (C) 2015 Sony Mobile Communications Inc.
   5 * Author: Werner Johansson <werner.johansson@sonymobile.com>
   6 *
   7 * Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
   8 */
   9
  10#include <linux/delay.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 <video/mipi_display.h>
  17
  18#include <drm/drm_crtc.h>
  19#include <drm/drm_device.h>
  20#include <drm/drm_mipi_dsi.h>
  21#include <drm/drm_panel.h>
  22
  23struct sharp_nt_panel {
  24        struct drm_panel base;
  25        struct mipi_dsi_device *dsi;
  26
  27        struct regulator *supply;
  28        struct gpio_desc *reset_gpio;
  29
  30        bool prepared;
  31        bool enabled;
  32
  33        const struct drm_display_mode *mode;
  34};
  35
  36static inline struct sharp_nt_panel *to_sharp_nt_panel(struct drm_panel *panel)
  37{
  38        return container_of(panel, struct sharp_nt_panel, base);
  39}
  40
  41static int sharp_nt_panel_init(struct sharp_nt_panel *sharp_nt)
  42{
  43        struct mipi_dsi_device *dsi = sharp_nt->dsi;
  44        int ret;
  45
  46        dsi->mode_flags |= MIPI_DSI_MODE_LPM;
  47
  48        ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
  49        if (ret < 0)
  50                return ret;
  51
  52        msleep(120);
  53
  54        /* Novatek two-lane operation */
  55        ret = mipi_dsi_dcs_write(dsi, 0xae, (u8[]){ 0x03 }, 1);
  56        if (ret < 0)
  57                return ret;
  58
  59        /* Set both MCU and RGB I/F to 24bpp */
  60        ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |
  61                                        (MIPI_DCS_PIXEL_FMT_24BIT << 4));
  62        if (ret < 0)
  63                return ret;
  64
  65        return 0;
  66}
  67
  68static int sharp_nt_panel_on(struct sharp_nt_panel *sharp_nt)
  69{
  70        struct mipi_dsi_device *dsi = sharp_nt->dsi;
  71        int ret;
  72
  73        dsi->mode_flags |= MIPI_DSI_MODE_LPM;
  74
  75        ret = mipi_dsi_dcs_set_display_on(dsi);
  76        if (ret < 0)
  77                return ret;
  78
  79        return 0;
  80}
  81
  82static int sharp_nt_panel_off(struct sharp_nt_panel *sharp_nt)
  83{
  84        struct mipi_dsi_device *dsi = sharp_nt->dsi;
  85        int ret;
  86
  87        dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
  88
  89        ret = mipi_dsi_dcs_set_display_off(dsi);
  90        if (ret < 0)
  91                return ret;
  92
  93        ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
  94        if (ret < 0)
  95                return ret;
  96
  97        return 0;
  98}
  99
 100
 101static int sharp_nt_panel_disable(struct drm_panel *panel)
 102{
 103        struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
 104
 105        if (!sharp_nt->enabled)
 106                return 0;
 107
 108        sharp_nt->enabled = false;
 109
 110        return 0;
 111}
 112
 113static int sharp_nt_panel_unprepare(struct drm_panel *panel)
 114{
 115        struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
 116        int ret;
 117
 118        if (!sharp_nt->prepared)
 119                return 0;
 120
 121        ret = sharp_nt_panel_off(sharp_nt);
 122        if (ret < 0) {
 123                dev_err(panel->dev, "failed to set panel off: %d\n", ret);
 124                return ret;
 125        }
 126
 127        regulator_disable(sharp_nt->supply);
 128        if (sharp_nt->reset_gpio)
 129                gpiod_set_value(sharp_nt->reset_gpio, 0);
 130
 131        sharp_nt->prepared = false;
 132
 133        return 0;
 134}
 135
 136static int sharp_nt_panel_prepare(struct drm_panel *panel)
 137{
 138        struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
 139        int ret;
 140
 141        if (sharp_nt->prepared)
 142                return 0;
 143
 144        ret = regulator_enable(sharp_nt->supply);
 145        if (ret < 0)
 146                return ret;
 147
 148        msleep(20);
 149
 150        if (sharp_nt->reset_gpio) {
 151                gpiod_set_value(sharp_nt->reset_gpio, 1);
 152                msleep(1);
 153                gpiod_set_value(sharp_nt->reset_gpio, 0);
 154                msleep(1);
 155                gpiod_set_value(sharp_nt->reset_gpio, 1);
 156                msleep(10);
 157        }
 158
 159        ret = sharp_nt_panel_init(sharp_nt);
 160        if (ret < 0) {
 161                dev_err(panel->dev, "failed to init panel: %d\n", ret);
 162                goto poweroff;
 163        }
 164
 165        ret = sharp_nt_panel_on(sharp_nt);
 166        if (ret < 0) {
 167                dev_err(panel->dev, "failed to set panel on: %d\n", ret);
 168                goto poweroff;
 169        }
 170
 171        sharp_nt->prepared = true;
 172
 173        return 0;
 174
 175poweroff:
 176        regulator_disable(sharp_nt->supply);
 177        if (sharp_nt->reset_gpio)
 178                gpiod_set_value(sharp_nt->reset_gpio, 0);
 179        return ret;
 180}
 181
 182static int sharp_nt_panel_enable(struct drm_panel *panel)
 183{
 184        struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
 185
 186        if (sharp_nt->enabled)
 187                return 0;
 188
 189        sharp_nt->enabled = true;
 190
 191        return 0;
 192}
 193
 194static const struct drm_display_mode default_mode = {
 195        .clock = 41118,
 196        .hdisplay = 540,
 197        .hsync_start = 540 + 48,
 198        .hsync_end = 540 + 48 + 80,
 199        .htotal = 540 + 48 + 80 + 32,
 200        .vdisplay = 960,
 201        .vsync_start = 960 + 3,
 202        .vsync_end = 960 + 3 + 15,
 203        .vtotal = 960 + 3 + 15 + 1,
 204        .vrefresh = 60,
 205};
 206
 207static int sharp_nt_panel_get_modes(struct drm_panel *panel,
 208                                    struct drm_connector *connector)
 209{
 210        struct drm_display_mode *mode;
 211
 212        mode = drm_mode_duplicate(connector->dev, &default_mode);
 213        if (!mode) {
 214                dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
 215                        default_mode.hdisplay, default_mode.vdisplay,
 216                        default_mode.vrefresh);
 217                return -ENOMEM;
 218        }
 219
 220        drm_mode_set_name(mode);
 221
 222        drm_mode_probed_add(connector, mode);
 223
 224        connector->display_info.width_mm = 54;
 225        connector->display_info.height_mm = 95;
 226
 227        return 1;
 228}
 229
 230static const struct drm_panel_funcs sharp_nt_panel_funcs = {
 231        .disable = sharp_nt_panel_disable,
 232        .unprepare = sharp_nt_panel_unprepare,
 233        .prepare = sharp_nt_panel_prepare,
 234        .enable = sharp_nt_panel_enable,
 235        .get_modes = sharp_nt_panel_get_modes,
 236};
 237
 238static int sharp_nt_panel_add(struct sharp_nt_panel *sharp_nt)
 239{
 240        struct device *dev = &sharp_nt->dsi->dev;
 241        int ret;
 242
 243        sharp_nt->mode = &default_mode;
 244
 245        sharp_nt->supply = devm_regulator_get(dev, "avdd");
 246        if (IS_ERR(sharp_nt->supply))
 247                return PTR_ERR(sharp_nt->supply);
 248
 249        sharp_nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
 250        if (IS_ERR(sharp_nt->reset_gpio)) {
 251                dev_err(dev, "cannot get reset-gpios %ld\n",
 252                        PTR_ERR(sharp_nt->reset_gpio));
 253                sharp_nt->reset_gpio = NULL;
 254        } else {
 255                gpiod_set_value(sharp_nt->reset_gpio, 0);
 256        }
 257
 258        drm_panel_init(&sharp_nt->base, &sharp_nt->dsi->dev,
 259                       &sharp_nt_panel_funcs, DRM_MODE_CONNECTOR_DSI);
 260
 261        ret = drm_panel_of_backlight(&sharp_nt->base);
 262        if (ret)
 263                return ret;
 264
 265        return drm_panel_add(&sharp_nt->base);
 266}
 267
 268static void sharp_nt_panel_del(struct sharp_nt_panel *sharp_nt)
 269{
 270        if (sharp_nt->base.dev)
 271                drm_panel_remove(&sharp_nt->base);
 272}
 273
 274static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi)
 275{
 276        struct sharp_nt_panel *sharp_nt;
 277        int ret;
 278
 279        dsi->lanes = 2;
 280        dsi->format = MIPI_DSI_FMT_RGB888;
 281        dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
 282                        MIPI_DSI_MODE_VIDEO_HSE |
 283                        MIPI_DSI_CLOCK_NON_CONTINUOUS |
 284                        MIPI_DSI_MODE_EOT_PACKET;
 285
 286        sharp_nt = devm_kzalloc(&dsi->dev, sizeof(*sharp_nt), GFP_KERNEL);
 287        if (!sharp_nt)
 288                return -ENOMEM;
 289
 290        mipi_dsi_set_drvdata(dsi, sharp_nt);
 291
 292        sharp_nt->dsi = dsi;
 293
 294        ret = sharp_nt_panel_add(sharp_nt);
 295        if (ret < 0)
 296                return ret;
 297
 298        return mipi_dsi_attach(dsi);
 299}
 300
 301static int sharp_nt_panel_remove(struct mipi_dsi_device *dsi)
 302{
 303        struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
 304        int ret;
 305
 306        ret = drm_panel_disable(&sharp_nt->base);
 307        if (ret < 0)
 308                dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
 309
 310        ret = mipi_dsi_detach(dsi);
 311        if (ret < 0)
 312                dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
 313
 314        sharp_nt_panel_del(sharp_nt);
 315
 316        return 0;
 317}
 318
 319static void sharp_nt_panel_shutdown(struct mipi_dsi_device *dsi)
 320{
 321        struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
 322
 323        drm_panel_disable(&sharp_nt->base);
 324}
 325
 326static const struct of_device_id sharp_nt_of_match[] = {
 327        { .compatible = "sharp,ls043t1le01-qhd", },
 328        { }
 329};
 330MODULE_DEVICE_TABLE(of, sharp_nt_of_match);
 331
 332static struct mipi_dsi_driver sharp_nt_panel_driver = {
 333        .driver = {
 334                .name = "panel-sharp-ls043t1le01-qhd",
 335                .of_match_table = sharp_nt_of_match,
 336        },
 337        .probe = sharp_nt_panel_probe,
 338        .remove = sharp_nt_panel_remove,
 339        .shutdown = sharp_nt_panel_shutdown,
 340};
 341module_mipi_dsi_driver(sharp_nt_panel_driver);
 342
 343MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
 344MODULE_DESCRIPTION("Sharp LS043T1LE01 NT35565-based qHD (540x960) video mode panel driver");
 345MODULE_LICENSE("GPL v2");
 346