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};
 205
 206static int sharp_nt_panel_get_modes(struct drm_panel *panel,
 207                                    struct drm_connector *connector)
 208{
 209        struct drm_display_mode *mode;
 210
 211        mode = drm_mode_duplicate(connector->dev, &default_mode);
 212        if (!mode) {
 213                dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
 214                        default_mode.hdisplay, default_mode.vdisplay,
 215                        drm_mode_vrefresh(&default_mode));
 216                return -ENOMEM;
 217        }
 218
 219        drm_mode_set_name(mode);
 220
 221        drm_mode_probed_add(connector, mode);
 222
 223        connector->display_info.width_mm = 54;
 224        connector->display_info.height_mm = 95;
 225
 226        return 1;
 227}
 228
 229static const struct drm_panel_funcs sharp_nt_panel_funcs = {
 230        .disable = sharp_nt_panel_disable,
 231        .unprepare = sharp_nt_panel_unprepare,
 232        .prepare = sharp_nt_panel_prepare,
 233        .enable = sharp_nt_panel_enable,
 234        .get_modes = sharp_nt_panel_get_modes,
 235};
 236
 237static int sharp_nt_panel_add(struct sharp_nt_panel *sharp_nt)
 238{
 239        struct device *dev = &sharp_nt->dsi->dev;
 240        int ret;
 241
 242        sharp_nt->mode = &default_mode;
 243
 244        sharp_nt->supply = devm_regulator_get(dev, "avdd");
 245        if (IS_ERR(sharp_nt->supply))
 246                return PTR_ERR(sharp_nt->supply);
 247
 248        sharp_nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
 249        if (IS_ERR(sharp_nt->reset_gpio)) {
 250                dev_err(dev, "cannot get reset-gpios %ld\n",
 251                        PTR_ERR(sharp_nt->reset_gpio));
 252                sharp_nt->reset_gpio = NULL;
 253        } else {
 254                gpiod_set_value(sharp_nt->reset_gpio, 0);
 255        }
 256
 257        drm_panel_init(&sharp_nt->base, &sharp_nt->dsi->dev,
 258                       &sharp_nt_panel_funcs, DRM_MODE_CONNECTOR_DSI);
 259
 260        ret = drm_panel_of_backlight(&sharp_nt->base);
 261        if (ret)
 262                return ret;
 263
 264        drm_panel_add(&sharp_nt->base);
 265
 266        return 0;
 267}
 268
 269static void sharp_nt_panel_del(struct sharp_nt_panel *sharp_nt)
 270{
 271        if (sharp_nt->base.dev)
 272                drm_panel_remove(&sharp_nt->base);
 273}
 274
 275static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi)
 276{
 277        struct sharp_nt_panel *sharp_nt;
 278        int ret;
 279
 280        dsi->lanes = 2;
 281        dsi->format = MIPI_DSI_FMT_RGB888;
 282        dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
 283                        MIPI_DSI_MODE_VIDEO_HSE |
 284                        MIPI_DSI_CLOCK_NON_CONTINUOUS |
 285                        MIPI_DSI_MODE_NO_EOT_PACKET;
 286
 287        sharp_nt = devm_kzalloc(&dsi->dev, sizeof(*sharp_nt), GFP_KERNEL);
 288        if (!sharp_nt)
 289                return -ENOMEM;
 290
 291        mipi_dsi_set_drvdata(dsi, sharp_nt);
 292
 293        sharp_nt->dsi = dsi;
 294
 295        ret = sharp_nt_panel_add(sharp_nt);
 296        if (ret < 0)
 297                return ret;
 298
 299        return mipi_dsi_attach(dsi);
 300}
 301
 302static int sharp_nt_panel_remove(struct mipi_dsi_device *dsi)
 303{
 304        struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
 305        int ret;
 306
 307        ret = drm_panel_disable(&sharp_nt->base);
 308        if (ret < 0)
 309                dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
 310
 311        ret = mipi_dsi_detach(dsi);
 312        if (ret < 0)
 313                dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
 314
 315        sharp_nt_panel_del(sharp_nt);
 316
 317        return 0;
 318}
 319
 320static void sharp_nt_panel_shutdown(struct mipi_dsi_device *dsi)
 321{
 322        struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
 323
 324        drm_panel_disable(&sharp_nt->base);
 325}
 326
 327static const struct of_device_id sharp_nt_of_match[] = {
 328        { .compatible = "sharp,ls043t1le01-qhd", },
 329        { }
 330};
 331MODULE_DEVICE_TABLE(of, sharp_nt_of_match);
 332
 333static struct mipi_dsi_driver sharp_nt_panel_driver = {
 334        .driver = {
 335                .name = "panel-sharp-ls043t1le01-qhd",
 336                .of_match_table = sharp_nt_of_match,
 337        },
 338        .probe = sharp_nt_panel_probe,
 339        .remove = sharp_nt_panel_remove,
 340        .shutdown = sharp_nt_panel_shutdown,
 341};
 342module_mipi_dsi_driver(sharp_nt_panel_driver);
 343
 344MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
 345MODULE_DESCRIPTION("Sharp LS043T1LE01 NT35565-based qHD (540x960) video mode panel driver");
 346MODULE_LICENSE("GPL v2");
 347