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

drm/panel: Add Ilitek ILI9322 driver

This adds support for the Ilitek ILI9322 QVGA (320x240)
TFT panel driver.

This panel driver supports serial or parallel RGB or
YUV input and also ITU-T BT.656 input streams.

The controller is combined with a physical panel and
configured through the device tree.

Cc: David Lechner <david@lechnology.com>
Cc: Stefano Babic <sbabic@denx.de>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20171221234411.12156-1-linus.walleij@linaro.org

+971
+8
drivers/gpu/drm/panel/Kconfig
··· 28 28 that it can be automatically turned off when the panel goes into a 29 29 low power state. 30 30 31 + config DRM_PANEL_ILITEK_IL9322 32 + tristate "Ilitek ILI9322 320x240 QVGA panels" 33 + depends on OF && SPI 34 + select REGMAP 35 + help 36 + Say Y here if you want to enable support for Ilitek IL9322 37 + QVGA (320x240) RGB, YUV and ITU-T BT.656 panels. 38 + 31 39 config DRM_PANEL_INNOLUX_P079ZCA 32 40 tristate "Innolux P079ZCA panel" 33 41 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o 3 3 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o 4 + obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o 4 5 obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o 5 6 obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o 6 7 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
+962
drivers/gpu/drm/panel/panel-ilitek-ili9322.c
··· 1 + /* 2 + * Ilitek ILI9322 TFT LCD drm_panel driver. 3 + * 4 + * This panel can be configured to support: 5 + * - 8-bit serial RGB interface 6 + * - 24-bit parallel RGB interface 7 + * - 8-bit ITU-R BT.601 interface 8 + * - 8-bit ITU-R BT.656 interface 9 + * - Up to 320RGBx240 dots resolution TFT LCD displays 10 + * - Scaling, brightness and contrast 11 + * 12 + * The scaling means that the display accepts a 640x480 or 720x480 13 + * input and rescales it to fit to the 320x240 display. So what we 14 + * present to the system is something else than what comes out on the 15 + * actual display. 16 + * 17 + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 18 + * Derived from drivers/drm/gpu/panel/panel-samsung-ld9040.c 19 + * 20 + * This program is free software; you can redistribute it and/or modify 21 + * it under the terms of the GNU General Public License version 2 as 22 + * published by the Free Software Foundation. 23 + */ 24 + 25 + #include <drm/drmP.h> 26 + #include <drm/drm_panel.h> 27 + 28 + #include <linux/of_device.h> 29 + #include <linux/bitops.h> 30 + #include <linux/gpio/consumer.h> 31 + #include <linux/module.h> 32 + #include <linux/regmap.h> 33 + #include <linux/regulator/consumer.h> 34 + #include <linux/spi/spi.h> 35 + 36 + #include <video/mipi_display.h> 37 + #include <video/of_videomode.h> 38 + #include <video/videomode.h> 39 + 40 + #define ILI9322_CHIP_ID 0x00 41 + #define ILI9322_CHIP_ID_MAGIC 0x96 42 + 43 + /* 44 + * Voltage on the communication interface, from 0.7 (0x00) 45 + * to 1.32 (0x1f) times the VREG1OUT voltage in 2% increments. 46 + * 1.00 (0x0f) is the default. 47 + */ 48 + #define ILI9322_VCOM_AMP 0x01 49 + 50 + /* 51 + * High voltage on the communication signals, from 0.37 (0x00) to 52 + * 1.0 (0x3f) times the VREGOUT1 voltage in 1% increments. 53 + * 0.83 (0x2e) is the default. 54 + */ 55 + #define ILI9322_VCOM_HIGH 0x02 56 + 57 + /* 58 + * VREG1 voltage regulator from 3.6V (0x00) to 6.0V (0x18) in 0.1V 59 + * increments. 5.4V (0x12) is the default. This is the reference 60 + * voltage for the VCOM levels and the greyscale level. 61 + */ 62 + #define ILI9322_VREG1_VOLTAGE 0x03 63 + 64 + /* Describes the incoming signal */ 65 + #define ILI9322_ENTRY 0x06 66 + /* 0 = right-to-left, 1 = left-to-right (default), horizontal flip */ 67 + #define ILI9322_ENTRY_HDIR BIT(0) 68 + /* 0 = down-to-up, 1 = up-to-down (default), vertical flip */ 69 + #define ILI9322_ENTRY_VDIR BIT(1) 70 + /* NTSC, PAL or autodetect */ 71 + #define ILI9322_ENTRY_NTSC (0 << 2) 72 + #define ILI9322_ENTRY_PAL (1 << 2) 73 + #define ILI9322_ENTRY_AUTODETECT (3 << 2) 74 + /* Input format */ 75 + #define ILI9322_ENTRY_SERIAL_RGB_THROUGH (0 << 4) 76 + #define ILI9322_ENTRY_SERIAL_RGB_ALIGNED (1 << 4) 77 + #define ILI9322_ENTRY_SERIAL_RGB_DUMMY_320X240 (2 << 4) 78 + #define ILI9322_ENTRY_SERIAL_RGB_DUMMY_360X240 (3 << 4) 79 + #define ILI9322_ENTRY_DISABLE_1 (4 << 4) 80 + #define ILI9322_ENTRY_PARALLEL_RGB_THROUGH (5 << 4) 81 + #define ILI9322_ENTRY_PARALLEL_RGB_ALIGNED (6 << 4) 82 + #define ILI9322_ENTRY_YUV_640Y_320CBCR_25_54_MHZ (7 << 4) 83 + #define ILI9322_ENTRY_YUV_720Y_360CBCR_27_MHZ (8 << 4) 84 + #define ILI9322_ENTRY_DISABLE_2 (9 << 4) 85 + #define ILI9322_ENTRY_ITU_R_BT_656_720X360 (10 << 4) 86 + #define ILI9322_ENTRY_ITU_R_BT_656_640X320 (11 << 4) 87 + 88 + /* Power control */ 89 + #define ILI9322_POW_CTRL 0x07 90 + #define ILI9322_POW_CTRL_STB BIT(0) /* 0 = standby, 1 = normal */ 91 + #define ILI9322_POW_CTRL_VGL BIT(1) /* 0 = off, 1 = on */ 92 + #define ILI9322_POW_CTRL_VGH BIT(2) /* 0 = off, 1 = on */ 93 + #define ILI9322_POW_CTRL_DDVDH BIT(3) /* 0 = off, 1 = on */ 94 + #define ILI9322_POW_CTRL_VCOM BIT(4) /* 0 = off, 1 = on */ 95 + #define ILI9322_POW_CTRL_VCL BIT(5) /* 0 = off, 1 = on */ 96 + #define ILI9322_POW_CTRL_AUTO BIT(6) /* 0 = interactive, 1 = auto */ 97 + #define ILI9322_POW_CTRL_STANDBY (ILI9322_POW_CTRL_VGL | \ 98 + ILI9322_POW_CTRL_VGH | \ 99 + ILI9322_POW_CTRL_DDVDH | \ 100 + ILI9322_POW_CTRL_VCL | \ 101 + ILI9322_POW_CTRL_AUTO | \ 102 + BIT(7)) 103 + #define ILI9322_POW_CTRL_DEFAULT (ILI9322_POW_CTRL_STANDBY | \ 104 + ILI9322_POW_CTRL_STB) 105 + 106 + /* Vertical back porch bits 0..5 */ 107 + #define ILI9322_VBP 0x08 108 + 109 + /* Horizontal back porch, 8 bits */ 110 + #define ILI9322_HBP 0x09 111 + 112 + /* 113 + * Polarity settings: 114 + * 1 = positive polarity 115 + * 0 = negative polarity 116 + */ 117 + #define ILI9322_POL 0x0a 118 + #define ILI9322_POL_DCLK BIT(0) /* 1 default */ 119 + #define ILI9322_POL_HSYNC BIT(1) /* 0 default */ 120 + #define ILI9322_POL_VSYNC BIT(2) /* 0 default */ 121 + #define ILI9322_POL_DE BIT(3) /* 1 default */ 122 + /* 123 + * 0 means YCBCR are ordered Cb0,Y0,Cr0,Y1,Cb2,Y2,Cr2,Y3 (default) 124 + * in RGB mode this means RGB comes in RGBRGB 125 + * 1 means YCBCR are ordered Cr0,Y0,Cb0,Y1,Cr2,Y2,Cb2,Y3 126 + * in RGB mode this means RGB comes in BGRBGR 127 + */ 128 + #define ILI9322_POL_YCBCR_MODE BIT(4) 129 + /* Formula A for YCbCR->RGB = 0, Formula B = 1 */ 130 + #define ILI9322_POL_FORMULA BIT(5) 131 + /* Reverse polarity: 0 = 0..255, 1 = 255..0 */ 132 + #define ILI9322_POL_REV BIT(6) 133 + 134 + #define ILI9322_IF_CTRL 0x0b 135 + #define ILI9322_IF_CTRL_HSYNC_VSYNC 0x00 136 + #define ILI9322_IF_CTRL_HSYNC_VSYNC_DE BIT(2) 137 + #define ILI9322_IF_CTRL_DE_ONLY BIT(3) 138 + #define ILI9322_IF_CTRL_SYNC_DISABLED (BIT(2) | BIT(3)) 139 + #define ILI9322_IF_CTRL_LINE_INVERSION BIT(0) /* Not set means frame inv */ 140 + 141 + #define ILI9322_GLOBAL_RESET 0x04 142 + #define ILI9322_GLOBAL_RESET_ASSERT 0x00 /* bit 0 = 0 -> reset */ 143 + 144 + /* 145 + * 4+4 bits of negative and positive gamma correction 146 + * Upper nybble, bits 4-7 are negative gamma 147 + * Lower nybble, bits 0-3 are positive gamma 148 + */ 149 + #define ILI9322_GAMMA_1 0x10 150 + #define ILI9322_GAMMA_2 0x11 151 + #define ILI9322_GAMMA_3 0x12 152 + #define ILI9322_GAMMA_4 0x13 153 + #define ILI9322_GAMMA_5 0x14 154 + #define ILI9322_GAMMA_6 0x15 155 + #define ILI9322_GAMMA_7 0x16 156 + #define ILI9322_GAMMA_8 0x17 157 + 158 + /** 159 + * enum ili9322_input - the format of the incoming signal to the panel 160 + * 161 + * The panel can be connected to various input streams and four of them can 162 + * be selected by electronic straps on the display. However it is possible 163 + * to select another mode or override the electronic default with this 164 + * setting. 165 + */ 166 + enum ili9322_input { 167 + ILI9322_INPUT_SRGB_THROUGH = 0x0, 168 + ILI9322_INPUT_SRGB_ALIGNED = 0x1, 169 + ILI9322_INPUT_SRGB_DUMMY_320X240 = 0x2, 170 + ILI9322_INPUT_SRGB_DUMMY_360X240 = 0x3, 171 + ILI9322_INPUT_DISABLED_1 = 0x4, 172 + ILI9322_INPUT_PRGB_THROUGH = 0x5, 173 + ILI9322_INPUT_PRGB_ALIGNED = 0x6, 174 + ILI9322_INPUT_YUV_640X320_YCBCR = 0x7, 175 + ILI9322_INPUT_YUV_720X360_YCBCR = 0x8, 176 + ILI9322_INPUT_DISABLED_2 = 0x9, 177 + ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR = 0xa, 178 + ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR = 0xb, 179 + ILI9322_INPUT_UNKNOWN = 0xc, 180 + }; 181 + 182 + const char *ili9322_inputs[] = { 183 + "8 bit serial RGB through", 184 + "8 bit serial RGB aligned", 185 + "8 bit serial RGB dummy 320x240", 186 + "8 bit serial RGB dummy 360x240", 187 + "disabled 1", 188 + "24 bit parallel RGB through", 189 + "24 bit parallel RGB aligned", 190 + "24 bit YUV 640Y 320CbCr", 191 + "24 bit YUV 720Y 360CbCr", 192 + "disabled 2", 193 + "8 bit ITU-R BT.656 720Y 360CbCr", 194 + "8 bit ITU-R BT.656 640Y 320CbCr", 195 + }; 196 + 197 + /** 198 + * struct ili9322_config - the system specific ILI9322 configuration 199 + * @width_mm: physical panel width [mm] 200 + * @height_mm: physical panel height [mm] 201 + * @flip_horizontal: flip the image horizontally (right-to-left scan) 202 + * (only in RGB and YUV modes) 203 + * @flip_vertical: flip the image vertically (down-to-up scan) 204 + * (only in RGB and YUV modes) 205 + * @input: the input/entry type used in this system, if this is set to 206 + * ILI9322_INPUT_UNKNOWN the driver will try to figure it out by probing 207 + * the hardware 208 + * @vreg1out_mv: the output in microvolts for the VREGOUT1 regulator used 209 + * to drive the physical display. Valid ranges are 3600 thru 6000 in 100 210 + * microvolt increments. If not specified, hardware defaults will be 211 + * used (4.5V). 212 + * @vcom_high_percent: the percentage of VREGOUT1 used for the peak 213 + * voltage on the communications link. Valid ranges are 37 thru 100 214 + * percent. If not specified, hardware defaults will be used (91%). 215 + * @vcom_amplitude_percent: the percentage of VREGOUT1 used for the 216 + * peak-to-peak amplitude of the communcation signals to the physical 217 + * display. Valid ranges are 70 thru 132 percent in increments if two 218 + * percent. Odd percentages will be truncated. If not specified, hardware 219 + * defaults will be used (114%). 220 + * @dclk_active_high: data/pixel clock active high, data will be clocked 221 + * in on the rising edge of the DCLK (this is usually the case). 222 + * @syncmode: The synchronization mode, what sync signals are emitted. 223 + * See the enum for details. 224 + * @de_active_high: DE (data entry) is active high 225 + * @hsync_active_high: HSYNC is active high 226 + * @vsync_active_high: VSYNC is active high 227 + * @gamma_corr_pos: a set of 8 nybbles describing positive 228 + * gamma correction for voltages V1 thru V8. Valid range 0..15 229 + * @gamma_corr_neg: a set of 8 nybbles describing negative 230 + * gamma correction for voltages V1 thru V8. Valid range 0..15 231 + * 232 + * These adjust what grayscale voltage will be output for input data V1 = 0, 233 + * V2 = 16, V3 = 48, V4 = 96, V5 = 160, V6 = 208, V7 = 240 and V8 = 255. 234 + * The curve is shaped like this: 235 + * 236 + * ^ 237 + * | V8 238 + * | V7 239 + * | V6 240 + * | V5 241 + * | V4 242 + * | V3 243 + * | V2 244 + * | V1 245 + * +-----------------------------------------------------------> 246 + * 0 16 48 96 160 208 240 255 247 + * 248 + * The negative and postive gamma values adjust the V1 thru V8 up/down 249 + * according to the datasheet specifications. This is a property of the 250 + * physical display connected to the display controller and may vary. 251 + * If defined, both arrays must be supplied in full. If the properties 252 + * are not supplied, hardware defaults will be used. 253 + */ 254 + struct ili9322_config { 255 + u32 width_mm; 256 + u32 height_mm; 257 + bool flip_horizontal; 258 + bool flip_vertical; 259 + enum ili9322_input input; 260 + u32 vreg1out_mv; 261 + u32 vcom_high_percent; 262 + u32 vcom_amplitude_percent; 263 + bool dclk_active_high; 264 + bool de_active_high; 265 + bool hsync_active_high; 266 + bool vsync_active_high; 267 + u8 syncmode; 268 + u8 gamma_corr_pos[8]; 269 + u8 gamma_corr_neg[8]; 270 + }; 271 + 272 + struct ili9322 { 273 + struct device *dev; 274 + const struct ili9322_config *conf; 275 + struct drm_panel panel; 276 + struct regmap *regmap; 277 + struct regulator_bulk_data supplies[3]; 278 + struct gpio_desc *reset_gpio; 279 + enum ili9322_input input; 280 + struct videomode vm; 281 + u8 gamma[8]; 282 + u8 vreg1out; 283 + u8 vcom_high; 284 + u8 vcom_amplitude; 285 + }; 286 + 287 + static inline struct ili9322 *panel_to_ili9322(struct drm_panel *panel) 288 + { 289 + return container_of(panel, struct ili9322, panel); 290 + } 291 + 292 + static int ili9322_regmap_spi_write(void *context, const void *data, 293 + size_t count) 294 + { 295 + struct device *dev = context; 296 + struct spi_device *spi = to_spi_device(dev); 297 + u8 buf[2]; 298 + 299 + /* Clear bit 7 to write */ 300 + memcpy(buf, data, 2); 301 + buf[0] &= ~0x80; 302 + 303 + dev_dbg(dev, "WRITE: %02x %02x\n", buf[0], buf[1]); 304 + return spi_write_then_read(spi, buf, 2, NULL, 0); 305 + } 306 + 307 + static int ili9322_regmap_spi_read(void *context, const void *reg, 308 + size_t reg_size, void *val, size_t val_size) 309 + { 310 + struct device *dev = context; 311 + struct spi_device *spi = to_spi_device(dev); 312 + u8 buf[1]; 313 + 314 + /* Set bit 7 to 1 to read */ 315 + memcpy(buf, reg, 1); 316 + dev_dbg(dev, "READ: %02x reg size = %zu, val size = %zu\n", 317 + buf[0], reg_size, val_size); 318 + buf[0] |= 0x80; 319 + 320 + return spi_write_then_read(spi, buf, 1, val, 1); 321 + } 322 + 323 + static struct regmap_bus ili9322_regmap_bus = { 324 + .write = ili9322_regmap_spi_write, 325 + .read = ili9322_regmap_spi_read, 326 + .reg_format_endian_default = REGMAP_ENDIAN_BIG, 327 + .val_format_endian_default = REGMAP_ENDIAN_BIG, 328 + }; 329 + 330 + static bool ili9322_volatile_reg(struct device *dev, unsigned int reg) 331 + { 332 + return false; 333 + } 334 + 335 + static bool ili9322_writeable_reg(struct device *dev, unsigned int reg) 336 + { 337 + /* Just register 0 is read-only */ 338 + if (reg == 0x00) 339 + return false; 340 + return true; 341 + } 342 + 343 + const struct regmap_config ili9322_regmap_config = { 344 + .reg_bits = 8, 345 + .val_bits = 8, 346 + .max_register = 0x44, 347 + .cache_type = REGCACHE_RBTREE, 348 + .volatile_reg = ili9322_volatile_reg, 349 + .writeable_reg = ili9322_writeable_reg, 350 + }; 351 + 352 + static int ili9322_init(struct drm_panel *panel, struct ili9322 *ili) 353 + { 354 + struct drm_connector *connector = panel->connector; 355 + u8 reg; 356 + int ret; 357 + int i; 358 + 359 + /* Reset display */ 360 + ret = regmap_write(ili->regmap, ILI9322_GLOBAL_RESET, 361 + ILI9322_GLOBAL_RESET_ASSERT); 362 + if (ret) { 363 + dev_err(ili->dev, "can't issue GRESET (%d)\n", ret); 364 + return ret; 365 + } 366 + 367 + /* Set up the main voltage regulator */ 368 + if (ili->vreg1out != U8_MAX) { 369 + ret = regmap_write(ili->regmap, ILI9322_VREG1_VOLTAGE, 370 + ili->vreg1out); 371 + if (ret) { 372 + dev_err(ili->dev, "can't set up VREG1OUT (%d)\n", ret); 373 + return ret; 374 + } 375 + } 376 + 377 + if (ili->vcom_amplitude != U8_MAX) { 378 + ret = regmap_write(ili->regmap, ILI9322_VCOM_AMP, 379 + ili->vcom_amplitude); 380 + if (ret) { 381 + dev_err(ili->dev, 382 + "can't set up VCOM amplitude (%d)\n", ret); 383 + return ret; 384 + } 385 + }; 386 + 387 + if (ili->vcom_high != U8_MAX) { 388 + ret = regmap_write(ili->regmap, ILI9322_VCOM_HIGH, 389 + ili->vcom_high); 390 + if (ret) { 391 + dev_err(ili->dev, "can't set up VCOM high (%d)\n", ret); 392 + return ret; 393 + } 394 + }; 395 + 396 + /* Set up gamma correction */ 397 + for (i = 0; i < ARRAY_SIZE(ili->gamma); i++) { 398 + ret = regmap_write(ili->regmap, ILI9322_GAMMA_1 + i, 399 + ili->gamma[i]); 400 + if (ret) { 401 + dev_err(ili->dev, 402 + "can't write gamma V%d to 0x%02x (%d)\n", 403 + i + 1, ILI9322_GAMMA_1 + i, ret); 404 + return ret; 405 + } 406 + } 407 + 408 + /* 409 + * Polarity and inverted color order for RGB input. 410 + * None of this applies in the BT.656 mode. 411 + */ 412 + if (ili->conf->dclk_active_high) { 413 + reg = ILI9322_POL_DCLK; 414 + connector->display_info.bus_flags |= 415 + DRM_BUS_FLAG_PIXDATA_POSEDGE; 416 + } else { 417 + reg = 0; 418 + connector->display_info.bus_flags |= 419 + DRM_BUS_FLAG_PIXDATA_NEGEDGE; 420 + } 421 + if (ili->conf->de_active_high) { 422 + reg |= ILI9322_POL_DE; 423 + connector->display_info.bus_flags |= 424 + DRM_BUS_FLAG_DE_HIGH; 425 + } else { 426 + connector->display_info.bus_flags |= 427 + DRM_BUS_FLAG_DE_LOW; 428 + } 429 + if (ili->conf->hsync_active_high) 430 + reg |= ILI9322_POL_HSYNC; 431 + if (ili->conf->vsync_active_high) 432 + reg |= ILI9322_POL_VSYNC; 433 + ret = regmap_write(ili->regmap, ILI9322_POL, reg); 434 + if (ret) { 435 + dev_err(ili->dev, "can't write POL register (%d)\n", ret); 436 + return ret; 437 + } 438 + 439 + /* 440 + * Set up interface control. 441 + * This is not used in the BT.656 mode (no H/Vsync or DE signals). 442 + */ 443 + reg = ili->conf->syncmode; 444 + reg |= ILI9322_IF_CTRL_LINE_INVERSION; 445 + ret = regmap_write(ili->regmap, ILI9322_IF_CTRL, reg); 446 + if (ret) { 447 + dev_err(ili->dev, "can't write IF CTRL register (%d)\n", ret); 448 + return ret; 449 + } 450 + 451 + /* Set up the input mode */ 452 + reg = (ili->input << 4); 453 + /* These are inverted, setting to 1 is the default, clearing flips */ 454 + if (!ili->conf->flip_horizontal) 455 + reg |= ILI9322_ENTRY_HDIR; 456 + if (!ili->conf->flip_vertical) 457 + reg |= ILI9322_ENTRY_VDIR; 458 + reg |= ILI9322_ENTRY_AUTODETECT; 459 + ret = regmap_write(ili->regmap, ILI9322_ENTRY, reg); 460 + if (ret) { 461 + dev_err(ili->dev, "can't write ENTRY reg (%d)\n", ret); 462 + return ret; 463 + } 464 + dev_info(ili->dev, "display is in %s mode, syncmode %02x\n", 465 + ili9322_inputs[ili->input], 466 + ili->conf->syncmode); 467 + 468 + dev_info(ili->dev, "initialized display\n"); 469 + 470 + return 0; 471 + } 472 + 473 + /* 474 + * This power-on sequence if from the datasheet, page 57. 475 + */ 476 + static int ili9322_power_on(struct ili9322 *ili) 477 + { 478 + int ret; 479 + 480 + /* Assert RESET */ 481 + gpiod_set_value(ili->reset_gpio, 1); 482 + 483 + ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies), ili->supplies); 484 + if (ret < 0) { 485 + dev_err(ili->dev, "unable to enable regulators\n"); 486 + return ret; 487 + } 488 + msleep(20); 489 + 490 + /* De-assert RESET */ 491 + gpiod_set_value(ili->reset_gpio, 0); 492 + 493 + msleep(10); 494 + 495 + return 0; 496 + } 497 + 498 + static int ili9322_power_off(struct ili9322 *ili) 499 + { 500 + return regulator_bulk_disable(ARRAY_SIZE(ili->supplies), ili->supplies); 501 + } 502 + 503 + static int ili9322_disable(struct drm_panel *panel) 504 + { 505 + struct ili9322 *ili = panel_to_ili9322(panel); 506 + int ret; 507 + 508 + ret = regmap_write(ili->regmap, ILI9322_POW_CTRL, 509 + ILI9322_POW_CTRL_STANDBY); 510 + if (ret) { 511 + dev_err(ili->dev, "unable to go to standby mode\n"); 512 + return ret; 513 + } 514 + 515 + return 0; 516 + } 517 + 518 + static int ili9322_unprepare(struct drm_panel *panel) 519 + { 520 + struct ili9322 *ili = panel_to_ili9322(panel); 521 + 522 + return ili9322_power_off(ili); 523 + } 524 + 525 + static int ili9322_prepare(struct drm_panel *panel) 526 + { 527 + struct ili9322 *ili = panel_to_ili9322(panel); 528 + int ret; 529 + 530 + ret = ili9322_power_on(ili); 531 + if (ret < 0) 532 + return ret; 533 + 534 + ret = ili9322_init(panel, ili); 535 + if (ret < 0) 536 + ili9322_unprepare(panel); 537 + 538 + return ret; 539 + } 540 + 541 + static int ili9322_enable(struct drm_panel *panel) 542 + { 543 + struct ili9322 *ili = panel_to_ili9322(panel); 544 + int ret; 545 + 546 + ret = regmap_write(ili->regmap, ILI9322_POW_CTRL, 547 + ILI9322_POW_CTRL_DEFAULT); 548 + if (ret) { 549 + dev_err(ili->dev, "unable to enable panel\n"); 550 + return ret; 551 + } 552 + 553 + return 0; 554 + } 555 + 556 + /* Serial RGB modes */ 557 + static const struct drm_display_mode srgb_320x240_mode = { 558 + .clock = 2453500, 559 + .hdisplay = 320, 560 + .hsync_start = 320 + 359, 561 + .hsync_end = 320 + 359 + 1, 562 + .htotal = 320 + 359 + 1 + 241, 563 + .vdisplay = 240, 564 + .vsync_start = 240 + 4, 565 + .vsync_end = 240 + 4 + 1, 566 + .vtotal = 262, 567 + .vrefresh = 60, 568 + .flags = 0, 569 + }; 570 + 571 + static const struct drm_display_mode srgb_360x240_mode = { 572 + .clock = 2700000, 573 + .hdisplay = 360, 574 + .hsync_start = 360 + 35, 575 + .hsync_end = 360 + 35 + 1, 576 + .htotal = 360 + 35 + 1 + 241, 577 + .vdisplay = 240, 578 + .vsync_start = 240 + 21, 579 + .vsync_end = 240 + 21 + 1, 580 + .vtotal = 262, 581 + .vrefresh = 60, 582 + .flags = 0, 583 + }; 584 + 585 + /* This is the only mode listed for parallel RGB in the datasheet */ 586 + static const struct drm_display_mode prgb_320x240_mode = { 587 + .clock = 6400000, 588 + .hdisplay = 320, 589 + .hsync_start = 320 + 38, 590 + .hsync_end = 320 + 38 + 1, 591 + .htotal = 320 + 38 + 1 + 50, 592 + .vdisplay = 240, 593 + .vsync_start = 240 + 4, 594 + .vsync_end = 240 + 4 + 1, 595 + .vtotal = 262, 596 + .vrefresh = 60, 597 + .flags = 0, 598 + }; 599 + 600 + /* YUV modes */ 601 + static const struct drm_display_mode yuv_640x320_mode = { 602 + .clock = 2454000, 603 + .hdisplay = 640, 604 + .hsync_start = 640 + 252, 605 + .hsync_end = 640 + 252 + 1, 606 + .htotal = 640 + 252 + 1 + 28, 607 + .vdisplay = 320, 608 + .vsync_start = 320 + 4, 609 + .vsync_end = 320 + 4 + 1, 610 + .vtotal = 320 + 4 + 1 + 18, 611 + .vrefresh = 60, 612 + .flags = 0, 613 + }; 614 + 615 + static const struct drm_display_mode yuv_720x360_mode = { 616 + .clock = 2700000, 617 + .hdisplay = 720, 618 + .hsync_start = 720 + 252, 619 + .hsync_end = 720 + 252 + 1, 620 + .htotal = 720 + 252 + 1 + 24, 621 + .vdisplay = 360, 622 + .vsync_start = 360 + 4, 623 + .vsync_end = 360 + 4 + 1, 624 + .vtotal = 360 + 4 + 1 + 18, 625 + .vrefresh = 60, 626 + .flags = 0, 627 + }; 628 + 629 + /* BT.656 VGA mode, 640x480 */ 630 + static const struct drm_display_mode itu_r_bt_656_640_mode = { 631 + .clock = 2454000, 632 + .hdisplay = 640, 633 + .hsync_start = 640 + 3, 634 + .hsync_end = 640 + 3 + 1, 635 + .htotal = 640 + 3 + 1 + 272, 636 + .vdisplay = 480, 637 + .vsync_start = 480 + 4, 638 + .vsync_end = 480 + 4 + 1, 639 + .vtotal = 500, 640 + .vrefresh = 60, 641 + .flags = 0, 642 + }; 643 + 644 + /* BT.656 D1 mode 720x480 */ 645 + static const struct drm_display_mode itu_r_bt_656_720_mode = { 646 + .clock = 2700000, 647 + .hdisplay = 720, 648 + .hsync_start = 720 + 3, 649 + .hsync_end = 720 + 3 + 1, 650 + .htotal = 720 + 3 + 1 + 272, 651 + .vdisplay = 480, 652 + .vsync_start = 480 + 4, 653 + .vsync_end = 480 + 4 + 1, 654 + .vtotal = 500, 655 + .vrefresh = 60, 656 + .flags = 0, 657 + }; 658 + 659 + static int ili9322_get_modes(struct drm_panel *panel) 660 + { 661 + struct drm_connector *connector = panel->connector; 662 + struct ili9322 *ili = panel_to_ili9322(panel); 663 + struct drm_display_mode *mode; 664 + 665 + strncpy(connector->display_info.name, "ILI9322 TFT LCD driver\0", 666 + DRM_DISPLAY_INFO_LEN); 667 + connector->display_info.width_mm = ili->conf->width_mm; 668 + connector->display_info.height_mm = ili->conf->height_mm; 669 + 670 + switch (ili->input) { 671 + case ILI9322_INPUT_SRGB_DUMMY_320X240: 672 + mode = drm_mode_duplicate(panel->drm, &srgb_320x240_mode); 673 + break; 674 + case ILI9322_INPUT_SRGB_DUMMY_360X240: 675 + mode = drm_mode_duplicate(panel->drm, &srgb_360x240_mode); 676 + break; 677 + case ILI9322_INPUT_PRGB_THROUGH: 678 + case ILI9322_INPUT_PRGB_ALIGNED: 679 + mode = drm_mode_duplicate(panel->drm, &prgb_320x240_mode); 680 + break; 681 + case ILI9322_INPUT_YUV_640X320_YCBCR: 682 + mode = drm_mode_duplicate(panel->drm, &yuv_640x320_mode); 683 + break; 684 + case ILI9322_INPUT_YUV_720X360_YCBCR: 685 + mode = drm_mode_duplicate(panel->drm, &yuv_720x360_mode); 686 + break; 687 + case ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR: 688 + mode = drm_mode_duplicate(panel->drm, &itu_r_bt_656_720_mode); 689 + break; 690 + case ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR: 691 + mode = drm_mode_duplicate(panel->drm, &itu_r_bt_656_640_mode); 692 + break; 693 + default: 694 + mode = NULL; 695 + break; 696 + } 697 + if (!mode) { 698 + DRM_ERROR("bad mode or failed to add mode\n"); 699 + return -EINVAL; 700 + } 701 + drm_mode_set_name(mode); 702 + /* 703 + * This is the preferred mode because most people are going 704 + * to want to use the display with VGA type graphics. 705 + */ 706 + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 707 + 708 + /* Set up the polarity */ 709 + if (ili->conf->hsync_active_high) 710 + mode->flags |= DRM_MODE_FLAG_PHSYNC; 711 + else 712 + mode->flags |= DRM_MODE_FLAG_NHSYNC; 713 + if (ili->conf->vsync_active_high) 714 + mode->flags |= DRM_MODE_FLAG_PVSYNC; 715 + else 716 + mode->flags |= DRM_MODE_FLAG_NVSYNC; 717 + 718 + mode->width_mm = ili->conf->width_mm; 719 + mode->height_mm = ili->conf->height_mm; 720 + drm_mode_probed_add(connector, mode); 721 + 722 + return 1; /* Number of modes */ 723 + } 724 + 725 + static const struct drm_panel_funcs ili9322_drm_funcs = { 726 + .disable = ili9322_disable, 727 + .unprepare = ili9322_unprepare, 728 + .prepare = ili9322_prepare, 729 + .enable = ili9322_enable, 730 + .get_modes = ili9322_get_modes, 731 + }; 732 + 733 + static int ili9322_probe(struct spi_device *spi) 734 + { 735 + struct device *dev = &spi->dev; 736 + struct ili9322 *ili; 737 + const struct regmap_config *regmap_config; 738 + u8 gamma; 739 + u32 val; 740 + int ret; 741 + int i; 742 + 743 + ili = devm_kzalloc(dev, sizeof(struct ili9322), GFP_KERNEL); 744 + if (!ili) 745 + return -ENOMEM; 746 + 747 + spi_set_drvdata(spi, ili); 748 + 749 + ili->dev = dev; 750 + 751 + /* 752 + * Every new incarnation of this display must have a unique 753 + * data entry for the system in this driver. 754 + */ 755 + ili->conf = of_device_get_match_data(dev); 756 + if (!ili->conf) { 757 + dev_err(dev, "missing device configuration\n"); 758 + return -ENODEV; 759 + } 760 + 761 + val = ili->conf->vreg1out_mv; 762 + if (!val) { 763 + /* Default HW value, do not touch (should be 4.5V) */ 764 + ili->vreg1out = U8_MAX; 765 + } else { 766 + if (val < 3600) { 767 + dev_err(dev, "too low VREG1OUT\n"); 768 + return -EINVAL; 769 + } 770 + if (val > 6000) { 771 + dev_err(dev, "too high VREG1OUT\n"); 772 + return -EINVAL; 773 + } 774 + if ((val % 100) != 0) { 775 + dev_err(dev, "VREG1OUT is no even 100 microvolt\n"); 776 + return -EINVAL; 777 + } 778 + val -= 3600; 779 + val /= 100; 780 + dev_dbg(dev, "VREG1OUT = 0x%02x\n", val); 781 + ili->vreg1out = val; 782 + } 783 + 784 + val = ili->conf->vcom_high_percent; 785 + if (!val) { 786 + /* Default HW value, do not touch (should be 91%) */ 787 + ili->vcom_high = U8_MAX; 788 + } else { 789 + if (val < 37) { 790 + dev_err(dev, "too low VCOM high\n"); 791 + return -EINVAL; 792 + } 793 + if (val > 100) { 794 + dev_err(dev, "too high VCOM high\n"); 795 + return -EINVAL; 796 + } 797 + val -= 37; 798 + dev_dbg(dev, "VCOM high = 0x%02x\n", val); 799 + ili->vcom_high = val; 800 + } 801 + 802 + val = ili->conf->vcom_amplitude_percent; 803 + if (!val) { 804 + /* Default HW value, do not touch (should be 114%) */ 805 + ili->vcom_high = U8_MAX; 806 + } else { 807 + if (val < 70) { 808 + dev_err(dev, "too low VCOM amplitude\n"); 809 + return -EINVAL; 810 + } 811 + if (val > 132) { 812 + dev_err(dev, "too high VCOM amplitude\n"); 813 + return -EINVAL; 814 + } 815 + val -= 70; 816 + val >>= 1; /* Increments of 2% */ 817 + dev_dbg(dev, "VCOM amplitude = 0x%02x\n", val); 818 + ili->vcom_amplitude = val; 819 + } 820 + 821 + for (i = 0; i < ARRAY_SIZE(ili->gamma); i++) { 822 + val = ili->conf->gamma_corr_neg[i]; 823 + if (val > 15) { 824 + dev_err(dev, "negative gamma %u > 15, capping\n", val); 825 + val = 15; 826 + } 827 + gamma = val << 4; 828 + val = ili->conf->gamma_corr_pos[i]; 829 + if (val > 15) { 830 + dev_err(dev, "positive gamma %u > 15, capping\n", val); 831 + val = 15; 832 + } 833 + gamma |= val; 834 + ili->gamma[i] = gamma; 835 + dev_dbg(dev, "gamma V%d: 0x%02x\n", i + 1, gamma); 836 + } 837 + 838 + ili->supplies[0].supply = "vcc"; /* 2.7-3.6 V */ 839 + ili->supplies[1].supply = "iovcc"; /* 1.65-3.6V */ 840 + ili->supplies[2].supply = "vci"; /* 2.7-3.6V */ 841 + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies), 842 + ili->supplies); 843 + if (ret < 0) 844 + return ret; 845 + ret = regulator_set_voltage(ili->supplies[0].consumer, 846 + 2700000, 3600000); 847 + if (ret) 848 + return ret; 849 + ret = regulator_set_voltage(ili->supplies[1].consumer, 850 + 1650000, 3600000); 851 + if (ret) 852 + return ret; 853 + ret = regulator_set_voltage(ili->supplies[2].consumer, 854 + 2700000, 3600000); 855 + if (ret) 856 + return ret; 857 + 858 + ili->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 859 + if (IS_ERR(ili->reset_gpio)) { 860 + dev_err(dev, "failed to get RESET GPIO\n"); 861 + return PTR_ERR(ili->reset_gpio); 862 + } 863 + 864 + spi->bits_per_word = 8; 865 + ret = spi_setup(spi); 866 + if (ret < 0) { 867 + dev_err(dev, "spi setup failed.\n"); 868 + return ret; 869 + } 870 + regmap_config = &ili9322_regmap_config; 871 + ili->regmap = devm_regmap_init(dev, &ili9322_regmap_bus, dev, 872 + regmap_config); 873 + if (IS_ERR(ili->regmap)) { 874 + dev_err(dev, "failed to allocate register map\n"); 875 + return PTR_ERR(ili->regmap); 876 + } 877 + 878 + ret = regmap_read(ili->regmap, ILI9322_CHIP_ID, &val); 879 + if (ret) { 880 + dev_err(dev, "can't get chip ID (%d)\n", ret); 881 + return ret; 882 + } 883 + if (val != ILI9322_CHIP_ID_MAGIC) { 884 + dev_err(dev, "chip ID 0x%0x2, expected 0x%02x\n", val, 885 + ILI9322_CHIP_ID_MAGIC); 886 + return -ENODEV; 887 + } 888 + 889 + /* Probe the system to find the display setting */ 890 + if (ili->conf->input == ILI9322_INPUT_UNKNOWN) { 891 + ret = regmap_read(ili->regmap, ILI9322_ENTRY, &val); 892 + if (ret) { 893 + dev_err(dev, "can't get entry setting (%d)\n", ret); 894 + return ret; 895 + } 896 + /* Input enum corresponds to HW setting */ 897 + ili->input = (val >> 4) & 0x0f; 898 + if (ili->input >= ILI9322_INPUT_UNKNOWN) 899 + ili->input = ILI9322_INPUT_UNKNOWN; 900 + } else { 901 + ili->input = ili->conf->input; 902 + } 903 + 904 + drm_panel_init(&ili->panel); 905 + ili->panel.dev = dev; 906 + ili->panel.funcs = &ili9322_drm_funcs; 907 + 908 + return drm_panel_add(&ili->panel); 909 + } 910 + 911 + static int ili9322_remove(struct spi_device *spi) 912 + { 913 + struct ili9322 *ili = spi_get_drvdata(spi); 914 + 915 + ili9322_power_off(ili); 916 + drm_panel_remove(&ili->panel); 917 + 918 + return 0; 919 + } 920 + 921 + /* 922 + * The D-Link DIR-685 panel is marked LM918A01-1A SY-B4-091116-E0199 923 + */ 924 + static const struct ili9322_config ili9322_dir_685 = { 925 + .width_mm = 65, 926 + .height_mm = 50, 927 + .input = ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR, 928 + .vreg1out_mv = 4600, 929 + .vcom_high_percent = 91, 930 + .vcom_amplitude_percent = 114, 931 + .syncmode = ILI9322_IF_CTRL_SYNC_DISABLED, 932 + .dclk_active_high = true, 933 + .gamma_corr_neg = { 0xa, 0x5, 0x7, 0x7, 0x7, 0x5, 0x1, 0x6 }, 934 + .gamma_corr_pos = { 0x7, 0x7, 0x3, 0x2, 0x3, 0x5, 0x7, 0x2 }, 935 + }; 936 + 937 + static const struct of_device_id ili9322_of_match[] = { 938 + { 939 + .compatible = "dlink,dir-685-panel", 940 + .data = &ili9322_dir_685, 941 + }, 942 + { 943 + .compatible = "ilitek,ili9322", 944 + .data = NULL, 945 + }, 946 + { } 947 + }; 948 + MODULE_DEVICE_TABLE(of, ili9322_of_match); 949 + 950 + static struct spi_driver ili9322_driver = { 951 + .probe = ili9322_probe, 952 + .remove = ili9322_remove, 953 + .driver = { 954 + .name = "panel-ilitek-ili9322", 955 + .of_match_table = ili9322_of_match, 956 + }, 957 + }; 958 + module_spi_driver(ili9322_driver); 959 + 960 + MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 961 + MODULE_DESCRIPTION("ILI9322 LCD panel driver"); 962 + MODULE_LICENSE("GPL v2");