18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * DRM driver for Ilitek ILI9341 panels 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2018 David Lechner <david@lechnology.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on mi0283qt.c: 88c2ecf20Sopenharmony_ci * Copyright 2016 Noralf Trønnes 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/backlight.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/property.h> 168c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_managed.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dbi.h> 258c2ecf20Sopenharmony_ci#include <drm/drm_modeset_helper.h> 268c2ecf20Sopenharmony_ci#include <video/mipi_display.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define ILI9341_FRMCTR1 0xb1 298c2ecf20Sopenharmony_ci#define ILI9341_DISCTRL 0xb6 308c2ecf20Sopenharmony_ci#define ILI9341_ETMOD 0xb7 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define ILI9341_PWCTRL1 0xc0 338c2ecf20Sopenharmony_ci#define ILI9341_PWCTRL2 0xc1 348c2ecf20Sopenharmony_ci#define ILI9341_VMCTRL1 0xc5 358c2ecf20Sopenharmony_ci#define ILI9341_VMCTRL2 0xc7 368c2ecf20Sopenharmony_ci#define ILI9341_PWCTRLA 0xcb 378c2ecf20Sopenharmony_ci#define ILI9341_PWCTRLB 0xcf 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define ILI9341_PGAMCTRL 0xe0 408c2ecf20Sopenharmony_ci#define ILI9341_NGAMCTRL 0xe1 418c2ecf20Sopenharmony_ci#define ILI9341_DTCTRLA 0xe8 428c2ecf20Sopenharmony_ci#define ILI9341_DTCTRLB 0xea 438c2ecf20Sopenharmony_ci#define ILI9341_PWRSEQ 0xed 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define ILI9341_EN3GAM 0xf2 468c2ecf20Sopenharmony_ci#define ILI9341_PUMPCTRL 0xf7 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define ILI9341_MADCTL_BGR BIT(3) 498c2ecf20Sopenharmony_ci#define ILI9341_MADCTL_MV BIT(5) 508c2ecf20Sopenharmony_ci#define ILI9341_MADCTL_MX BIT(6) 518c2ecf20Sopenharmony_ci#define ILI9341_MADCTL_MY BIT(7) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void yx240qv29_enable(struct drm_simple_display_pipe *pipe, 548c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 558c2ecf20Sopenharmony_ci struct drm_plane_state *plane_state) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 588c2ecf20Sopenharmony_ci struct mipi_dbi *dbi = &dbidev->dbi; 598c2ecf20Sopenharmony_ci u8 addr_mode; 608c2ecf20Sopenharmony_ci int ret, idx; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (!drm_dev_enter(pipe->crtc.dev, &idx)) 638c2ecf20Sopenharmony_ci return; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci ret = mipi_dbi_poweron_conditional_reset(dbidev); 688c2ecf20Sopenharmony_ci if (ret < 0) 698c2ecf20Sopenharmony_ci goto out_exit; 708c2ecf20Sopenharmony_ci if (ret == 1) 718c2ecf20Sopenharmony_ci goto out_enable; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_PWCTRLB, 0x00, 0xc1, 0x30); 768c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_PWRSEQ, 0x64, 0x03, 0x12, 0x81); 778c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_DTCTRLA, 0x85, 0x00, 0x78); 788c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_PWCTRLA, 0x39, 0x2c, 0x00, 0x34, 0x02); 798c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_PUMPCTRL, 0x20); 808c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_DTCTRLB, 0x00, 0x00); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Power Control */ 838c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_PWCTRL1, 0x23); 848c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_PWCTRL2, 0x10); 858c2ecf20Sopenharmony_ci /* VCOM */ 868c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_VMCTRL1, 0x3e, 0x28); 878c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_VMCTRL2, 0x86); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Memory Access Control */ 908c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* Frame Rate */ 938c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_FRMCTR1, 0x00, 0x1b); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Gamma */ 968c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_EN3GAM, 0x00); 978c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01); 988c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_PGAMCTRL, 998c2ecf20Sopenharmony_ci 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 1008c2ecf20Sopenharmony_ci 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00); 1018c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_NGAMCTRL, 1028c2ecf20Sopenharmony_ci 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, 1038c2ecf20Sopenharmony_ci 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* DDRAM */ 1068c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_ETMOD, 0x07); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Display */ 1098c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, ILI9341_DISCTRL, 0x08, 0x82, 0x27, 0x00); 1108c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 1118c2ecf20Sopenharmony_ci msleep(100); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 1148c2ecf20Sopenharmony_ci msleep(100); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ciout_enable: 1178c2ecf20Sopenharmony_ci switch (dbidev->rotation) { 1188c2ecf20Sopenharmony_ci default: 1198c2ecf20Sopenharmony_ci addr_mode = ILI9341_MADCTL_MX; 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci case 90: 1228c2ecf20Sopenharmony_ci addr_mode = ILI9341_MADCTL_MV; 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci case 180: 1258c2ecf20Sopenharmony_ci addr_mode = ILI9341_MADCTL_MY; 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci case 270: 1288c2ecf20Sopenharmony_ci addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY | 1298c2ecf20Sopenharmony_ci ILI9341_MADCTL_MX; 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci addr_mode |= ILI9341_MADCTL_BGR; 1338c2ecf20Sopenharmony_ci mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); 1348c2ecf20Sopenharmony_ci mipi_dbi_enable_flush(dbidev, crtc_state, plane_state); 1358c2ecf20Sopenharmony_ciout_exit: 1368c2ecf20Sopenharmony_ci drm_dev_exit(idx); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic const struct drm_simple_display_pipe_funcs ili9341_pipe_funcs = { 1408c2ecf20Sopenharmony_ci .enable = yx240qv29_enable, 1418c2ecf20Sopenharmony_ci .disable = mipi_dbi_pipe_disable, 1428c2ecf20Sopenharmony_ci .update = mipi_dbi_pipe_update, 1438c2ecf20Sopenharmony_ci .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic const struct drm_display_mode yx240qv29_mode = { 1478c2ecf20Sopenharmony_ci DRM_SIMPLE_MODE(240, 320, 37, 49), 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_CMA_FOPS(ili9341_fops); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic struct drm_driver ili9341_driver = { 1538c2ecf20Sopenharmony_ci .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 1548c2ecf20Sopenharmony_ci .fops = &ili9341_fops, 1558c2ecf20Sopenharmony_ci DRM_GEM_CMA_DRIVER_OPS_VMAP, 1568c2ecf20Sopenharmony_ci .debugfs_init = mipi_dbi_debugfs_init, 1578c2ecf20Sopenharmony_ci .name = "ili9341", 1588c2ecf20Sopenharmony_ci .desc = "Ilitek ILI9341", 1598c2ecf20Sopenharmony_ci .date = "20180514", 1608c2ecf20Sopenharmony_ci .major = 1, 1618c2ecf20Sopenharmony_ci .minor = 0, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const struct of_device_id ili9341_of_match[] = { 1658c2ecf20Sopenharmony_ci { .compatible = "adafruit,yx240qv29" }, 1668c2ecf20Sopenharmony_ci { } 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ili9341_of_match); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const struct spi_device_id ili9341_id[] = { 1718c2ecf20Sopenharmony_ci { "yx240qv29", 0 }, 1728c2ecf20Sopenharmony_ci { } 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, ili9341_id); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int ili9341_probe(struct spi_device *spi) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct device *dev = &spi->dev; 1798c2ecf20Sopenharmony_ci struct mipi_dbi_dev *dbidev; 1808c2ecf20Sopenharmony_ci struct drm_device *drm; 1818c2ecf20Sopenharmony_ci struct mipi_dbi *dbi; 1828c2ecf20Sopenharmony_ci struct gpio_desc *dc; 1838c2ecf20Sopenharmony_ci u32 rotation = 0; 1848c2ecf20Sopenharmony_ci int ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci dbidev = devm_drm_dev_alloc(dev, &ili9341_driver, 1878c2ecf20Sopenharmony_ci struct mipi_dbi_dev, drm); 1888c2ecf20Sopenharmony_ci if (IS_ERR(dbidev)) 1898c2ecf20Sopenharmony_ci return PTR_ERR(dbidev); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci dbi = &dbidev->dbi; 1928c2ecf20Sopenharmony_ci drm = &dbidev->drm; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 1958c2ecf20Sopenharmony_ci if (IS_ERR(dbi->reset)) { 1968c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); 1978c2ecf20Sopenharmony_ci return PTR_ERR(dbi->reset); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); 2018c2ecf20Sopenharmony_ci if (IS_ERR(dc)) { 2028c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); 2038c2ecf20Sopenharmony_ci return PTR_ERR(dc); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci dbidev->backlight = devm_of_find_backlight(dev); 2078c2ecf20Sopenharmony_ci if (IS_ERR(dbidev->backlight)) 2088c2ecf20Sopenharmony_ci return PTR_ERR(dbidev->backlight); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci device_property_read_u32(dev, "rotation", &rotation); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = mipi_dbi_spi_init(spi, dbi, dc); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ret = mipi_dbi_dev_init(dbidev, &ili9341_pipe_funcs, &yx240qv29_mode, rotation); 2178c2ecf20Sopenharmony_ci if (ret) 2188c2ecf20Sopenharmony_ci return ret; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci drm_mode_config_reset(drm); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci ret = drm_dev_register(drm, 0); 2238c2ecf20Sopenharmony_ci if (ret) 2248c2ecf20Sopenharmony_ci return ret; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci spi_set_drvdata(spi, drm); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci drm_fbdev_generic_setup(drm, 0); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int ili9341_remove(struct spi_device *spi) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct drm_device *drm = spi_get_drvdata(spi); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci drm_dev_unplug(drm); 2388c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic void ili9341_shutdown(struct spi_device *spi) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic struct spi_driver ili9341_spi_driver = { 2498c2ecf20Sopenharmony_ci .driver = { 2508c2ecf20Sopenharmony_ci .name = "ili9341", 2518c2ecf20Sopenharmony_ci .of_match_table = ili9341_of_match, 2528c2ecf20Sopenharmony_ci }, 2538c2ecf20Sopenharmony_ci .id_table = ili9341_id, 2548c2ecf20Sopenharmony_ci .probe = ili9341_probe, 2558c2ecf20Sopenharmony_ci .remove = ili9341_remove, 2568c2ecf20Sopenharmony_ci .shutdown = ili9341_shutdown, 2578c2ecf20Sopenharmony_ci}; 2588c2ecf20Sopenharmony_cimodule_spi_driver(ili9341_spi_driver); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Ilitek ILI9341 DRM driver"); 2618c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Lechner <david@lechnology.com>"); 2628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 263