rt-thread/bsp/x1000/drivers/slcd/drv_slcdc.c

497 lines
15 KiB
C

/*
* File : drv_slcd_new.c
* COPYRIGHT (C) 2008 - 2016, RT-Thread Development Team
*
* Change Logs:
* Date Author Notes
* 2017Äê4ÔÂ10ÈÕ Urey the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <cache.h>
#include "board.h"
#include "drv_slcdc.h"
#include "drv_clock.h"
#include "drv_gpio.h"
static struct slcdc_dev_s *_slcd_device;
static void _slcd_enable(struct slcdc_dev_s *lcd_dev);
static void _slcd_disable(struct slcdc_dev_s *lcd_dev);
static void udelay(uint32_t x)
{
volatile uint32_t n ;
while(x--)
{
for (n = 0; n < 200; ++n);
}
}
static int _slcd_convert_bpp(uint32_t bpp)
{
switch (bpp)
{
case 18:
case 24:
return 32;
case 15:
return 16;
default:
return bpp;
}
}
static uint32_t refresh_pixclock_auto_adapt(struct slcdc_dev_s *lcd_dev)
{
uint32_t pixclk = 0;
if((lcd_dev->cfg->refresh < 10) || (lcd_dev->cfg->refresh > 100))
lcd_dev->cfg->refresh = 60;
pixclk = lcd_dev->fb_size * lcd_dev->cfg->refresh * 9;// Range 7 to 10
return pixclk;
}
static void _slcd_wait_busy(void)
{
int count = 10000;
while ((slcd_reg_read(SLCDC_STATE) & SLCDC_STATE_BUSY) && count--);
}
/* Sent a command without data (18-bit bus, 16-bit index) */
static void _slcd_mcu_send_command(struct slcdc_dev_s *lcd_dev,uint16_t cmd)
{
_slcd_wait_busy();
cmd &= 0xffffff;
slcd_reg_write(SLCDC_DATA, SLCDC_DATA_RS_COMMAND | cmd);
}
static void _slcd_mcu_send_data(struct slcdc_dev_s *lcd_dev,uint16_t data)
{
_slcd_wait_busy();
data &= 0xffffff;
slcd_reg_write(SLCDC_DATA, SLCDC_DATA_RS_DATA | data);
}
/* Sent a command with data (18-bit bus, 16-bit index, 16-bit register value) */
static void _slcd_mcu_set_register(struct slcdc_dev_s *lcd_dev,uint16_t cmd, uint16_t data)
{
_slcd_mcu_send_command(lcd_dev,cmd);
_slcd_mcu_send_data(lcd_dev,data);
}
static void _slcd_init_mcu(struct slcdc_dev_s *lcd_dev)
{
struct slcd_configure *cfg = lcd_dev->cfg;
uint32_t index,j;
uint32_t reg_width = lcd_dev->cfg->reg_width;
uint32_t bus_width = lcd_dev->cfg->bus_width;
if(reg_width < bus_width)
reg_width = bus_width;
if (cfg->data_table_num && cfg->data_table)
{
for (index = 0; index < cfg->data_table_num; index ++)
{
uint32_t value = cfg->data_table[index].value;
switch (cfg->data_table[index].type)
{
case SMART_CONFIG_CMD:
for (j = reg_width / bus_width; j > 0; j--)
_slcd_mcu_send_command(lcd_dev, ((value << (32 - bus_width * j)) >> (32 - bus_width)));
break;
case SMART_CONFIG_DATA:
for (j = reg_width / bus_width; j > 0; j--)
_slcd_mcu_send_data(lcd_dev,((value << (32 - bus_width * j))>> (32 - bus_width)));
break;
case SMART_CONFIG_UDELAY:
udelay(cfg->data_table[index].value);
break;
}
}
_slcd_wait_busy();
}
if (cfg->bpp / cfg->bus_width != 1)
{
int tmp = slcd_reg_read(SLCDC_CFG_NEW);
tmp &= ~(SMART_LCD_DWIDTH_MASK); //mask the 8~9bit
tmp |= (cfg->bpp / cfg->bus_width) == 2 ? SMART_LCD_NEW_DTIMES_TWICE : SMART_LCD_NEW_DTIMES_THICE ;
slcd_reg_write(SLCDC_CFG_NEW, tmp);
}
}
static void _slcd_init_mem(struct slcdc_dev_s *lcd_dev)
{
struct slcd_configure *cfg = lcd_dev->cfg;
int i;
uint32_t bypes_per_panel = (((cfg->width * _slcd_convert_bpp(cfg->bpp) / 8 + 3) >> 2 << 2) * cfg->height);
#ifdef FB_BASE
lcd_dev->fb_base = FB_BASE;
#else
#ifdef SLCDC_USING_DUAL_BUFFER
lcd_dev->fb_base = (rt_uint32_t)rt_malloc_align((bypes_per_panel + FB_PAGE_SIZE) * 2, 32);
#else
lcd_dev->fb_base = (rt_uint32_t)rt_malloc_align((bypes_per_panel + FB_PAGE_SIZE) * 1, 32);
#endif
#endif
lcd_dev->desc_cmd = (struct slcdc_dma_descriptor *)(lcd_dev->fb_base + 0 * sizeof(struct slcdc_dma_descriptor));
lcd_dev->desc_tmp = (struct slcdc_dma_descriptor *)(lcd_dev->fb_base + 1 * sizeof(struct slcdc_dma_descriptor));
lcd_dev->desc_dat = (struct slcdc_dma_descriptor *)(lcd_dev->fb_base + 2 * sizeof(struct slcdc_dma_descriptor));
//nop
lcd_dev->fb_cmd = (rt_uint32_t)lcd_dev->fb_base + 4 * sizeof(struct slcdc_dma_descriptor);
lcd_dev->fb_screen= (rt_uint32_t)lcd_dev->fb_base + FB_PAGE_SIZE;
rt_memset((void *) lcd_dev->fb_screen, 0, bypes_per_panel);
#ifdef SLCDC_USING_DUAL_BUFFER
lcd_dev->fb_dual = (lcd_dev->fb_screen + bypes_per_panel + FB_PAGE_SIZE) & ~(FB_PAGE_SIZE - 1);
rt_memset((void *)lcd_dev->fb_dual,0,bypes_per_panel);
#endif
lcd_dev->fb_size = bypes_per_panel;
/* copy command tbl */
{
uint32_t* cmd_ptr = (uint32_t*) lcd_dev->fb_cmd;
for (i = 0; i < cfg->cmd_table_num; ++i)
{
cmd_ptr[i] = cfg->cmd_table[i];
}
}
}
static void _slcd_init_dma_desc(struct slcdc_dev_s *lcd_dev)
{
struct slcd_configure *cfg = lcd_dev->cfg;
uint32_t bypes_per_panel = (((cfg->width * _slcd_convert_bpp(cfg->bpp) / 8 + 3) >> 2 << 2) * cfg->height);
//dmadesc_tmp used to start DMA
lcd_dev->desc_tmp->fdadr = virt_to_phys((void *)lcd_dev->desc_dat);
lcd_dev->desc_tmp->fsadr = 0;
lcd_dev->desc_tmp->fidr = 0xda0c0;
lcd_dev->desc_tmp->ldcmd = LCDC_CMD_CMD | LCDC_CMD_FRM_EN | 0;
lcd_dev->desc_tmp->offsize = 0;
lcd_dev->desc_tmp->page_width = 0;
lcd_dev->desc_tmp->cmd_num = 0;
lcd_dev->desc_tmp->desc_size = 0;
//dmadesc_cmd used to write CMD
lcd_dev->desc_cmd->fdadr = virt_to_phys((void *)lcd_dev->desc_dat);
lcd_dev->desc_cmd->fsadr = virt_to_phys((void *)lcd_dev->fb_cmd);
lcd_dev->desc_cmd->fidr = 0xda0c1;
lcd_dev->desc_cmd->offsize = 0;
lcd_dev->desc_cmd->page_width = 0;
lcd_dev->desc_cmd->desc_size = 0;
/* if connect mipi smart lcd, do not sent command by slcdc, send command by mipi dsi controller. */
switch (cfg->bus_width)
{
case 8:
lcd_dev->desc_cmd->ldcmd = LCDC_CMD_CMD | LCDC_CMD_FRM_EN | 1;
lcd_dev->desc_cmd->cmd_num = 4;
break;
case 9:
case 16:
lcd_dev->desc_cmd->ldcmd = LCDC_CMD_CMD | LCDC_CMD_FRM_EN | 1;
lcd_dev->desc_cmd->cmd_num = 2;
break;
default:
lcd_dev->desc_cmd->ldcmd = LCDC_CMD_CMD | LCDC_CMD_FRM_EN | 1;
lcd_dev->desc_cmd->cmd_num = 1;
break;
}
//frame_desc[1] used to update GRAM
lcd_dev->desc_dat->fdadr = virt_to_phys((void *)lcd_dev->desc_cmd);
lcd_dev->desc_dat->fsadr = virt_to_phys((void *)lcd_dev->fb_screen);
lcd_dev->desc_dat->fidr = 0xda0d0;
lcd_dev->desc_dat->ldcmd = LCDC_CMD_EOFINT | LCDC_CMD_FRM_EN | (bypes_per_panel / 4);
lcd_dev->desc_dat->offsize = 0;
lcd_dev->desc_dat->page_width = 0;
switch(_slcd_convert_bpp(cfg->bpp))
{
case 16 :
lcd_dev->desc_dat->cmd_num = LCDC_CPOS_RGB_RGB565 | LCDC_CPOS_BPP_16;;
break;
case 30 :
lcd_dev->desc_dat->cmd_num = LCDC_CPOS_BPP_30;
break;
default:
lcd_dev->desc_dat->cmd_num = LCDC_CPOS_BPP_18_24;
break;
}
/* data has not been premultied */
lcd_dev->desc_dat->cmd_num |= LCDC_CPOS_PREMULTI;
/* coef_sle 0 use 1 */
lcd_dev->desc_dat->cmd_num |= LCDC_CPOS_COEF_SLE_1;
lcd_dev->desc_dat->desc_size = (((cfg->height - 1) << LCDC_DESSIZE_HEIGHT_BIT) | ((cfg->width - 1) << LCDC_DESSIZE_WIDTH_BIT));
slcd_reg_write(LCDC_DA0, virt_to_phys(lcd_dev->desc_cmd));
//desc self
rt_hw_flush_cache_all();
}
static void _slcd_init_ctrl(struct slcdc_dev_s *lcd_dev)
{
struct slcd_configure *lcd_cfg = lcd_dev->cfg;
struct clk *clk,*gate_clk;
uint32_t ctrl;
uint32_t size0;
uint32_t smart_cfg = 0, smart_ctrl = 0;
uint32_t pcfg;
uint32_t smart_new_cfg = 0;
uint32_t smart_wtime = 0, smart_tas = 0;
/* clear all registers*/
_slcd_disable(lcd_dev);
slcd_reg_write(SLCDC_CTRL,0);
/*The SLCD rd and ce function only can be used by set PB16/PB18 as normal GPIO function
* SLCS_D00 PA00
* ...
* SLCS_D07 PA07
*
* slcd_rd PB16 (not use,must set high)
* slcd_wr PB17
* slcd_ce PB18
* slcd_te PB19 (not use)
* slcd_dc PB20
* 2. setup SLCD for register mode
* */
gpio_set_func(GPIO_PORT_A, 0x000000FF, GPIO_FUNC_1);
gpio_set_func(GPIO_PORT_B, (GPIO_Pin_17 | GPIO_Pin_18 | GPIO_Pin_20), GPIO_FUNC_1);
#ifdef CONFIG_SLCDC_USE_TE
gpio_set_func(GPIO_PORT_B, (GPIO_Pin_19), GPIO_FUNC_1);
#endif
gpio_set_func(GPIO_PORT_B, GPIO_Pin_16, GPIO_OUTPUT1);
/* set clock */
gate_clk = clk_get("lcd");
clk = clk_get("cgu_lcd");
clk_disable(clk);
clk_set_rate(clk, refresh_pixclock_auto_adapt(lcd_dev));
clk_enable(clk);
clk_enable(gate_clk);
ctrl = LCDC_CTRL_BST_64 | LCDC_CTRL_OFUM;
if(lcd_cfg->pinmd)
ctrl |= LCDC_CTRL_PINMD;
smart_cfg = SMART_LCD_DWIDTH_24_BIT_ONCE_PARALLEL;
switch (lcd_cfg->bus_width)
{
case 8:
smart_cfg |= SMART_LCD_CWIDTH_8_BIT_ONCE;
smart_new_cfg |= SMART_LCD_NEW_DWIDTH_8_BIT;
break;
case 9:
smart_cfg |= SMART_LCD_CWIDTH_9_BIT_ONCE;
smart_new_cfg |= SMART_LCD_NEW_DWIDTH_9_BIT;
break;
case 16:
smart_cfg |= SMART_LCD_CWIDTH_16_BIT_ONCE;
smart_new_cfg |= SMART_LCD_NEW_DWIDTH_16_BIT;
break;
case 18:
smart_cfg |= SMART_LCD_CWIDTH_18_BIT_ONCE;
smart_new_cfg |= SMART_LCD_NEW_DWIDTH_18_BIT;
break;
case 24:
smart_cfg |= SMART_LCD_CWIDTH_24_BIT_ONCE;
smart_new_cfg |= SMART_LCD_NEW_DWIDTH_24_BIT;
break;
default:
rt_kprintf("ERR: please check out your bus width config\n");
break;
}
if (lcd_cfg->clkply_active_rising)
smart_cfg |= SLCDC_CFG_CLK_ACTIVE_RISING;
if (lcd_cfg->rsply_cmd_high)
smart_cfg |= SLCDC_CFG_RS_CMD_HIGH;
if (lcd_cfg->csply_active_high)
smart_cfg |= SLCDC_CFG_CS_ACTIVE_HIGH;
/* SLCD DMA mode select 0 */
smart_ctrl = SLCDC_CTRL_DMA_MODE;
smart_ctrl &= ~SLCDC_CTRL_GATE_MASK;
smart_ctrl |= (SLCDC_CTRL_NEW_MODE | SLCDC_CTRL_NOT_USE_TE | SLCDC_CTRL_FAST_MODE); //new slcd mode
smart_ctrl &= ~SLCDC_CTRL_MIPI_MODE;
smart_new_cfg |= SMART_LCD_NEW_DTIMES_ONCE;
if (lcd_cfg->newcfg_6800_md)
smart_new_cfg |= SLCDC_NEW_CFG_6800_MD;
if (lcd_cfg->newcfg_cmd_9bit)
smart_new_cfg |= SLCDC_NEW_CFG_CMD_9BIT;
slcd_reg_write(LCDC_VAT, (lcd_cfg->width << 16) | lcd_cfg->height);
slcd_reg_write(LCDC_DAH, lcd_cfg->width);
slcd_reg_write(LCDC_DAV, lcd_cfg->height);
slcd_reg_write(SLCDC_CFG, smart_cfg);
slcd_reg_write(SLCDC_CTRL, smart_ctrl);
slcd_reg_write(SLCDC_CFG_NEW, smart_new_cfg);
slcd_reg_write(SLCDC_WTIME, smart_wtime);
slcd_reg_write(SLCDC_TAS, smart_tas);
slcd_reg_write(SLCDC_SLOW_TIME, 0x0000FFFF);
slcd_reg_write(LCDC_CTRL, ctrl);
pcfg = 0xC0000000 | (511 << 18) | (400 << 9) | (256 << 0);
slcd_reg_write(LCDC_PCFG, pcfg);
size0 = (lcd_cfg->width << LCDC_SIZE_WIDTH_BIT) & LCDC_SIZE_WIDTH_MASK;
size0 |= (lcd_cfg->height << LCDC_SIZE_HEIGHT_BIT) & LCDC_SIZE_HEIGHT_MASK;
slcd_reg_write(LCDC_SIZE0, size0);
slcd_reg_write(LCDC_CTRL,slcd_reg_read(LCDC_CTRL) | LCDC_CTRL_ENA);
_slcd_init_dma_desc(lcd_dev);
_slcd_init_mcu(lcd_dev);
_slcd_enable(lcd_dev);
if (lcd_cfg->newcfg_fmt_conv)
{
smart_new_cfg = slcd_reg_read(SLCDC_CFG_NEW);
smart_new_cfg |= SLCDC_NEW_CFG_FMT_CONV_EN;
slcd_reg_write(SLCDC_CFG_NEW, smart_new_cfg);
}
#ifdef CONFIG_SLCDC_CONTINUA
smart_ctrl &= ~SLCDC_CTRL_DMA_MODE;
#else
smart_ctrl |= SLCDC_CTRL_DMA_START;
#endif
smart_ctrl |= SLCDC_CTRL_DMA_EN;
#ifdef CONFIG_SLCDC_USE_TE
smart_ctrl &= ~SLCDC_CTRL_NOT_USE_TE;
//smart_ctrl |= SLCDC_CTRL_TE_INV;
smart_ctrl &= ~SLCDC_CTRL_TE_INV;
#endif
slcd_reg_write(SLCDC_CTRL, smart_ctrl);
}
static void _slcd_enable(struct slcdc_dev_s *lcd_dev)
{
uint32_t ctrl,state;
int count = 2000;
while ((slcd_reg_read(SLCDC_STATE) & SLCDC_STATE_BUSY) && count--)
{
udelay(10);
}
slcd_reg_write(LCDC_STATE, 0);
slcd_reg_write(LCDC_CTRL,slcd_reg_read(LCDC_CTRL) | LCDC_CTRL_ENA);
}
static void _slcd_disable(struct slcdc_dev_s *lcd_dev)
{
/* SLCD and TVE only support quick disable */
slcd_reg_write(LCDC_CTRL, slcd_reg_read(LCDC_CTRL) & ~LCDC_CTRL_ENA);
}
/* common device interface */
static rt_err_t _slcd_device_control(rt_device_t dev, int cmd, void *args)
{
struct slcdc_dev_s *slcd;
uint32_t smart_ctrl = 0;
slcd = (struct slcdc_dev_s *)dev;
RT_ASSERT(slcd != RT_NULL);
rt_mutex_take(&(slcd->lock), RT_WAITING_FOREVER);
switch (cmd)
{
case RTGRAPHIC_CTRL_GET_INFO:
{
struct rt_device_graphic_info *info = (struct rt_device_graphic_info *) args;
info->bits_per_pixel = slcd->cfg->bpp;
info->pixel_format = slcd->cfg->fmt;
#ifdef SLCDC_USING_DUAL_BUFFER
info->framebuffer = (rt_uint8_t *)(KSEG1ADDR(slcd->fb_dual));
#else
info->framebuffer = (rt_uint8_t *)(KSEG1ADDR(slcd->fb_screen));
#endif
info->width = slcd->cfg->width;
info->height = slcd->cfg->height;
break;
}
case RTGRAPHIC_CTRL_RECT_UPDATE:
{
#ifdef SLCDC_USING_DUAL_BUFFER
rt_memcpy((void *)(slcd->fb_screen), (void *)(slcd->fb_dual), slcd->fb_size);
#endif
rt_hw_dcache_flush_range((rt_uint32_t)slcd->fb_screen,slcd->fb_size);
smart_ctrl = slcd_reg_read(SLCDC_CTRL);
smart_ctrl |= SLCDC_CTRL_DMA_START;
slcd_reg_write(SLCDC_CTRL, smart_ctrl);
while (slcd_reg_read(SLCDC_STATE) & SLCDC_STATE_BUSY);
break;
}
case RTGRAPHIC_CTRL_SET_MODE:
break;
}
rt_mutex_release(&(slcd->lock));
return RT_EOK;
}
int rt_hw_slcd_init(struct slcd_configure *cfg)
{
struct slcdc_dev_s *slcd;
slcd = (struct slcdc_dev_s *)rt_malloc(sizeof(struct slcdc_dev_s));
if(slcd == RT_NULL)
{
rt_kprintf("error no memory!\n");
return -RT_ENOMEM;
}
_slcd_device = slcd;
slcd->cfg = cfg;
rt_mutex_init(&slcd->lock, "lcdfb", RT_IPC_FLAG_FIFO);
_slcd_disable(slcd);
_slcd_init_mem(slcd);
_slcd_init_ctrl(slcd);
_slcd_enable(slcd);
/* device support */
slcd->parent.type = RT_Device_Class_Graphic;
slcd->parent.init = RT_NULL;
slcd->parent.open = RT_NULL;
slcd->parent.close = RT_NULL;
slcd->parent.read = RT_NULL;
slcd->parent.write = RT_NULL;
slcd->parent.control = _slcd_device_control;
rt_device_register(&slcd->parent, "lcd", RT_DEVICE_FLAG_RDWR);
return RT_EOK;
}