cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

panel-tpo-td043mtea1.c (11764B)


      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
     49static const u16 td043mtea1_def_gamma[12] = {
     50	105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
     51};
     52
     53struct 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
     74static 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
     99static 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 (i = 0; i < 12; i++)
    120		td043mtea1_write(lcd, 0x14 + i, gamma[i] & 0xff);
    121}
    122
    123static 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
    133static 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
    161static 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
    184static 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 sysfs_emit(buf, "%d\n", lcd->vmirror);
    190}
    191
    192static 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
    212static 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 sysfs_emit(buf, "%d\n", lcd->mode);
    218}
    219
    220static 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
    239static 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
    259static 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
    281static DEVICE_ATTR_RW(vmirror);
    282static DEVICE_ATTR_RW(mode);
    283static DEVICE_ATTR_RW(gamma);
    284
    285static struct attribute *td043mtea1_attrs[] = {
    286	&dev_attr_vmirror.attr,
    287	&dev_attr_mode.attr,
    288	&dev_attr_gamma.attr,
    289	NULL,
    290};
    291
    292static const struct attribute_group td043mtea1_attr_group = {
    293	.attrs = td043mtea1_attrs,
    294};
    295
    296/* -----------------------------------------------------------------------------
    297 * Panel Operations
    298 */
    299
    300static 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
    310static 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
    332static 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	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
    343	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
    344	.width_mm = 94,
    345	.height_mm = 56,
    346};
    347
    348static int td043mtea1_get_modes(struct drm_panel *panel,
    349				struct drm_connector *connector)
    350{
    351	struct drm_display_mode *mode;
    352
    353	mode = drm_mode_duplicate(connector->dev, &td043mtea1_mode);
    354	if (!mode)
    355		return -ENOMEM;
    356
    357	drm_mode_set_name(mode);
    358	drm_mode_probed_add(connector, mode);
    359
    360	connector->display_info.width_mm = td043mtea1_mode.width_mm;
    361	connector->display_info.height_mm = td043mtea1_mode.height_mm;
    362	/*
    363	 * FIXME: According to the datasheet sync signals are sampled on the
    364	 * rising edge of the clock, but the code running on the OMAP3 Pandora
    365	 * indicates sampling on the falling edge. This should be tested on a
    366	 * real device.
    367	 */
    368	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
    369					  | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
    370					  | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE;
    371
    372	return 1;
    373}
    374
    375static const struct drm_panel_funcs td043mtea1_funcs = {
    376	.unprepare = td043mtea1_unprepare,
    377	.prepare = td043mtea1_prepare,
    378	.get_modes = td043mtea1_get_modes,
    379};
    380
    381/* -----------------------------------------------------------------------------
    382 * Power Management, Probe and Remove
    383 */
    384
    385static int __maybe_unused td043mtea1_suspend(struct device *dev)
    386{
    387	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
    388
    389	if (lcd->powered_on) {
    390		td043mtea1_power_off(lcd);
    391		lcd->powered_on = true;
    392	}
    393
    394	lcd->spi_suspended = true;
    395
    396	return 0;
    397}
    398
    399static int __maybe_unused td043mtea1_resume(struct device *dev)
    400{
    401	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
    402	int ret;
    403
    404	lcd->spi_suspended = false;
    405
    406	if (lcd->powered_on) {
    407		lcd->powered_on = false;
    408		ret = td043mtea1_power_on(lcd);
    409		if (ret)
    410			return ret;
    411	}
    412
    413	return 0;
    414}
    415
    416static SIMPLE_DEV_PM_OPS(td043mtea1_pm_ops, td043mtea1_suspend,
    417			 td043mtea1_resume);
    418
    419static int td043mtea1_probe(struct spi_device *spi)
    420{
    421	struct td043mtea1_panel *lcd;
    422	int ret;
    423
    424	lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
    425	if (lcd == NULL)
    426		return -ENOMEM;
    427
    428	spi_set_drvdata(spi, lcd);
    429	lcd->spi = spi;
    430	lcd->mode = TPO_R02_MODE_800x480;
    431	memcpy(lcd->gamma, td043mtea1_def_gamma, sizeof(lcd->gamma));
    432
    433	lcd->vcc_reg = devm_regulator_get(&spi->dev, "vcc");
    434	if (IS_ERR(lcd->vcc_reg))
    435		return dev_err_probe(&spi->dev, PTR_ERR(lcd->vcc_reg),
    436				     "failed to get VCC regulator\n");
    437
    438	lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
    439	if (IS_ERR(lcd->reset_gpio))
    440		return dev_err_probe(&spi->dev, PTR_ERR(lcd->reset_gpio),
    441				     "failed to get reset GPIO\n");
    442
    443	spi->bits_per_word = 16;
    444	spi->mode = SPI_MODE_0;
    445
    446	ret = spi_setup(spi);
    447	if (ret < 0) {
    448		dev_err(&spi->dev, "failed to setup SPI: %d\n", ret);
    449		return ret;
    450	}
    451
    452	ret = sysfs_create_group(&spi->dev.kobj, &td043mtea1_attr_group);
    453	if (ret < 0) {
    454		dev_err(&spi->dev, "failed to create sysfs files\n");
    455		return ret;
    456	}
    457
    458	drm_panel_init(&lcd->panel, &lcd->spi->dev, &td043mtea1_funcs,
    459		       DRM_MODE_CONNECTOR_DPI);
    460
    461	drm_panel_add(&lcd->panel);
    462
    463	return 0;
    464}
    465
    466static void td043mtea1_remove(struct spi_device *spi)
    467{
    468	struct td043mtea1_panel *lcd = spi_get_drvdata(spi);
    469
    470	drm_panel_remove(&lcd->panel);
    471	drm_panel_disable(&lcd->panel);
    472	drm_panel_unprepare(&lcd->panel);
    473
    474	sysfs_remove_group(&spi->dev.kobj, &td043mtea1_attr_group);
    475}
    476
    477static const struct of_device_id td043mtea1_of_match[] = {
    478	{ .compatible = "tpo,td043mtea1", },
    479	{ /* sentinel */ },
    480};
    481
    482MODULE_DEVICE_TABLE(of, td043mtea1_of_match);
    483
    484static const struct spi_device_id td043mtea1_ids[] = {
    485	{ "td043mtea1", 0 },
    486	{ /* sentinel */ }
    487};
    488
    489MODULE_DEVICE_TABLE(spi, td043mtea1_ids);
    490
    491static struct spi_driver td043mtea1_driver = {
    492	.probe		= td043mtea1_probe,
    493	.remove		= td043mtea1_remove,
    494	.id_table	= td043mtea1_ids,
    495	.driver		= {
    496		.name	= "panel-tpo-td043mtea1",
    497		.pm	= &td043mtea1_pm_ops,
    498		.of_match_table = td043mtea1_of_match,
    499	},
    500};
    501
    502module_spi_driver(td043mtea1_driver);
    503
    504MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
    505MODULE_DESCRIPTION("TPO TD043MTEA1 Panel Driver");
    506MODULE_LICENSE("GPL");