linux/drivers/gpu/drm/tiny/hx8357d.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * DRM driver for the HX8357D LCD controller
   4 *
   5 * Copyright 2018 Broadcom
   6 * Copyright 2018 David Lechner <david@lechnology.com>
   7 * Copyright 2016 Noralf Trønnes
   8 * Copyright (C) 2015 Adafruit Industries
   9 * Copyright (C) 2013 Christian Vogelgsang
  10 */
  11
  12#include <linux/backlight.h>
  13#include <linux/delay.h>
  14#include <linux/gpio/consumer.h>
  15#include <linux/module.h>
  16#include <linux/property.h>
  17#include <linux/spi/spi.h>
  18
  19#include <drm/drm_atomic_helper.h>
  20#include <drm/drm_drv.h>
  21#include <drm/drm_fb_helper.h>
  22#include <drm/drm_gem_atomic_helper.h>
  23#include <drm/drm_gem_cma_helper.h>
  24#include <drm/drm_managed.h>
  25#include <drm/drm_mipi_dbi.h>
  26#include <drm/drm_modeset_helper.h>
  27#include <video/mipi_display.h>
  28
  29#define HX8357D_SETOSC 0xb0
  30#define HX8357D_SETPOWER 0xb1
  31#define HX8357D_SETRGB 0xb3
  32#define HX8357D_SETCYC 0xb3
  33#define HX8357D_SETCOM 0xb6
  34#define HX8357D_SETEXTC 0xb9
  35#define HX8357D_SETSTBA 0xc0
  36#define HX8357D_SETPANEL 0xcc
  37#define HX8357D_SETGAMMA 0xe0
  38
  39#define HX8357D_MADCTL_MY  0x80
  40#define HX8357D_MADCTL_MX  0x40
  41#define HX8357D_MADCTL_MV  0x20
  42#define HX8357D_MADCTL_ML  0x10
  43#define HX8357D_MADCTL_RGB 0x00
  44#define HX8357D_MADCTL_BGR 0x08
  45#define HX8357D_MADCTL_MH  0x04
  46
  47static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
  48                             struct drm_crtc_state *crtc_state,
  49                             struct drm_plane_state *plane_state)
  50{
  51        struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
  52        struct mipi_dbi *dbi = &dbidev->dbi;
  53        u8 addr_mode;
  54        int ret, idx;
  55
  56        if (!drm_dev_enter(pipe->crtc.dev, &idx))
  57                return;
  58
  59        DRM_DEBUG_KMS("\n");
  60
  61        ret = mipi_dbi_poweron_conditional_reset(dbidev);
  62        if (ret < 0)
  63                goto out_exit;
  64        if (ret == 1)
  65                goto out_enable;
  66
  67        /* setextc */
  68        mipi_dbi_command(dbi, HX8357D_SETEXTC, 0xFF, 0x83, 0x57);
  69        msleep(150);
  70
  71        /* setRGB which also enables SDO */
  72        mipi_dbi_command(dbi, HX8357D_SETRGB, 0x00, 0x00, 0x06, 0x06);
  73
  74        /* -1.52V */
  75        mipi_dbi_command(dbi, HX8357D_SETCOM, 0x25);
  76
  77        /* Normal mode 70Hz, Idle mode 55 Hz */
  78        mipi_dbi_command(dbi, HX8357D_SETOSC, 0x68);
  79
  80        /* Set Panel - BGR, Gate direction swapped */
  81        mipi_dbi_command(dbi, HX8357D_SETPANEL, 0x05);
  82
  83        mipi_dbi_command(dbi, HX8357D_SETPOWER,
  84                         0x00,  /* Not deep standby */
  85                         0x15,  /* BT */
  86                         0x1C,  /* VSPR */
  87                         0x1C,  /* VSNR */
  88                         0x83,  /* AP */
  89                         0xAA);  /* FS */
  90
  91        mipi_dbi_command(dbi, HX8357D_SETSTBA,
  92                         0x50,  /* OPON normal */
  93                         0x50,  /* OPON idle */
  94                         0x01,  /* STBA */
  95                         0x3C,  /* STBA */
  96                         0x1E,  /* STBA */
  97                         0x08);  /* GEN */
  98
  99        mipi_dbi_command(dbi, HX8357D_SETCYC,
 100                         0x02,  /* NW 0x02 */
 101                         0x40,  /* RTN */
 102                         0x00,  /* DIV */
 103                         0x2A,  /* DUM */
 104                         0x2A,  /* DUM */
 105                         0x0D,  /* GDON */
 106                         0x78);  /* GDOFF */
 107
 108        mipi_dbi_command(dbi, HX8357D_SETGAMMA,
 109                         0x02,
 110                         0x0A,
 111                         0x11,
 112                         0x1d,
 113                         0x23,
 114                         0x35,
 115                         0x41,
 116                         0x4b,
 117                         0x4b,
 118                         0x42,
 119                         0x3A,
 120                         0x27,
 121                         0x1B,
 122                         0x08,
 123                         0x09,
 124                         0x03,
 125                         0x02,
 126                         0x0A,
 127                         0x11,
 128                         0x1d,
 129                         0x23,
 130                         0x35,
 131                         0x41,
 132                         0x4b,
 133                         0x4b,
 134                         0x42,
 135                         0x3A,
 136                         0x27,
 137                         0x1B,
 138                         0x08,
 139                         0x09,
 140                         0x03,
 141                         0x00,
 142                         0x01);
 143
 144        /* 16 bit */
 145        mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT,
 146                         MIPI_DCS_PIXEL_FMT_16BIT);
 147
 148        /* TE off */
 149        mipi_dbi_command(dbi, MIPI_DCS_SET_TEAR_ON, 0x00);
 150
 151        /* tear line */
 152        mipi_dbi_command(dbi, MIPI_DCS_SET_TEAR_SCANLINE, 0x00, 0x02);
 153
 154        /* Exit Sleep */
 155        mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
 156        msleep(150);
 157
 158        /* display on */
 159        mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
 160        usleep_range(5000, 7000);
 161
 162out_enable:
 163        switch (dbidev->rotation) {
 164        default:
 165                addr_mode = HX8357D_MADCTL_MX | HX8357D_MADCTL_MY;
 166                break;
 167        case 90:
 168                addr_mode = HX8357D_MADCTL_MV | HX8357D_MADCTL_MY;
 169                break;
 170        case 180:
 171                addr_mode = 0;
 172                break;
 173        case 270:
 174                addr_mode = HX8357D_MADCTL_MV | HX8357D_MADCTL_MX;
 175                break;
 176        }
 177        mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
 178        mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
 179out_exit:
 180        drm_dev_exit(idx);
 181}
 182
 183static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
 184        .enable = yx240qv29_enable,
 185        .disable = mipi_dbi_pipe_disable,
 186        .update = mipi_dbi_pipe_update,
 187};
 188
 189static const struct drm_display_mode yx350hv15_mode = {
 190        DRM_SIMPLE_MODE(320, 480, 60, 75),
 191};
 192
 193DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops);
 194
 195static const struct drm_driver hx8357d_driver = {
 196        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
 197        .fops                   = &hx8357d_fops,
 198        DRM_GEM_CMA_DRIVER_OPS_VMAP,
 199        .debugfs_init           = mipi_dbi_debugfs_init,
 200        .name                   = "hx8357d",
 201        .desc                   = "HX8357D",
 202        .date                   = "20181023",
 203        .major                  = 1,
 204        .minor                  = 0,
 205};
 206
 207static const struct of_device_id hx8357d_of_match[] = {
 208        { .compatible = "adafruit,yx350hv15" },
 209        { }
 210};
 211MODULE_DEVICE_TABLE(of, hx8357d_of_match);
 212
 213static const struct spi_device_id hx8357d_id[] = {
 214        { "yx350hv15", 0 },
 215        { }
 216};
 217MODULE_DEVICE_TABLE(spi, hx8357d_id);
 218
 219static int hx8357d_probe(struct spi_device *spi)
 220{
 221        struct device *dev = &spi->dev;
 222        struct mipi_dbi_dev *dbidev;
 223        struct drm_device *drm;
 224        struct gpio_desc *dc;
 225        u32 rotation = 0;
 226        int ret;
 227
 228        dbidev = devm_drm_dev_alloc(dev, &hx8357d_driver,
 229                                    struct mipi_dbi_dev, drm);
 230        if (IS_ERR(dbidev))
 231                return PTR_ERR(dbidev);
 232
 233        drm = &dbidev->drm;
 234
 235        dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
 236        if (IS_ERR(dc))
 237                return dev_err_probe(dev, PTR_ERR(dc), "Failed to get GPIO 'dc'\n");
 238
 239        dbidev->backlight = devm_of_find_backlight(dev);
 240        if (IS_ERR(dbidev->backlight))
 241                return PTR_ERR(dbidev->backlight);
 242
 243        device_property_read_u32(dev, "rotation", &rotation);
 244
 245        ret = mipi_dbi_spi_init(spi, &dbidev->dbi, dc);
 246        if (ret)
 247                return ret;
 248
 249        ret = mipi_dbi_dev_init(dbidev, &hx8357d_pipe_funcs, &yx350hv15_mode, rotation);
 250        if (ret)
 251                return ret;
 252
 253        drm_mode_config_reset(drm);
 254
 255        ret = drm_dev_register(drm, 0);
 256        if (ret)
 257                return ret;
 258
 259        spi_set_drvdata(spi, drm);
 260
 261        drm_fbdev_generic_setup(drm, 0);
 262
 263        return 0;
 264}
 265
 266static void hx8357d_remove(struct spi_device *spi)
 267{
 268        struct drm_device *drm = spi_get_drvdata(spi);
 269
 270        drm_dev_unplug(drm);
 271        drm_atomic_helper_shutdown(drm);
 272}
 273
 274static void hx8357d_shutdown(struct spi_device *spi)
 275{
 276        drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 277}
 278
 279static struct spi_driver hx8357d_spi_driver = {
 280        .driver = {
 281                .name = "hx8357d",
 282                .of_match_table = hx8357d_of_match,
 283        },
 284        .id_table = hx8357d_id,
 285        .probe = hx8357d_probe,
 286        .remove = hx8357d_remove,
 287        .shutdown = hx8357d_shutdown,
 288};
 289module_spi_driver(hx8357d_spi_driver);
 290
 291MODULE_DESCRIPTION("HX8357D DRM driver");
 292MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
 293MODULE_LICENSE("GPL");
 294