📄 lcd_linux.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(<v350qv_notifier);
return spi_register_driver(<v350qv_driver);
}
static void __exit ltv350qv_exit(void)
{
unregister_reboot_notifier(<v350qv_notifier);
spi_unregister_driver(<v350qv_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(<v350qv_notifier);
return spi_register_driver(<v350qv_driver);
}
static void __exit ltv350qv_exit(void)
{
unregister_reboot_notifier(<v350qv_notifier);
spi_unregister_driver(<v350qv_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 + -