diff options
Diffstat (limited to 'drivers/video/omap2/displays')
| -rw-r--r-- | drivers/video/omap2/displays/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/video/omap2/displays/Makefile | 1 | ||||
| -rw-r--r-- | drivers/video/omap2/displays/panel-ili9342.c | 598 | 
3 files changed, 606 insertions, 1 deletions
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index af8f97ebbe0..92f8d32b63e 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig @@ -16,7 +16,13 @@ config PANEL_TFP410  	help  	  Driver for TFP410 DPI-to-DVI chip. The driver uses i2c to read EDID  	  information from the monitor. - +	   +config PANEL_ILI_9342 +	tristate "ili9342 display controller" +	depends on OMAP2_DSS_DPI && SPI +	help +	  LCD Display controller used on the Olio H1 +	    config PANEL_LGPHILIPS_LB035Q02  	tristate "LG.Philips LB035Q02 LCD Panel"  	depends on OMAP2_DSS_DPI && SPI diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile index abc0d9dae56..3fb5d3ac750 100644 --- a/drivers/video/omap2/displays/Makefile +++ b/drivers/video/omap2/displays/Makefile @@ -1,5 +1,6 @@  obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o  obj-$(CONFIG_PANEL_TFP410) += panel-tfp410.o +obj-$(CONFIG_PANEL_ILI_9342) += panel-ili9342.o  obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o  obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o  obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o diff --git a/drivers/video/omap2/displays/panel-ili9342.c b/drivers/video/omap2/displays/panel-ili9342.c new file mode 100644 index 00000000000..40a9713aa0d --- /dev/null +++ b/drivers/video/omap2/displays/panel-ili9342.c @@ -0,0 +1,598 @@ +#define DEBUG +/* + * Driver for ili9342 display driver + * + * Copyright (C) 2014,2015 Olio Devices Inc. + * Author: Evan Wilson <evan@oliodevices.com> + * Author: Mattis Fjallstrom <mattis@oliodevices.com> + * + * Adapted from panel-generic-dpi.c, panel-nec-nl8048hl11-01b.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/gpio.h> +#include <linux/device.h> +#include <linux/regulator/consumer.h> + +#include <video/omapdss.h> + +#define OLIODEBUG +#ifdef OLIODEBUG +#define oliodebug(...) printk ( __VA_ARGS__ ) +#else  +#define oliodebug(...)  +#endif  + +struct panel_config { +	struct omap_video_timings timings; + +	int power_on_delay; +	int power_off_delay; + +	/* +	 * Used to match device to panel configuration +	 * when use generic panel driver +	 */ +	const char *name; +}; + +/* Panel configurations */ +static struct panel_config ili9342_panels[] = { +	/* Olio H1 panel */ +	{ +		{ +			.x_res		= 320, +			.y_res		= 240, + +			.pixel_clock	= 5333, + +			.hsw		= 10, +			.hfp		= 10, +			.hbp		= 10, + +			.vsw		= 1, +			.vfp		= 1, +			.vbp		= 1, + +			.vsync_level	= OMAPDSS_SIG_ACTIVE_LOW, +			.hsync_level	= OMAPDSS_SIG_ACTIVE_LOW, +			.data_pclk_edge	= OMAPDSS_DRIVE_SIG_RISING_EDGE, +			.de_level	= OMAPDSS_SIG_ACTIVE_HIGH, +			.sync_pclk_edge	= OMAPDSS_DRIVE_SIG_RISING_EDGE, +		}, +		.name			= "olio_h1_panel", +	}, +}; + +struct panel_drv_data { + +	struct omap_dss_device *dssdev; + +	struct panel_config *panel_config; + +	struct mutex lock; +}; + +static int ili9342_panel_power_on(struct omap_dss_device *dssdev) +{ +	int r; +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); +	struct panel_config *panel_config = drv_data->panel_config; + +	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) +		return 0; + +	omapdss_dpi_set_timings(dssdev, &dssdev->panel.timings); +	omapdss_dpi_set_data_lines(dssdev, dssdev->phy.dpi.data_lines); + +	r = omapdss_dpi_display_enable(dssdev); +	if (r) +		goto err0; + +	/* wait couple of vsyncs until enabling the LCD */ +	if (panel_config->power_on_delay) +		msleep(panel_config->power_on_delay); + +	if (dssdev->platform_enable) { +		r = dssdev->platform_enable(dssdev); +		if (r) +			goto err1; +	} + +	return 0; +err1: +	omapdss_dpi_display_disable(dssdev); +err0: +	return r; +} + +static void ili9342_panel_power_off(struct omap_dss_device *dssdev) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); +	struct panel_config *panel_config = drv_data->panel_config; + +	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) +		return; + +	if (dssdev->platform_disable) +		dssdev->platform_disable(dssdev); + +	/* wait couple of vsyncs after disabling the LCD */ +	if (panel_config->power_off_delay) +		msleep(panel_config->power_off_delay); + +	omapdss_dpi_display_disable(dssdev); +} + +static int ili9342_panel_probe(struct omap_dss_device *dssdev) +{ +	struct panel_config *panel_config = NULL; +	struct panel_drv_data *drv_data = NULL; +	int i; + +	dev_dbg(&dssdev->dev, "probe\n"); + +	if (!dssdev || !dssdev->name) +		return -EINVAL; + +	for (i = 0; i < ARRAY_SIZE(ili9342_panels); i++) { +		if (strcmp(dssdev->name, ili9342_panels[i].name) == 0) { +			panel_config = &ili9342_panels[i]; +			break; +		} +	} + +	if (!panel_config) { +		dev_err(&dssdev->dev, "Could not find %s in ili9342 panel configs\n", dssdev->name); +		return -EINVAL; +	} + +	dssdev->panel.timings = panel_config->timings; + +	drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL); +	if (!drv_data) +		return -ENOMEM; + +	drv_data->dssdev = dssdev; +	drv_data->panel_config = panel_config; + +	mutex_init(&drv_data->lock); + +	dev_set_drvdata(&dssdev->dev, drv_data); + +	return 0; +} + +static void __exit ili9342_panel_remove(struct omap_dss_device *dssdev) +{ +	dev_dbg(&dssdev->dev, "remove\n"); + +	dev_set_drvdata(&dssdev->dev, NULL); +} + +static int ili9342_panel_enable(struct omap_dss_device *dssdev) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); +	int r; + +	mutex_lock(&drv_data->lock); + +	r = ili9342_panel_power_on(dssdev); +	if (r) +		goto err; + +	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; +err: +	mutex_unlock(&drv_data->lock); + +	return r; +} + +static void ili9342_panel_disable(struct omap_dss_device *dssdev) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + +	mutex_lock(&drv_data->lock); + +	ili9342_panel_power_off(dssdev); + +	dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + +	mutex_unlock(&drv_data->lock); +} + +static void ili9342_panel_set_timings(struct omap_dss_device *dssdev, +		struct omap_video_timings *timings) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + +	mutex_lock(&drv_data->lock); + +	omapdss_dpi_set_timings(dssdev, timings); + +	dssdev->panel.timings = *timings; + +	mutex_unlock(&drv_data->lock); +} + +static void ili9342_panel_get_timings(struct omap_dss_device *dssdev, +		struct omap_video_timings *timings) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + +	mutex_lock(&drv_data->lock); + +	*timings = dssdev->panel.timings; + +	mutex_unlock(&drv_data->lock); +} + +static int ili9342_panel_check_timings(struct omap_dss_device *dssdev, +		struct omap_video_timings *timings) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); +	int r; + +	mutex_lock(&drv_data->lock); + +	r = dpi_check_timings(dssdev, timings); + +	mutex_unlock(&drv_data->lock); + +	return r; +} + +static int ili9342_get_recommended_bpp(struct omap_dss_device *dssdev) { +	return 24; +} + +/*************************************************************************** + * suspend & resume  + *  + * For now, this is all handled by the SPI driver (see below). + * Leaving this functions here should we change our minds.  + */ + +static int ili9342_disp_suspend (struct device * dev) { +  ili9342_panel_disable((struct omap_dss_device *) dev); +  return 0; +} + +static int ili9342_disp_resume (struct device * dev) { +  ili9342_panel_enable((struct omap_dss_device *) dev); +  return 0; +} + +static struct dev_pm_ops ili9342_disp_pm_ops = { +    SET_SYSTEM_SLEEP_PM_OPS(ili9342_disp_suspend, ili9342_disp_resume) +}; + + +static struct omap_dss_driver ili9342_driver = { +	.probe		= ili9342_panel_probe, +	.remove		= __exit_p(ili9342_panel_remove), + +	.enable		= ili9342_panel_enable, +	.disable	= ili9342_panel_disable, + +	.set_timings	= ili9342_panel_set_timings, +	.get_timings	= ili9342_panel_get_timings, +	.check_timings	= ili9342_panel_check_timings, + +	.get_recommended_bpp = ili9342_get_recommended_bpp, + +	.driver         = { +		.name   = "ili9342_panel", +		.owner  = THIS_MODULE, +        .pm     = &ili9342_disp_pm_ops, +	}, +}; + +/* ====================================================================== */ + +/* Here follows the SPI driver - it's required by the panel, but the  + * coupling is rather weak. It registers another driver. + */ + +/* ====================================================================== */ + +static struct regulator *spi_regulator; + +static int ili9342_spi_write(struct spi_device *spi, bool cmd, unsigned char val) { +	unsigned short buf; +	struct spi_message m; +	struct spi_transfer t = { +			.tx_buf = &buf, +			.len = 2, +			.bits_per_word = 9, +	}; +	int r; + +	if(cmd) { +		buf = 0; +	} else { +		buf = 1 << 8; +	} +	buf |= val; + +	dev_dbg(&spi->dev, "SPI sync: %x", buf); +	spi_message_init(&m); +	spi_message_add_tail(&t, &m); +	r = spi_sync(spi, &m); +	if(r < 0) { +		dev_err(&spi->dev, "SPI sync failed."); +		return -EINVAL; +	} +	return 0; +} + +static int ili9342_write_cmd(struct spi_device *spi, unsigned char val) { +	return ili9342_spi_write(spi, 1, val); +} + +static int ili9342_write_data(struct spi_device *spi, unsigned char val) { +	return ili9342_spi_write(spi, 0, val); +} + +static inline void ili9342_init_seq(struct spi_device *spi) { +	ili9342_write_cmd(spi, 0xC8); +	ili9342_write_data(spi, 0xFF); +	ili9342_write_data(spi, 0x93); +	ili9342_write_data(spi, 0x42); + +	ili9342_write_cmd(spi, 0xB0); +	ili9342_write_data(spi, 0xE0); + +//  Timing settings + +//	ili9342_write_cmd(spi, 0xB5); +//	ili9342_write_data(spi, 0x04); +//	ili9342_write_data(spi, 0x01); +//	ili9342_write_data(spi, 0x0a); +//	ili9342_write_data(spi, 0x14); + +	//ili9342_write_cmd(spi, 0xB1); +	//ili9342_write_data(spi, 0x00); +	//ili9342_write_data(spi, 0x10); + +	ili9342_write_cmd(spi, 0x0B); +	ili9342_write_data(spi, 0x20); + +	ili9342_write_cmd(spi, 0xF6); +	ili9342_write_data(spi, 0x01); +	ili9342_write_data(spi, 0x00); +	ili9342_write_data(spi, 0x06); + + +	ili9342_write_cmd(spi, 0xB4); +	ili9342_write_data(spi, 0x00); + +	ili9342_write_cmd(spi, 0xC0); +	ili9342_write_data(spi, 0x16); +	ili9342_write_data(spi, 0x11); + +	ili9342_write_cmd(spi, 0xC1); +	ili9342_write_data(spi, 0x01); + +	ili9342_write_cmd(spi, 0xC5); +	ili9342_write_data(spi, 0xDB); + +// RGB color mode + +	//ili9342_write_cmd(spi, 0x36); +	//ili9342_write_data(spi, 0x08); + +	ili9342_write_cmd(spi, 0xB6); +	ili9342_write_data(spi, 0x0A); +	ili9342_write_data(spi, 0x00); +	ili9342_write_data(spi, 0x1D); +	ili9342_write_data(spi, 0x04); +// GAMMA settings +	//ili9342_write_cmd(spi, 0x26); +	//ili9342_write_data(spi, 0x04); + +	ili9342_write_cmd(spi, 0xE0); +	ili9342_write_data(spi, 0x00);  //63 +	ili9342_write_data(spi, 0x1b);  //62 +	ili9342_write_data(spi, 0x22);  //61 +	ili9342_write_data(spi, 0x05);  //59 +	ili9342_write_data(spi, 0x13);  //57 +	ili9342_write_data(spi, 0x07);  //50 +	ili9342_write_data(spi, 0x4c);  //43 +	ili9342_write_data(spi, 0xa7);  //27 +	ili9342_write_data(spi, 0x5f);  //20 +	ili9342_write_data(spi, 0x05);  //13 +	ili9342_write_data(spi, 0x0b);  //06 +	ili9342_write_data(spi, 0x09);  //04 +	ili9342_write_data(spi, 0x32);  //02 +	ili9342_write_data(spi, 0x36);  //01 +	ili9342_write_data(spi, 0x0F);  //00 + +	ili9342_write_cmd(spi, 0xE1); +	ili9342_write_data(spi, 0x00);  //00 +	ili9342_write_data(spi, 0x0c);  //01 +	ili9342_write_data(spi, 0x0d);  //02 +	ili9342_write_data(spi, 0x05);  //04 +	ili9342_write_data(spi, 0x11);  //06 +	ili9342_write_data(spi, 0x06);  //13 +	ili9342_write_data(spi, 0x30);  //20 +	ili9342_write_data(spi, 0x58);  //27 +	ili9342_write_data(spi, 0x44);  //43 +	ili9342_write_data(spi, 0x08);  //50 +	ili9342_write_data(spi, 0x14);  //57 +	ili9342_write_data(spi, 0x0c);  //59 +	ili9342_write_data(spi, 0x1e);  //61 +	ili9342_write_data(spi, 0x24);  //62 +	ili9342_write_data(spi, 0x0F);  //63 + +	ili9342_write_cmd(spi, 0x35); +	ili9342_write_data(spi, 0x00); + +	ili9342_write_cmd(spi, 0x3A); +	ili9342_write_data(spi, 0x66); + +	ili9342_write_cmd(spi, 0x11); +	msleep(120); +	ili9342_write_cmd(spi, 0x29); +} + + +static inline void init_ili9342_hw (struct spi_device *spi) { +	struct omap_dss_device *panel = spi->dev.platform_data; + +	gpio_set_value(panel->reset_gpio, 1); +	mdelay(1); +	gpio_set_value(panel->reset_gpio, 0); +	mdelay(50); +	gpio_set_value(panel->reset_gpio, 1); +	mdelay(120); + +	ili9342_init_seq(spi); +} + +static inline int init_ili9342_spi(struct spi_device *spi) { +	struct omap_dss_device *panel = spi->dev.platform_data; +	int reset_gpio; + +	if(!panel->reset_gpio) { +		dev_err(&spi->dev, "platform data requires reset\n"); +		return -EINVAL; +	} + +	reset_gpio = panel->reset_gpio; + +	if (!panel) { +		dev_err(&spi->dev, "no platform data\n"); +		return -EINVAL; +	} + +	if(gpio_request_one(reset_gpio, GPIOF_OUT_INIT_LOW, "ili9342-reset")) { +		dev_err(&spi->dev, "Could not request reset gpio %d", panel->reset_gpio); +		return -EINVAL; +	} + +	if(gpio_export(reset_gpio, 0)) { +		dev_err(&spi->dev, "Could not export reset gpio %d", panel->reset_gpio); +		return -EINVAL; +	} + +    init_ili9342_hw (spi); + +	return 0; +} + +static int ili9342_spi_probe(struct spi_device *spi) +{ +    int err; +    int ret; + +    spi_regulator = devm_regulator_get(&spi->dev, "vdd"); + +	if (IS_ERR(spi_regulator)) { +		dev_err(&spi->dev, "regulator get failed\n"); +		err = PTR_ERR(spi_regulator); +		spi_regulator = NULL; +		return err; +	} + +    ret = regulator_enable(spi_regulator); + +	if (ret) { +		dev_err(&spi->dev, "Failed to enable vdd: %d\n", ret); +		return ret; +	} + +	init_ili9342_spi(spi); + +	return omap_dss_register_driver(&ili9342_driver); +} + +static int ili9342_spi_remove(struct spi_device *spi) +{ +    oliodebug ("OLIO %s:%s Removing SPI\n", __FILE__, __FUNCTION__); + +	omap_dss_unregister_driver(&ili9342_driver); +	return 0; +} + +static int ili9342_suspend(struct device *dev) { +    int ret; + +    oliodebug ("OLIO %s:%s Suspending SPI for %s\n", __FILE__, __FUNCTION__, dev_name(dev)); + +    ret = regulator_disable(spi_regulator); +	if (ret) { +		dev_err(dev, "Failed to disable vdd:%d\n", +			ret); +		return ret; +	} +     +    return 0; +} + + +static int ili9342_resume(struct device *dev) { +    int ret; + +    /* HACK WARNING: We need an spi_device here. If, as can be assumed, +     * the device pointer passed in points to a device in an spi_device, +     * it's the first device in the spi_device struct. In other words,  +     * it's address is the same as the spi_device and a cast should be OK. +     */ +     +    /* IF, otoh, that's an incorrect assumption ... then this will lead  +     * to horrible crashes. But oh well, you can't win 'em all. +     */ + +    struct spi_device * spi = (struct spi_device *) dev; + +    oliodebug ("OLIO %s:%s Resuming SPI for %s\n", __FILE__, __FUNCTION__, dev_name(dev)); + +    ret = regulator_enable(spi_regulator); + +	if (ret) { +		dev_err(&spi->dev, "Failed to enable vdd: %d\n", ret); +		return ret; +	} + +	ili9342_init_seq(spi);     /* init_ili9342_hw (spi); */ + +    return 0; +} + + +static struct dev_pm_ops ili9342_pm_ops = { +    SET_SYSTEM_SLEEP_PM_OPS(ili9342_suspend, ili9342_resume) +}; + + +static struct spi_driver ili9342_spi_driver = { +	.probe = ili9342_spi_probe, +	.remove = ili9342_spi_remove, +	.driver = { +		.name = "ili9342-spi", +		.owner = THIS_MODULE, +        .pm = &ili9342_pm_ops, +	}, +}; + +module_spi_driver(ili9342_spi_driver); + +MODULE_AUTHOR("Evan Wilson <evan@oliodevice.com"); +MODULE_DESCRIPTION("ili9342 Display Controller Driver"); +MODULE_LICENSE("GPL");  |