brintos

brintos / linux-shallow public Read only

0
0
Text · 11.5 KiB · cf4609b Raw
507 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Toppoly TD043MTEA1 Panel Driver4 *5 * Copyright (C) 2019 Texas Instruments Incorporated6 *7 * Based on the omapdrm-specific panel-tpo-td043mtea1 driver8 *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		723#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, 102351};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 Access72 */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 * sysfs182 */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 Operations298 */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 enabled317	 * 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 the364	 * rising edge of the clock, but the code running on the OMAP3 Pandora365	 * indicates sampling on the falling edge. This should be tested on a366	 * real device.367	 */368	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH369					  | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE370					  | 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 Remove383 */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");507