1*043386a0SNoralf Trønnes // SPDX-License-Identifier: GPL-2.0+ 2*043386a0SNoralf Trønnes /* 3*043386a0SNoralf Trønnes * DRM driver for Ilitek ILI9341 panels 4*043386a0SNoralf Trønnes * 5*043386a0SNoralf Trønnes * Copyright 2018 David Lechner <david@lechnology.com> 6*043386a0SNoralf Trønnes * 7*043386a0SNoralf Trønnes * Based on mi0283qt.c: 8*043386a0SNoralf Trønnes * Copyright 2016 Noralf Trønnes 9*043386a0SNoralf Trønnes */ 10*043386a0SNoralf Trønnes 11*043386a0SNoralf Trønnes #include <linux/backlight.h> 12*043386a0SNoralf Trønnes #include <linux/delay.h> 13*043386a0SNoralf Trønnes #include <linux/gpio/consumer.h> 14*043386a0SNoralf Trønnes #include <linux/module.h> 15*043386a0SNoralf Trønnes #include <linux/property.h> 16*043386a0SNoralf Trønnes #include <linux/spi/spi.h> 17*043386a0SNoralf Trønnes 18*043386a0SNoralf Trønnes #include <drm/drm_atomic_helper.h> 19*043386a0SNoralf Trønnes #include <drm/drm_drv.h> 20*043386a0SNoralf Trønnes #include <drm/drm_fb_helper.h> 21*043386a0SNoralf Trønnes #include <drm/drm_gem_cma_helper.h> 22*043386a0SNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 23*043386a0SNoralf Trønnes #include <drm/drm_mipi_dbi.h> 24*043386a0SNoralf Trønnes #include <drm/drm_modeset_helper.h> 25*043386a0SNoralf Trønnes #include <video/mipi_display.h> 26*043386a0SNoralf Trønnes 27*043386a0SNoralf Trønnes #define ILI9341_FRMCTR1 0xb1 28*043386a0SNoralf Trønnes #define ILI9341_DISCTRL 0xb6 29*043386a0SNoralf Trønnes #define ILI9341_ETMOD 0xb7 30*043386a0SNoralf Trønnes 31*043386a0SNoralf Trønnes #define ILI9341_PWCTRL1 0xc0 32*043386a0SNoralf Trønnes #define ILI9341_PWCTRL2 0xc1 33*043386a0SNoralf Trønnes #define ILI9341_VMCTRL1 0xc5 34*043386a0SNoralf Trønnes #define ILI9341_VMCTRL2 0xc7 35*043386a0SNoralf Trønnes #define ILI9341_PWCTRLA 0xcb 36*043386a0SNoralf Trønnes #define ILI9341_PWCTRLB 0xcf 37*043386a0SNoralf Trønnes 38*043386a0SNoralf Trønnes #define ILI9341_PGAMCTRL 0xe0 39*043386a0SNoralf Trønnes #define ILI9341_NGAMCTRL 0xe1 40*043386a0SNoralf Trønnes #define ILI9341_DTCTRLA 0xe8 41*043386a0SNoralf Trønnes #define ILI9341_DTCTRLB 0xea 42*043386a0SNoralf Trønnes #define ILI9341_PWRSEQ 0xed 43*043386a0SNoralf Trønnes 44*043386a0SNoralf Trønnes #define ILI9341_EN3GAM 0xf2 45*043386a0SNoralf Trønnes #define ILI9341_PUMPCTRL 0xf7 46*043386a0SNoralf Trønnes 47*043386a0SNoralf Trønnes #define ILI9341_MADCTL_BGR BIT(3) 48*043386a0SNoralf Trønnes #define ILI9341_MADCTL_MV BIT(5) 49*043386a0SNoralf Trønnes #define ILI9341_MADCTL_MX BIT(6) 50*043386a0SNoralf Trønnes #define ILI9341_MADCTL_MY BIT(7) 51*043386a0SNoralf Trønnes 52*043386a0SNoralf Trønnes static void yx240qv29_enable(struct drm_simple_display_pipe *pipe, 53*043386a0SNoralf Trønnes struct drm_crtc_state *crtc_state, 54*043386a0SNoralf Trønnes struct drm_plane_state *plane_state) 55*043386a0SNoralf Trønnes { 56*043386a0SNoralf Trønnes struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 57*043386a0SNoralf Trønnes struct mipi_dbi *dbi = &dbidev->dbi; 58*043386a0SNoralf Trønnes u8 addr_mode; 59*043386a0SNoralf Trønnes int ret, idx; 60*043386a0SNoralf Trønnes 61*043386a0SNoralf Trønnes if (!drm_dev_enter(pipe->crtc.dev, &idx)) 62*043386a0SNoralf Trønnes return; 63*043386a0SNoralf Trønnes 64*043386a0SNoralf Trønnes DRM_DEBUG_KMS("\n"); 65*043386a0SNoralf Trønnes 66*043386a0SNoralf Trønnes ret = mipi_dbi_poweron_conditional_reset(dbidev); 67*043386a0SNoralf Trønnes if (ret < 0) 68*043386a0SNoralf Trønnes goto out_exit; 69*043386a0SNoralf Trønnes if (ret == 1) 70*043386a0SNoralf Trønnes goto out_enable; 71*043386a0SNoralf Trønnes 72*043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 73*043386a0SNoralf Trønnes 74*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PWCTRLB, 0x00, 0xc1, 0x30); 75*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PWRSEQ, 0x64, 0x03, 0x12, 0x81); 76*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_DTCTRLA, 0x85, 0x00, 0x78); 77*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PWCTRLA, 0x39, 0x2c, 0x00, 0x34, 0x02); 78*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PUMPCTRL, 0x20); 79*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_DTCTRLB, 0x00, 0x00); 80*043386a0SNoralf Trønnes 81*043386a0SNoralf Trønnes /* Power Control */ 82*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PWCTRL1, 0x23); 83*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PWCTRL2, 0x10); 84*043386a0SNoralf Trønnes /* VCOM */ 85*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_VMCTRL1, 0x3e, 0x28); 86*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_VMCTRL2, 0x86); 87*043386a0SNoralf Trønnes 88*043386a0SNoralf Trønnes /* Memory Access Control */ 89*043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT); 90*043386a0SNoralf Trønnes 91*043386a0SNoralf Trønnes /* Frame Rate */ 92*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_FRMCTR1, 0x00, 0x1b); 93*043386a0SNoralf Trønnes 94*043386a0SNoralf Trønnes /* Gamma */ 95*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_EN3GAM, 0x00); 96*043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01); 97*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_PGAMCTRL, 98*043386a0SNoralf Trønnes 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 99*043386a0SNoralf Trønnes 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00); 100*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_NGAMCTRL, 101*043386a0SNoralf Trønnes 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, 102*043386a0SNoralf Trønnes 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f); 103*043386a0SNoralf Trønnes 104*043386a0SNoralf Trønnes /* DDRAM */ 105*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_ETMOD, 0x07); 106*043386a0SNoralf Trønnes 107*043386a0SNoralf Trønnes /* Display */ 108*043386a0SNoralf Trønnes mipi_dbi_command(dbi, ILI9341_DISCTRL, 0x08, 0x82, 0x27, 0x00); 109*043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 110*043386a0SNoralf Trønnes msleep(100); 111*043386a0SNoralf Trønnes 112*043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 113*043386a0SNoralf Trønnes msleep(100); 114*043386a0SNoralf Trønnes 115*043386a0SNoralf Trønnes out_enable: 116*043386a0SNoralf Trønnes switch (dbidev->rotation) { 117*043386a0SNoralf Trønnes default: 118*043386a0SNoralf Trønnes addr_mode = ILI9341_MADCTL_MX; 119*043386a0SNoralf Trønnes break; 120*043386a0SNoralf Trønnes case 90: 121*043386a0SNoralf Trønnes addr_mode = ILI9341_MADCTL_MV; 122*043386a0SNoralf Trønnes break; 123*043386a0SNoralf Trønnes case 180: 124*043386a0SNoralf Trønnes addr_mode = ILI9341_MADCTL_MY; 125*043386a0SNoralf Trønnes break; 126*043386a0SNoralf Trønnes case 270: 127*043386a0SNoralf Trønnes addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY | 128*043386a0SNoralf Trønnes ILI9341_MADCTL_MX; 129*043386a0SNoralf Trønnes break; 130*043386a0SNoralf Trønnes } 131*043386a0SNoralf Trønnes addr_mode |= ILI9341_MADCTL_BGR; 132*043386a0SNoralf Trønnes mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); 133*043386a0SNoralf Trønnes mipi_dbi_enable_flush(dbidev, crtc_state, plane_state); 134*043386a0SNoralf Trønnes out_exit: 135*043386a0SNoralf Trønnes drm_dev_exit(idx); 136*043386a0SNoralf Trønnes } 137*043386a0SNoralf Trønnes 138*043386a0SNoralf Trønnes static const struct drm_simple_display_pipe_funcs ili9341_pipe_funcs = { 139*043386a0SNoralf Trønnes .enable = yx240qv29_enable, 140*043386a0SNoralf Trønnes .disable = mipi_dbi_pipe_disable, 141*043386a0SNoralf Trønnes .update = mipi_dbi_pipe_update, 142*043386a0SNoralf Trønnes .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, 143*043386a0SNoralf Trønnes }; 144*043386a0SNoralf Trønnes 145*043386a0SNoralf Trønnes static const struct drm_display_mode yx240qv29_mode = { 146*043386a0SNoralf Trønnes DRM_SIMPLE_MODE(240, 320, 37, 49), 147*043386a0SNoralf Trønnes }; 148*043386a0SNoralf Trønnes 149*043386a0SNoralf Trønnes DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops); 150*043386a0SNoralf Trønnes 151*043386a0SNoralf Trønnes static struct drm_driver ili9341_driver = { 152*043386a0SNoralf Trønnes .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 153*043386a0SNoralf Trønnes .fops = &ili9341_fops, 154*043386a0SNoralf Trønnes .release = mipi_dbi_release, 155*043386a0SNoralf Trønnes DRM_GEM_CMA_VMAP_DRIVER_OPS, 156*043386a0SNoralf Trønnes .debugfs_init = mipi_dbi_debugfs_init, 157*043386a0SNoralf Trønnes .name = "ili9341", 158*043386a0SNoralf Trønnes .desc = "Ilitek ILI9341", 159*043386a0SNoralf Trønnes .date = "20180514", 160*043386a0SNoralf Trønnes .major = 1, 161*043386a0SNoralf Trønnes .minor = 0, 162*043386a0SNoralf Trønnes }; 163*043386a0SNoralf Trønnes 164*043386a0SNoralf Trønnes static const struct of_device_id ili9341_of_match[] = { 165*043386a0SNoralf Trønnes { .compatible = "adafruit,yx240qv29" }, 166*043386a0SNoralf Trønnes { } 167*043386a0SNoralf Trønnes }; 168*043386a0SNoralf Trønnes MODULE_DEVICE_TABLE(of, ili9341_of_match); 169*043386a0SNoralf Trønnes 170*043386a0SNoralf Trønnes static const struct spi_device_id ili9341_id[] = { 171*043386a0SNoralf Trønnes { "yx240qv29", 0 }, 172*043386a0SNoralf Trønnes { } 173*043386a0SNoralf Trønnes }; 174*043386a0SNoralf Trønnes MODULE_DEVICE_TABLE(spi, ili9341_id); 175*043386a0SNoralf Trønnes 176*043386a0SNoralf Trønnes static int ili9341_probe(struct spi_device *spi) 177*043386a0SNoralf Trønnes { 178*043386a0SNoralf Trønnes struct device *dev = &spi->dev; 179*043386a0SNoralf Trønnes struct mipi_dbi_dev *dbidev; 180*043386a0SNoralf Trønnes struct drm_device *drm; 181*043386a0SNoralf Trønnes struct mipi_dbi *dbi; 182*043386a0SNoralf Trønnes struct gpio_desc *dc; 183*043386a0SNoralf Trønnes u32 rotation = 0; 184*043386a0SNoralf Trønnes int ret; 185*043386a0SNoralf Trønnes 186*043386a0SNoralf Trønnes dbidev = kzalloc(sizeof(*dbidev), GFP_KERNEL); 187*043386a0SNoralf Trønnes if (!dbidev) 188*043386a0SNoralf Trønnes return -ENOMEM; 189*043386a0SNoralf Trønnes 190*043386a0SNoralf Trønnes dbi = &dbidev->dbi; 191*043386a0SNoralf Trønnes drm = &dbidev->drm; 192*043386a0SNoralf Trønnes ret = devm_drm_dev_init(dev, drm, &ili9341_driver); 193*043386a0SNoralf Trønnes if (ret) { 194*043386a0SNoralf Trønnes kfree(dbidev); 195*043386a0SNoralf Trønnes return ret; 196*043386a0SNoralf Trønnes } 197*043386a0SNoralf Trønnes 198*043386a0SNoralf Trønnes drm_mode_config_init(drm); 199*043386a0SNoralf Trønnes 200*043386a0SNoralf Trønnes dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 201*043386a0SNoralf Trønnes if (IS_ERR(dbi->reset)) { 202*043386a0SNoralf Trønnes DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); 203*043386a0SNoralf Trønnes return PTR_ERR(dbi->reset); 204*043386a0SNoralf Trønnes } 205*043386a0SNoralf Trønnes 206*043386a0SNoralf Trønnes dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); 207*043386a0SNoralf Trønnes if (IS_ERR(dc)) { 208*043386a0SNoralf Trønnes DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); 209*043386a0SNoralf Trønnes return PTR_ERR(dc); 210*043386a0SNoralf Trønnes } 211*043386a0SNoralf Trønnes 212*043386a0SNoralf Trønnes dbidev->backlight = devm_of_find_backlight(dev); 213*043386a0SNoralf Trønnes if (IS_ERR(dbidev->backlight)) 214*043386a0SNoralf Trønnes return PTR_ERR(dbidev->backlight); 215*043386a0SNoralf Trønnes 216*043386a0SNoralf Trønnes device_property_read_u32(dev, "rotation", &rotation); 217*043386a0SNoralf Trønnes 218*043386a0SNoralf Trønnes ret = mipi_dbi_spi_init(spi, dbi, dc); 219*043386a0SNoralf Trønnes if (ret) 220*043386a0SNoralf Trønnes return ret; 221*043386a0SNoralf Trønnes 222*043386a0SNoralf Trønnes ret = mipi_dbi_dev_init(dbidev, &ili9341_pipe_funcs, &yx240qv29_mode, rotation); 223*043386a0SNoralf Trønnes if (ret) 224*043386a0SNoralf Trønnes return ret; 225*043386a0SNoralf Trønnes 226*043386a0SNoralf Trønnes drm_mode_config_reset(drm); 227*043386a0SNoralf Trønnes 228*043386a0SNoralf Trønnes ret = drm_dev_register(drm, 0); 229*043386a0SNoralf Trønnes if (ret) 230*043386a0SNoralf Trønnes return ret; 231*043386a0SNoralf Trønnes 232*043386a0SNoralf Trønnes spi_set_drvdata(spi, drm); 233*043386a0SNoralf Trønnes 234*043386a0SNoralf Trønnes drm_fbdev_generic_setup(drm, 0); 235*043386a0SNoralf Trønnes 236*043386a0SNoralf Trønnes return 0; 237*043386a0SNoralf Trønnes } 238*043386a0SNoralf Trønnes 239*043386a0SNoralf Trønnes static int ili9341_remove(struct spi_device *spi) 240*043386a0SNoralf Trønnes { 241*043386a0SNoralf Trønnes struct drm_device *drm = spi_get_drvdata(spi); 242*043386a0SNoralf Trønnes 243*043386a0SNoralf Trønnes drm_dev_unplug(drm); 244*043386a0SNoralf Trønnes drm_atomic_helper_shutdown(drm); 245*043386a0SNoralf Trønnes 246*043386a0SNoralf Trønnes return 0; 247*043386a0SNoralf Trønnes } 248*043386a0SNoralf Trønnes 249*043386a0SNoralf Trønnes static void ili9341_shutdown(struct spi_device *spi) 250*043386a0SNoralf Trønnes { 251*043386a0SNoralf Trønnes drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 252*043386a0SNoralf Trønnes } 253*043386a0SNoralf Trønnes 254*043386a0SNoralf Trønnes static struct spi_driver ili9341_spi_driver = { 255*043386a0SNoralf Trønnes .driver = { 256*043386a0SNoralf Trønnes .name = "ili9341", 257*043386a0SNoralf Trønnes .of_match_table = ili9341_of_match, 258*043386a0SNoralf Trønnes }, 259*043386a0SNoralf Trønnes .id_table = ili9341_id, 260*043386a0SNoralf Trønnes .probe = ili9341_probe, 261*043386a0SNoralf Trønnes .remove = ili9341_remove, 262*043386a0SNoralf Trønnes .shutdown = ili9341_shutdown, 263*043386a0SNoralf Trønnes }; 264*043386a0SNoralf Trønnes module_spi_driver(ili9341_spi_driver); 265*043386a0SNoralf Trønnes 266*043386a0SNoralf Trønnes MODULE_DESCRIPTION("Ilitek ILI9341 DRM driver"); 267*043386a0SNoralf Trønnes MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 268*043386a0SNoralf Trønnes MODULE_LICENSE("GPL"); 269