Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/panel: Add ilitek ili9341 panel driver

This driver combines tiny/ili9341.c mipi_dbi_interface driver
with mipi_dpi_interface driver, can support ili9341 with serial
mode and parallel rgb interface mode by different dts bindings.

Signed-off-by: Dillon Min <dillon.minfei@gmail.com>
Reported-by: kernel test robot <lkp@intel.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/1627098243-2742-4-git-send-email-dillon.minfei@gmail.com

authored by

Dillon Min and committed by
Linus Walleij
5a042273 7dbdce80

+805
+12
drivers/gpu/drm/panel/Kconfig
··· 125 125 Say Y here if you want to enable support for Ilitek IL9322 126 126 QVGA (320x240) RGB, YUV and ITU-T BT.656 panels. 127 127 128 + config DRM_PANEL_ILITEK_ILI9341 129 + tristate "Ilitek ILI9341 240x320 QVGA panels" 130 + depends on OF && SPI 131 + depends on DRM_KMS_HELPER 132 + depends on DRM_KMS_CMA_HELPER 133 + depends on BACKLIGHT_CLASS_DEVICE 134 + select DRM_MIPI_DBI 135 + help 136 + Say Y here if you want to enable support for Ilitek IL9341 137 + QVGA (240x320) RGB panels. support serial & parallel rgb 138 + interface. 139 + 128 140 config DRM_PANEL_ILITEK_ILI9881C 129 141 tristate "Ilitek ILI9881C-based panels" 130 142 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 11 11 obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o 12 12 obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o 13 13 obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o 14 + obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o 14 15 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o 15 16 obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o 16 17 obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
+792
drivers/gpu/drm/panel/panel-ilitek-ili9341.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Ilitek ILI9341 TFT LCD drm_panel driver. 4 + * 5 + * This panel can be configured to support: 6 + * - 16-bit parallel RGB interface 7 + * - 18-bit parallel RGB interface 8 + * - 4-line serial spi interface 9 + * 10 + * Copyright (C) 2021 Dillon Min <dillon.minfei@gmail.com> 11 + * 12 + * For dbi+dpi part: 13 + * Derived from drivers/drm/gpu/panel/panel-ilitek-ili9322.c 14 + * the reuse of DBI abstraction part referred from Linus's patch 15 + * "drm/panel: s6e63m0: Switch to DBI abstraction for SPI" 16 + * 17 + * For only-dbi part, copy from David's code (drm/tiny/ili9341.c) 18 + * Copyright 2018 David Lechner <david@lechnology.com> 19 + */ 20 + 21 + #include <linux/bitops.h> 22 + #include <linux/delay.h> 23 + #include <linux/gpio/consumer.h> 24 + #include <linux/module.h> 25 + #include <linux/of_device.h> 26 + #include <linux/regulator/consumer.h> 27 + #include <linux/spi/spi.h> 28 + 29 + #include <video/mipi_display.h> 30 + 31 + #include <drm/drm_atomic_helper.h> 32 + #include <drm/drm_drv.h> 33 + #include <drm/drm_fb_helper.h> 34 + #include <drm/drm_gem_atomic_helper.h> 35 + #include <drm/drm_gem_cma_helper.h> 36 + #include <drm/drm_gem_framebuffer_helper.h> 37 + #include <drm/drm_mipi_dbi.h> 38 + #include <drm/drm_modes.h> 39 + #include <drm/drm_panel.h> 40 + #include <drm/drm_print.h> 41 + 42 + #define ILI9341_RGB_INTERFACE 0xb0 /* RGB Interface Signal Control */ 43 + #define ILI9341_FRC 0xb1 /* Frame Rate Control register */ 44 + #define ILI9341_DFC 0xb6 /* Display Function Control register */ 45 + #define ILI9341_POWER1 0xc0 /* Power Control 1 register */ 46 + #define ILI9341_POWER2 0xc1 /* Power Control 2 register */ 47 + #define ILI9341_VCOM1 0xc5 /* VCOM Control 1 register */ 48 + #define ILI9341_VCOM2 0xc7 /* VCOM Control 2 register */ 49 + #define ILI9341_POWERA 0xcb /* Power control A register */ 50 + #define ILI9341_POWERB 0xcf /* Power control B register */ 51 + #define ILI9341_PGAMMA 0xe0 /* Positive Gamma Correction register */ 52 + #define ILI9341_NGAMMA 0xe1 /* Negative Gamma Correction register */ 53 + #define ILI9341_DTCA 0xe8 /* Driver timing control A */ 54 + #define ILI9341_DTCB 0xea /* Driver timing control B */ 55 + #define ILI9341_POWER_SEQ 0xed /* Power on sequence register */ 56 + #define ILI9341_3GAMMA_EN 0xf2 /* 3 Gamma enable register */ 57 + #define ILI9341_INTERFACE 0xf6 /* Interface control register */ 58 + #define ILI9341_PRC 0xf7 /* Pump ratio control register */ 59 + #define ILI9341_ETMOD 0xb7 /* Entry mode set */ 60 + 61 + #define ILI9341_MADCTL_BGR BIT(3) 62 + #define ILI9341_MADCTL_MV BIT(5) 63 + #define ILI9341_MADCTL_MX BIT(6) 64 + #define ILI9341_MADCTL_MY BIT(7) 65 + 66 + #define ILI9341_POWER_B_LEN 3 67 + #define ILI9341_POWER_SEQ_LEN 4 68 + #define ILI9341_DTCA_LEN 3 69 + #define ILI9341_DTCB_LEN 2 70 + #define ILI9341_POWER_A_LEN 5 71 + #define ILI9341_DFC_1_LEN 2 72 + #define ILI9341_FRC_LEN 2 73 + #define ILI9341_VCOM_1_LEN 2 74 + #define ILI9341_DFC_2_LEN 4 75 + #define ILI9341_COLUMN_ADDR_LEN 4 76 + #define ILI9341_PAGE_ADDR_LEN 4 77 + #define ILI9341_INTERFACE_LEN 3 78 + #define ILI9341_PGAMMA_LEN 15 79 + #define ILI9341_NGAMMA_LEN 15 80 + #define ILI9341_CA_LEN 3 81 + 82 + #define ILI9341_PIXEL_DPI_16_BITS (BIT(6) | BIT(4)) 83 + #define ILI9341_PIXEL_DPI_18_BITS (BIT(6) | BIT(5)) 84 + #define ILI9341_GAMMA_CURVE_1 BIT(0) 85 + #define ILI9341_IF_WE_MODE BIT(0) 86 + #define ILI9341_IF_BIG_ENDIAN 0x00 87 + #define ILI9341_IF_DM_RGB BIT(2) 88 + #define ILI9341_IF_DM_INTERNAL 0x00 89 + #define ILI9341_IF_DM_VSYNC BIT(3) 90 + #define ILI9341_IF_RM_RGB BIT(1) 91 + #define ILI9341_IF_RIM_RGB 0x00 92 + 93 + #define ILI9341_COLUMN_ADDR 0x00ef 94 + #define ILI9341_PAGE_ADDR 0x013f 95 + 96 + #define ILI9341_RGB_EPL BIT(0) 97 + #define ILI9341_RGB_DPL BIT(1) 98 + #define ILI9341_RGB_HSPL BIT(2) 99 + #define ILI9341_RGB_VSPL BIT(3) 100 + #define ILI9341_RGB_DE_MODE BIT(6) 101 + #define ILI9341_RGB_DISP_PATH_MEM BIT(7) 102 + 103 + #define ILI9341_DBI_VCOMH_4P6V 0x23 104 + #define ILI9341_DBI_PWR_2_DEFAULT 0x10 105 + #define ILI9341_DBI_PRC_NORMAL 0x20 106 + #define ILI9341_DBI_VCOM_1_VMH_4P25V 0x3e 107 + #define ILI9341_DBI_VCOM_1_VML_1P5V 0x28 108 + #define ILI9341_DBI_VCOM_2_DEC_58 0x86 109 + #define ILI9341_DBI_FRC_DIVA 0x00 110 + #define ILI9341_DBI_FRC_RTNA 0x1b 111 + #define ILI9341_DBI_EMS_GAS BIT(0) 112 + #define ILI9341_DBI_EMS_DTS BIT(1) 113 + #define ILI9341_DBI_EMS_GON BIT(2) 114 + 115 + /* struct ili9341_config - the system specific ILI9341 configuration */ 116 + struct ili9341_config { 117 + u32 max_spi_speed; 118 + /* mode: the drm display mode */ 119 + const struct drm_display_mode mode; 120 + /* ca: TODO: need comments for this register */ 121 + u8 ca[ILI9341_CA_LEN]; 122 + /* power_b: TODO: need comments for this register */ 123 + u8 power_b[ILI9341_POWER_B_LEN]; 124 + /* power_seq: TODO: need comments for this register */ 125 + u8 power_seq[ILI9341_POWER_SEQ_LEN]; 126 + /* dtca: TODO: need comments for this register */ 127 + u8 dtca[ILI9341_DTCA_LEN]; 128 + /* dtcb: TODO: need comments for this register */ 129 + u8 dtcb[ILI9341_DTCB_LEN]; 130 + /* power_a: TODO: need comments for this register */ 131 + u8 power_a[ILI9341_POWER_A_LEN]; 132 + /* frc: Frame Rate Control (In Normal Mode/Full Colors) (B1h) */ 133 + u8 frc[ILI9341_FRC_LEN]; 134 + /* prc: TODO: need comments for this register */ 135 + u8 prc; 136 + /* dfc_1: B6h DISCTRL (Display Function Control) */ 137 + u8 dfc_1[ILI9341_DFC_1_LEN]; 138 + /* power_1: Power Control 1 (C0h) */ 139 + u8 power_1; 140 + /* power_2: Power Control 2 (C1h) */ 141 + u8 power_2; 142 + /* vcom_1: VCOM Control 1(C5h) */ 143 + u8 vcom_1[ILI9341_VCOM_1_LEN]; 144 + /* vcom_2: VCOM Control 2(C7h) */ 145 + u8 vcom_2; 146 + /* address_mode: Memory Access Control (36h) */ 147 + u8 address_mode; 148 + /* g3amma_en: TODO: need comments for this register */ 149 + u8 g3amma_en; 150 + /* rgb_interface: RGB Interface Signal Control (B0h) */ 151 + u8 rgb_interface; 152 + /* dfc_2: refer to dfc_1 */ 153 + u8 dfc_2[ILI9341_DFC_2_LEN]; 154 + /* column_addr: Column Address Set (2Ah) */ 155 + u8 column_addr[ILI9341_COLUMN_ADDR_LEN]; 156 + /* page_addr: Page Address Set (2Bh) */ 157 + u8 page_addr[ILI9341_PAGE_ADDR_LEN]; 158 + /* interface: Interface Control (F6h) */ 159 + u8 interface[ILI9341_INTERFACE_LEN]; 160 + /* 161 + * pixel_format: This command sets the pixel format for the RGB 162 + * image data used by 163 + */ 164 + u8 pixel_format; 165 + /* 166 + * gamma_curve: This command is used to select the desired Gamma 167 + * curve for the 168 + */ 169 + u8 gamma_curve; 170 + /* pgamma: Positive Gamma Correction (E0h) */ 171 + u8 pgamma[ILI9341_PGAMMA_LEN]; 172 + /* ngamma: Negative Gamma Correction (E1h) */ 173 + u8 ngamma[ILI9341_NGAMMA_LEN]; 174 + }; 175 + 176 + struct ili9341 { 177 + struct device *dev; 178 + const struct ili9341_config *conf; 179 + struct drm_panel panel; 180 + struct gpio_desc *reset_gpio; 181 + struct gpio_desc *dc_gpio; 182 + struct mipi_dbi *dbi; 183 + u32 max_spi_speed; 184 + struct regulator_bulk_data supplies[3]; 185 + }; 186 + 187 + /* 188 + * The Stm32f429-disco board has a panel ili9341 connected to ltdc controller 189 + */ 190 + static const struct ili9341_config ili9341_stm32f429_disco_data = { 191 + .max_spi_speed = 10000000, 192 + .mode = { 193 + .clock = 6100, 194 + .hdisplay = 240, 195 + .hsync_start = 240 + 10,/* hfp 10 */ 196 + .hsync_end = 240 + 10 + 10,/* hsync 10 */ 197 + .htotal = 240 + 10 + 10 + 20,/* hbp 20 */ 198 + .vdisplay = 320, 199 + .vsync_start = 320 + 4,/* vfp 4 */ 200 + .vsync_end = 320 + 4 + 2,/* vsync 2 */ 201 + .vtotal = 320 + 4 + 2 + 2,/* vbp 2 */ 202 + .flags = 0, 203 + .width_mm = 65, 204 + .height_mm = 50, 205 + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 206 + }, 207 + .ca = {0xc3, 0x08, 0x50}, 208 + .power_b = {0x00, 0xc1, 0x30}, 209 + .power_seq = {0x64, 0x03, 0x12, 0x81}, 210 + .dtca = {0x85, 0x00, 0x78}, 211 + .power_a = {0x39, 0x2c, 0x00, 0x34, 0x02}, 212 + .prc = 0x20, 213 + .dtcb = {0x00, 0x00}, 214 + /* 0x00 fosc, 0x1b 70hz */ 215 + .frc = {0x00, 0x1b}, 216 + /* 217 + * 0x0a Interval scan, AGND AGND AGND AGND 218 + * 0xa2 Normally white, G1 -> G320, S720 -> S1, 219 + * Scan Cycle 5 frames,85ms 220 + */ 221 + .dfc_1 = {0x0a, 0xa2}, 222 + /* 0x10 3.65v */ 223 + .power_1 = 0x10, 224 + /* 0x10 AVDD=vci*2, VGH=vci*7, VGL=-vci*4 */ 225 + .power_2 = 0x10, 226 + /* 0x45 VCOMH 4.425v, 0x15 VCOML -1.975*/ 227 + .vcom_1 = {0x45, 0x15}, 228 + /* 0x90 offset voltage, VMH-48, VML-48 */ 229 + .vcom_2 = 0x90, 230 + /* 231 + * 0xc8 Row Address Order, Column Address Order 232 + * BGR 1 233 + */ 234 + .address_mode = 0xc8, 235 + .g3amma_en = 0x00, 236 + /* 237 + * 0xc2 238 + * Display Data Path: Memory 239 + * RGB: DE mode 240 + * DOTCLK polarity set (data fetched at the falling time) 241 + */ 242 + .rgb_interface = ILI9341_RGB_DISP_PATH_MEM | 243 + ILI9341_RGB_DE_MODE | 244 + ILI9341_RGB_DPL, 245 + /* 246 + * 0x0a 247 + * Gate outputs in non-display area: Interval scan 248 + * Determine source/VCOM output in a non-display area in the partial 249 + * display mode: AGND AGND AGND AGND 250 + * 251 + * 0xa7 252 + * Scan Cycle: 15 frames 253 + * fFLM = 60Hz: 255ms 254 + * Liquid crystal type: Normally white 255 + * Gate Output Scan Direction: G1 -> G320 256 + * Source Output Scan Direction: S720 -> S1 257 + * 258 + * 0x27 259 + * LCD Driver Line: 320 lines 260 + * 261 + * 0x04 262 + * PCDIV: 4 263 + */ 264 + .dfc_2 = {0x0a, 0xa7, 0x27, 0x04}, 265 + /* column address: 240 */ 266 + .column_addr = {0x00, 0x00, (ILI9341_COLUMN_ADDR >> 4) & 0xff, 267 + ILI9341_COLUMN_ADDR & 0xff}, 268 + /* page address: 320 */ 269 + .page_addr = {0x00, 0x00, (ILI9341_PAGE_ADDR >> 4) & 0xff, 270 + ILI9341_PAGE_ADDR & 0xff}, 271 + /* 272 + * Memory write control: When the transfer number of data exceeds 273 + * (EC-SC+1)*(EP-SP+1), the column and page number will be 274 + * reset, and the exceeding data will be written into the following 275 + * column and page. 276 + * Display Operation Mode: RGB Interface Mode 277 + * Interface for RAM Access: RGB interface 278 + * 16- bit RGB interface (1 transfer/pixel) 279 + */ 280 + .interface = {ILI9341_IF_WE_MODE, 0x00, 281 + ILI9341_IF_DM_RGB | ILI9341_IF_RM_RGB}, 282 + /* DPI: 16 bits / pixel */ 283 + .pixel_format = ILI9341_PIXEL_DPI_16_BITS, 284 + /* Curve Selected: Gamma curve 1 (G2.2) */ 285 + .gamma_curve = ILI9341_GAMMA_CURVE_1, 286 + .pgamma = {0x0f, 0x29, 0x24, 0x0c, 0x0e, 287 + 0x09, 0x4e, 0x78, 0x3c, 0x09, 288 + 0x13, 0x05, 0x17, 0x11, 0x00}, 289 + .ngamma = {0x00, 0x16, 0x1b, 0x04, 0x11, 290 + 0x07, 0x31, 0x33, 0x42, 0x05, 291 + 0x0c, 0x0a, 0x28, 0x2f, 0x0f}, 292 + }; 293 + 294 + static inline struct ili9341 *panel_to_ili9341(struct drm_panel *panel) 295 + { 296 + return container_of(panel, struct ili9341, panel); 297 + } 298 + 299 + static void ili9341_dpi_init(struct ili9341 *ili) 300 + { 301 + struct device *dev = (&ili->panel)->dev; 302 + struct mipi_dbi *dbi = ili->dbi; 303 + struct ili9341_config *cfg = (struct ili9341_config *)ili->conf; 304 + 305 + /* Power Control */ 306 + mipi_dbi_command_stackbuf(dbi, 0xca, cfg->ca, ILI9341_CA_LEN); 307 + mipi_dbi_command_stackbuf(dbi, ILI9341_POWERB, cfg->power_b, 308 + ILI9341_POWER_B_LEN); 309 + mipi_dbi_command_stackbuf(dbi, ILI9341_POWER_SEQ, cfg->power_seq, 310 + ILI9341_POWER_SEQ_LEN); 311 + mipi_dbi_command_stackbuf(dbi, ILI9341_DTCA, cfg->dtca, 312 + ILI9341_DTCA_LEN); 313 + mipi_dbi_command_stackbuf(dbi, ILI9341_POWERA, cfg->power_a, 314 + ILI9341_POWER_A_LEN); 315 + mipi_dbi_command(ili->dbi, ILI9341_PRC, cfg->prc); 316 + mipi_dbi_command_stackbuf(dbi, ILI9341_DTCB, cfg->dtcb, 317 + ILI9341_DTCB_LEN); 318 + mipi_dbi_command_stackbuf(dbi, ILI9341_FRC, cfg->frc, ILI9341_FRC_LEN); 319 + mipi_dbi_command_stackbuf(dbi, ILI9341_DFC, cfg->dfc_1, 320 + ILI9341_DFC_1_LEN); 321 + mipi_dbi_command(dbi, ILI9341_POWER1, cfg->power_1); 322 + mipi_dbi_command(dbi, ILI9341_POWER2, cfg->power_2); 323 + 324 + /* VCOM */ 325 + mipi_dbi_command_stackbuf(dbi, ILI9341_VCOM1, cfg->vcom_1, 326 + ILI9341_VCOM_1_LEN); 327 + mipi_dbi_command(dbi, ILI9341_VCOM2, cfg->vcom_2); 328 + mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, cfg->address_mode); 329 + 330 + /* Gamma */ 331 + mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, cfg->g3amma_en); 332 + mipi_dbi_command(dbi, ILI9341_RGB_INTERFACE, cfg->rgb_interface); 333 + mipi_dbi_command_stackbuf(dbi, ILI9341_DFC, cfg->dfc_2, 334 + ILI9341_DFC_2_LEN); 335 + 336 + /* Colomn address set */ 337 + mipi_dbi_command_stackbuf(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, 338 + cfg->column_addr, ILI9341_COLUMN_ADDR_LEN); 339 + 340 + /* Page address set */ 341 + mipi_dbi_command_stackbuf(dbi, MIPI_DCS_SET_PAGE_ADDRESS, 342 + cfg->page_addr, ILI9341_PAGE_ADDR_LEN); 343 + mipi_dbi_command_stackbuf(dbi, ILI9341_INTERFACE, cfg->interface, 344 + ILI9341_INTERFACE_LEN); 345 + 346 + /* Format */ 347 + mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, cfg->pixel_format); 348 + mipi_dbi_command(dbi, MIPI_DCS_WRITE_MEMORY_START); 349 + msleep(200); 350 + mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, cfg->gamma_curve); 351 + mipi_dbi_command_stackbuf(dbi, ILI9341_PGAMMA, cfg->pgamma, 352 + ILI9341_PGAMMA_LEN); 353 + mipi_dbi_command_stackbuf(dbi, ILI9341_NGAMMA, cfg->ngamma, 354 + ILI9341_NGAMMA_LEN); 355 + mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 356 + msleep(200); 357 + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 358 + mipi_dbi_command(dbi, MIPI_DCS_WRITE_MEMORY_START); 359 + 360 + dev_info(dev, "Initialized display rgb interface\n"); 361 + } 362 + 363 + static int ili9341_dpi_power_on(struct ili9341 *ili) 364 + { 365 + struct device *dev = (&ili->panel)->dev; 366 + int ret = 0; 367 + 368 + /* Assert RESET */ 369 + gpiod_set_value(ili->reset_gpio, 1); 370 + 371 + /* Enable power */ 372 + ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies), 373 + ili->supplies); 374 + if (ret < 0) { 375 + dev_err(dev, "unable to enable vcc\n"); 376 + return ret; 377 + } 378 + msleep(20); 379 + 380 + /* De-assert RESET */ 381 + gpiod_set_value(ili->reset_gpio, 0); 382 + msleep(20); 383 + 384 + return 0; 385 + } 386 + 387 + static int ili9341_dpi_power_off(struct ili9341 *ili) 388 + { 389 + /* Assert RESET */ 390 + gpiod_set_value(ili->reset_gpio, 1); 391 + 392 + /* Disable power */ 393 + return regulator_bulk_disable(ARRAY_SIZE(ili->supplies), 394 + ili->supplies); 395 + } 396 + 397 + static int ili9341_dpi_disable(struct drm_panel *panel) 398 + { 399 + struct ili9341 *ili = panel_to_ili9341(panel); 400 + 401 + mipi_dbi_command(ili->dbi, MIPI_DCS_SET_DISPLAY_OFF); 402 + return 0; 403 + } 404 + 405 + static int ili9341_dpi_unprepare(struct drm_panel *panel) 406 + { 407 + struct ili9341 *ili = panel_to_ili9341(panel); 408 + 409 + return ili9341_dpi_power_off(ili); 410 + } 411 + 412 + static int ili9341_dpi_prepare(struct drm_panel *panel) 413 + { 414 + struct ili9341 *ili = panel_to_ili9341(panel); 415 + int ret; 416 + 417 + ret = ili9341_dpi_power_on(ili); 418 + if (ret < 0) 419 + return ret; 420 + 421 + ili9341_dpi_init(ili); 422 + 423 + return ret; 424 + } 425 + 426 + static int ili9341_dpi_enable(struct drm_panel *panel) 427 + { 428 + struct ili9341 *ili = panel_to_ili9341(panel); 429 + 430 + mipi_dbi_command(ili->dbi, MIPI_DCS_SET_DISPLAY_ON); 431 + return 0; 432 + } 433 + 434 + static int ili9341_dpi_get_modes(struct drm_panel *panel, 435 + struct drm_connector *connector) 436 + { 437 + struct ili9341 *ili = panel_to_ili9341(panel); 438 + struct drm_device *drm = connector->dev; 439 + struct drm_display_mode *mode; 440 + struct drm_display_info *info; 441 + 442 + info = &connector->display_info; 443 + info->width_mm = ili->conf->mode.width_mm; 444 + info->height_mm = ili->conf->mode.height_mm; 445 + 446 + if (ili->conf->rgb_interface & ILI9341_RGB_DPL) 447 + info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; 448 + else 449 + info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; 450 + 451 + if (ili->conf->rgb_interface & ILI9341_RGB_EPL) 452 + info->bus_flags |= DRM_BUS_FLAG_DE_LOW; 453 + else 454 + info->bus_flags |= DRM_BUS_FLAG_DE_HIGH; 455 + 456 + mode = drm_mode_duplicate(drm, &ili->conf->mode); 457 + if (!mode) { 458 + drm_err(drm, "bad mode or failed to add mode\n"); 459 + return -EINVAL; 460 + } 461 + drm_mode_set_name(mode); 462 + 463 + /* Set up the polarity */ 464 + if (ili->conf->rgb_interface & ILI9341_RGB_HSPL) 465 + mode->flags |= DRM_MODE_FLAG_PHSYNC; 466 + else 467 + mode->flags |= DRM_MODE_FLAG_NHSYNC; 468 + 469 + if (ili->conf->rgb_interface & ILI9341_RGB_VSPL) 470 + mode->flags |= DRM_MODE_FLAG_PVSYNC; 471 + else 472 + mode->flags |= DRM_MODE_FLAG_NVSYNC; 473 + 474 + drm_mode_probed_add(connector, mode); 475 + 476 + return 1; /* Number of modes */ 477 + } 478 + 479 + static const struct drm_panel_funcs ili9341_dpi_funcs = { 480 + .disable = ili9341_dpi_disable, 481 + .unprepare = ili9341_dpi_unprepare, 482 + .prepare = ili9341_dpi_prepare, 483 + .enable = ili9341_dpi_enable, 484 + .get_modes = ili9341_dpi_get_modes, 485 + }; 486 + 487 + static void ili9341_dbi_enable(struct drm_simple_display_pipe *pipe, 488 + struct drm_crtc_state *crtc_state, 489 + struct drm_plane_state *plane_state) 490 + { 491 + struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 492 + struct mipi_dbi *dbi = &dbidev->dbi; 493 + u8 addr_mode; 494 + int ret, idx; 495 + 496 + if (!drm_dev_enter(pipe->crtc.dev, &idx)) 497 + return; 498 + 499 + ret = mipi_dbi_poweron_conditional_reset(dbidev); 500 + if (ret < 0) 501 + goto out_exit; 502 + if (ret == 1) 503 + goto out_enable; 504 + 505 + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 506 + 507 + mipi_dbi_command(dbi, ILI9341_POWERB, 0x00, 0xc1, 0x30); 508 + mipi_dbi_command(dbi, ILI9341_POWER_SEQ, 0x64, 0x03, 0x12, 0x81); 509 + mipi_dbi_command(dbi, ILI9341_DTCA, 0x85, 0x00, 0x78); 510 + mipi_dbi_command(dbi, ILI9341_POWERA, 0x39, 0x2c, 0x00, 0x34, 0x02); 511 + mipi_dbi_command(dbi, ILI9341_PRC, ILI9341_DBI_PRC_NORMAL); 512 + mipi_dbi_command(dbi, ILI9341_DTCB, 0x00, 0x00); 513 + 514 + /* Power Control */ 515 + mipi_dbi_command(dbi, ILI9341_POWER1, ILI9341_DBI_VCOMH_4P6V); 516 + mipi_dbi_command(dbi, ILI9341_POWER2, ILI9341_DBI_PWR_2_DEFAULT); 517 + /* VCOM */ 518 + mipi_dbi_command(dbi, ILI9341_VCOM1, ILI9341_DBI_VCOM_1_VMH_4P25V, 519 + ILI9341_DBI_VCOM_1_VML_1P5V); 520 + mipi_dbi_command(dbi, ILI9341_VCOM2, ILI9341_DBI_VCOM_2_DEC_58); 521 + 522 + /* Memory Access Control */ 523 + mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, 524 + MIPI_DCS_PIXEL_FMT_16BIT); 525 + 526 + /* Frame Rate */ 527 + mipi_dbi_command(dbi, ILI9341_FRC, ILI9341_DBI_FRC_DIVA & 0x03, 528 + ILI9341_DBI_FRC_RTNA & 0x1f); 529 + 530 + /* Gamma */ 531 + mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, 0x00); 532 + mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, ILI9341_GAMMA_CURVE_1); 533 + mipi_dbi_command(dbi, ILI9341_PGAMMA, 534 + 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 535 + 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00); 536 + mipi_dbi_command(dbi, ILI9341_NGAMMA, 537 + 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, 538 + 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f); 539 + 540 + /* DDRAM */ 541 + mipi_dbi_command(dbi, ILI9341_ETMOD, ILI9341_DBI_EMS_GAS | 542 + ILI9341_DBI_EMS_DTS | 543 + ILI9341_DBI_EMS_GON); 544 + 545 + /* Display */ 546 + mipi_dbi_command(dbi, ILI9341_DFC, 0x08, 0x82, 0x27, 0x00); 547 + mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 548 + msleep(100); 549 + 550 + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 551 + msleep(100); 552 + 553 + out_enable: 554 + switch (dbidev->rotation) { 555 + default: 556 + addr_mode = ILI9341_MADCTL_MX; 557 + break; 558 + case 90: 559 + addr_mode = ILI9341_MADCTL_MV; 560 + break; 561 + case 180: 562 + addr_mode = ILI9341_MADCTL_MY; 563 + break; 564 + case 270: 565 + addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY | 566 + ILI9341_MADCTL_MX; 567 + break; 568 + } 569 + 570 + addr_mode |= ILI9341_MADCTL_BGR; 571 + mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); 572 + mipi_dbi_enable_flush(dbidev, crtc_state, plane_state); 573 + drm_info(&dbidev->drm, "Initialized display serial interface\n"); 574 + out_exit: 575 + drm_dev_exit(idx); 576 + } 577 + 578 + static const struct drm_simple_display_pipe_funcs ili9341_dbi_funcs = { 579 + .enable = ili9341_dbi_enable, 580 + .disable = mipi_dbi_pipe_disable, 581 + .update = mipi_dbi_pipe_update, 582 + .prepare_fb = drm_gem_simple_display_pipe_prepare_fb, 583 + }; 584 + 585 + static const struct drm_display_mode ili9341_dbi_mode = { 586 + DRM_SIMPLE_MODE(240, 320, 37, 49), 587 + }; 588 + 589 + DEFINE_DRM_GEM_CMA_FOPS(ili9341_dbi_fops); 590 + 591 + static struct drm_driver ili9341_dbi_driver = { 592 + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 593 + .fops = &ili9341_dbi_fops, 594 + DRM_GEM_CMA_DRIVER_OPS_VMAP, 595 + .debugfs_init = mipi_dbi_debugfs_init, 596 + .name = "ili9341", 597 + .desc = "Ilitek ILI9341", 598 + .date = "20210716", 599 + .major = 1, 600 + .minor = 0, 601 + }; 602 + 603 + static int ili9341_dbi_probe(struct spi_device *spi, struct gpio_desc *dc, 604 + struct gpio_desc *reset) 605 + { 606 + struct device *dev = &spi->dev; 607 + struct mipi_dbi_dev *dbidev; 608 + struct mipi_dbi *dbi; 609 + struct drm_device *drm; 610 + struct regulator *vcc; 611 + u32 rotation = 0; 612 + int ret; 613 + 614 + vcc = devm_regulator_get_optional(dev, "vcc"); 615 + if (IS_ERR(vcc)) 616 + dev_err(dev, "get optional vcc failed\n"); 617 + 618 + dbidev = devm_drm_dev_alloc(dev, &ili9341_dbi_driver, 619 + struct mipi_dbi_dev, drm); 620 + if (IS_ERR(dbidev)) 621 + return PTR_ERR(dbidev); 622 + 623 + dbi = &dbidev->dbi; 624 + drm = &dbidev->drm; 625 + dbi->reset = reset; 626 + dbidev->regulator = vcc; 627 + 628 + drm_mode_config_init(drm); 629 + 630 + dbidev->backlight = devm_of_find_backlight(dev); 631 + if (IS_ERR(dbidev->backlight)) 632 + return PTR_ERR(dbidev->backlight); 633 + 634 + device_property_read_u32(dev, "rotation", &rotation); 635 + 636 + ret = mipi_dbi_spi_init(spi, dbi, dc); 637 + if (ret) 638 + return ret; 639 + 640 + ret = mipi_dbi_dev_init(dbidev, &ili9341_dbi_funcs, 641 + &ili9341_dbi_mode, rotation); 642 + if (ret) 643 + return ret; 644 + 645 + drm_mode_config_reset(drm); 646 + 647 + ret = drm_dev_register(drm, 0); 648 + if (ret) 649 + return ret; 650 + 651 + spi_set_drvdata(spi, drm); 652 + 653 + drm_fbdev_generic_setup(drm, 0); 654 + 655 + return 0; 656 + } 657 + 658 + static int ili9341_dpi_probe(struct spi_device *spi, struct gpio_desc *dc, 659 + struct gpio_desc *reset) 660 + { 661 + struct device *dev = &spi->dev; 662 + struct ili9341 *ili; 663 + int ret; 664 + 665 + ili = devm_kzalloc(dev, sizeof(struct ili9341), GFP_KERNEL); 666 + if (!ili) 667 + return -ENOMEM; 668 + 669 + ili->dbi = devm_kzalloc(dev, sizeof(struct mipi_dbi), 670 + GFP_KERNEL); 671 + if (!ili->dbi) 672 + return -ENOMEM; 673 + 674 + ili->supplies[0].supply = "vci"; 675 + ili->supplies[1].supply = "vddi"; 676 + ili->supplies[2].supply = "vddi-led"; 677 + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies), 678 + ili->supplies); 679 + if (ret < 0) { 680 + dev_err(dev, "failed to get regulators: %d\n", ret); 681 + return ret; 682 + } 683 + 684 + ret = mipi_dbi_spi_init(spi, ili->dbi, dc); 685 + if (ret) 686 + return ret; 687 + 688 + spi_set_drvdata(spi, ili); 689 + ili->reset_gpio = reset; 690 + /* 691 + * Every new incarnation of this display must have a unique 692 + * data entry for the system in this driver. 693 + */ 694 + ili->conf = of_device_get_match_data(dev); 695 + if (!ili->conf) { 696 + dev_err(dev, "missing device configuration\n"); 697 + return -ENODEV; 698 + } 699 + 700 + ili->max_spi_speed = ili->conf->max_spi_speed; 701 + drm_panel_init(&ili->panel, dev, &ili9341_dpi_funcs, 702 + DRM_MODE_CONNECTOR_DPI); 703 + drm_panel_add(&ili->panel); 704 + 705 + return 0; 706 + } 707 + 708 + static int ili9341_probe(struct spi_device *spi) 709 + { 710 + struct device *dev = &spi->dev; 711 + struct gpio_desc *dc; 712 + struct gpio_desc *reset; 713 + const struct spi_device_id *id = spi_get_device_id(spi); 714 + 715 + reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 716 + if (IS_ERR(reset)) 717 + dev_err(dev, "Failed to get gpio 'reset'\n"); 718 + 719 + dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); 720 + if (IS_ERR(dc)) 721 + dev_err(dev, "Failed to get gpio 'dc'\n"); 722 + 723 + if (!strcmp(id->name, "sf-tc240t-9370-t")) 724 + return ili9341_dpi_probe(spi, dc, reset); 725 + else if (!strcmp(id->name, "yx240qv29")) 726 + return ili9341_dbi_probe(spi, dc, reset); 727 + 728 + return -1; 729 + } 730 + 731 + static int ili9341_remove(struct spi_device *spi) 732 + { 733 + const struct spi_device_id *id = spi_get_device_id(spi); 734 + struct ili9341 *ili = spi_get_drvdata(spi); 735 + struct drm_device *drm = spi_get_drvdata(spi); 736 + 737 + if (!strcmp(id->name, "sf-tc240t-9370-t")) { 738 + ili9341_dpi_power_off(ili); 739 + drm_panel_remove(&ili->panel); 740 + } else if (!strcmp(id->name, "yx240qv29")) { 741 + drm_dev_unplug(drm); 742 + drm_atomic_helper_shutdown(drm); 743 + } 744 + return 0; 745 + } 746 + 747 + static void ili9341_shutdown(struct spi_device *spi) 748 + { 749 + const struct spi_device_id *id = spi_get_device_id(spi); 750 + 751 + if (!strcmp(id->name, "yx240qv29")) 752 + drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 753 + } 754 + 755 + static const struct of_device_id ili9341_of_match[] = { 756 + { 757 + .compatible = "st,sf-tc240t-9370-t", 758 + .data = &ili9341_stm32f429_disco_data, 759 + }, 760 + { 761 + /* porting from tiny/ili9341.c 762 + * for original mipi dbi compitable 763 + */ 764 + .compatible = "adafruit,yx240qv29", 765 + .data = NULL, 766 + }, 767 + { } 768 + }; 769 + MODULE_DEVICE_TABLE(of, ili9341_of_match); 770 + 771 + static const struct spi_device_id ili9341_id[] = { 772 + { "yx240qv29", 0 }, 773 + { "sf-tc240t-9370-t", 0 }, 774 + { } 775 + }; 776 + MODULE_DEVICE_TABLE(spi, ili9341_id); 777 + 778 + static struct spi_driver ili9341_driver = { 779 + .probe = ili9341_probe, 780 + .remove = ili9341_remove, 781 + .shutdown = ili9341_shutdown, 782 + .id_table = ili9341_id, 783 + .driver = { 784 + .name = "panel-ilitek-ili9341", 785 + .of_match_table = ili9341_of_match, 786 + }, 787 + }; 788 + module_spi_driver(ili9341_driver); 789 + 790 + MODULE_AUTHOR("Dillon Min <dillon.minfei@gmail.com>"); 791 + MODULE_DESCRIPTION("ILI9341 LCD panel driver"); 792 + MODULE_LICENSE("GPL v2");