1043386a0SNoralf Trønnes // SPDX-License-Identifier: GPL-2.0+ 2043386a0SNoralf Trønnes /* 3043386a0SNoralf Trønnes * DRM driver for Ilitek ILI9341 panels 4043386a0SNoralf Trønnes * 5043386a0SNoralf Trønnes * Copyright 2018 David Lechner <david@lechnology.com> 6043386a0SNoralf Trønnes * 7043386a0SNoralf Trønnes * Based on mi0283qt.c: 8043386a0SNoralf Trønnes * Copyright 2016 Noralf Trønnes 9043386a0SNoralf Trønnes */ 10043386a0SNoralf Trønnes 11043386a0SNoralf Trønnes #include <linux/backlight.h> 12043386a0SNoralf Trønnes #include <linux/delay.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 18043386a0SNoralf Trønnes #include <drm/drm_atomic_helper.h> 19043386a0SNoralf Trønnes #include <drm/drm_drv.h> 20043386a0SNoralf Trønnes #include <drm/drm_fb_helper.h> 21043386a0SNoralf Trønnes #include <drm/drm_gem_cma_helper.h> 22043386a0SNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 23f5ad671bSDaniel Vetter #include <drm/drm_managed.h> 24043386a0SNoralf Trønnes #include <drm/drm_mipi_dbi.h> 25043386a0SNoralf Trønnes #include <drm/drm_modeset_helper.h> 26043386a0SNoralf Trønnes #include <video/mipi_display.h> 27043386a0SNoralf Trønnes 28043386a0SNoralf Trønnes #define ILI9341_FRMCTR1 0xb1 29043386a0SNoralf Trønnes #define ILI9341_DISCTRL 0xb6 30043386a0SNoralf Trønnes #define ILI9341_ETMOD 0xb7 31043386a0SNoralf Trønnes 32043386a0SNoralf Trønnes #define ILI9341_PWCTRL1 0xc0 33043386a0SNoralf Trønnes #define ILI9341_PWCTRL2 0xc1 34043386a0SNoralf Trønnes #define ILI9341_VMCTRL1 0xc5 35043386a0SNoralf Trønnes #define ILI9341_VMCTRL2 0xc7 36043386a0SNoralf Trønnes #define ILI9341_PWCTRLA 0xcb 37043386a0SNoralf Trønnes #define ILI9341_PWCTRLB 0xcf 38043386a0SNoralf Trønnes 39043386a0SNoralf Trønnes #define ILI9341_PGAMCTRL 0xe0 40043386a0SNoralf Trønnes #define ILI9341_NGAMCTRL 0xe1 41043386a0SNoralf Trønnes #define ILI9341_DTCTRLA 0xe8 42043386a0SNoralf Trønnes #define ILI9341_DTCTRLB 0xea 43043386a0SNoralf Trønnes #define ILI9341_PWRSEQ 0xed 44043386a0SNoralf Trønnes 45043386a0SNoralf Trønnes #define ILI9341_EN3GAM 0xf2 46043386a0SNoralf Trønnes #define ILI9341_PUMPCTRL 0xf7 47043386a0SNoralf Trønnes 48043386a0SNoralf Trønnes #define ILI9341_MADCTL_BGR BIT(3) 49043386a0SNoralf Trønnes #define ILI9341_MADCTL_MV BIT(5) 50043386a0SNoralf Trønnes #define ILI9341_MADCTL_MX BIT(6) 51043386a0SNoralf Trønnes #define ILI9341_MADCTL_MY BIT(7) 52043386a0SNoralf Trønnes 53043386a0SNoralf Trønnes static void yx240qv29_enable(struct drm_simple_display_pipe *pipe, 54043386a0SNoralf Trønnes struct drm_crtc_state *crtc_state, 55043386a0SNoralf Trønnes struct drm_plane_state *plane_state) 56043386a0SNoralf Trønnes { 57043386a0SNoralf Trønnes struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 58043386a0SNoralf Trønnes struct mipi_dbi *dbi = &dbidev->dbi; 59043386a0SNoralf Trønnes u8 addr_mode; 60043386a0SNoralf Trønnes int ret, idx; 61043386a0SNoralf Trønnes 62043386a0SNoralf Trønnes if (!drm_dev_enter(pipe->crtc.dev, &idx)) 63043386a0SNoralf Trønnes return; 64043386a0SNoralf Trønnes 65043386a0SNoralf Trønnes DRM_DEBUG_KMS("\n"); 66043386a0SNoralf Trønnes 67043386a0SNoralf Trønnes ret = mipi_dbi_poweron_conditional_reset(dbidev); 68043386a0SNoralf Trønnes if (ret < 0) 69043386a0SNoralf Trønnes goto out_exit; 70043386a0SNoralf Trønnes if (ret == 1) 71043386a0SNoralf Trønnes goto out_enable; 72043386a0SNoralf Trønnes 73043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 74043386a0SNoralf Trønnes 75043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PWCTRLB, 0x00, 0xc1, 0x30); 76043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PWRSEQ, 0x64, 0x03, 0x12, 0x81); 77043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_DTCTRLA, 0x85, 0x00, 0x78); 78043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PWCTRLA, 0x39, 0x2c, 0x00, 0x34, 0x02); 79043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PUMPCTRL, 0x20); 80043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_DTCTRLB, 0x00, 0x00); 81043386a0SNoralf Trønnes 82043386a0SNoralf Trønnes /* Power Control */ 83043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PWCTRL1, 0x23); 84043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PWCTRL2, 0x10); 85043386a0SNoralf Trønnes /* VCOM */ 86043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_VMCTRL1, 0x3e, 0x28); 87043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_VMCTRL2, 0x86); 88043386a0SNoralf Trønnes 89043386a0SNoralf Trønnes /* Memory Access Control */ 90043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT); 91043386a0SNoralf Trønnes 92043386a0SNoralf Trønnes /* Frame Rate */ 93043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_FRMCTR1, 0x00, 0x1b); 94043386a0SNoralf Trønnes 95043386a0SNoralf Trønnes /* Gamma */ 96043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_EN3GAM, 0x00); 97043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01); 98043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PGAMCTRL, 99043386a0SNoralf Trønnes 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 100043386a0SNoralf Trønnes 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00); 101043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_NGAMCTRL, 102043386a0SNoralf Trønnes 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, 103043386a0SNoralf Trønnes 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f); 104043386a0SNoralf Trønnes 105043386a0SNoralf Trønnes /* DDRAM */ 106043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_ETMOD, 0x07); 107043386a0SNoralf Trønnes 108043386a0SNoralf Trønnes /* Display */ 109043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_DISCTRL, 0x08, 0x82, 0x27, 0x00); 110043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 111043386a0SNoralf Trønnes msleep(100); 112043386a0SNoralf Trønnes 113043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 114043386a0SNoralf Trønnes msleep(100); 115043386a0SNoralf Trønnes 116043386a0SNoralf Trønnes out_enable: 117043386a0SNoralf Trønnes switch (dbidev->rotation) { 118043386a0SNoralf Trønnes default: 119043386a0SNoralf Trønnes addr_mode = ILI9341_MADCTL_MX; 120043386a0SNoralf Trønnes break; 121043386a0SNoralf Trønnes case 90: 122043386a0SNoralf Trønnes addr_mode = ILI9341_MADCTL_MV; 123043386a0SNoralf Trønnes break; 124043386a0SNoralf Trønnes case 180: 125043386a0SNoralf Trønnes addr_mode = ILI9341_MADCTL_MY; 126043386a0SNoralf Trønnes break; 127043386a0SNoralf Trønnes case 270: 128043386a0SNoralf Trønnes addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY | 129043386a0SNoralf Trønnes ILI9341_MADCTL_MX; 130043386a0SNoralf Trønnes break; 131043386a0SNoralf Trønnes } 132043386a0SNoralf Trønnes addr_mode |= ILI9341_MADCTL_BGR; 133043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); 134043386a0SNoralf Trønnes mipi_dbi_enable_flush(dbidev, crtc_state, plane_state); 135043386a0SNoralf Trønnes out_exit: 136043386a0SNoralf Trønnes drm_dev_exit(idx); 137043386a0SNoralf Trønnes } 138043386a0SNoralf Trønnes 139043386a0SNoralf Trønnes static const struct drm_simple_display_pipe_funcs ili9341_pipe_funcs = { 140043386a0SNoralf Trønnes .enable = yx240qv29_enable, 141043386a0SNoralf Trønnes .disable = mipi_dbi_pipe_disable, 142043386a0SNoralf Trønnes .update = mipi_dbi_pipe_update, 143043386a0SNoralf Trønnes .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, 144043386a0SNoralf Trønnes }; 145043386a0SNoralf Trønnes 146043386a0SNoralf Trønnes static const struct drm_display_mode yx240qv29_mode = { 147043386a0SNoralf Trønnes DRM_SIMPLE_MODE(240, 320, 37, 49), 148043386a0SNoralf Trønnes }; 149043386a0SNoralf Trønnes 150043386a0SNoralf Trønnes DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops); 151043386a0SNoralf Trønnes 152*70a59dd8SDaniel Vetter static const struct drm_driver ili9341_driver = { 153043386a0SNoralf Trønnes .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 154043386a0SNoralf Trønnes .fops = &ili9341_fops, 15506d66201SThomas Zimmermann DRM_GEM_CMA_DRIVER_OPS_VMAP, 156043386a0SNoralf Trønnes .debugfs_init = mipi_dbi_debugfs_init, 157043386a0SNoralf Trønnes .name = "ili9341", 158043386a0SNoralf Trønnes .desc = "Ilitek ILI9341", 159043386a0SNoralf Trønnes .date = "20180514", 160043386a0SNoralf Trønnes .major = 1, 161043386a0SNoralf Trønnes .minor = 0, 162043386a0SNoralf Trønnes }; 163043386a0SNoralf Trønnes 164043386a0SNoralf Trønnes static const struct of_device_id ili9341_of_match[] = { 165043386a0SNoralf Trønnes { .compatible = "adafruit,yx240qv29" }, 166043386a0SNoralf Trønnes { } 167043386a0SNoralf Trønnes }; 168043386a0SNoralf Trønnes MODULE_DEVICE_TABLE(of, ili9341_of_match); 169043386a0SNoralf Trønnes 170043386a0SNoralf Trønnes static const struct spi_device_id ili9341_id[] = { 171043386a0SNoralf Trønnes { "yx240qv29", 0 }, 172043386a0SNoralf Trønnes { } 173043386a0SNoralf Trønnes }; 174043386a0SNoralf Trønnes MODULE_DEVICE_TABLE(spi, ili9341_id); 175043386a0SNoralf Trønnes 176043386a0SNoralf Trønnes static int ili9341_probe(struct spi_device *spi) 177043386a0SNoralf Trønnes { 178043386a0SNoralf Trønnes struct device *dev = &spi->dev; 179043386a0SNoralf Trønnes struct mipi_dbi_dev *dbidev; 180043386a0SNoralf Trønnes struct drm_device *drm; 181043386a0SNoralf Trønnes struct mipi_dbi *dbi; 182043386a0SNoralf Trønnes struct gpio_desc *dc; 183043386a0SNoralf Trønnes u32 rotation = 0; 184043386a0SNoralf Trønnes int ret; 185043386a0SNoralf Trønnes 1865301e305SDaniel Vetter dbidev = devm_drm_dev_alloc(dev, &ili9341_driver, 1875301e305SDaniel Vetter struct mipi_dbi_dev, drm); 1885301e305SDaniel Vetter if (IS_ERR(dbidev)) 1895301e305SDaniel Vetter return PTR_ERR(dbidev); 190043386a0SNoralf Trønnes 191043386a0SNoralf Trønnes dbi = &dbidev->dbi; 192043386a0SNoralf Trønnes drm = &dbidev->drm; 193043386a0SNoralf Trønnes 194043386a0SNoralf Trønnes dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 195043386a0SNoralf Trønnes if (IS_ERR(dbi->reset)) { 196043386a0SNoralf Trønnes DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); 197043386a0SNoralf Trønnes return PTR_ERR(dbi->reset); 198043386a0SNoralf Trønnes } 199043386a0SNoralf Trønnes 200043386a0SNoralf Trønnes dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); 201043386a0SNoralf Trønnes if (IS_ERR(dc)) { 202043386a0SNoralf Trønnes DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); 203043386a0SNoralf Trønnes return PTR_ERR(dc); 204043386a0SNoralf Trønnes } 205043386a0SNoralf Trønnes 206043386a0SNoralf Trønnes dbidev->backlight = devm_of_find_backlight(dev); 207043386a0SNoralf Trønnes if (IS_ERR(dbidev->backlight)) 208043386a0SNoralf Trønnes return PTR_ERR(dbidev->backlight); 209043386a0SNoralf Trønnes 210043386a0SNoralf Trønnes device_property_read_u32(dev, "rotation", &rotation); 211043386a0SNoralf Trønnes 212043386a0SNoralf Trønnes ret = mipi_dbi_spi_init(spi, dbi, dc); 213043386a0SNoralf Trønnes if (ret) 214043386a0SNoralf Trønnes return ret; 215043386a0SNoralf Trønnes 216043386a0SNoralf Trønnes ret = mipi_dbi_dev_init(dbidev, &ili9341_pipe_funcs, &yx240qv29_mode, rotation); 217043386a0SNoralf Trønnes if (ret) 218043386a0SNoralf Trønnes return ret; 219043386a0SNoralf Trønnes 220043386a0SNoralf Trønnes drm_mode_config_reset(drm); 221043386a0SNoralf Trønnes 222043386a0SNoralf Trønnes ret = drm_dev_register(drm, 0); 223043386a0SNoralf Trønnes if (ret) 224043386a0SNoralf Trønnes return ret; 225043386a0SNoralf Trønnes 226043386a0SNoralf Trønnes spi_set_drvdata(spi, drm); 227043386a0SNoralf Trønnes 228043386a0SNoralf Trønnes drm_fbdev_generic_setup(drm, 0); 229043386a0SNoralf Trønnes 230043386a0SNoralf Trønnes return 0; 231043386a0SNoralf Trønnes } 232043386a0SNoralf Trønnes 233043386a0SNoralf Trønnes static int ili9341_remove(struct spi_device *spi) 234043386a0SNoralf Trønnes { 235043386a0SNoralf Trønnes struct drm_device *drm = spi_get_drvdata(spi); 236043386a0SNoralf Trønnes 237043386a0SNoralf Trønnes drm_dev_unplug(drm); 238043386a0SNoralf Trønnes drm_atomic_helper_shutdown(drm); 239043386a0SNoralf Trønnes 240043386a0SNoralf Trønnes return 0; 241043386a0SNoralf Trønnes } 242043386a0SNoralf Trønnes 243043386a0SNoralf Trønnes static void ili9341_shutdown(struct spi_device *spi) 244043386a0SNoralf Trønnes { 245043386a0SNoralf Trønnes drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 246043386a0SNoralf Trønnes } 247043386a0SNoralf Trønnes 248043386a0SNoralf Trønnes static struct spi_driver ili9341_spi_driver = { 249043386a0SNoralf Trønnes .driver = { 250043386a0SNoralf Trønnes .name = "ili9341", 251043386a0SNoralf Trønnes .of_match_table = ili9341_of_match, 252043386a0SNoralf Trønnes }, 253043386a0SNoralf Trønnes .id_table = ili9341_id, 254043386a0SNoralf Trønnes .probe = ili9341_probe, 255043386a0SNoralf Trønnes .remove = ili9341_remove, 256043386a0SNoralf Trønnes .shutdown = ili9341_shutdown, 257043386a0SNoralf Trønnes }; 258043386a0SNoralf Trønnes module_spi_driver(ili9341_spi_driver); 259043386a0SNoralf Trønnes 260043386a0SNoralf Trønnes MODULE_DESCRIPTION("Ilitek ILI9341 DRM driver"); 261043386a0SNoralf Trønnes MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 262043386a0SNoralf Trønnes MODULE_LICENSE("GPL"); 263