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

drm/panel: Add driver for the Toppoly TD043MTEA1 panel

This panel is used on the OMAP3 Pandora.

The code is based on the omapdrm-specific panel-tpo-td043mtea1 driver.

v2:
- fix checkpatch warnings
o (lcd == NULL) => (!lcd) (sam)
o alignment to open '(' (sam)

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20190813201101.30980-10-laurent.pinchart@ideasonboard.com

authored by

Laurent Pinchart and committed by
Sam Ravnborg
dc2e1e5b 415b8dd0

+518 -1
+7
drivers/gpu/drm/panel/Kconfig
··· 332 332 Say Y here if you want to enable support for TPO TD028TTEC1 480x640 333 333 2.8" panel (found on the OpenMoko Neo FreeRunner and Neo 1973). 334 334 335 + config DRM_PANEL_TPO_TD043MTEA1 336 + tristate "Toppoly (TPO) TD043MTEA1 panel driver" 337 + depends on GPIOLIB && OF && REGULATOR && SPI 338 + help 339 + Say Y here if you want to enable support for TPO TD043MTEA1 800x480 340 + 4.3" panel (found on the OMAP3 Pandora board). 341 + 335 342 config DRM_PANEL_TPO_TPG110 336 343 tristate "TPO TPG 800x400 panel" 337 344 depends on OF && SPI && GPIOLIB
+1
drivers/gpu/drm/panel/Makefile
··· 35 35 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o 36 36 obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o 37 37 obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o 38 + obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o 38 39 obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o 39 40 obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
+1 -1
drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
··· 328 328 int ret; 329 329 330 330 lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); 331 - if (lcd == NULL) 331 + if (!lcd) 332 332 return -ENOMEM; 333 333 334 334 spi_set_drvdata(spi, lcd);
+509
drivers/gpu/drm/panel/panel-tpo-td043mtea1.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Toppoly TD043MTEA1 Panel Driver 4 + * 5 + * Copyright (C) 2019 Texas Instruments Incorporated 6 + * 7 + * Based on the omapdrm-specific panel-tpo-td043mtea1 driver 8 + * 9 + * Author: Gražvydas Ignotas <notasas@gmail.com> 10 + */ 11 + 12 + #include <linux/delay.h> 13 + #include <linux/module.h> 14 + #include <linux/regulator/consumer.h> 15 + #include <linux/spi/spi.h> 16 + 17 + #include <drm/drm_connector.h> 18 + #include <drm/drm_modes.h> 19 + #include <drm/drm_panel.h> 20 + 21 + #define TPO_R02_MODE(x) ((x) & 7) 22 + #define TPO_R02_MODE_800x480 7 23 + #define TPO_R02_NCLK_RISING BIT(3) 24 + #define TPO_R02_HSYNC_HIGH BIT(4) 25 + #define TPO_R02_VSYNC_HIGH BIT(5) 26 + 27 + #define TPO_R03_NSTANDBY BIT(0) 28 + #define TPO_R03_EN_CP_CLK BIT(1) 29 + #define TPO_R03_EN_VGL_PUMP BIT(2) 30 + #define TPO_R03_EN_PWM BIT(3) 31 + #define TPO_R03_DRIVING_CAP_100 BIT(4) 32 + #define TPO_R03_EN_PRE_CHARGE BIT(6) 33 + #define TPO_R03_SOFTWARE_CTL BIT(7) 34 + 35 + #define TPO_R04_NFLIP_H BIT(0) 36 + #define TPO_R04_NFLIP_V BIT(1) 37 + #define TPO_R04_CP_CLK_FREQ_1H BIT(2) 38 + #define TPO_R04_VGL_FREQ_1H BIT(4) 39 + 40 + #define TPO_R03_VAL_NORMAL \ 41 + (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | TPO_R03_EN_VGL_PUMP | \ 42 + TPO_R03_EN_PWM | TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \ 43 + TPO_R03_SOFTWARE_CTL) 44 + 45 + #define TPO_R03_VAL_STANDBY \ 46 + (TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \ 47 + TPO_R03_SOFTWARE_CTL) 48 + 49 + static const u16 td043mtea1_def_gamma[12] = { 50 + 105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023 51 + }; 52 + 53 + struct td043mtea1_panel { 54 + struct drm_panel panel; 55 + 56 + struct spi_device *spi; 57 + struct regulator *vcc_reg; 58 + struct gpio_desc *reset_gpio; 59 + 60 + unsigned int mode; 61 + u16 gamma[12]; 62 + bool vmirror; 63 + bool powered_on; 64 + bool spi_suspended; 65 + bool power_on_resume; 66 + }; 67 + 68 + #define to_td043mtea1_device(p) container_of(p, struct td043mtea1_panel, panel) 69 + 70 + /* ----------------------------------------------------------------------------- 71 + * Hardware Access 72 + */ 73 + 74 + static int td043mtea1_write(struct td043mtea1_panel *lcd, u8 addr, u8 value) 75 + { 76 + struct spi_message msg; 77 + struct spi_transfer xfer; 78 + u16 data; 79 + int ret; 80 + 81 + spi_message_init(&msg); 82 + 83 + memset(&xfer, 0, sizeof(xfer)); 84 + 85 + data = ((u16)addr << 10) | (1 << 8) | value; 86 + xfer.tx_buf = &data; 87 + xfer.bits_per_word = 16; 88 + xfer.len = 2; 89 + spi_message_add_tail(&xfer, &msg); 90 + 91 + ret = spi_sync(lcd->spi, &msg); 92 + if (ret < 0) 93 + dev_warn(&lcd->spi->dev, "failed to write to LCD reg (%d)\n", 94 + ret); 95 + 96 + return ret; 97 + } 98 + 99 + static void td043mtea1_write_gamma(struct td043mtea1_panel *lcd) 100 + { 101 + const u16 *gamma = lcd->gamma; 102 + unsigned int i; 103 + u8 val; 104 + 105 + /* gamma bits [9:8] */ 106 + for (val = i = 0; i < 4; i++) 107 + val |= (gamma[i] & 0x300) >> ((i + 1) * 2); 108 + td043mtea1_write(lcd, 0x11, val); 109 + 110 + for (val = i = 0; i < 4; i++) 111 + val |= (gamma[i + 4] & 0x300) >> ((i + 1) * 2); 112 + td043mtea1_write(lcd, 0x12, val); 113 + 114 + for (val = i = 0; i < 4; i++) 115 + val |= (gamma[i + 8] & 0x300) >> ((i + 1) * 2); 116 + td043mtea1_write(lcd, 0x13, val); 117 + 118 + /* gamma bits [7:0] */ 119 + for (val = i = 0; i < 12; i++) 120 + td043mtea1_write(lcd, 0x14 + i, gamma[i] & 0xff); 121 + } 122 + 123 + static int td043mtea1_write_mirror(struct td043mtea1_panel *lcd) 124 + { 125 + u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V | 126 + TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H; 127 + if (lcd->vmirror) 128 + reg4 &= ~TPO_R04_NFLIP_V; 129 + 130 + return td043mtea1_write(lcd, 4, reg4); 131 + } 132 + 133 + static int td043mtea1_power_on(struct td043mtea1_panel *lcd) 134 + { 135 + int ret; 136 + 137 + if (lcd->powered_on) 138 + return 0; 139 + 140 + ret = regulator_enable(lcd->vcc_reg); 141 + if (ret < 0) 142 + return ret; 143 + 144 + /* Wait for the panel to stabilize. */ 145 + msleep(160); 146 + 147 + gpiod_set_value(lcd->reset_gpio, 0); 148 + 149 + td043mtea1_write(lcd, 2, TPO_R02_MODE(lcd->mode) | TPO_R02_NCLK_RISING); 150 + td043mtea1_write(lcd, 3, TPO_R03_VAL_NORMAL); 151 + td043mtea1_write(lcd, 0x20, 0xf0); 152 + td043mtea1_write(lcd, 0x21, 0xf0); 153 + td043mtea1_write_mirror(lcd); 154 + td043mtea1_write_gamma(lcd); 155 + 156 + lcd->powered_on = true; 157 + 158 + return 0; 159 + } 160 + 161 + static void td043mtea1_power_off(struct td043mtea1_panel *lcd) 162 + { 163 + if (!lcd->powered_on) 164 + return; 165 + 166 + td043mtea1_write(lcd, 3, TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM); 167 + 168 + gpiod_set_value(lcd->reset_gpio, 1); 169 + 170 + /* wait for at least 2 vsyncs before cutting off power */ 171 + msleep(50); 172 + 173 + td043mtea1_write(lcd, 3, TPO_R03_VAL_STANDBY); 174 + 175 + regulator_disable(lcd->vcc_reg); 176 + 177 + lcd->powered_on = false; 178 + } 179 + 180 + /* ----------------------------------------------------------------------------- 181 + * sysfs 182 + */ 183 + 184 + static ssize_t vmirror_show(struct device *dev, struct device_attribute *attr, 185 + char *buf) 186 + { 187 + struct td043mtea1_panel *lcd = dev_get_drvdata(dev); 188 + 189 + return snprintf(buf, PAGE_SIZE, "%d\n", lcd->vmirror); 190 + } 191 + 192 + static ssize_t vmirror_store(struct device *dev, struct device_attribute *attr, 193 + const char *buf, size_t count) 194 + { 195 + struct td043mtea1_panel *lcd = dev_get_drvdata(dev); 196 + int val; 197 + int ret; 198 + 199 + ret = kstrtoint(buf, 0, &val); 200 + if (ret < 0) 201 + return ret; 202 + 203 + lcd->vmirror = !!val; 204 + 205 + ret = td043mtea1_write_mirror(lcd); 206 + if (ret < 0) 207 + return ret; 208 + 209 + return count; 210 + } 211 + 212 + static ssize_t mode_show(struct device *dev, struct device_attribute *attr, 213 + char *buf) 214 + { 215 + struct td043mtea1_panel *lcd = dev_get_drvdata(dev); 216 + 217 + return snprintf(buf, PAGE_SIZE, "%d\n", lcd->mode); 218 + } 219 + 220 + static ssize_t mode_store(struct device *dev, struct device_attribute *attr, 221 + const char *buf, size_t count) 222 + { 223 + struct td043mtea1_panel *lcd = dev_get_drvdata(dev); 224 + long val; 225 + int ret; 226 + 227 + ret = kstrtol(buf, 0, &val); 228 + if (ret != 0 || val & ~7) 229 + return -EINVAL; 230 + 231 + lcd->mode = val; 232 + 233 + val |= TPO_R02_NCLK_RISING; 234 + td043mtea1_write(lcd, 2, val); 235 + 236 + return count; 237 + } 238 + 239 + static ssize_t gamma_show(struct device *dev, struct device_attribute *attr, 240 + char *buf) 241 + { 242 + struct td043mtea1_panel *lcd = dev_get_drvdata(dev); 243 + ssize_t len = 0; 244 + unsigned int i; 245 + int ret; 246 + 247 + for (i = 0; i < ARRAY_SIZE(lcd->gamma); i++) { 248 + ret = snprintf(buf + len, PAGE_SIZE - len, "%u ", 249 + lcd->gamma[i]); 250 + if (ret < 0) 251 + return ret; 252 + len += ret; 253 + } 254 + buf[len - 1] = '\n'; 255 + 256 + return len; 257 + } 258 + 259 + static ssize_t gamma_store(struct device *dev, struct device_attribute *attr, 260 + const char *buf, size_t count) 261 + { 262 + struct td043mtea1_panel *lcd = dev_get_drvdata(dev); 263 + unsigned int g[12]; 264 + unsigned int i; 265 + int ret; 266 + 267 + ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u", 268 + &g[0], &g[1], &g[2], &g[3], &g[4], &g[5], 269 + &g[6], &g[7], &g[8], &g[9], &g[10], &g[11]); 270 + if (ret != 12) 271 + return -EINVAL; 272 + 273 + for (i = 0; i < 12; i++) 274 + lcd->gamma[i] = g[i]; 275 + 276 + td043mtea1_write_gamma(lcd); 277 + 278 + return count; 279 + } 280 + 281 + static DEVICE_ATTR_RW(vmirror); 282 + static DEVICE_ATTR_RW(mode); 283 + static DEVICE_ATTR_RW(gamma); 284 + 285 + static struct attribute *td043mtea1_attrs[] = { 286 + &dev_attr_vmirror.attr, 287 + &dev_attr_mode.attr, 288 + &dev_attr_gamma.attr, 289 + NULL, 290 + }; 291 + 292 + static const struct attribute_group td043mtea1_attr_group = { 293 + .attrs = td043mtea1_attrs, 294 + }; 295 + 296 + /* ----------------------------------------------------------------------------- 297 + * Panel Operations 298 + */ 299 + 300 + static int td043mtea1_unprepare(struct drm_panel *panel) 301 + { 302 + struct td043mtea1_panel *lcd = to_td043mtea1_device(panel); 303 + 304 + if (!lcd->spi_suspended) 305 + td043mtea1_power_off(lcd); 306 + 307 + return 0; 308 + } 309 + 310 + static int td043mtea1_prepare(struct drm_panel *panel) 311 + { 312 + struct td043mtea1_panel *lcd = to_td043mtea1_device(panel); 313 + int ret; 314 + 315 + /* 316 + * If we are resuming from system suspend, SPI might not be enabled 317 + * yet, so we'll program the LCD from SPI PM resume callback. 318 + */ 319 + if (lcd->spi_suspended) 320 + return 0; 321 + 322 + ret = td043mtea1_power_on(lcd); 323 + if (ret) { 324 + dev_err(&lcd->spi->dev, "%s: power on failed (%d)\n", 325 + __func__, ret); 326 + return ret; 327 + } 328 + 329 + return 0; 330 + } 331 + 332 + static const struct drm_display_mode td043mtea1_mode = { 333 + .clock = 36000, 334 + .hdisplay = 800, 335 + .hsync_start = 800 + 68, 336 + .hsync_end = 800 + 68 + 1, 337 + .htotal = 800 + 68 + 1 + 214, 338 + .vdisplay = 480, 339 + .vsync_start = 480 + 39, 340 + .vsync_end = 480 + 39 + 1, 341 + .vtotal = 480 + 39 + 1 + 34, 342 + .vrefresh = 60, 343 + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 344 + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 345 + .width_mm = 94, 346 + .height_mm = 56, 347 + }; 348 + 349 + static int td043mtea1_get_modes(struct drm_panel *panel) 350 + { 351 + struct drm_connector *connector = panel->connector; 352 + struct drm_display_mode *mode; 353 + 354 + mode = drm_mode_duplicate(panel->drm, &td043mtea1_mode); 355 + if (!mode) 356 + return -ENOMEM; 357 + 358 + drm_mode_set_name(mode); 359 + drm_mode_probed_add(connector, mode); 360 + 361 + connector->display_info.width_mm = td043mtea1_mode.width_mm; 362 + connector->display_info.height_mm = td043mtea1_mode.height_mm; 363 + /* 364 + * FIXME: According to the datasheet sync signals are sampled on the 365 + * rising edge of the clock, but the code running on the OMAP3 Pandora 366 + * indicates sampling on the falling edge. This should be tested on a 367 + * real device. 368 + */ 369 + connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH 370 + | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE 371 + | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE; 372 + 373 + return 1; 374 + } 375 + 376 + static const struct drm_panel_funcs td043mtea1_funcs = { 377 + .unprepare = td043mtea1_unprepare, 378 + .prepare = td043mtea1_prepare, 379 + .get_modes = td043mtea1_get_modes, 380 + }; 381 + 382 + /* ----------------------------------------------------------------------------- 383 + * Power Management, Probe and Remove 384 + */ 385 + 386 + static int __maybe_unused td043mtea1_suspend(struct device *dev) 387 + { 388 + struct td043mtea1_panel *lcd = dev_get_drvdata(dev); 389 + 390 + if (lcd->powered_on) { 391 + td043mtea1_power_off(lcd); 392 + lcd->powered_on = true; 393 + } 394 + 395 + lcd->spi_suspended = true; 396 + 397 + return 0; 398 + } 399 + 400 + static int __maybe_unused td043mtea1_resume(struct device *dev) 401 + { 402 + struct td043mtea1_panel *lcd = dev_get_drvdata(dev); 403 + int ret; 404 + 405 + lcd->spi_suspended = false; 406 + 407 + if (lcd->powered_on) { 408 + lcd->powered_on = false; 409 + ret = td043mtea1_power_on(lcd); 410 + if (ret) 411 + return ret; 412 + } 413 + 414 + return 0; 415 + } 416 + 417 + static SIMPLE_DEV_PM_OPS(td043mtea1_pm_ops, td043mtea1_suspend, 418 + td043mtea1_resume); 419 + 420 + static int td043mtea1_probe(struct spi_device *spi) 421 + { 422 + struct td043mtea1_panel *lcd; 423 + int ret; 424 + 425 + lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); 426 + if (lcd == NULL) 427 + return -ENOMEM; 428 + 429 + spi_set_drvdata(spi, lcd); 430 + lcd->spi = spi; 431 + lcd->mode = TPO_R02_MODE_800x480; 432 + memcpy(lcd->gamma, td043mtea1_def_gamma, sizeof(lcd->gamma)); 433 + 434 + lcd->vcc_reg = devm_regulator_get(&spi->dev, "vcc"); 435 + if (IS_ERR(lcd->vcc_reg)) { 436 + dev_err(&spi->dev, "failed to get VCC regulator\n"); 437 + return PTR_ERR(lcd->vcc_reg); 438 + } 439 + 440 + lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH); 441 + if (IS_ERR(lcd->reset_gpio)) { 442 + dev_err(&spi->dev, "failed to get reset GPIO\n"); 443 + return PTR_ERR(lcd->reset_gpio); 444 + } 445 + 446 + spi->bits_per_word = 16; 447 + spi->mode = SPI_MODE_0; 448 + 449 + ret = spi_setup(spi); 450 + if (ret < 0) { 451 + dev_err(&spi->dev, "failed to setup SPI: %d\n", ret); 452 + return ret; 453 + } 454 + 455 + ret = sysfs_create_group(&spi->dev.kobj, &td043mtea1_attr_group); 456 + if (ret < 0) { 457 + dev_err(&spi->dev, "failed to create sysfs files\n"); 458 + return ret; 459 + } 460 + 461 + drm_panel_init(&lcd->panel); 462 + lcd->panel.dev = &lcd->spi->dev; 463 + lcd->panel.funcs = &td043mtea1_funcs; 464 + 465 + ret = drm_panel_add(&lcd->panel); 466 + if (ret < 0) { 467 + sysfs_remove_group(&spi->dev.kobj, &td043mtea1_attr_group); 468 + return ret; 469 + } 470 + 471 + return 0; 472 + } 473 + 474 + static int td043mtea1_remove(struct spi_device *spi) 475 + { 476 + struct td043mtea1_panel *lcd = spi_get_drvdata(spi); 477 + 478 + drm_panel_remove(&lcd->panel); 479 + drm_panel_disable(&lcd->panel); 480 + drm_panel_unprepare(&lcd->panel); 481 + 482 + sysfs_remove_group(&spi->dev.kobj, &td043mtea1_attr_group); 483 + 484 + return 0; 485 + } 486 + 487 + static const struct of_device_id td043mtea1_of_match[] = { 488 + { .compatible = "tpo,td043mtea1", }, 489 + { /* sentinel */ }, 490 + }; 491 + 492 + MODULE_DEVICE_TABLE(of, td043mtea1_of_match); 493 + 494 + static struct spi_driver td043mtea1_driver = { 495 + .probe = td043mtea1_probe, 496 + .remove = td043mtea1_remove, 497 + .driver = { 498 + .name = "panel-tpo-td043mtea1", 499 + .pm = &td043mtea1_pm_ops, 500 + .of_match_table = td043mtea1_of_match, 501 + }, 502 + }; 503 + 504 + module_spi_driver(td043mtea1_driver); 505 + 506 + MODULE_ALIAS("spi:tpo,td043mtea1"); 507 + MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>"); 508 + MODULE_DESCRIPTION("TPO TD043MTEA1 Panel Driver"); 509 + MODULE_LICENSE("GPL");