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

drm: Add an hx8367d tinydrm driver.

I want to sort out support for tinydrm in vc4, so I needed to get a
tinydrm-appropriate panel working and this is what I had on hand.
This is derived from a combination of ili9341.c from tinydrm and
fb_hx8357d.c from staging's fbtft.

v2: Write my own register defs from the spec to not need the header
from fbtft. Fix spi device string to enable module autoloading.
(Suggestions by Noralf)

Signed-off-by: Eric Anholt <eric@anholt.net>
Link: https://patchwork.freedesktop.org/patch/msgid/20181024184313.2967-3-eric@anholt.net
Reviewed-by: Noralf Trønnes <noralf@tronnes.org> (v1)

+289
+7
MAINTAINERS
··· 4623 4623 F: drivers/gpu/drm/tinydrm/ili9225.c 4624 4624 F: Documentation/devicetree/bindings/display/ilitek,ili9225.txt 4625 4625 4626 + DRM DRIVER FOR HX8357D PANELS 4627 + M: Eric Anholt <eric@anholt.net> 4628 + T: git git://anongit.freedesktop.org/drm/drm-misc 4629 + S: Maintained 4630 + F: drivers/gpu/drm/tinydrm/hx8357d.c 4631 + F: Documentation/devicetree/bindings/display/himax,hx8357d.txt 4632 + 4626 4633 DRM DRIVER FOR INTEL I810 VIDEO CARDS 4627 4634 S: Orphan / Obsolete 4628 4635 F: drivers/gpu/drm/i810/
+11
drivers/gpu/drm/tinydrm/Kconfig
··· 10 10 config TINYDRM_MIPI_DBI 11 11 tristate 12 12 13 + config TINYDRM_HX8357D 14 + tristate "DRM support for HX8357D display panels" 15 + depends on DRM_TINYDRM && SPI 16 + depends on BACKLIGHT_CLASS_DEVICE 17 + select TINYDRM_MIPI_DBI 18 + help 19 + DRM driver for the following HX8357D panels: 20 + * YX350HV15-T 3.5" 340x350 TFT (Adafruit 3.5") 21 + 22 + If M is selected the module will be called hx8357d. 23 + 13 24 config TINYDRM_ILI9225 14 25 tristate "DRM support for ILI9225 display panels" 15 26 depends on DRM_TINYDRM && SPI
+1
drivers/gpu/drm/tinydrm/Makefile
··· 4 4 obj-$(CONFIG_TINYDRM_MIPI_DBI) += mipi-dbi.o 5 5 6 6 # Displays 7 + obj-$(CONFIG_TINYDRM_HX8357D) += hx8357d.o 7 8 obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o 8 9 obj-$(CONFIG_TINYDRM_ILI9341) += ili9341.o 9 10 obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o
+270
drivers/gpu/drm/tinydrm/hx8357d.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * DRM driver for the HX8357D LCD controller 4 + * 5 + * Copyright 2018 Broadcom 6 + * Copyright 2018 David Lechner <david@lechnology.com> 7 + * Copyright 2016 Noralf Trønnes 8 + * Copyright (C) 2015 Adafruit Industries 9 + * Copyright (C) 2013 Christian Vogelgsang 10 + */ 11 + 12 + #include <linux/backlight.h> 13 + #include <linux/delay.h> 14 + #include <linux/gpio/consumer.h> 15 + #include <linux/module.h> 16 + #include <linux/property.h> 17 + #include <linux/spi/spi.h> 18 + 19 + #include <drm/drm_fb_helper.h> 20 + #include <drm/drm_gem_framebuffer_helper.h> 21 + #include <drm/drm_modeset_helper.h> 22 + #include <drm/tinydrm/mipi-dbi.h> 23 + #include <drm/tinydrm/tinydrm-helpers.h> 24 + #include <video/mipi_display.h> 25 + 26 + #define HX8357D_SETOSC 0xb0 27 + #define HX8357D_SETPOWER 0xb1 28 + #define HX8357D_SETRGB 0xb3 29 + #define HX8357D_SETCYC 0xb3 30 + #define HX8357D_SETCOM 0xb6 31 + #define HX8357D_SETEXTC 0xb9 32 + #define HX8357D_SETSTBA 0xc0 33 + #define HX8357D_SETPANEL 0xcc 34 + #define HX8357D_SETGAMMA 0xe0 35 + 36 + #define HX8357D_MADCTL_MY 0x80 37 + #define HX8357D_MADCTL_MX 0x40 38 + #define HX8357D_MADCTL_MV 0x20 39 + #define HX8357D_MADCTL_ML 0x10 40 + #define HX8357D_MADCTL_RGB 0x00 41 + #define HX8357D_MADCTL_BGR 0x08 42 + #define HX8357D_MADCTL_MH 0x04 43 + 44 + static void yx240qv29_enable(struct drm_simple_display_pipe *pipe, 45 + struct drm_crtc_state *crtc_state, 46 + struct drm_plane_state *plane_state) 47 + { 48 + struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); 49 + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); 50 + u8 addr_mode; 51 + int ret; 52 + 53 + DRM_DEBUG_KMS("\n"); 54 + 55 + ret = mipi_dbi_poweron_conditional_reset(mipi); 56 + if (ret < 0) 57 + return; 58 + if (ret == 1) 59 + goto out_enable; 60 + 61 + /* setextc */ 62 + mipi_dbi_command(mipi, HX8357D_SETEXTC, 0xFF, 0x83, 0x57); 63 + msleep(150); 64 + 65 + /* setRGB which also enables SDO */ 66 + mipi_dbi_command(mipi, HX8357D_SETRGB, 0x00, 0x00, 0x06, 0x06); 67 + 68 + /* -1.52V */ 69 + mipi_dbi_command(mipi, HX8357D_SETCOM, 0x25); 70 + 71 + /* Normal mode 70Hz, Idle mode 55 Hz */ 72 + mipi_dbi_command(mipi, HX8357D_SETOSC, 0x68); 73 + 74 + /* Set Panel - BGR, Gate direction swapped */ 75 + mipi_dbi_command(mipi, HX8357D_SETPANEL, 0x05); 76 + 77 + mipi_dbi_command(mipi, HX8357D_SETPOWER, 78 + 0x00, /* Not deep standby */ 79 + 0x15, /* BT */ 80 + 0x1C, /* VSPR */ 81 + 0x1C, /* VSNR */ 82 + 0x83, /* AP */ 83 + 0xAA); /* FS */ 84 + 85 + mipi_dbi_command(mipi, HX8357D_SETSTBA, 86 + 0x50, /* OPON normal */ 87 + 0x50, /* OPON idle */ 88 + 0x01, /* STBA */ 89 + 0x3C, /* STBA */ 90 + 0x1E, /* STBA */ 91 + 0x08); /* GEN */ 92 + 93 + mipi_dbi_command(mipi, HX8357D_SETCYC, 94 + 0x02, /* NW 0x02 */ 95 + 0x40, /* RTN */ 96 + 0x00, /* DIV */ 97 + 0x2A, /* DUM */ 98 + 0x2A, /* DUM */ 99 + 0x0D, /* GDON */ 100 + 0x78); /* GDOFF */ 101 + 102 + mipi_dbi_command(mipi, HX8357D_SETGAMMA, 103 + 0x02, 104 + 0x0A, 105 + 0x11, 106 + 0x1d, 107 + 0x23, 108 + 0x35, 109 + 0x41, 110 + 0x4b, 111 + 0x4b, 112 + 0x42, 113 + 0x3A, 114 + 0x27, 115 + 0x1B, 116 + 0x08, 117 + 0x09, 118 + 0x03, 119 + 0x02, 120 + 0x0A, 121 + 0x11, 122 + 0x1d, 123 + 0x23, 124 + 0x35, 125 + 0x41, 126 + 0x4b, 127 + 0x4b, 128 + 0x42, 129 + 0x3A, 130 + 0x27, 131 + 0x1B, 132 + 0x08, 133 + 0x09, 134 + 0x03, 135 + 0x00, 136 + 0x01); 137 + 138 + /* 16 bit */ 139 + mipi_dbi_command(mipi, MIPI_DCS_SET_PIXEL_FORMAT, 140 + MIPI_DCS_PIXEL_FMT_16BIT); 141 + 142 + /* TE off */ 143 + mipi_dbi_command(mipi, MIPI_DCS_SET_TEAR_ON, 0x00); 144 + 145 + /* tear line */ 146 + mipi_dbi_command(mipi, MIPI_DCS_SET_TEAR_SCANLINE, 0x00, 0x02); 147 + 148 + /* Exit Sleep */ 149 + mipi_dbi_command(mipi, MIPI_DCS_EXIT_SLEEP_MODE); 150 + msleep(150); 151 + 152 + /* display on */ 153 + mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON); 154 + usleep_range(5000, 7000); 155 + 156 + out_enable: 157 + switch (mipi->rotation) { 158 + default: 159 + addr_mode = HX8357D_MADCTL_MX | HX8357D_MADCTL_MY; 160 + break; 161 + case 90: 162 + addr_mode = HX8357D_MADCTL_MV | HX8357D_MADCTL_MY; 163 + break; 164 + case 180: 165 + addr_mode = 0; 166 + break; 167 + case 270: 168 + addr_mode = HX8357D_MADCTL_MV | HX8357D_MADCTL_MX; 169 + break; 170 + } 171 + mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); 172 + mipi_dbi_enable_flush(mipi, crtc_state, plane_state); 173 + } 174 + 175 + static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = { 176 + .enable = yx240qv29_enable, 177 + .disable = mipi_dbi_pipe_disable, 178 + .update = tinydrm_display_pipe_update, 179 + .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, 180 + }; 181 + 182 + static const struct drm_display_mode yx350hv15_mode = { 183 + TINYDRM_MODE(320, 480, 60, 75), 184 + }; 185 + 186 + DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops); 187 + 188 + static struct drm_driver hx8357d_driver = { 189 + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, 190 + .fops = &hx8357d_fops, 191 + TINYDRM_GEM_DRIVER_OPS, 192 + .debugfs_init = mipi_dbi_debugfs_init, 193 + .name = "hx8357d", 194 + .desc = "HX8357D", 195 + .date = "20181023", 196 + .major = 1, 197 + .minor = 0, 198 + }; 199 + 200 + static const struct of_device_id hx8357d_of_match[] = { 201 + { .compatible = "adafruit,yx350hv15" }, 202 + { } 203 + }; 204 + MODULE_DEVICE_TABLE(of, hx8357d_of_match); 205 + 206 + static const struct spi_device_id hx8357d_id[] = { 207 + { "yx350hv15", 0 }, 208 + { } 209 + }; 210 + MODULE_DEVICE_TABLE(spi, hx8357d_id); 211 + 212 + static int hx8357d_probe(struct spi_device *spi) 213 + { 214 + struct device *dev = &spi->dev; 215 + struct mipi_dbi *mipi; 216 + struct gpio_desc *dc; 217 + u32 rotation = 0; 218 + int ret; 219 + 220 + mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL); 221 + if (!mipi) 222 + return -ENOMEM; 223 + 224 + dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW); 225 + if (IS_ERR(dc)) { 226 + DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); 227 + return PTR_ERR(dc); 228 + } 229 + 230 + mipi->backlight = devm_of_find_backlight(dev); 231 + if (IS_ERR(mipi->backlight)) 232 + return PTR_ERR(mipi->backlight); 233 + 234 + device_property_read_u32(dev, "rotation", &rotation); 235 + 236 + ret = mipi_dbi_spi_init(spi, mipi, dc); 237 + if (ret) 238 + return ret; 239 + 240 + ret = mipi_dbi_init(&spi->dev, mipi, &hx8357d_pipe_funcs, 241 + &hx8357d_driver, &yx350hv15_mode, rotation); 242 + if (ret) 243 + return ret; 244 + 245 + spi_set_drvdata(spi, mipi); 246 + 247 + return devm_tinydrm_register(&mipi->tinydrm); 248 + } 249 + 250 + static void hx8357d_shutdown(struct spi_device *spi) 251 + { 252 + struct mipi_dbi *mipi = spi_get_drvdata(spi); 253 + 254 + tinydrm_shutdown(&mipi->tinydrm); 255 + } 256 + 257 + static struct spi_driver hx8357d_spi_driver = { 258 + .driver = { 259 + .name = "hx8357d", 260 + .of_match_table = hx8357d_of_match, 261 + }, 262 + .id_table = hx8357d_id, 263 + .probe = hx8357d_probe, 264 + .shutdown = hx8357d_shutdown, 265 + }; 266 + module_spi_driver(hx8357d_spi_driver); 267 + 268 + MODULE_DESCRIPTION("HX8357D DRM driver"); 269 + MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); 270 + MODULE_LICENSE("GPL");