1*9b8f3200SMarcus Folkesson // SPDX-License-Identifier: GPL-2.0+ 2*9b8f3200SMarcus Folkesson /* 3*9b8f3200SMarcus Folkesson * DRM driver for display panels connected to a Sitronix ST7715R or ST7735R 4*9b8f3200SMarcus Folkesson * display controller in SPI mode. 5*9b8f3200SMarcus Folkesson * 6*9b8f3200SMarcus Folkesson * Copyright 2017 David Lechner <david@lechnology.com> 7*9b8f3200SMarcus Folkesson * Copyright (C) 2019 Glider bvba 8*9b8f3200SMarcus Folkesson */ 9*9b8f3200SMarcus Folkesson 10*9b8f3200SMarcus Folkesson #include <linux/backlight.h> 11*9b8f3200SMarcus Folkesson #include <linux/delay.h> 12*9b8f3200SMarcus Folkesson #include <linux/dma-buf.h> 13*9b8f3200SMarcus Folkesson #include <linux/gpio/consumer.h> 14*9b8f3200SMarcus Folkesson #include <linux/module.h> 15*9b8f3200SMarcus Folkesson #include <linux/property.h> 16*9b8f3200SMarcus Folkesson #include <linux/spi/spi.h> 17*9b8f3200SMarcus Folkesson #include <video/mipi_display.h> 18*9b8f3200SMarcus Folkesson 19*9b8f3200SMarcus Folkesson #include <drm/clients/drm_client_setup.h> 20*9b8f3200SMarcus Folkesson #include <drm/drm_atomic_helper.h> 21*9b8f3200SMarcus Folkesson #include <drm/drm_drv.h> 22*9b8f3200SMarcus Folkesson #include <drm/drm_fbdev_dma.h> 23*9b8f3200SMarcus Folkesson #include <drm/drm_gem_atomic_helper.h> 24*9b8f3200SMarcus Folkesson #include <drm/drm_gem_dma_helper.h> 25*9b8f3200SMarcus Folkesson #include <drm/drm_managed.h> 26*9b8f3200SMarcus Folkesson #include <drm/drm_mipi_dbi.h> 27*9b8f3200SMarcus Folkesson 28*9b8f3200SMarcus Folkesson #define ST7735R_FRMCTR1 0xb1 29*9b8f3200SMarcus Folkesson #define ST7735R_FRMCTR2 0xb2 30*9b8f3200SMarcus Folkesson #define ST7735R_FRMCTR3 0xb3 31*9b8f3200SMarcus Folkesson #define ST7735R_INVCTR 0xb4 32*9b8f3200SMarcus Folkesson #define ST7735R_PWCTR1 0xc0 33*9b8f3200SMarcus Folkesson #define ST7735R_PWCTR2 0xc1 34*9b8f3200SMarcus Folkesson #define ST7735R_PWCTR3 0xc2 35*9b8f3200SMarcus Folkesson #define ST7735R_PWCTR4 0xc3 36*9b8f3200SMarcus Folkesson #define ST7735R_PWCTR5 0xc4 37*9b8f3200SMarcus Folkesson #define ST7735R_VMCTR1 0xc5 38*9b8f3200SMarcus Folkesson #define ST7735R_GAMCTRP1 0xe0 39*9b8f3200SMarcus Folkesson #define ST7735R_GAMCTRN1 0xe1 40*9b8f3200SMarcus Folkesson 41*9b8f3200SMarcus Folkesson #define ST7735R_MY BIT(7) 42*9b8f3200SMarcus Folkesson #define ST7735R_MX BIT(6) 43*9b8f3200SMarcus Folkesson #define ST7735R_MV BIT(5) 44*9b8f3200SMarcus Folkesson #define ST7735R_RGB BIT(3) 45*9b8f3200SMarcus Folkesson 46*9b8f3200SMarcus Folkesson struct st7735r_cfg { 47*9b8f3200SMarcus Folkesson const struct drm_display_mode mode; 48*9b8f3200SMarcus Folkesson unsigned int left_offset; 49*9b8f3200SMarcus Folkesson unsigned int top_offset; 50*9b8f3200SMarcus Folkesson unsigned int write_only:1; 51*9b8f3200SMarcus Folkesson unsigned int rgb:1; /* RGB (vs. BGR) */ 52*9b8f3200SMarcus Folkesson }; 53*9b8f3200SMarcus Folkesson 54*9b8f3200SMarcus Folkesson struct st7735r_priv { 55*9b8f3200SMarcus Folkesson struct mipi_dbi_dev dbidev; /* Must be first for .release() */ 56*9b8f3200SMarcus Folkesson const struct st7735r_cfg *cfg; 57*9b8f3200SMarcus Folkesson }; 58*9b8f3200SMarcus Folkesson 59*9b8f3200SMarcus Folkesson static void st7735r_pipe_enable(struct drm_simple_display_pipe *pipe, 60*9b8f3200SMarcus Folkesson struct drm_crtc_state *crtc_state, 61*9b8f3200SMarcus Folkesson struct drm_plane_state *plane_state) 62*9b8f3200SMarcus Folkesson { 63*9b8f3200SMarcus Folkesson struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 64*9b8f3200SMarcus Folkesson struct st7735r_priv *priv = container_of(dbidev, struct st7735r_priv, 65*9b8f3200SMarcus Folkesson dbidev); 66*9b8f3200SMarcus Folkesson struct mipi_dbi *dbi = &dbidev->dbi; 67*9b8f3200SMarcus Folkesson int ret, idx; 68*9b8f3200SMarcus Folkesson u8 addr_mode; 69*9b8f3200SMarcus Folkesson 70*9b8f3200SMarcus Folkesson if (!drm_dev_enter(pipe->crtc.dev, &idx)) 71*9b8f3200SMarcus Folkesson return; 72*9b8f3200SMarcus Folkesson 73*9b8f3200SMarcus Folkesson DRM_DEBUG_KMS("\n"); 74*9b8f3200SMarcus Folkesson 75*9b8f3200SMarcus Folkesson ret = mipi_dbi_poweron_reset(dbidev); 76*9b8f3200SMarcus Folkesson if (ret) 77*9b8f3200SMarcus Folkesson goto out_exit; 78*9b8f3200SMarcus Folkesson 79*9b8f3200SMarcus Folkesson msleep(150); 80*9b8f3200SMarcus Folkesson 81*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 82*9b8f3200SMarcus Folkesson msleep(500); 83*9b8f3200SMarcus Folkesson 84*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_FRMCTR1, 0x01, 0x2c, 0x2d); 85*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_FRMCTR2, 0x01, 0x2c, 0x2d); 86*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_FRMCTR3, 0x01, 0x2c, 0x2d, 0x01, 0x2c, 87*9b8f3200SMarcus Folkesson 0x2d); 88*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_INVCTR, 0x07); 89*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_PWCTR1, 0xa2, 0x02, 0x84); 90*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_PWCTR2, 0xc5); 91*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_PWCTR3, 0x0a, 0x00); 92*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_PWCTR4, 0x8a, 0x2a); 93*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_PWCTR5, 0x8a, 0xee); 94*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_VMCTR1, 0x0e); 95*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE); 96*9b8f3200SMarcus Folkesson switch (dbidev->rotation) { 97*9b8f3200SMarcus Folkesson default: 98*9b8f3200SMarcus Folkesson addr_mode = ST7735R_MX | ST7735R_MY; 99*9b8f3200SMarcus Folkesson break; 100*9b8f3200SMarcus Folkesson case 90: 101*9b8f3200SMarcus Folkesson addr_mode = ST7735R_MX | ST7735R_MV; 102*9b8f3200SMarcus Folkesson break; 103*9b8f3200SMarcus Folkesson case 180: 104*9b8f3200SMarcus Folkesson addr_mode = 0; 105*9b8f3200SMarcus Folkesson break; 106*9b8f3200SMarcus Folkesson case 270: 107*9b8f3200SMarcus Folkesson addr_mode = ST7735R_MY | ST7735R_MV; 108*9b8f3200SMarcus Folkesson break; 109*9b8f3200SMarcus Folkesson } 110*9b8f3200SMarcus Folkesson 111*9b8f3200SMarcus Folkesson if (priv->cfg->rgb) 112*9b8f3200SMarcus Folkesson addr_mode |= ST7735R_RGB; 113*9b8f3200SMarcus Folkesson 114*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); 115*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, 116*9b8f3200SMarcus Folkesson MIPI_DCS_PIXEL_FMT_16BIT); 117*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_GAMCTRP1, 0x02, 0x1c, 0x07, 0x12, 0x37, 118*9b8f3200SMarcus Folkesson 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2b, 0x39, 0x00, 0x01, 119*9b8f3200SMarcus Folkesson 0x03, 0x10); 120*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7735R_GAMCTRN1, 0x03, 0x1d, 0x07, 0x06, 0x2e, 121*9b8f3200SMarcus Folkesson 0x2c, 0x29, 0x2d, 0x2e, 0x2e, 0x37, 0x3f, 0x00, 0x00, 122*9b8f3200SMarcus Folkesson 0x02, 0x10); 123*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 124*9b8f3200SMarcus Folkesson 125*9b8f3200SMarcus Folkesson msleep(100); 126*9b8f3200SMarcus Folkesson 127*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_ENTER_NORMAL_MODE); 128*9b8f3200SMarcus Folkesson 129*9b8f3200SMarcus Folkesson msleep(20); 130*9b8f3200SMarcus Folkesson 131*9b8f3200SMarcus Folkesson mipi_dbi_enable_flush(dbidev, crtc_state, plane_state); 132*9b8f3200SMarcus Folkesson out_exit: 133*9b8f3200SMarcus Folkesson drm_dev_exit(idx); 134*9b8f3200SMarcus Folkesson } 135*9b8f3200SMarcus Folkesson 136*9b8f3200SMarcus Folkesson static const struct drm_simple_display_pipe_funcs st7735r_pipe_funcs = { 137*9b8f3200SMarcus Folkesson DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(st7735r_pipe_enable), 138*9b8f3200SMarcus Folkesson }; 139*9b8f3200SMarcus Folkesson 140*9b8f3200SMarcus Folkesson static const struct st7735r_cfg jd_t18003_t01_cfg = { 141*9b8f3200SMarcus Folkesson .mode = { DRM_SIMPLE_MODE(128, 160, 28, 35) }, 142*9b8f3200SMarcus Folkesson /* Cannot read from Adafruit 1.8" display via SPI */ 143*9b8f3200SMarcus Folkesson .write_only = true, 144*9b8f3200SMarcus Folkesson }; 145*9b8f3200SMarcus Folkesson 146*9b8f3200SMarcus Folkesson static const struct st7735r_cfg rh128128t_cfg = { 147*9b8f3200SMarcus Folkesson .mode = { DRM_SIMPLE_MODE(128, 128, 25, 26) }, 148*9b8f3200SMarcus Folkesson .left_offset = 2, 149*9b8f3200SMarcus Folkesson .top_offset = 3, 150*9b8f3200SMarcus Folkesson .rgb = true, 151*9b8f3200SMarcus Folkesson }; 152*9b8f3200SMarcus Folkesson 153*9b8f3200SMarcus Folkesson DEFINE_DRM_GEM_DMA_FOPS(st7735r_fops); 154*9b8f3200SMarcus Folkesson 155*9b8f3200SMarcus Folkesson static const struct drm_driver st7735r_driver = { 156*9b8f3200SMarcus Folkesson .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 157*9b8f3200SMarcus Folkesson .fops = &st7735r_fops, 158*9b8f3200SMarcus Folkesson DRM_GEM_DMA_DRIVER_OPS_VMAP, 159*9b8f3200SMarcus Folkesson DRM_FBDEV_DMA_DRIVER_OPS, 160*9b8f3200SMarcus Folkesson .debugfs_init = mipi_dbi_debugfs_init, 161*9b8f3200SMarcus Folkesson .name = "st7735r", 162*9b8f3200SMarcus Folkesson .desc = "Sitronix ST7735R", 163*9b8f3200SMarcus Folkesson .major = 1, 164*9b8f3200SMarcus Folkesson .minor = 0, 165*9b8f3200SMarcus Folkesson }; 166*9b8f3200SMarcus Folkesson 167*9b8f3200SMarcus Folkesson static const struct of_device_id st7735r_of_match[] = { 168*9b8f3200SMarcus Folkesson { .compatible = "jianda,jd-t18003-t01", .data = &jd_t18003_t01_cfg }, 169*9b8f3200SMarcus Folkesson { .compatible = "okaya,rh128128t", .data = &rh128128t_cfg }, 170*9b8f3200SMarcus Folkesson { }, 171*9b8f3200SMarcus Folkesson }; 172*9b8f3200SMarcus Folkesson MODULE_DEVICE_TABLE(of, st7735r_of_match); 173*9b8f3200SMarcus Folkesson 174*9b8f3200SMarcus Folkesson static const struct spi_device_id st7735r_id[] = { 175*9b8f3200SMarcus Folkesson { "jd-t18003-t01", (uintptr_t)&jd_t18003_t01_cfg }, 176*9b8f3200SMarcus Folkesson { "rh128128t", (uintptr_t)&rh128128t_cfg }, 177*9b8f3200SMarcus Folkesson { }, 178*9b8f3200SMarcus Folkesson }; 179*9b8f3200SMarcus Folkesson MODULE_DEVICE_TABLE(spi, st7735r_id); 180*9b8f3200SMarcus Folkesson 181*9b8f3200SMarcus Folkesson static int st7735r_probe(struct spi_device *spi) 182*9b8f3200SMarcus Folkesson { 183*9b8f3200SMarcus Folkesson struct device *dev = &spi->dev; 184*9b8f3200SMarcus Folkesson const struct st7735r_cfg *cfg; 185*9b8f3200SMarcus Folkesson struct mipi_dbi_dev *dbidev; 186*9b8f3200SMarcus Folkesson struct st7735r_priv *priv; 187*9b8f3200SMarcus Folkesson struct drm_device *drm; 188*9b8f3200SMarcus Folkesson struct mipi_dbi *dbi; 189*9b8f3200SMarcus Folkesson struct gpio_desc *dc; 190*9b8f3200SMarcus Folkesson u32 rotation = 0; 191*9b8f3200SMarcus Folkesson int ret; 192*9b8f3200SMarcus Folkesson 193*9b8f3200SMarcus Folkesson cfg = device_get_match_data(&spi->dev); 194*9b8f3200SMarcus Folkesson if (!cfg) 195*9b8f3200SMarcus Folkesson cfg = (void *)spi_get_device_id(spi)->driver_data; 196*9b8f3200SMarcus Folkesson 197*9b8f3200SMarcus Folkesson priv = devm_drm_dev_alloc(dev, &st7735r_driver, 198*9b8f3200SMarcus Folkesson struct st7735r_priv, dbidev.drm); 199*9b8f3200SMarcus Folkesson if (IS_ERR(priv)) 200*9b8f3200SMarcus Folkesson return PTR_ERR(priv); 201*9b8f3200SMarcus Folkesson 202*9b8f3200SMarcus Folkesson dbidev = &priv->dbidev; 203*9b8f3200SMarcus Folkesson priv->cfg = cfg; 204*9b8f3200SMarcus Folkesson 205*9b8f3200SMarcus Folkesson dbi = &dbidev->dbi; 206*9b8f3200SMarcus Folkesson drm = &dbidev->drm; 207*9b8f3200SMarcus Folkesson 208*9b8f3200SMarcus Folkesson dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 209*9b8f3200SMarcus Folkesson if (IS_ERR(dbi->reset)) 210*9b8f3200SMarcus Folkesson return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n"); 211*9b8f3200SMarcus Folkesson 212*9b8f3200SMarcus Folkesson dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW); 213*9b8f3200SMarcus Folkesson if (IS_ERR(dc)) 214*9b8f3200SMarcus Folkesson return dev_err_probe(dev, PTR_ERR(dc), "Failed to get GPIO 'dc'\n"); 215*9b8f3200SMarcus Folkesson 216*9b8f3200SMarcus Folkesson dbidev->backlight = devm_of_find_backlight(dev); 217*9b8f3200SMarcus Folkesson if (IS_ERR(dbidev->backlight)) 218*9b8f3200SMarcus Folkesson return PTR_ERR(dbidev->backlight); 219*9b8f3200SMarcus Folkesson 220*9b8f3200SMarcus Folkesson device_property_read_u32(dev, "rotation", &rotation); 221*9b8f3200SMarcus Folkesson 222*9b8f3200SMarcus Folkesson ret = mipi_dbi_spi_init(spi, dbi, dc); 223*9b8f3200SMarcus Folkesson if (ret) 224*9b8f3200SMarcus Folkesson return ret; 225*9b8f3200SMarcus Folkesson 226*9b8f3200SMarcus Folkesson if (cfg->write_only) 227*9b8f3200SMarcus Folkesson dbi->read_commands = NULL; 228*9b8f3200SMarcus Folkesson 229*9b8f3200SMarcus Folkesson dbidev->left_offset = cfg->left_offset; 230*9b8f3200SMarcus Folkesson dbidev->top_offset = cfg->top_offset; 231*9b8f3200SMarcus Folkesson 232*9b8f3200SMarcus Folkesson ret = mipi_dbi_dev_init(dbidev, &st7735r_pipe_funcs, &cfg->mode, 233*9b8f3200SMarcus Folkesson rotation); 234*9b8f3200SMarcus Folkesson if (ret) 235*9b8f3200SMarcus Folkesson return ret; 236*9b8f3200SMarcus Folkesson 237*9b8f3200SMarcus Folkesson drm_mode_config_reset(drm); 238*9b8f3200SMarcus Folkesson 239*9b8f3200SMarcus Folkesson ret = drm_dev_register(drm, 0); 240*9b8f3200SMarcus Folkesson if (ret) 241*9b8f3200SMarcus Folkesson return ret; 242*9b8f3200SMarcus Folkesson 243*9b8f3200SMarcus Folkesson spi_set_drvdata(spi, drm); 244*9b8f3200SMarcus Folkesson 245*9b8f3200SMarcus Folkesson drm_client_setup(drm, NULL); 246*9b8f3200SMarcus Folkesson 247*9b8f3200SMarcus Folkesson return 0; 248*9b8f3200SMarcus Folkesson } 249*9b8f3200SMarcus Folkesson 250*9b8f3200SMarcus Folkesson static void st7735r_remove(struct spi_device *spi) 251*9b8f3200SMarcus Folkesson { 252*9b8f3200SMarcus Folkesson struct drm_device *drm = spi_get_drvdata(spi); 253*9b8f3200SMarcus Folkesson 254*9b8f3200SMarcus Folkesson drm_dev_unplug(drm); 255*9b8f3200SMarcus Folkesson drm_atomic_helper_shutdown(drm); 256*9b8f3200SMarcus Folkesson } 257*9b8f3200SMarcus Folkesson 258*9b8f3200SMarcus Folkesson static void st7735r_shutdown(struct spi_device *spi) 259*9b8f3200SMarcus Folkesson { 260*9b8f3200SMarcus Folkesson drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 261*9b8f3200SMarcus Folkesson } 262*9b8f3200SMarcus Folkesson 263*9b8f3200SMarcus Folkesson static struct spi_driver st7735r_spi_driver = { 264*9b8f3200SMarcus Folkesson .driver = { 265*9b8f3200SMarcus Folkesson .name = "st7735r", 266*9b8f3200SMarcus Folkesson .of_match_table = st7735r_of_match, 267*9b8f3200SMarcus Folkesson }, 268*9b8f3200SMarcus Folkesson .id_table = st7735r_id, 269*9b8f3200SMarcus Folkesson .probe = st7735r_probe, 270*9b8f3200SMarcus Folkesson .remove = st7735r_remove, 271*9b8f3200SMarcus Folkesson .shutdown = st7735r_shutdown, 272*9b8f3200SMarcus Folkesson }; 273*9b8f3200SMarcus Folkesson module_spi_driver(st7735r_spi_driver); 274*9b8f3200SMarcus Folkesson 275*9b8f3200SMarcus Folkesson MODULE_DESCRIPTION("Sitronix ST7735R DRM driver"); 276*9b8f3200SMarcus Folkesson MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 277*9b8f3200SMarcus Folkesson MODULE_LICENSE("GPL"); 278