⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lcd_linux.cpp

📁 一个Linux的lcd驱动程序。摘抄于avr32linux开源项目。
💻 CPP
字号:
/*
(摘抄于avr32linux开源项目)
1、    确保数据、时钟、电源等连接正常。 
2、    确保LCD的几组工作电源VDD/AVDD/VGL/VGH正常。 
3、    LCD配置:有的LCD的TCON IC需要使用SPI接口进行配置。 
a)    配置的内容主要是时钟信号的极性、扫描方向等,还有一些TCON IC支持CCIR601/656/OSD功能等,主要根据实际情况配置。 
b)    GAMMA校正:一般根据LCD厂家提供的参数进行校正,以前调LTV350QV就是因为厂家给的GAMMA参数不正确,造成色彩显示不正常。 
c)    SPI时序:一般不同的LCD屏的SPI时序和寄存器都会有一些差别,我一般是根据时序图进行操作寄存器(如下图),通过写寄存器,只要LCD有反应了,表明SPI通讯基本没有什么问题了。 

  4、    时钟设置: 
  a)    一般的LCD SPEC中都会给出关于时序的参数以及时序图,我们按照图中进行设置就可以了。如下图:我们就可以知道时钟频率、脉冲宽度、前扫、回扫等。 
  
	通过如下图的画面我们就可以知道HSYNC和VSYNC时钟极性为负。 
	
	  通过下图我们就可以知道是上升沿锁存数据,下降沿改变数据了 
	  
		通过以上步骤LCD上面应该会出现美丽动人的画面了,有可能图像位置还会有一些偏差,不过没关系,看着屏幕的图像调节前扫、回扫进行左右上下移动就OK了。 
		图像异常处理: 
		图像颜色不正常:有可能时钟型号极性反,还有可能VCOM调节不正常。 
		出现水波纹:确保电源VDD/AVDD/VGL/VGH纹波足够小,确保VCOM波形正确,VCOM电路端的电源稳定。 
		上电出现白屏:一般TFT LCD对上电要求都比较严格,需要按照LCD SPEC中时序上电
*/
/*
* Power control for Samsung LTV350QV Quarter VGA LCD Panel
*
* Copyright (C) 2006 Atmel Corporation
*
* 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.
*/
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/lcd.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/spi/spi.h>
#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
struct ltv350qv {
	struct spi_device *spi;
	u8 *buffer;
	int power;
	struct semaphore lock;
	struct lcd_device *ld;
	struct list_head list;
	int halt_done;
};
static LIST_HEAD(lcd_list);
static int ltv350qv_write_reg(struct ltv350qv *lcd, u8 reg, u16 val)
{
	struct spi_message msg;
	struct spi_transfer index_xfer = {
		.len  = 3,
			.cs_change = 1,
	};
	struct spi_transfer value_xfer = {
		.len  = 3,
			.cs_change = 1,
	};
	spi_message_init(&msg);
	/* register index */
	lcd->buffer[0] = 0x74;
	lcd->buffer[1] = 0x00;
	lcd->buffer[2] = reg & 0x7f;
	index_xfer.tx_buf = lcd->buffer;
	spi_message_add_tail(&index_xfer, &msg);
	/* register value */
	lcd->buffer[4] = 0x76;
	lcd->buffer[5] = val >> 8;
	lcd->buffer[6] = val;
	value_xfer.tx_buf = lcd->buffer + 4;
	spi_message_add_tail(&value_xfer, &msg);
	return spi_sync(lcd->spi, &msg);
}
#define write_reg(_spi, reg, val)    \
	do {       \
	ret = ltv350qv_write_reg(_spi, reg, val); \
	if (ret)     \
	goto out;    \
	} while (0)
static int ltv350qv_power_on(struct ltv350qv *lcd)
{
	int ret;
	write_reg(lcd,  9, 0x0000);
	msleep(15);
	write_reg(lcd,  9, 0x4000);
	write_reg(lcd, 10, 0x2000);
	write_reg(lcd,  9, 0x4055);
	msleep(55);
	write_reg(lcd,  1, 0x409d);
	write_reg(lcd,  2, 0x0204);
	write_reg(lcd,  3, 0x0100);
	write_reg(lcd,  4, 0x3000);
	write_reg(lcd,  5, 0x4003);
	write_reg(lcd,  6, 0x000a);
	write_reg(lcd,  7, 0x0021);
	write_reg(lcd,  8, 0x0c00);
	write_reg(lcd, 10, 0x0103);
	write_reg(lcd, 11, 0x0301);
	write_reg(lcd, 12, 0x1f0f);
	write_reg(lcd, 13, 0x1f0f);
	write_reg(lcd, 14, 0x0707);
	write_reg(lcd, 15, 0x0307);
	write_reg(lcd, 16, 0x0707);
	write_reg(lcd, 17, 0x0000);
	write_reg(lcd, 18, 0x0004);
	write_reg(lcd, 19, 0x0000);
	msleep(20);
	write_reg(lcd,  9, 0x4a55);
	write_reg(lcd,  5, 0x5003);
out:
	return ret;
}
static int ltv350qv_power_off(struct ltv350qv *lcd)
{
	int ret;
	/* GON -> 0, POC -> 0 */
	write_reg(lcd,  9, 0x4055);
	/* DSC -> 0 */
	write_reg(lcd,  5, 0x4003);
	/* VCOMG -> 0 */
	write_reg(lcd, 10, 0x2103);
	msleep(1);
	/* AP[2:0] -> 000 */
	write_reg(lcd,  9, 0x4050);
out:
	return ret;
}
static int ltv350qv_power(struct ltv350qv *lcd, int power)
{
	int ret = 0;
	down(&lcd->lock);
	if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
		ret = ltv350qv_power_on(lcd);
	else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
		ret = ltv350qv_power_off(lcd);
	if (!ret)
		lcd->power = power;
	up(&lcd->lock);
	return ret;
}
static int ltv350qv_set_power(struct lcd_device *ld, int power)
{
	struct ltv350qv *lcd;
	lcd = class_get_devdata(&ld->class_dev);
	return ltv350qv_power(lcd, power);
}
static int ltv350qv_get_power(struct lcd_device *ld)
{
	struct ltv350qv *lcd;
	lcd = class_get_devdata(&ld->class_dev);
	return lcd->power;
}
static struct lcd_properties lcd_properties = {
	.owner  = THIS_MODULE,
		.get_power = ltv350qv_get_power,
		.set_power = ltv350qv_set_power,
};
static int __devinit ltv350qv_probe(struct spi_device *spi)
{
	struct ltv350qv *lcd;
	struct lcd_device *ld;
	int ret;
	lcd = kzalloc(sizeof(struct ltv350qv), GFP_KERNEL);
	if (!lcd)
		return -ENOMEM;
	lcd->spi = spi;
	lcd->power = FB_BLANK_POWERDOWN;
	init_MUTEX(&lcd->lock);
	lcd->buffer = kzalloc(8, GFP_KERNEL);
	spi->mode = SPI_MODE_3;
	spi->bits_per_word = 8;
	ret = spi_setup(spi);
	if (ret)
		goto out_free_lcd;
	ld = lcd_device_register("ltv350qv", lcd, &lcd_properties);
	if (IS_ERR(ld)) {
		ret = PTR_ERR(ld);
		goto out_free_lcd;
	}
	lcd->ld = ld;
	list_add(&lcd->list, &lcd_list);
	ret = ltv350qv_power(lcd, FB_BLANK_UNBLANK);
	if (ret)
		goto out_unregister;
	dev_set_drvdata(&spi->dev, lcd);
	return 0;
out_unregister:
	lcd_device_unregister(ld);
out_free_lcd:
	kfree(lcd);
	return ret;
}
static int __devexit ltv350qv_remove(struct spi_device *spi)
{
	struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
	ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
	list_del(&lcd->list);
	lcd_device_unregister(lcd->ld);
	kfree(lcd);
	return 0;
}
#ifdef CONFIG_PM
static int ltv350qv_suspend(struct spi_device *spi,
							pm_message_t state, u32 level)
{
	struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
	if (level == SUSPEND_POWER_DOWN)
		return ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
	return 0;
}
static int ltv350qv_resume(struct spi_device *spi, u32 level)
{
	struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
	if (level == RESUME_POWER_ON)
		return ltv350qv_power(lcd, FB_BLANK_UNBLANK);
	return 0;
}
#else
#define ltv350qv_suspend NULL
#define ltv350qv_resume  NULL
#endif
/* Power down all displays on reboot, poweroff or halt */
static int ltv350qv_halt(struct notifier_block *nb, unsigned long event,
						 void *p)
{
	struct ltv350qv *lcd;
	list_for_each_entry(lcd, &lcd_list, list) {
		if (!lcd->halt_done)
			ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
		lcd->halt_done = 1;
	}
	return NOTIFY_OK;
}
static struct spi_driver ltv350qv_driver = {
	.driver = {
		.name  = "ltv350qv",
			.bus  = &spi_bus_type,
			.owner  = THIS_MODULE,
	},
	.probe  = ltv350qv_probe,
	.remove  = __devexit_p(ltv350qv_remove),
	.suspend = ltv350qv_suspend,
	.resume  = ltv350qv_resume,
};
static struct notifier_block ltv350qv_notifier = {
	.notifier_call = ltv350qv_halt,
};
static int __init ltv350qv_init(void)
{
	register_reboot_notifier(&ltv350qv_notifier);
	return spi_register_driver(&ltv350qv_driver);
}
static void __exit ltv350qv_exit(void)
{
	unregister_reboot_notifier(&ltv350qv_notifier);
	spi_unregister_driver(&ltv350qv_driver);
}
module_init(ltv350qv_init);
module_exit(ltv350qv_exit);
MODULE_AUTHOR("Atmel Norway");
MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver");
MODULE_LICENSE("GPL");
static int ltv350qv_power_on(struct ltv350qv *lcd)
{
	int ret;
	write_reg(lcd,  9, 0x0000);
	msleep(15);
	write_reg(lcd,  9, 0x4000);
	write_reg(lcd, 10, 0x2000);
	write_reg(lcd,  9, 0x4055);
	msleep(55);
	write_reg(lcd,  1, 0x409d);
	write_reg(lcd,  2, 0x0204);
	write_reg(lcd,  3, 0x0100);
	write_reg(lcd,  4, 0x3000);
	write_reg(lcd,  5, 0x4003);
	write_reg(lcd,  6, 0x000a);
	write_reg(lcd,  7, 0x0021);
	write_reg(lcd,  8, 0x0c00);
	write_reg(lcd, 10, 0x0103);
	write_reg(lcd, 11, 0x0301);
	write_reg(lcd, 12, 0x1f0f);
	write_reg(lcd, 13, 0x1f0f);
	write_reg(lcd, 14, 0x0707);
	write_reg(lcd, 15, 0x0307);
	write_reg(lcd, 16, 0x0707);
	write_reg(lcd, 17, 0x0000);
	write_reg(lcd, 18, 0x0004);
	write_reg(lcd, 19, 0x0000);
	msleep(20);
	write_reg(lcd,  9, 0x4a55);
	write_reg(lcd,  5, 0x5003);
out:
	return ret;
}
static int ltv350qv_power_off(struct ltv350qv *lcd)
{
	int ret;
	/* GON -> 0, POC -> 0 */
	write_reg(lcd,  9, 0x4055);
	/* DSC -> 0 */
	write_reg(lcd,  5, 0x4003);
	/* VCOMG -> 0 */
	write_reg(lcd, 10, 0x2103);
	msleep(1);
	/* AP[2:0] -> 000 */
	write_reg(lcd,  9, 0x4050);
out:
	return ret;
}
static int ltv350qv_power(struct ltv350qv *lcd, int power)
{
	int ret = 0;
	down(&lcd->lock);
	if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
		ret = ltv350qv_power_on(lcd);
	else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
		ret = ltv350qv_power_off(lcd);
	if (!ret)
		lcd->power = power;
	up(&lcd->lock);
	return ret;
}
static int ltv350qv_set_power(struct lcd_device *ld, int power)
{
	struct ltv350qv *lcd;
	lcd = class_get_devdata(&ld->class_dev);
	return ltv350qv_power(lcd, power);
}
static int ltv350qv_get_power(struct lcd_device *ld)
{
	struct ltv350qv *lcd;
	lcd = class_get_devdata(&ld->class_dev);
	return lcd->power;
}
static struct lcd_properties lcd_properties = {
	.owner  = THIS_MODULE,
		.get_power = ltv350qv_get_power,
		.set_power = ltv350qv_set_power,
};
static int __devinit ltv350qv_probe(struct spi_device *spi)
{
	struct ltv350qv *lcd;
	struct lcd_device *ld;
	int ret;
	lcd = kzalloc(sizeof(struct ltv350qv), GFP_KERNEL);
	if (!lcd)
		return -ENOMEM;
	lcd->spi = spi;
	lcd->power = FB_BLANK_POWERDOWN;
	init_MUTEX(&lcd->lock);
	lcd->buffer = kzalloc(8, GFP_KERNEL);
	spi->mode = SPI_MODE_3;
	spi->bits_per_word = 8;
	ret = spi_setup(spi);
	if (ret)
		goto out_free_lcd;
	ld = lcd_device_register("ltv350qv", lcd, &lcd_properties);
	if (IS_ERR(ld)) {
		ret = PTR_ERR(ld);
		goto out_free_lcd;
	}
	lcd->ld = ld;
	list_add(&lcd->list, &lcd_list);
	ret = ltv350qv_power(lcd, FB_BLANK_UNBLANK);
	if (ret)
		goto out_unregister;
	dev_set_drvdata(&spi->dev, lcd);
	return 0;
out_unregister:
	lcd_device_unregister(ld);
out_free_lcd:
	kfree(lcd);
	return ret;
}
static int __devexit ltv350qv_remove(struct spi_device *spi)
{
	struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
	ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
	list_del(&lcd->list);
	lcd_device_unregister(lcd->ld);
	kfree(lcd);
	return 0;
}
#ifdef CONFIG_PM
static int ltv350qv_suspend(struct spi_device *spi,
							pm_message_t state, u32 level)
{
	struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
	if (level == SUSPEND_POWER_DOWN)
		return ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
	return 0;
}
static int ltv350qv_resume(struct spi_device *spi, u32 level)
{
	struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
	if (level == RESUME_POWER_ON)
		return ltv350qv_power(lcd, FB_BLANK_UNBLANK);
	return 0;
}
#else
#define ltv350qv_suspend NULL
#define ltv350qv_resume  NULL
#endif
/* Power down all displays on reboot, poweroff or halt */
static int ltv350qv_halt(struct notifier_block *nb, unsigned long event,
						 void *p)
{
	struct ltv350qv *lcd;
	list_for_each_entry(lcd, &lcd_list, list) {
		if (!lcd->halt_done)
			ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
		lcd->halt_done = 1;
	}
	return NOTIFY_OK;
}
static struct spi_driver ltv350qv_driver = {
	.driver = {
		.name  = "ltv350qv",
			.bus  = &spi_bus_type,
			.owner  = THIS_MODULE,
	},
	.probe  = ltv350qv_probe,
	.remove  = __devexit_p(ltv350qv_remove),
	.suspend = ltv350qv_suspend,
	.resume  = ltv350qv_resume,
};
static struct notifier_block ltv350qv_notifier = {
	.notifier_call = ltv350qv_halt,
};
static int __init ltv350qv_init(void)
{
	register_reboot_notifier(&ltv350qv_notifier);
	return spi_register_driver(&ltv350qv_driver);
}
static void __exit ltv350qv_exit(void)
{
	unregister_reboot_notifier(&ltv350qv_notifier);
	spi_unregister_driver(&ltv350qv_driver);
}
module_init(ltv350qv_init);
module_exit(ltv350qv_exit);
MODULE_AUTHOR("Atmel Norway");
MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver");
MODULE_LICENSE("GPL");


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -