1043386a0SNoralf Trønnes // SPDX-License-Identifier: GPL-2.0+ 2043386a0SNoralf Trønnes /* 3d1d511d5SGeert Uytterhoeven * DRM driver for display panels connected to a Sitronix ST7715R or ST7735R 4d1d511d5SGeert Uytterhoeven * display controller in SPI mode. 5043386a0SNoralf Trønnes * 6043386a0SNoralf Trønnes * Copyright 2017 David Lechner <david@lechnology.com> 798823f37SGeert Uytterhoeven * Copyright (C) 2019 Glider bvba 8043386a0SNoralf Trønnes */ 9043386a0SNoralf Trønnes 10043386a0SNoralf Trønnes #include <linux/backlight.h> 11043386a0SNoralf Trønnes #include <linux/delay.h> 12043386a0SNoralf Trønnes #include <linux/dma-buf.h> 13043386a0SNoralf Trønnes #include <linux/gpio/consumer.h> 14043386a0SNoralf Trønnes #include <linux/module.h> 15043386a0SNoralf Trønnes #include <linux/property.h> 16043386a0SNoralf Trønnes #include <linux/spi/spi.h> 17043386a0SNoralf Trønnes #include <video/mipi_display.h> 18043386a0SNoralf Trønnes 19043386a0SNoralf Trønnes #include <drm/drm_atomic_helper.h> 20043386a0SNoralf Trønnes #include <drm/drm_drv.h> 21043386a0SNoralf Trønnes #include <drm/drm_fb_helper.h> 22043386a0SNoralf Trønnes #include <drm/drm_gem_cma_helper.h> 23043386a0SNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 24f5ad671bSDaniel Vetter #include <drm/drm_managed.h> 25043386a0SNoralf Trønnes #include <drm/drm_mipi_dbi.h> 26043386a0SNoralf Trønnes 27043386a0SNoralf Trønnes #define ST7735R_FRMCTR1 0xb1 28043386a0SNoralf Trønnes #define ST7735R_FRMCTR2 0xb2 29043386a0SNoralf Trønnes #define ST7735R_FRMCTR3 0xb3 30043386a0SNoralf Trønnes #define ST7735R_INVCTR 0xb4 31043386a0SNoralf Trønnes #define ST7735R_PWCTR1 0xc0 32043386a0SNoralf Trønnes #define ST7735R_PWCTR2 0xc1 33043386a0SNoralf Trønnes #define ST7735R_PWCTR3 0xc2 34043386a0SNoralf Trønnes #define ST7735R_PWCTR4 0xc3 35043386a0SNoralf Trønnes #define ST7735R_PWCTR5 0xc4 36043386a0SNoralf Trønnes #define ST7735R_VMCTR1 0xc5 37043386a0SNoralf Trønnes #define ST7735R_GAMCTRP1 0xe0 38043386a0SNoralf Trønnes #define ST7735R_GAMCTRN1 0xe1 39043386a0SNoralf Trønnes 40043386a0SNoralf Trønnes #define ST7735R_MY BIT(7) 41043386a0SNoralf Trønnes #define ST7735R_MX BIT(6) 42043386a0SNoralf Trønnes #define ST7735R_MV BIT(5) 4398823f37SGeert Uytterhoeven #define ST7735R_RGB BIT(3) 44043386a0SNoralf Trønnes 4598823f37SGeert Uytterhoeven struct st7735r_cfg { 4698823f37SGeert Uytterhoeven const struct drm_display_mode mode; 4798823f37SGeert Uytterhoeven unsigned int left_offset; 4898823f37SGeert Uytterhoeven unsigned int top_offset; 4998823f37SGeert Uytterhoeven unsigned int write_only:1; 5098823f37SGeert Uytterhoeven unsigned int rgb:1; /* RGB (vs. BGR) */ 5198823f37SGeert Uytterhoeven }; 5298823f37SGeert Uytterhoeven 5398823f37SGeert Uytterhoeven struct st7735r_priv { 5498823f37SGeert Uytterhoeven struct mipi_dbi_dev dbidev; /* Must be first for .release() */ 5598823f37SGeert Uytterhoeven const struct st7735r_cfg *cfg; 5698823f37SGeert Uytterhoeven }; 5798823f37SGeert Uytterhoeven 5898823f37SGeert Uytterhoeven static void st7735r_pipe_enable(struct drm_simple_display_pipe *pipe, 59043386a0SNoralf Trønnes struct drm_crtc_state *crtc_state, 60043386a0SNoralf Trønnes struct drm_plane_state *plane_state) 61043386a0SNoralf Trønnes { 62043386a0SNoralf Trønnes struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 6398823f37SGeert Uytterhoeven struct st7735r_priv *priv = container_of(dbidev, struct st7735r_priv, 6498823f37SGeert Uytterhoeven dbidev); 65043386a0SNoralf Trønnes struct mipi_dbi *dbi = &dbidev->dbi; 66043386a0SNoralf Trønnes int ret, idx; 67043386a0SNoralf Trønnes u8 addr_mode; 68043386a0SNoralf Trønnes 69043386a0SNoralf Trønnes if (!drm_dev_enter(pipe->crtc.dev, &idx)) 70043386a0SNoralf Trønnes return; 71043386a0SNoralf Trønnes 72043386a0SNoralf Trønnes DRM_DEBUG_KMS("\n"); 73043386a0SNoralf Trønnes 74043386a0SNoralf Trønnes ret = mipi_dbi_poweron_reset(dbidev); 75043386a0SNoralf Trønnes if (ret) 76043386a0SNoralf Trønnes goto out_exit; 77043386a0SNoralf Trønnes 78043386a0SNoralf Trønnes msleep(150); 79043386a0SNoralf Trønnes 80043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 81043386a0SNoralf Trønnes msleep(500); 82043386a0SNoralf Trønnes 83043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_FRMCTR1, 0x01, 0x2c, 0x2d); 84043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_FRMCTR2, 0x01, 0x2c, 0x2d); 85043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_FRMCTR3, 0x01, 0x2c, 0x2d, 0x01, 0x2c, 86043386a0SNoralf Trønnes 0x2d); 87043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_INVCTR, 0x07); 88043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_PWCTR1, 0xa2, 0x02, 0x84); 89043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_PWCTR2, 0xc5); 90043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_PWCTR3, 0x0a, 0x00); 91043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_PWCTR4, 0x8a, 0x2a); 92043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_PWCTR5, 0x8a, 0xee); 93043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_VMCTR1, 0x0e); 94043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE); 95043386a0SNoralf Trønnes switch (dbidev->rotation) { 96043386a0SNoralf Trønnes default: 97043386a0SNoralf Trønnes addr_mode = ST7735R_MX | ST7735R_MY; 98043386a0SNoralf Trønnes break; 99043386a0SNoralf Trønnes case 90: 100043386a0SNoralf Trønnes addr_mode = ST7735R_MX | ST7735R_MV; 101043386a0SNoralf Trønnes break; 102043386a0SNoralf Trønnes case 180: 103043386a0SNoralf Trønnes addr_mode = 0; 104043386a0SNoralf Trønnes break; 105043386a0SNoralf Trønnes case 270: 106043386a0SNoralf Trønnes addr_mode = ST7735R_MY | ST7735R_MV; 107043386a0SNoralf Trønnes break; 108043386a0SNoralf Trønnes } 10998823f37SGeert Uytterhoeven 11098823f37SGeert Uytterhoeven if (priv->cfg->rgb) 11198823f37SGeert Uytterhoeven addr_mode |= ST7735R_RGB; 11298823f37SGeert Uytterhoeven 113043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); 114043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, 115043386a0SNoralf Trønnes MIPI_DCS_PIXEL_FMT_16BIT); 116043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_GAMCTRP1, 0x02, 0x1c, 0x07, 0x12, 0x37, 117043386a0SNoralf Trønnes 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2b, 0x39, 0x00, 0x01, 118043386a0SNoralf Trønnes 0x03, 0x10); 119043386a0SNoralf Trønnes mipi_dbi_command(dbi, ST7735R_GAMCTRN1, 0x03, 0x1d, 0x07, 0x06, 0x2e, 120043386a0SNoralf Trønnes 0x2c, 0x29, 0x2d, 0x2e, 0x2e, 0x37, 0x3f, 0x00, 0x00, 121043386a0SNoralf Trønnes 0x02, 0x10); 122043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 123043386a0SNoralf Trønnes 124043386a0SNoralf Trønnes msleep(100); 125043386a0SNoralf Trønnes 126043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_ENTER_NORMAL_MODE); 127043386a0SNoralf Trønnes 128043386a0SNoralf Trønnes msleep(20); 129043386a0SNoralf Trønnes 130043386a0SNoralf Trønnes mipi_dbi_enable_flush(dbidev, crtc_state, plane_state); 131043386a0SNoralf Trønnes out_exit: 132043386a0SNoralf Trønnes drm_dev_exit(idx); 133043386a0SNoralf Trønnes } 134043386a0SNoralf Trønnes 13598823f37SGeert Uytterhoeven static const struct drm_simple_display_pipe_funcs st7735r_pipe_funcs = { 13698823f37SGeert Uytterhoeven .enable = st7735r_pipe_enable, 137043386a0SNoralf Trønnes .disable = mipi_dbi_pipe_disable, 138043386a0SNoralf Trønnes .update = mipi_dbi_pipe_update, 139043386a0SNoralf Trønnes .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, 140043386a0SNoralf Trønnes }; 141043386a0SNoralf Trønnes 14298823f37SGeert Uytterhoeven static const struct st7735r_cfg jd_t18003_t01_cfg = { 14398823f37SGeert Uytterhoeven .mode = { DRM_SIMPLE_MODE(128, 160, 28, 35) }, 14498823f37SGeert Uytterhoeven /* Cannot read from Adafruit 1.8" display via SPI */ 14598823f37SGeert Uytterhoeven .write_only = true, 14698823f37SGeert Uytterhoeven }; 14798823f37SGeert Uytterhoeven 148d1d511d5SGeert Uytterhoeven static const struct st7735r_cfg rh128128t_cfg = { 149d1d511d5SGeert Uytterhoeven .mode = { DRM_SIMPLE_MODE(128, 128, 25, 26) }, 150d1d511d5SGeert Uytterhoeven .left_offset = 2, 151d1d511d5SGeert Uytterhoeven .top_offset = 3, 152d1d511d5SGeert Uytterhoeven .rgb = true, 153043386a0SNoralf Trønnes }; 154043386a0SNoralf Trønnes 155043386a0SNoralf Trønnes DEFINE_DRM_GEM_CMA_FOPS(st7735r_fops); 156043386a0SNoralf Trønnes 157*70a59dd8SDaniel Vetter static const struct drm_driver st7735r_driver = { 158043386a0SNoralf Trønnes .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 159043386a0SNoralf Trønnes .fops = &st7735r_fops, 16006d66201SThomas Zimmermann DRM_GEM_CMA_DRIVER_OPS_VMAP, 161043386a0SNoralf Trønnes .debugfs_init = mipi_dbi_debugfs_init, 162043386a0SNoralf Trønnes .name = "st7735r", 163043386a0SNoralf Trønnes .desc = "Sitronix ST7735R", 164043386a0SNoralf Trønnes .date = "20171128", 165043386a0SNoralf Trønnes .major = 1, 166043386a0SNoralf Trønnes .minor = 0, 167043386a0SNoralf Trønnes }; 168043386a0SNoralf Trønnes 169043386a0SNoralf Trønnes static const struct of_device_id st7735r_of_match[] = { 17098823f37SGeert Uytterhoeven { .compatible = "jianda,jd-t18003-t01", .data = &jd_t18003_t01_cfg }, 171d1d511d5SGeert Uytterhoeven { .compatible = "okaya,rh128128t", .data = &rh128128t_cfg }, 172043386a0SNoralf Trønnes { }, 173043386a0SNoralf Trønnes }; 174043386a0SNoralf Trønnes MODULE_DEVICE_TABLE(of, st7735r_of_match); 175043386a0SNoralf Trønnes 176043386a0SNoralf Trønnes static const struct spi_device_id st7735r_id[] = { 17798823f37SGeert Uytterhoeven { "jd-t18003-t01", (uintptr_t)&jd_t18003_t01_cfg }, 178043386a0SNoralf Trønnes { }, 179043386a0SNoralf Trønnes }; 180043386a0SNoralf Trønnes MODULE_DEVICE_TABLE(spi, st7735r_id); 181043386a0SNoralf Trønnes 182043386a0SNoralf Trønnes static int st7735r_probe(struct spi_device *spi) 183043386a0SNoralf Trønnes { 184043386a0SNoralf Trønnes struct device *dev = &spi->dev; 18598823f37SGeert Uytterhoeven const struct st7735r_cfg *cfg; 186043386a0SNoralf Trønnes struct mipi_dbi_dev *dbidev; 18798823f37SGeert Uytterhoeven struct st7735r_priv *priv; 188043386a0SNoralf Trønnes struct drm_device *drm; 189043386a0SNoralf Trønnes struct mipi_dbi *dbi; 190043386a0SNoralf Trønnes struct gpio_desc *dc; 191043386a0SNoralf Trønnes u32 rotation = 0; 192043386a0SNoralf Trønnes int ret; 193043386a0SNoralf Trønnes 1945703d6aeSAndy Shevchenko cfg = device_get_match_data(&spi->dev); 19598823f37SGeert Uytterhoeven if (!cfg) 19698823f37SGeert Uytterhoeven cfg = (void *)spi_get_device_id(spi)->driver_data; 19798823f37SGeert Uytterhoeven 19814877bc7SDaniel Vetter priv = devm_drm_dev_alloc(dev, &st7735r_driver, 19914877bc7SDaniel Vetter struct st7735r_priv, dbidev.drm); 20014877bc7SDaniel Vetter if (IS_ERR(priv)) 20114877bc7SDaniel Vetter return PTR_ERR(priv); 202043386a0SNoralf Trønnes 20398823f37SGeert Uytterhoeven dbidev = &priv->dbidev; 20498823f37SGeert Uytterhoeven priv->cfg = cfg; 20598823f37SGeert Uytterhoeven 206043386a0SNoralf Trønnes dbi = &dbidev->dbi; 207043386a0SNoralf Trønnes drm = &dbidev->drm; 208043386a0SNoralf Trønnes 209043386a0SNoralf Trønnes dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 210043386a0SNoralf Trønnes if (IS_ERR(dbi->reset)) { 211043386a0SNoralf Trønnes DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); 212043386a0SNoralf Trønnes return PTR_ERR(dbi->reset); 213043386a0SNoralf Trønnes } 214043386a0SNoralf Trønnes 215043386a0SNoralf Trønnes dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW); 216043386a0SNoralf Trønnes if (IS_ERR(dc)) { 217043386a0SNoralf Trønnes DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); 218043386a0SNoralf Trønnes return PTR_ERR(dc); 219043386a0SNoralf Trønnes } 220043386a0SNoralf Trønnes 221043386a0SNoralf Trønnes dbidev->backlight = devm_of_find_backlight(dev); 222043386a0SNoralf Trønnes if (IS_ERR(dbidev->backlight)) 223043386a0SNoralf Trønnes return PTR_ERR(dbidev->backlight); 224043386a0SNoralf Trønnes 225043386a0SNoralf Trønnes device_property_read_u32(dev, "rotation", &rotation); 226043386a0SNoralf Trønnes 227043386a0SNoralf Trønnes ret = mipi_dbi_spi_init(spi, dbi, dc); 228043386a0SNoralf Trønnes if (ret) 229043386a0SNoralf Trønnes return ret; 230043386a0SNoralf Trønnes 23198823f37SGeert Uytterhoeven if (cfg->write_only) 232043386a0SNoralf Trønnes dbi->read_commands = NULL; 233043386a0SNoralf Trønnes 23498823f37SGeert Uytterhoeven dbidev->left_offset = cfg->left_offset; 23598823f37SGeert Uytterhoeven dbidev->top_offset = cfg->top_offset; 23698823f37SGeert Uytterhoeven 23798823f37SGeert Uytterhoeven ret = mipi_dbi_dev_init(dbidev, &st7735r_pipe_funcs, &cfg->mode, 23898823f37SGeert Uytterhoeven rotation); 239043386a0SNoralf Trønnes if (ret) 240043386a0SNoralf Trønnes return ret; 241043386a0SNoralf Trønnes 242043386a0SNoralf Trønnes drm_mode_config_reset(drm); 243043386a0SNoralf Trønnes 244043386a0SNoralf Trønnes ret = drm_dev_register(drm, 0); 245043386a0SNoralf Trønnes if (ret) 246043386a0SNoralf Trønnes return ret; 247043386a0SNoralf Trønnes 248043386a0SNoralf Trønnes spi_set_drvdata(spi, drm); 249043386a0SNoralf Trønnes 250043386a0SNoralf Trønnes drm_fbdev_generic_setup(drm, 0); 251043386a0SNoralf Trønnes 252043386a0SNoralf Trønnes return 0; 253043386a0SNoralf Trønnes } 254043386a0SNoralf Trønnes 255043386a0SNoralf Trønnes static int st7735r_remove(struct spi_device *spi) 256043386a0SNoralf Trønnes { 257043386a0SNoralf Trønnes struct drm_device *drm = spi_get_drvdata(spi); 258043386a0SNoralf Trønnes 259043386a0SNoralf Trønnes drm_dev_unplug(drm); 260043386a0SNoralf Trønnes drm_atomic_helper_shutdown(drm); 261043386a0SNoralf Trønnes 262043386a0SNoralf Trønnes return 0; 263043386a0SNoralf Trønnes } 264043386a0SNoralf Trønnes 265043386a0SNoralf Trønnes static void st7735r_shutdown(struct spi_device *spi) 266043386a0SNoralf Trønnes { 267043386a0SNoralf Trønnes drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 268043386a0SNoralf Trønnes } 269043386a0SNoralf Trønnes 270043386a0SNoralf Trønnes static struct spi_driver st7735r_spi_driver = { 271043386a0SNoralf Trønnes .driver = { 272043386a0SNoralf Trønnes .name = "st7735r", 273043386a0SNoralf Trønnes .of_match_table = st7735r_of_match, 274043386a0SNoralf Trønnes }, 275043386a0SNoralf Trønnes .id_table = st7735r_id, 276043386a0SNoralf Trønnes .probe = st7735r_probe, 277043386a0SNoralf Trønnes .remove = st7735r_remove, 278043386a0SNoralf Trønnes .shutdown = st7735r_shutdown, 279043386a0SNoralf Trønnes }; 280043386a0SNoralf Trønnes module_spi_driver(st7735r_spi_driver); 281043386a0SNoralf Trønnes 282043386a0SNoralf Trønnes MODULE_DESCRIPTION("Sitronix ST7735R DRM driver"); 283043386a0SNoralf Trønnes MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 284043386a0SNoralf Trønnes MODULE_LICENSE("GPL"); 285