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

Configure Feed

Select the types of activity you want to include in your feed.

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