blob: 705ec13ab49a9903b04235158dfdace725c01c41 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <common.h>
#include <malloc.h>
#include <asm/arch/io.h>
#ifdef CONFIG_AML_VPP
#include <amlogic/media/vpp/vpp.h>
#endif
#include <amlogic/media/vout/lcd/aml_lcd.h>
#include "lcd_reg.h"
#include "lcd_common.h"
#define LCD_WAIT_VSYNC_TIMEOUT 50000
void lcd_wait_vsync(struct aml_lcd_drv_s *pdrv)
{
#ifndef CONFIG_AML_LCD_PXP
unsigned int offset, reg;
int line_cnt, line_cnt_previous;
int i = 0;
if (pdrv->data->chip_type >= LCD_CHIP_T7) {
offset = pdrv->data->offset_venc[pdrv->index];
reg = VPU_VENCP_STAT + offset;
} else {
reg = ENCL_INFO_READ;
}
line_cnt = 0x1fff;
line_cnt_previous = lcd_vcbus_getb(reg, 16, 13);
while (i++ < LCD_WAIT_VSYNC_TIMEOUT) {
line_cnt = lcd_vcbus_getb(reg, 16, 13);
if (line_cnt < line_cnt_previous)
break;
line_cnt_previous = line_cnt;
udelay(2);
}
/*LCDPR("line_cnt=%d, line_cnt_previous=%d, i=%d\n",
* line_cnt, line_cnt_previous, i);
*/
#endif
}
static void lcd_gamma_init(struct aml_lcd_drv_s *pdrv)
{
if (pdrv->data->chip_type == LCD_CHIP_T7)
return;
#ifndef CONFIG_AML_LCD_PXP
#ifdef CONFIG_AML_VPP
lcd_wait_vsync(pdrv);
vpp_disable_lcd_gamma_table(pdrv->index);
vpp_init_lcd_gamma_table(pdrv->index);
lcd_wait_vsync(pdrv);
vpp_enable_lcd_gamma_table(pdrv->index);
#endif
#endif
}
static void lcd_set_encl_tcon(struct aml_lcd_drv_s *pdrv)
{
struct lcd_config_s *pconf = &pdrv->config;
unsigned int offset_if, offset_data;
unsigned int reg_rgb_base, reg_rgb_coeff, reg_dith_ctrl, reg_pol_ctrl;
unsigned int reg_de_hs, reg_de_he, reg_de_vs, reg_de_ve;
unsigned int reg_hsync_hs, reg_hsync_he, reg_hsync_vs, reg_hsync_ve;
unsigned int reg_vsync_hs, reg_vsync_he, reg_vsync_vs, reg_vsync_ve;
if (pdrv->data->chip_type == LCD_CHIP_T7) {
offset_data = pdrv->data->offset_venc_data[pdrv->index];
offset_if = pdrv->data->offset_venc_if[pdrv->index];
reg_rgb_base = LCD_RGB_BASE_ADDR + offset_data;
reg_rgb_coeff = LCD_RGB_COEFF_ADDR + offset_data;
reg_dith_ctrl = LCD_DITH_CNTL_ADDR + offset_data;
reg_pol_ctrl = LCD_POL_CNTL_ADDR + offset_data;
reg_de_hs = DE_HS_ADDR + offset_if;
reg_de_he = DE_HE_ADDR + offset_if;
reg_de_vs = DE_VS_ADDR + offset_if;
reg_de_ve = DE_VE_ADDR + offset_if;
reg_hsync_hs = HSYNC_HS_ADDR + offset_if;
reg_hsync_he = HSYNC_HE_ADDR + offset_if;
reg_hsync_vs = HSYNC_VS_ADDR + offset_if;
reg_hsync_ve = HSYNC_VE_ADDR + offset_if;
reg_vsync_hs = VSYNC_HS_ADDR + offset_if;
reg_vsync_he = VSYNC_HE_ADDR + offset_if;
reg_vsync_vs = VSYNC_VS_ADDR + offset_if;
reg_vsync_ve = VSYNC_VE_ADDR + offset_if;
} else {
reg_rgb_base = L_RGB_BASE_ADDR;
reg_rgb_coeff = L_RGB_COEFF_ADDR;
reg_dith_ctrl = L_POL_CNTL_ADDR;
reg_pol_ctrl = L_DITH_CNTL_ADDR;
reg_de_hs = L_DE_HS_ADDR;
reg_de_he = L_DE_HE_ADDR;
reg_de_vs = L_DE_VS_ADDR;
reg_de_ve = L_DE_VE_ADDR;
reg_hsync_hs = L_HSYNC_HS_ADDR;
reg_hsync_he = L_HSYNC_HE_ADDR;
reg_hsync_vs = L_HSYNC_VS_ADDR;
reg_hsync_ve = L_HSYNC_VE_ADDR;
reg_vsync_hs = L_VSYNC_HS_ADDR;
reg_vsync_he = L_VSYNC_HE_ADDR;
reg_vsync_vs = L_VSYNC_VS_ADDR;
reg_vsync_ve = L_VSYNC_VE_ADDR;
}
lcd_vcbus_write(reg_rgb_base, 0x0);
lcd_vcbus_write(reg_rgb_coeff, 0x400);
switch (pconf->basic.lcd_bits) {
case 6:
lcd_vcbus_write(reg_dith_ctrl, 0x600);
break;
case 8:
lcd_vcbus_write(reg_dith_ctrl, 0x400);
break;
case 10:
default:
lcd_vcbus_write(reg_dith_ctrl, 0x0);
break;
}
switch (pconf->basic.lcd_type) {
case LCD_LVDS:
lcd_vcbus_setb(reg_pol_ctrl, 1, 0, 1);
if (pconf->timing.vsync_pol)
lcd_vcbus_setb(reg_pol_ctrl, 1, 1, 1);
break;
case LCD_VBYONE:
if (pconf->timing.hsync_pol)
lcd_vcbus_setb(reg_pol_ctrl, 1, 0, 1);
if (pconf->timing.vsync_pol)
lcd_vcbus_setb(reg_pol_ctrl, 1, 1, 1);
break;
case LCD_MIPI:
//lcd_vcbus_setb(reg_pol_ctrl, 0x3, 0, 2);
/*lcd_vcbus_write(reg_pol_ctrl,
* (lcd_vcbus_read(reg_pol_ctrl) |
* ((0 << 2) | (vs_pol_adj << 1) | (hs_pol_adj << 0))));
*/
/*lcd_vcbus_write(reg_pol_ctrl, (lcd_vcbus_read(reg_pol_ctrl) |
* ((1 << LCD_TCON_DE_SEL) | (1 << LCD_TCON_VS_SEL) |
* (1 << LCD_TCON_HS_SEL))));
*/
break;
case LCD_EDP:
lcd_vcbus_setb(reg_pol_ctrl, 1, 0, 1);
break;
default:
break;
}
/* DE signal */
lcd_vcbus_write(reg_de_hs, pconf->timing.de_hs_addr);
lcd_vcbus_write(reg_de_he, pconf->timing.de_he_addr);
lcd_vcbus_write(reg_de_vs, pconf->timing.de_vs_addr);
lcd_vcbus_write(reg_de_ve, pconf->timing.de_ve_addr);
/* Hsync signal */
lcd_vcbus_write(reg_hsync_hs, pconf->timing.hs_hs_addr);
lcd_vcbus_write(reg_hsync_he, pconf->timing.hs_he_addr);
lcd_vcbus_write(reg_hsync_vs, pconf->timing.hs_vs_addr);
lcd_vcbus_write(reg_hsync_ve, pconf->timing.hs_ve_addr);
/* Vsync signal */
lcd_vcbus_write(reg_vsync_hs, pconf->timing.vs_hs_addr);
lcd_vcbus_write(reg_vsync_he, pconf->timing.vs_he_addr);
lcd_vcbus_write(reg_vsync_vs, pconf->timing.vs_vs_addr);
lcd_vcbus_write(reg_vsync_ve, pconf->timing.vs_ve_addr);
}
void lcd_set_venc(struct aml_lcd_drv_s *pdrv)
{
struct lcd_config_s *pconf = &pdrv->config;
unsigned int hstart, hend, vstart, vend;
unsigned int reg_disp_viu_ctrl, offset;
unsigned int pre_de_vs = 0, pre_de_ve = 0, pre_de_hs = 0, pre_de_he = 0;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s\n", pdrv->index, __func__);
hstart = pconf->timing.video_on_pixel;
hend = pconf->basic.h_active + hstart - 1;
vstart = pconf->timing.video_on_line;
vend = pconf->basic.v_active + vstart - 1;
offset = pdrv->data->offset_venc[pdrv->index];
switch (pdrv->index) {
case 0:
reg_disp_viu_ctrl = VPU_DISP_VIU0_CTRL;
break;
case 1:
reg_disp_viu_ctrl = VPU_DISP_VIU1_CTRL;
break;
case 2:
reg_disp_viu_ctrl = VPU_DISP_VIU2_CTRL;
break;
default:
LCDERR("[%d]: %s: invalid drv_index\n", pdrv->index, __func__);
return;
}
lcd_vcbus_write(ENCL_VIDEO_EN + offset, 0);
lcd_vcbus_write(ENCL_VIDEO_MODE + offset, 0x8000); /* bit[15] shadown en */
lcd_vcbus_write(ENCL_VIDEO_MODE_ADV + offset, 0x0418); /* Sampling rate: 1 */
lcd_vcbus_write(ENCL_VIDEO_FILT_CTRL + offset, 0x1000); /* bypass filter */
lcd_vcbus_write(ENCL_VIDEO_MAX_PXCNT + offset, pconf->basic.h_period - 1);
lcd_vcbus_write(ENCL_VIDEO_MAX_LNCNT + offset, pconf->basic.v_period - 1);
lcd_vcbus_write(ENCL_VIDEO_HAVON_BEGIN + offset, hstart);
lcd_vcbus_write(ENCL_VIDEO_HAVON_END + offset, hend);
lcd_vcbus_write(ENCL_VIDEO_VAVON_BLINE + offset, vstart);
lcd_vcbus_write(ENCL_VIDEO_VAVON_ELINE + offset, vend);
if (pconf->basic.lcd_type == LCD_P2P ||
pconf->basic.lcd_type == LCD_MLVDS) {
switch (pdrv->data->chip_type) {
case LCD_CHIP_TL1:
case LCD_CHIP_TM2:
pre_de_vs = vstart - 1 - 4;
pre_de_ve = vstart - 1;
pre_de_hs = hstart + PRE_DE_DELAY;
pre_de_he = pconf->basic.h_active - 1 + pre_de_hs;
break;
default:
pre_de_vs = vstart - 8;
pre_de_ve = pconf->basic.v_active + pre_de_vs;
pre_de_hs = hstart + PRE_DE_DELAY;
pre_de_he = pconf->basic.h_active - 1 + pre_de_hs;
break;
}
lcd_vcbus_write(ENCL_VIDEO_V_PRE_DE_BLINE + offset, pre_de_vs);
lcd_vcbus_write(ENCL_VIDEO_V_PRE_DE_ELINE + offset, pre_de_ve);
lcd_vcbus_write(ENCL_VIDEO_H_PRE_DE_BEGIN + offset, pre_de_hs);
lcd_vcbus_write(ENCL_VIDEO_H_PRE_DE_END + offset, pre_de_he);
}
lcd_vcbus_write(ENCL_VIDEO_HSO_BEGIN + offset, pconf->timing.hs_hs_addr);
lcd_vcbus_write(ENCL_VIDEO_HSO_END + offset, pconf->timing.hs_he_addr);
lcd_vcbus_write(ENCL_VIDEO_VSO_BEGIN + offset, pconf->timing.vs_hs_addr);
lcd_vcbus_write(ENCL_VIDEO_VSO_END + offset, pconf->timing.vs_he_addr);
lcd_vcbus_write(ENCL_VIDEO_VSO_BLINE + offset, pconf->timing.vs_vs_addr);
lcd_vcbus_write(ENCL_VIDEO_VSO_ELINE + offset, pconf->timing.vs_ve_addr);
lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL + offset, 3);
switch (pdrv->data->chip_type) {
case LCD_CHIP_TL1:
case LCD_CHIP_TM2:
case LCD_CHIP_T5:
case LCD_CHIP_T5D:
/*[15:14]: 2'b10 or 2'b01*/
lcd_vcbus_write(ENCL_INBUF_CNTL1 + offset,
(2 << 14) | (pconf->basic.h_active - 1));
lcd_vcbus_write(ENCL_INBUF_CNTL0 + offset, 0x200);
break;
case LCD_CHIP_T7:
lcd_vcbus_write(ENCL_INBUF_CNTL1 + offset,
(5 << 13) | (pconf->basic.h_active - 1));
lcd_vcbus_write(ENCL_INBUF_CNTL0 + offset, 0x200);
break;
default:
break;
}
/* default black pattern */
lcd_vcbus_write(ENCL_TST_MDSEL + offset, 0);
lcd_vcbus_write(ENCL_TST_Y + offset, 0);
lcd_vcbus_write(ENCL_TST_CB + offset, 0);
lcd_vcbus_write(ENCL_TST_CR + offset, 0);
lcd_vcbus_write(ENCL_TST_EN + offset, 1);
lcd_vcbus_setb(ENCL_VIDEO_MODE_ADV + offset, 0, 3, 1);
lcd_vcbus_write(ENCL_VIDEO_EN + offset, 1);
if (pdrv->data->chip_type == LCD_CHIP_T7) {
/*
* bit31: lvds enable
* bit30: vx1 enable
* bit29: hdmitx enable
* bit28: dsi_edp enable
*/
switch (pdrv->config.basic.lcd_type) {
case LCD_LVDS:
lcd_vcbus_write(reg_disp_viu_ctrl, (1 << 31) |
(0 << 30) |
(0 << 29) |
(0 << 28));
break;
case LCD_VBYONE:
lcd_vcbus_write(reg_disp_viu_ctrl, (0 << 31) |
(1 << 30) |
(0 << 29) |
(0 << 28));
break;
case LCD_MIPI:
case LCD_EDP:
lcd_vcbus_write(reg_disp_viu_ctrl, (0 << 31) |
(0 << 30) |
(0 << 29) |
(1 << 28));
break;
default:
break;
}
}
lcd_vcbus_write(VPU_VENC_CTRL + offset, 2);
lcd_set_encl_tcon(pdrv);
lcd_gamma_init(pdrv);
}