mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 12:17:12 +09:00
HDMI: add rk3288 hdmi PHY MPLL config
This commit is contained in:
@@ -35,9 +35,28 @@ extern irqreturn_t hdmi_irq(int irq, void *priv);
|
||||
static struct rk3288_hdmi_device *hdmi_dev = NULL;
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
static const struct rk3288_hdmi_reg_table hdmi_reg_table[] = {
|
||||
{IDENTIFICATION_BASE, CONFIG3_ID},
|
||||
{INTERRUPT_BASE, IH_MUTE},
|
||||
{VIDEO_SAMPLER_BASE, TX_BCBDATA1},
|
||||
{VIDEO_PACKETIZER_BASE, VP_MASK},
|
||||
{FRAME_COMPOSER_BASE, FC_DBGTMDS2},
|
||||
{HDMI_SOURCE_PHY_BASE, PHY_PLLCFGFREQ2},
|
||||
{I2C_MASTER_PHY_BASE, PHY_I2CM_SDA_HOLD},
|
||||
{AUDIO_SAMPLER_BASE, AHB_DMA_STPADDR_SET1_0},
|
||||
{MAIN_CONTROLLER_BASE, MC_SWRSTZREQ_2},
|
||||
{COLOR_SPACE_CONVERTER_BASE, CSC_SPARE_2},
|
||||
{HDCP_ENCRYPTION_ENGINE_BASE, HDCP_REVOC_LIST}, //HDCP_REVOC_LIST+5059
|
||||
{HDCP_BKSV_BASE, HDCPREG_BKSV4},
|
||||
{HDCP_AN_BASE, HDCPREG_AN7},
|
||||
{ENCRYPTED_DPK_EMBEDDED_BASE, HDCPREG_DPK6},
|
||||
{CEC_ENGINE_BASE, CEC_WKUPCTRL},
|
||||
{I2C_MASTER_BASE, I2CM_SCDC_UPDATE1},
|
||||
};
|
||||
|
||||
static int rk3288_hdmi_reg_show(struct seq_file *s, void *v)
|
||||
{
|
||||
int i = 0;
|
||||
int i = 0,j = 0;
|
||||
u32 val = 0;
|
||||
seq_printf(s, "\n>>>hdmi_ctl reg");
|
||||
for (i = 0; i < 16; i++) {
|
||||
@@ -45,12 +64,14 @@ static int rk3288_hdmi_reg_show(struct seq_file *s, void *v)
|
||||
}
|
||||
seq_printf(s, "\n-----------------------------------------------------------------");
|
||||
|
||||
for(i=0; i<= I2CM_SCDC_UPDATE1; i++) {
|
||||
val = hdmi_readl(hdmi_dev, i);
|
||||
if(i%16==0)
|
||||
seq_printf(s,"\n>>>hdmi_ctl %2x:", i);
|
||||
seq_printf(s," %02x",val);
|
||||
for(i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) {
|
||||
for(j = hdmi_reg_table[i].reg_base; j <= hdmi_reg_table[i].reg_end; j++) {
|
||||
val = hdmi_readl(hdmi_dev, j);
|
||||
if((j - hdmi_reg_table[i].reg_base)%16==0)
|
||||
seq_printf(s,"\n>>>hdmi_ctl %2x:", j);
|
||||
seq_printf(s," %02x",val);
|
||||
|
||||
}
|
||||
}
|
||||
seq_printf(s, "\n-----------------------------------------------------------------\n");
|
||||
|
||||
@@ -158,6 +179,7 @@ static int rk3288_hdmi_probe(struct platform_device *pdev)
|
||||
|
||||
hdmi_dev->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, hdmi_dev);
|
||||
mutex_init(&hdmi_dev->int_mutex);
|
||||
|
||||
rk3288_hdmi_parse_dt(hdmi_dev);
|
||||
//TODO Daisen wait to add cec iomux
|
||||
|
||||
@@ -2,6 +2,34 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include "rk3288_hdmi_hw.h"
|
||||
|
||||
|
||||
static const struct phy_mpll_config_tab PHY_MPLL_TABLE[] = { //opmode: 0:HDMI1.4 1:HDMI2.0
|
||||
// |pixclock|pixrepet|colordepth|prepdiv|tmdsmhl|opmode|fbdiv2|fbdiv1|ref_cntrl|nctrl|propctrl|intctrl|gmpctrl|
|
||||
{27000000, 0, 8, 0, 0, 0, 2, 3, 0, 3, 3, 0, 0},
|
||||
{74250000, 0, 8, 0, 0, 0, 1, 3, 0, 2, 5, 0, 1},
|
||||
{148500000, 0, 8, 0, 0, 0, 1, 1, 0, 1, 7, 0, 2},
|
||||
{297000000, 0, 8, 0, 0, 0, 1, 0, 0, 0, 7, 0, 3},
|
||||
{297000000, 0, 16, 3, 3, 1, 1, 1, 0, 0, 5, 0, 3},
|
||||
{594000000, 0, 8, 0, 3, 1, 1, 0, 0, 0, 3, 0, 3},
|
||||
};
|
||||
|
||||
const struct phy_mpll_config_tab* get_phy_mpll_tab(int pixClock, char pixRepet, char colorDepth)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(pixClock == 0)
|
||||
return NULL;
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(PHY_MPLL_TABLE); i++)
|
||||
{
|
||||
if((PHY_MPLL_TABLE[i].pix_clock == pixClock) && (PHY_MPLL_TABLE[i].pix_repet == pixRepet)
|
||||
&& (PHY_MPLL_TABLE[i].color_depth == colorDepth))
|
||||
return &PHY_MPLL_TABLE[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//i2c master reset
|
||||
void rk3288_hdmi_i2cm_reset(struct rk3288_hdmi_device *hdmi_dev)
|
||||
{
|
||||
@@ -93,9 +121,77 @@ int rk3288_hdmi_read_edid(struct hdmi *hdmi_drv, int block, unsigned char *buff)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rk3288_hdmi_config_phy(unsigned char vic)
|
||||
static int rk3288_hdmi_write_phy(struct rk3288_hdmi_device *hdmi_dev, int reg_addr, int val)
|
||||
{
|
||||
//TODO Daisen wait to add code
|
||||
int trytime = 2, i = 0, op_status = 0;
|
||||
|
||||
mutex_lock(&hdmi_dev->int_mutex);
|
||||
hdmi_dev->phy_status = 0;
|
||||
mutex_unlock(&hdmi_dev->int_mutex);
|
||||
|
||||
while(trytime--) {
|
||||
hdmi_writel(hdmi_dev, PHY_I2CM_ADDRESS, reg_addr);
|
||||
hdmi_writel(hdmi_dev, PHY_I2CM_DATAO_1, (val >> 8) & 0xff);
|
||||
hdmi_writel(hdmi_dev, PHY_I2CM_DATAO_0, val & 0xff);
|
||||
hdmi_writel(hdmi_dev, PHY_I2CM_OPERATION, m_PHY_I2CM_WRITE);
|
||||
|
||||
i = 100;
|
||||
while(i--) {
|
||||
mutex_lock(&hdmi_dev->int_mutex);
|
||||
//op_status = hdmi_readl(hdmi_dev, PHY_I2CM_INT);
|
||||
op_status = hdmi_dev->phy_status;
|
||||
hdmi_dev->phy_status = 0;
|
||||
mutex_unlock(&hdmi_dev->int_mutex);
|
||||
if(op_status & (m_I2CMPHY_DONE | m_I2CMPHY_ERR)) {
|
||||
break;
|
||||
}
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
if(op_status & m_I2CMPHY_DONE) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
hdmi_err(hdmi_dev->dev, "[%s] operation error,trytime=%d\n", __FUNCTION__,trytime);
|
||||
}
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void rk3288_hdmi_config_phy(struct hdmi *hdmi_drv)
|
||||
{
|
||||
int stat = 0;
|
||||
char color_depth = COLOR_DEPTH_24BIT_DEFAULT;
|
||||
char pix_repet = NO_PIXEL_REPET;
|
||||
const struct phy_mpll_config_tab *phy_mpll = NULL;
|
||||
struct rk3288_hdmi_device *hdmi_dev = container_of(hdmi_drv, struct rk3288_hdmi_device, driver);
|
||||
|
||||
hdmi_writel(hdmi_dev, PHY_CONF0, 0x32);
|
||||
hdmi_writel(hdmi_dev, MC_PHYRSTZ, v_PHY_RSTZ(1));
|
||||
//the resolution that we used is no pixel repet,refer to the CEA-861 specification.
|
||||
hdmi_writel(hdmi_dev, VP_PR_CD, v_COLOR_DEPTH(color_depth) | v_DESIRED_PR_FACTOR(pix_repet));
|
||||
msleep(5);
|
||||
hdmi_writel(hdmi_dev, MC_PHYRSTZ, v_PHY_RSTZ(0));
|
||||
|
||||
//Set slave address as PHY GEN2 address
|
||||
hdmi_writel(hdmi_dev, PHY_I2CM_SLAVE, PHY_GEN2_ADDR);
|
||||
|
||||
//config the required PHY I2C register
|
||||
phy_mpll = get_phy_mpll_tab(hdmi_drv->tmdsclk, pix_repet, color_depth);
|
||||
rk3288_hdmi_write_phy(hdmi_dev, PHYTX_OPMODE_PLLCFG, v_PREP_DIV(phy_mpll->prep_div) | v_TMDS_CNTRL(phy_mpll->tmdsmhl_cntrl) | v_OPMODE(phy_mpll->opmode) |
|
||||
v_FBDIV2_CNTRL(phy_mpll->fbdiv2_cntrl) | v_FBDIV1_CNTRL(phy_mpll->fbdiv1_cntrl) | v_REF_CNTRL(phy_mpll->ref_cntrl) | v_MPLL_N_CNTRL(phy_mpll->n_cntrl));
|
||||
rk3288_hdmi_write_phy(hdmi_dev, PHYTX_PLLCURRCTRL, v_MPLL_PROP_CNTRL(phy_mpll->prop_cntrl) | v_MPLL_INT_CNTRL(phy_mpll->int_cntrl));
|
||||
rk3288_hdmi_write_phy(hdmi_dev, PHYTX_PLLGMPCTRL, v_MPLL_GMP_CNTRL(phy_mpll->gmp_cntrl));
|
||||
|
||||
//power on PHY
|
||||
hdmi_writel(hdmi_dev, PHY_CONF0, 0x2a);
|
||||
|
||||
//check if the PHY PLL is locked
|
||||
stat = hdmi_readl(hdmi_dev, PHY_STAT0);
|
||||
if(stat & m_PHY_LOCK)
|
||||
hdmi_writel(hdmi_dev, PHY_STAT0, v_PHY_LOCK(1));
|
||||
}
|
||||
|
||||
static void rk3288_hdmi_config_avi(struct hdmi *hdmi_drv, unsigned char vic, unsigned char output_color)
|
||||
@@ -271,11 +367,6 @@ int rk3288_hdmi_config_video(struct hdmi *hdmi_drv, struct hdmi_video_para *vpar
|
||||
//Set Data enable signal from external and set video sample input mapping
|
||||
hdmi_msk_reg(hdmi_dev, TX_INVID0, m_INTERNAL_DE_GEN | m_VIDEO_MAPPING, v_INTERNAL_DE_GEN(0) | v_VIDEO_MAPPING(input_color));
|
||||
|
||||
if(hdmi_drv->tmdsclk > 340000000) { //used for HDMI 2.0 TX
|
||||
hdmi_msk_reg(hdmi_dev, FC_INVIDCONF, m_FC_HDCP_KEEPOUT, v_FC_HDCP_KEEPOUT(1));
|
||||
hdmi_msk_reg(hdmi_dev, FC_SCRAMBLER_CTRL, m_FC_SCRAMBLE_EN, v_FC_SCRAMBLE_EN(1));
|
||||
}
|
||||
|
||||
//Color space convert
|
||||
rk3288_hdmi_config_csc(hdmi_drv, vpara);
|
||||
|
||||
@@ -287,6 +378,12 @@ int rk3288_hdmi_config_video(struct hdmi *hdmi_drv, struct hdmi_video_para *vpar
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
hdmi_drv->tmdsclk = mode->pixclock;
|
||||
if(hdmi_drv->tmdsclk > 340000000) { //used for HDMI 2.0 TX
|
||||
hdmi_msk_reg(hdmi_dev, FC_INVIDCONF, m_FC_HDCP_KEEPOUT, v_FC_HDCP_KEEPOUT(1));
|
||||
hdmi_msk_reg(hdmi_dev, FC_SCRAMBLER_CTRL, m_FC_SCRAMBLE_EN, v_FC_SCRAMBLE_EN(1));
|
||||
}
|
||||
|
||||
hdmi_msk_reg(hdmi_dev, FC_INVIDCONF, m_FC_VSYNC_POL | m_FC_HSYNC_POL | m_FC_DE_POL | m_FC_INTERLACE_MODE,
|
||||
v_FC_VSYNC_POL(vsync_pol) | v_FC_HSYNC_POL(hsync_pol) | v_FC_DE_POL(de_pol) | v_FC_INTERLACE_MODE(mode->vmode)); //TODO Daisen wait to set m_FC_VBLANK value!!
|
||||
|
||||
@@ -329,7 +426,7 @@ int rk3288_hdmi_config_video(struct hdmi *hdmi_drv, struct hdmi_video_para *vpar
|
||||
hdmi_dbg(hdmi_drv->dev, "[%s] sucess output DVI.\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
rk3288_hdmi_config_phy(vpara->vic);
|
||||
rk3288_hdmi_config_phy(hdmi_drv);
|
||||
rk3288_hdmi_control_output(hdmi_drv, 0);
|
||||
return 0;
|
||||
}
|
||||
@@ -486,16 +583,18 @@ irqreturn_t hdmi_irq(int irq, void *priv)
|
||||
{
|
||||
struct hdmi *hdmi_drv = (struct hdmi *)priv;
|
||||
struct rk3288_hdmi_device *hdmi_dev = container_of(hdmi_drv, struct rk3288_hdmi_device, driver);
|
||||
int phy_int,i2cm_int,cec_int,hdcp_int;
|
||||
int phy_int, i2cm_int, phy_i2cm_int, cec_int, hdcp_int;
|
||||
|
||||
phy_int = hdmi_readl(hdmi_dev, IH_PHY_STAT0);
|
||||
i2cm_int = hdmi_readl(hdmi_dev, IH_I2CM_STAT0);
|
||||
phy_i2cm_int = hdmi_readl(hdmi_dev, IH_I2CMPHY_STAT0);
|
||||
cec_int = hdmi_readl(hdmi_dev, IH_CEC_STAT0);
|
||||
hdcp_int = hdmi_readl(hdmi_dev, A_APIINTSTAT);
|
||||
|
||||
//clear interrupt
|
||||
hdmi_writel(hdmi_dev, IH_PHY_STAT0, phy_int);
|
||||
hdmi_writel(hdmi_dev, IH_I2CM_STAT0, i2cm_int);
|
||||
hdmi_writel(hdmi_dev, IH_I2CMPHY_STAT0, phy_i2cm_int);
|
||||
hdmi_writel(hdmi_dev, IH_CEC_STAT0, cec_int);
|
||||
hdmi_writel(hdmi_dev, A_APIINTCLR, hdcp_int);
|
||||
|
||||
@@ -513,6 +612,13 @@ irqreturn_t hdmi_irq(int irq, void *priv)
|
||||
spin_unlock(&hdmi_drv->irq_lock);
|
||||
}
|
||||
|
||||
//PHY I2CM write or read status
|
||||
if(phy_i2cm_int & (m_I2CMPHY_DONE | m_I2CMPHY_ERR)) {
|
||||
mutex_lock(&hdmi_dev->int_mutex);
|
||||
hdmi_dev->phy_status = phy_i2cm_int;
|
||||
mutex_unlock(&hdmi_dev->int_mutex);
|
||||
}
|
||||
|
||||
//CEC
|
||||
if(cec_int) { //TODO Daisen wait to modify
|
||||
}
|
||||
|
||||
@@ -639,6 +639,9 @@ enum {
|
||||
#define I2C_MASTER_PHY_BASE 0x3020
|
||||
|
||||
#define PHY_I2CM_SLAVE 0x3020
|
||||
#define PHY_GEN2_ADDR 0x69
|
||||
#define PHY_HEAC_ADDR 0x49
|
||||
|
||||
#define PHY_I2CM_ADDRESS 0x3021
|
||||
#define PHY_I2CM_DATAO_1 0x3022
|
||||
#define PHY_I2CM_DATAO_0 0x3023
|
||||
@@ -687,7 +690,7 @@ enum {
|
||||
#define PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030
|
||||
#define PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031
|
||||
#define PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032
|
||||
#define I2CM_PHY_SDA_HOLD 0x3033
|
||||
#define PHY_I2CM_SDA_HOLD 0x3033
|
||||
|
||||
|
||||
/*Audio Sampler Registers*/
|
||||
@@ -1147,6 +1150,90 @@ enum {
|
||||
#define I2CM_SCDC_UPDATE1 0x7e31
|
||||
|
||||
|
||||
|
||||
/*********************************************HDMI TX PHY Define Start*********************************************/
|
||||
#define PHYTX_OPMODE_PLLCFG 0x06
|
||||
enum {
|
||||
PREP_DIV_BY_2 = 0, //16 bits
|
||||
PREP_DIV_BY_15, //12 bits
|
||||
PREP_DIV_BY_125, //10 bits
|
||||
PREP_DIV_BY_1, //8 bits
|
||||
};
|
||||
#define m_PREP_DIV (0x03 << 13)
|
||||
#define v_PREP_DIV(n) (((n)&0x03) << 13)
|
||||
enum {
|
||||
TMDS_DIV_BY_1 = 0,
|
||||
TMDS_DIV_NOT_USED,
|
||||
TMDS_DIV_BY_3,
|
||||
TMDS_DIV_BY_4,
|
||||
};
|
||||
#define m_TMDS_CNTRL (0x03 << 11)
|
||||
#define v_TMDS_CNTRL(n) (((n)&0x03) << 11)
|
||||
enum OPMODE{
|
||||
OP_HDMI_14 = 0,
|
||||
OP_HDMI_20,
|
||||
};
|
||||
#define m_OPMODE (0x03 << 9)
|
||||
#define v_OPMODE(n) (((n)&0x03) << 9)
|
||||
enum {
|
||||
FBDIV2_BY_1 = 1,
|
||||
FBDIV2_BY_2,
|
||||
FBDIV2_BY_3,
|
||||
FBDIV2_BY_4,
|
||||
FBDIV2_BY_5,
|
||||
FBDIV2_BY_6,
|
||||
};
|
||||
#define m_FBDIV2_CNTRL (0x07 << 6)
|
||||
#define v_FBDIV2_CNTRL(n) (((n)&0x07) << 6)
|
||||
enum {
|
||||
FBDIV1_BY_1 = 0,
|
||||
FBDIV1_BY_2,
|
||||
FBDIV1_BY_3,
|
||||
FBDIV1_BY_4,
|
||||
};
|
||||
#define m_FBDIV1_CNTRL (0x03 << 4)
|
||||
#define v_FBDIV1_CNTRL(n) (((n)&0x03) << 4)
|
||||
enum {
|
||||
REF_DIV_BY_1 = 0,
|
||||
REF_DIV_BY_2,
|
||||
REF_DIV_NOT_USED,
|
||||
REF_DIV_BY_4,
|
||||
};
|
||||
#define m_REF_CNTRL (0x03 << 2)
|
||||
#define v_REF_CNTRL(n) (((n)&0x03) << 2)
|
||||
#define m_MPLL_N_CNTRL (0x03 << 0)
|
||||
#define v_MPLL_N_CNTRL(n) (((n)&0x03) << 0)
|
||||
|
||||
#define PHYTX_PLLCURRCTRL 0x10
|
||||
#define m_MPLL_PROP_CNTRL (0x07 << 3)
|
||||
#define v_MPLL_PROP_CNTRL(n) (((n)&0x07) << 3)
|
||||
#define m_MPLL_INT_CNTRL (0x07 << 0)
|
||||
#define v_MPLL_INT_CNTRL(n) (((n)&0x07) << 0)
|
||||
|
||||
#define PHYTX_PLLGMPCTRL 0x15
|
||||
#define m_MPLL_GMP_CNTRL (0x03 << 0)
|
||||
#define v_MPLL_GMP_CNTRL(n) (((n)&0x03) << 0)
|
||||
|
||||
struct phy_mpll_config_tab {
|
||||
u32 pix_clock;
|
||||
u8 pix_repet;
|
||||
u8 color_depth;
|
||||
u16 prep_div;
|
||||
u16 tmdsmhl_cntrl;
|
||||
u16 opmode;
|
||||
u32 fbdiv2_cntrl;
|
||||
u16 fbdiv1_cntrl;
|
||||
u16 ref_cntrl;
|
||||
u16 n_cntrl;
|
||||
u32 prop_cntrl;
|
||||
u32 int_cntrl;
|
||||
u16 gmp_cntrl;
|
||||
};
|
||||
|
||||
/********************************************* HDMI TX PHY Define End *********************************************/
|
||||
|
||||
|
||||
|
||||
enum{
|
||||
INPUT_IIS,
|
||||
INPUT_SPDIF
|
||||
@@ -1162,6 +1249,10 @@ enum {
|
||||
CSC_ITU709_16_235_TO_RGB_0_255 //YCbCr 16-235 input to RGB 0-255 output according BT709
|
||||
};
|
||||
|
||||
struct rk3288_hdmi_reg_table {
|
||||
int reg_base;
|
||||
int reg_end;
|
||||
};
|
||||
|
||||
struct rk3288_hdmi_device {
|
||||
int irq;
|
||||
@@ -1170,6 +1261,8 @@ struct rk3288_hdmi_device {
|
||||
int regsize_phy;
|
||||
int lcdc_id;
|
||||
int edid_status;
|
||||
int phy_status;
|
||||
struct mutex int_mutex;
|
||||
struct device *dev;
|
||||
struct clk *hclk; //HDMI AHP clk
|
||||
struct hdmi driver;
|
||||
@@ -1198,6 +1291,7 @@ static inline int hdmi_msk_reg(struct rk3288_hdmi_device *hdmi_dev, u16 offset,
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int rk3288_hdmi_initial(struct hdmi *hdmi_drv);
|
||||
void rk3288_hdmi_control_output(struct hdmi *hdmi_drv, int enable);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user