自从用F1C200S调试8080接口16位并行LCD屏幕成功后,就迫不及待的用T113-S3做实验,本来想着挺简单的,但是被一条指令搞了一天,就是它:
if (info->lcd_cpu_mode == LCD_CPU_AUTO_MODE)
{
DBG_INFO(" cpu auto mode.\n");
sunxi_lcd_cpu_set_auto_mode(sel);
}
不知道是不是我的LCD特殊的原因,看着别人的屏幕不用这条指令也能点亮,无语中,LCD屏幕是ST7796SV芯片,背光PWM用的是PWM4(PE10),屏幕RESET管脚是PG10,未开启“TE”模式。写一下配置,为后面的兄弟避坑了。
board.dts
&pio {
......
rgb16_pins_a: rgb16@0 {
allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", \
"PD6", "PD7", "PD8", "PD10", "PD11", "PD12", \
"PD13", "PD14", "PD15", "PD16", "PD17", \
"PD18", "PD19", "PD20", "PD21";
allwinner,pname = "lcdb0", "lcdb1", "lcdb2", "lcdb3", "lcdb4", \
"lcdg0", "lcdg1", "lcdg2", "lcdg3", "lcdg4", "lcdg5", \
"lcdr0", "lcdr1", "lcdr2", "lcdr3", "lcdr4", \
"lcdwr", "lcdrs", "lcdrd", "lcdcs";
allwinner,function = "lcd0";
allwinner,muxsel = <2>;
allwinner,drive = <3>;
allwinner,pull = <0>;
};
rgb16_pins_b: rgb16@1 {
allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", \
"PD6", "PD7", "PD8", "PD10", "PD11", "PD12", \
"PD13", "PD14", "PD15", "PD16", "PD17", \
"PD18", "PD19", "PD20", "PD21";
allwinner,pname = "lcdb0", "lcdb1", "lcdb2", "lcdb3", "lcdb4", \
"lcdg0", "lcdg1", "lcdg2", "lcdg3", "lcdg4", "lcdg5", \
"lcdr0", "lcdr1", "lcdr2", "lcdr3", "lcdr4", \
"lcdwr", "lcdrs", "lcdrd", "lcdcs";
allwinner,function = "io_disabled";
allwinner,muxsel = <7>;
allwinner,drive = <3>;
allwinner,pull = <0>;
};
}
/* 8080i 16bit */
&lcd0 {
// part 1
lcd_used = <1>;
lcd_driver_name = "st7796sv_cpu";
// part 2
lcd_if = <1>;
lcd_cpu_if = <8>;
// part 3
lcd_x = <480>;
lcd_y = <320>;
lcd_width = <84>;
lcd_height = <56>;
lcd_dclk_freq = <24>;
lcd_hbp = <100>;
lcd_ht = <618>;
lcd_hspw = <50>;
lcd_vbp = <8>;
lcd_vt = <336>;
lcd_vspw = <4>;
// part 4
lcd_pwm_used = <1>;
lcd_pwm_ch = <4>;
lcd_pwm_freq = <50000>;
lcd_pwm_pol = <1>;
lcd_pwm_max_limit = <255>;
lcd_backlight = <150>;
// part 5
lcd_cpu_mode = <0>;
lcd_cpu_te = <0>;
// part 6
lcd_frm = <0>;
lcd_gamma_en = <0>;
lcd_cmap_en = <0>;
lcd_rb_swap = <0>;
// part 7
// reset pin
lcd_gpio_0 = <&pio PG 10 1 0 3 1>;
pinctrl-0 = <&rgb16_pins_a>;
pinctrl-1 = <&rgb16_pins_b>;
};
st7796sv_cpu.c
#include "st7796sv_cpu.h"
// #define CPU_TRI_MODE
#define DBG_INFO(format, args...) (printk("[ST7796SV LCD INFO] LINE:%04d-->%s:"format, __LINE__, __func__, ##args))
#define DBG_ERR(format, args...) (printk("[ST7796SV LCD ERR] LINE:%04d-->%s:"format, __LINE__, __func__, ##args))
#define panel_reset(val) sunxi_lcd_gpio_set_value(sel, 0, val)
// #define lcd_cs(val) sunxi_lcd_gpio_set_value(sel, 1, val)
#define LCD_WIDTH 480
#define LCD_HEIGHT 320
#define MADCTL_MY 0x80 // 纵向交换
#define MADCTL_MX 0x40 // 横向交换
#define MADCTL_MV 0x20 // 纵横交换
#define MADCTL_ML 0x10 // 从下到上刷新
#define MADCTL_RGB 0x00 // RGB
#define MADCTL_BGR 0x08 // BGR
#define MADCTL_MH 0x04 // 从右到左刷新
typedef enum
{
LCD_DIRECTION_0,
LCD_DIRECTION_90,
LCD_DIRECTION_180,
LCD_DIRECTION_270
} eLcdDirection;
static void lcd_panel_st7796sv_init(u32 sel, struct disp_panel_para *info);
static void LCD_power_on(u32 sel);
static void LCD_power_off(u32 sel);
static void LCD_bl_open(u32 sel);
static void LCD_bl_close(u32 sel);
static void LCD_panel_init(u32 sel);
static void LCD_panel_exit(u32 sel);
static void LCD_cfg_panel_info(struct panel_extend_para *info)
{
}
static s32 LCD_open_flow(u32 sel)
{
LCD_OPEN_FUNC(sel, LCD_power_on, 120);
#ifdef CPU_TRI_MODE
LCD_OPEN_FUNC(sel, LCD_panel_init, 100);
LCD_OPEN_FUNC(sel, sunxi_lcd_tcon_enable, 50);
#else
LCD_OPEN_FUNC(sel, sunxi_lcd_tcon_enable, 100);
LCD_OPEN_FUNC(sel, LCD_panel_init, 50);
#endif
LCD_OPEN_FUNC(sel, LCD_bl_open, 0);
return 0;
}
static s32 LCD_close_flow(u32 sel)
{
LCD_CLOSE_FUNC(sel, LCD_bl_close, 20);
#ifdef CPU_TRI_MODE
LCD_CLOSE_FUNC(sel, sunxi_lcd_tcon_disable, 10);
LCD_CLOSE_FUNC(sel, LCD_panel_exit, 50);
#else
LCD_CLOSE_FUNC(sel, LCD_panel_exit, 10);
LCD_CLOSE_FUNC(sel, sunxi_lcd_tcon_disable, 10);
#endif
LCD_CLOSE_FUNC(sel, LCD_power_off, 0);
return 0;
}
static void LCD_power_on(u32 sel)
{
/*config lcd_power pin to open lcd power0 */
sunxi_lcd_power_enable(sel, 0);
sunxi_lcd_pin_cfg(sel, 1);
}
static void LCD_power_off(u32 sel)
{
/*lcd_cs, active low */
// lcd_cs(1);
sunxi_lcd_delay_ms(10);
/*lcd_rst, active hight */
panel_reset(1);
sunxi_lcd_delay_ms(10);
sunxi_lcd_pin_cfg(sel, 0);
/*config lcd_power pin to close lcd power0 */
sunxi_lcd_power_disable(sel, 0);
}
static void LCD_bl_open(u32 sel)
{
sunxi_lcd_pwm_enable(sel);
/*config lcd_bl_en pin to open lcd backlight */
sunxi_lcd_backlight_enable(sel);
}
static void LCD_bl_close(u32 sel)
{
/*config lcd_bl_en pin to close lcd backlight */
sunxi_lcd_backlight_disable(sel);
sunxi_lcd_pwm_disable(sel);
}
/*static int bootup_flag = 0;*/
static void LCD_panel_init(u32 sel)
{
struct disp_panel_para *info =
kmalloc(sizeof(struct disp_panel_para), GFP_KERNEL);
DBG_INFO("\n");
bsp_disp_get_panel_info(sel, info);
lcd_panel_st7796sv_init(sel, info);
kfree(info);
return;
}
static void LCD_panel_exit(u32 sel)
{
sunxi_lcd_cpu_write_index(sel, 0x28);
sunxi_lcd_cpu_write_index(sel, 0x10);
}
void lcdSetWindows(u32 sel, u16 x, u16 y, u16 width, u16 height)
{
u16 temp;
// 横向
temp = x + width - 1;
if (temp >= LCD_WIDTH) {
temp = LCD_WIDTH - 1;
}
sunxi_lcd_cpu_write_index(sel, 0x2a);
sunxi_lcd_cpu_write_data(sel, (x >> 8) & 0xFF);
sunxi_lcd_cpu_write_data(sel, x & 0xFF);
sunxi_lcd_cpu_write_data(sel, (temp >> 8) && 0xFF);
sunxi_lcd_cpu_write_data(sel, temp & 0xFF);
// 纵向
temp = y + height - 1;
if (temp >= LCD_HEIGHT) {
temp = LCD_HEIGHT - 1;
}
sunxi_lcd_cpu_write_index(sel, 0x2b);
sunxi_lcd_cpu_write_data(sel, (y >> 8) && 0xFF);
sunxi_lcd_cpu_write_data(sel, y & 0xFF);
sunxi_lcd_cpu_write_data(sel, (temp >> 8) && 0xFF);
sunxi_lcd_cpu_write_data(sel, temp & 0xFF);
// 开始写入屏幕
sunxi_lcd_cpu_write_index(sel, 0x2c);
}
void lcdSetDirection(u32 sel, eLcdDirection dir)
{
u8 temp = 0;
switch (dir) {
case LCD_DIRECTION_0:
temp = MADCTL_MX;
break;
case LCD_DIRECTION_90:
temp = MADCTL_MV;
break;
case LCD_DIRECTION_180:
temp = MADCTL_MY;
break;
case LCD_DIRECTION_270:
temp = MADCTL_MY | MADCTL_MX | MADCTL_MV;
break;
default:
break;
}
temp |= MADCTL_BGR;
sunxi_lcd_cpu_write_index(sel, 0x36);
sunxi_lcd_cpu_write_data(sel, temp);
lcdSetWindows(sel, 0, 0, LCD_WIDTH, LCD_HEIGHT);
}
static void lcd_panel_st7796sv_init(u32 sel, struct disp_panel_para *info)
{
u8 initCmd[] = {
0x81, 0x11,
2, 0xfb, 0xc3,
2, 0xfb, 0x96,
2, 0x36, 0x48,
2, 0x3a, 0x55,
2, 0xb4, 0x01,
2, 0xb7, 0xc6,
9, 0xe8, 0x40, 0x8a, 0x00, 0x00, 0x29, 0x19, 0xa5, 0x33,
2, 0xc1, 0x06,
2, 0xc2, 0xa7,
2, 0xc5, 0x18,
15, 0xe0, 0x0F, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0X54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b,
15, 0xe1, 0xf0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2d, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b,
2, 0xeb, 0x3c,
0x82, 0xeb, 0x69,
1, 0x29,
0x00
};
u8* cmdIndex = initCmd;
u8 count, temp;
u16 index;
DBG_INFO(" %d, %d\n", info->lcd_x, info->lcd_y);
/*lcd_cs, active low */
// lcd_cs(0);
// reset
panel_reset(1);
sunxi_lcd_delay_ms(10);
panel_reset(0);
sunxi_lcd_delay_ms(20);
panel_reset(1);
sunxi_lcd_delay_ms(150);
// init lcd
while (*cmdIndex) {
temp = *cmdIndex++;
count = (temp & 0x7F) - 1;
sunxi_lcd_cpu_write_index(sel, *cmdIndex++);
for (index = 0; index < count; ++index)
{
sunxi_lcd_cpu_write_data(sel, *cmdIndex++);
}
if (temp >= 0x80) {
sunxi_lcd_delay_ms(150);
}
}
#if defined(CPU_TRI_MODE)
/* enable te, mode 0 */
sunxi_lcd_cpu_write_index(0, 0x35);
sunxi_lcd_cpu_write_data(0, 0x00);
sunxi_lcd_cpu_write_index(0, 0x44);
sunxi_lcd_cpu_write_data(0, 0x00);
sunxi_lcd_cpu_write_data(0, 0x80);
#endif
lcdSetDirection(sel, LCD_DIRECTION_90);
sunxi_lcd_cpu_write_index(sel, 0x2c);
if (info->lcd_cpu_mode == LCD_CPU_AUTO_MODE)
{
DBG_INFO(" cpu auto mode.\n");
sunxi_lcd_cpu_set_auto_mode(sel);
}
}
/* panel driver name, must mach the name of lcd_drv_name in sys_config.fex */
struct __lcd_panel st7796sv_cpu_panel = {
.name = "st7796sv_cpu",
.func = {
.cfg_panel_info = LCD_cfg_panel_info,
.cfg_open_flow = LCD_open_flow,
.cfg_close_flow = LCD_close_flow,
},
};
离线