1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * DRM driver for Multi-Inno MI0283QT panels 4 * 5 * Copyright 2016 Noralf Trønnes 6 */ 7 8 #include <linux/backlight.h> 9 #include <linux/delay.h> 10 #include <linux/gpio/consumer.h> 11 #include <linux/module.h> 12 #include <linux/property.h> 13 #include <linux/regulator/consumer.h> 14 #include <linux/spi/spi.h> 15 16 #include <drm/drm_atomic_helper.h> 17 #include <drm/drm_drv.h> 18 #include <drm/drm_fb_helper.h> 19 #include <drm/drm_gem_cma_helper.h> 20 #include <drm/drm_gem_framebuffer_helper.h> 21 #include <drm/drm_mipi_dbi.h> 22 #include <drm/drm_modeset_helper.h> 23 #include <video/mipi_display.h> 24 25 #define ILI9341_FRMCTR1 0xb1 26 #define ILI9341_DISCTRL 0xb6 27 #define ILI9341_ETMOD 0xb7 28 29 #define ILI9341_PWCTRL1 0xc0 30 #define ILI9341_PWCTRL2 0xc1 31 #define ILI9341_VMCTRL1 0xc5 32 #define ILI9341_VMCTRL2 0xc7 33 #define ILI9341_PWCTRLA 0xcb 34 #define ILI9341_PWCTRLB 0xcf 35 36 #define ILI9341_PGAMCTRL 0xe0 37 #define ILI9341_NGAMCTRL 0xe1 38 #define ILI9341_DTCTRLA 0xe8 39 #define ILI9341_DTCTRLB 0xea 40 #define ILI9341_PWRSEQ 0xed 41 42 #define ILI9341_EN3GAM 0xf2 43 #define ILI9341_PUMPCTRL 0xf7 44 45 #define ILI9341_MADCTL_BGR BIT(3) 46 #define ILI9341_MADCTL_MV BIT(5) 47 #define ILI9341_MADCTL_MX BIT(6) 48 #define ILI9341_MADCTL_MY BIT(7) 49 50 static void mi0283qt_enable(struct drm_simple_display_pipe *pipe, 51 struct drm_crtc_state *crtc_state, 52 struct drm_plane_state *plane_state) 53 { 54 struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 55 struct mipi_dbi *dbi = &dbidev->dbi; 56 u8 addr_mode; 57 int ret, idx; 58 59 if (!drm_dev_enter(pipe->crtc.dev, &idx)) 60 return; 61 62 DRM_DEBUG_KMS("\n"); 63 64 ret = mipi_dbi_poweron_conditional_reset(dbidev); 65 if (ret < 0) 66 goto out_exit; 67 if (ret == 1) 68 goto out_enable; 69 70 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 71 72 mipi_dbi_command(dbi, ILI9341_PWCTRLB, 0x00, 0x83, 0x30); 73 mipi_dbi_command(dbi, ILI9341_PWRSEQ, 0x64, 0x03, 0x12, 0x81); 74 mipi_dbi_command(dbi, ILI9341_DTCTRLA, 0x85, 0x01, 0x79); 75 mipi_dbi_command(dbi, ILI9341_PWCTRLA, 0x39, 0x2c, 0x00, 0x34, 0x02); 76 mipi_dbi_command(dbi, ILI9341_PUMPCTRL, 0x20); 77 mipi_dbi_command(dbi, ILI9341_DTCTRLB, 0x00, 0x00); 78 79 /* Power Control */ 80 mipi_dbi_command(dbi, ILI9341_PWCTRL1, 0x26); 81 mipi_dbi_command(dbi, ILI9341_PWCTRL2, 0x11); 82 /* VCOM */ 83 mipi_dbi_command(dbi, ILI9341_VMCTRL1, 0x35, 0x3e); 84 mipi_dbi_command(dbi, ILI9341_VMCTRL2, 0xbe); 85 86 /* Memory Access Control */ 87 mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT); 88 89 /* Frame Rate */ 90 mipi_dbi_command(dbi, ILI9341_FRMCTR1, 0x00, 0x1b); 91 92 /* Gamma */ 93 mipi_dbi_command(dbi, ILI9341_EN3GAM, 0x08); 94 mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01); 95 mipi_dbi_command(dbi, ILI9341_PGAMCTRL, 96 0x1f, 0x1a, 0x18, 0x0a, 0x0f, 0x06, 0x45, 0x87, 97 0x32, 0x0a, 0x07, 0x02, 0x07, 0x05, 0x00); 98 mipi_dbi_command(dbi, ILI9341_NGAMCTRL, 99 0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3a, 0x78, 100 0x4d, 0x05, 0x18, 0x0d, 0x38, 0x3a, 0x1f); 101 102 /* DDRAM */ 103 mipi_dbi_command(dbi, ILI9341_ETMOD, 0x07); 104 105 /* Display */ 106 mipi_dbi_command(dbi, ILI9341_DISCTRL, 0x0a, 0x82, 0x27, 0x00); 107 mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 108 msleep(100); 109 110 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 111 msleep(100); 112 113 out_enable: 114 /* The PiTFT (ili9340) has a hardware reset circuit that 115 * resets only on power-on and not on each reboot through 116 * a gpio like the rpi-display does. 117 * As a result, we need to always apply the rotation value 118 * regardless of the display "on/off" state. 119 */ 120 switch (dbidev->rotation) { 121 default: 122 addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY | 123 ILI9341_MADCTL_MX; 124 break; 125 case 90: 126 addr_mode = ILI9341_MADCTL_MY; 127 break; 128 case 180: 129 addr_mode = ILI9341_MADCTL_MV; 130 break; 131 case 270: 132 addr_mode = ILI9341_MADCTL_MX; 133 break; 134 } 135 addr_mode |= ILI9341_MADCTL_BGR; 136 mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); 137 mipi_dbi_enable_flush(dbidev, crtc_state, plane_state); 138 out_exit: 139 drm_dev_exit(idx); 140 } 141 142 static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = { 143 .enable = mi0283qt_enable, 144 .disable = mipi_dbi_pipe_disable, 145 .update = mipi_dbi_pipe_update, 146 .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, 147 }; 148 149 static const struct drm_display_mode mi0283qt_mode = { 150 DRM_SIMPLE_MODE(320, 240, 58, 43), 151 }; 152 153 DEFINE_DRM_GEM_CMA_FOPS(mi0283qt_fops); 154 155 static struct drm_driver mi0283qt_driver = { 156 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 157 .fops = &mi0283qt_fops, 158 .release = mipi_dbi_release, 159 DRM_GEM_CMA_VMAP_DRIVER_OPS, 160 .debugfs_init = mipi_dbi_debugfs_init, 161 .name = "mi0283qt", 162 .desc = "Multi-Inno MI0283QT", 163 .date = "20160614", 164 .major = 1, 165 .minor = 0, 166 }; 167 168 static const struct of_device_id mi0283qt_of_match[] = { 169 { .compatible = "multi-inno,mi0283qt" }, 170 {}, 171 }; 172 MODULE_DEVICE_TABLE(of, mi0283qt_of_match); 173 174 static const struct spi_device_id mi0283qt_id[] = { 175 { "mi0283qt", 0 }, 176 { }, 177 }; 178 MODULE_DEVICE_TABLE(spi, mi0283qt_id); 179 180 static int mi0283qt_probe(struct spi_device *spi) 181 { 182 struct device *dev = &spi->dev; 183 struct mipi_dbi_dev *dbidev; 184 struct drm_device *drm; 185 struct mipi_dbi *dbi; 186 struct gpio_desc *dc; 187 u32 rotation = 0; 188 int ret; 189 190 dbidev = kzalloc(sizeof(*dbidev), GFP_KERNEL); 191 if (!dbidev) 192 return -ENOMEM; 193 194 dbi = &dbidev->dbi; 195 drm = &dbidev->drm; 196 ret = devm_drm_dev_init(dev, drm, &mi0283qt_driver); 197 if (ret) { 198 kfree(dbidev); 199 return ret; 200 } 201 202 drm_mode_config_init(drm); 203 204 dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 205 if (IS_ERR(dbi->reset)) { 206 DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); 207 return PTR_ERR(dbi->reset); 208 } 209 210 dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); 211 if (IS_ERR(dc)) { 212 DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); 213 return PTR_ERR(dc); 214 } 215 216 dbidev->regulator = devm_regulator_get(dev, "power"); 217 if (IS_ERR(dbidev->regulator)) 218 return PTR_ERR(dbidev->regulator); 219 220 dbidev->backlight = devm_of_find_backlight(dev); 221 if (IS_ERR(dbidev->backlight)) 222 return PTR_ERR(dbidev->backlight); 223 224 device_property_read_u32(dev, "rotation", &rotation); 225 226 ret = mipi_dbi_spi_init(spi, dbi, dc); 227 if (ret) 228 return ret; 229 230 ret = mipi_dbi_dev_init(dbidev, &mi0283qt_pipe_funcs, &mi0283qt_mode, rotation); 231 if (ret) 232 return ret; 233 234 drm_mode_config_reset(drm); 235 236 ret = drm_dev_register(drm, 0); 237 if (ret) 238 return ret; 239 240 spi_set_drvdata(spi, drm); 241 242 drm_fbdev_generic_setup(drm, 0); 243 244 return 0; 245 } 246 247 static int mi0283qt_remove(struct spi_device *spi) 248 { 249 struct drm_device *drm = spi_get_drvdata(spi); 250 251 drm_dev_unplug(drm); 252 drm_atomic_helper_shutdown(drm); 253 254 return 0; 255 } 256 257 static void mi0283qt_shutdown(struct spi_device *spi) 258 { 259 drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 260 } 261 262 static int __maybe_unused mi0283qt_pm_suspend(struct device *dev) 263 { 264 return drm_mode_config_helper_suspend(dev_get_drvdata(dev)); 265 } 266 267 static int __maybe_unused mi0283qt_pm_resume(struct device *dev) 268 { 269 drm_mode_config_helper_resume(dev_get_drvdata(dev)); 270 271 return 0; 272 } 273 274 static const struct dev_pm_ops mi0283qt_pm_ops = { 275 SET_SYSTEM_SLEEP_PM_OPS(mi0283qt_pm_suspend, mi0283qt_pm_resume) 276 }; 277 278 static struct spi_driver mi0283qt_spi_driver = { 279 .driver = { 280 .name = "mi0283qt", 281 .owner = THIS_MODULE, 282 .of_match_table = mi0283qt_of_match, 283 .pm = &mi0283qt_pm_ops, 284 }, 285 .id_table = mi0283qt_id, 286 .probe = mi0283qt_probe, 287 .remove = mi0283qt_remove, 288 .shutdown = mi0283qt_shutdown, 289 }; 290 module_spi_driver(mi0283qt_spi_driver); 291 292 MODULE_DESCRIPTION("Multi-Inno MI0283QT DRM driver"); 293 MODULE_AUTHOR("Noralf Trønnes"); 294 MODULE_LICENSE("GPL"); 295