mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 12:17:12 +09:00
drm/rockchip: hdmi: use inno-hdmi-phy driver to operate rk3328 hdmi phy
Change-Id: Ic090fec14f076ba634d35cb85c59c160c47d0058 Signed-off-by: Zheng Yang <zhengyang@rock-chips.com>
This commit is contained in:
@@ -56,310 +56,27 @@ struct rockchip_hdmi {
|
||||
enum dw_hdmi_devtype dev_type;
|
||||
struct clk *vpll_clk;
|
||||
struct clk *grf_clk;
|
||||
struct clk *hdmiphy_pclk;
|
||||
struct clk *hdmiphy_clk;
|
||||
struct clk *hclk_vio;
|
||||
struct clk_hw hdmiphy_clkhw;
|
||||
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
#define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x)
|
||||
|
||||
/*
|
||||
* Inno HDMI phy pre pll output 4 clock: tmdsclock_tx/tmdsclock_ctrl/
|
||||
* preclock_ctrl/pclk_vop.
|
||||
* tmdsclock_tx is used for transmit hdmi signal, it's is equal to
|
||||
* tmdsclock.
|
||||
* Ftmdsclock_tx = Fvco / (4 * tmds_div_a * tmds_div_b)
|
||||
* tmdsclock_ctrl is output to dw-hdmi controller, it's is equal to
|
||||
* tmdsclock.
|
||||
* Ftmdsclock_ctrl = Fvco / (4 * tmds_div_a * tmds_div_c)
|
||||
* preclock_ctrl is output to dw-hdmi controller, it's equal to mode
|
||||
* pixel clock.
|
||||
* Fpreclock_ctrl = (pclk_div_a == 1) ?
|
||||
* Fvco / (pclk_div_b * pclk_div_c) :
|
||||
* Fvco / (pclk_div_a * pclk_div_c) :
|
||||
* pclk_vop is clock source of dclk_lcdc, it's equal to mode pixel clock.
|
||||
* Fpclk_vop = (vco_div_5 == 1) ? Fvco / 5 : (pclk_div_a == 1) ?
|
||||
* Fvco / (2 * pclk_div_b * pclk_div_d) :
|
||||
* Fvco / (2 * pclk_div_a * pclk_div_d)
|
||||
* Fvco = Fref * (nf + frac / (1 << 24)) / nd
|
||||
*/
|
||||
struct inno_pre_pll_config {
|
||||
unsigned long pixclock;
|
||||
u32 tmdsclock;
|
||||
u8 nd;
|
||||
u16 nf;
|
||||
u8 tmds_div_a;
|
||||
u8 tmds_div_b;
|
||||
u8 tmds_div_c;
|
||||
u8 pclk_div_a;
|
||||
u8 pclk_div_b;
|
||||
u8 pclk_div_c;
|
||||
u8 pclk_div_d;
|
||||
u8 vco_div_5;
|
||||
u32 frac;
|
||||
};
|
||||
|
||||
struct inno_post_pll_config {
|
||||
unsigned long tmdsclock;
|
||||
u8 nd;
|
||||
u16 nf;
|
||||
u8 no;
|
||||
u8 version;
|
||||
};
|
||||
|
||||
struct inno_phy_config {
|
||||
unsigned long tmdsclock;
|
||||
u8 regs[14];
|
||||
};
|
||||
|
||||
static const struct inno_pre_pll_config inno_pre_pll_cfg[] = {
|
||||
{27000000, 27000000, 1, 90, 3, 2,
|
||||
2, 10, 3, 3, 4, 0, 0},
|
||||
{27000000, 33750000, 1, 90, 1, 3,
|
||||
3, 10, 3, 3, 4, 0, 0},
|
||||
{40000000, 40000000, 1, 80, 2, 2,
|
||||
2, 12, 2, 2, 2, 0, 0},
|
||||
{59341000, 59341000, 1, 98, 3, 1,
|
||||
2, 1, 3, 3, 4, 0, 0xE6AE6B},
|
||||
{59400000, 59400000, 1, 99, 3, 1,
|
||||
1, 1, 3, 3, 4, 0, 0},
|
||||
{59341000, 74176250, 1, 98, 0, 3,
|
||||
3, 1, 3, 3, 4, 0, 0xE6AE6B},
|
||||
{59400000, 74250000, 1, 99, 1, 2,
|
||||
2, 1, 3, 3, 4, 0, 0},
|
||||
{74176000, 74176000, 1, 98, 1, 2,
|
||||
2, 1, 2, 3, 4, 0, 0xE6AE6B},
|
||||
{74250000, 74250000, 1, 99, 1, 2,
|
||||
2, 1, 2, 3, 4, 0, 0},
|
||||
{74176000, 92720000, 4, 494, 1, 2,
|
||||
2, 1, 3, 3, 4, 0, 0x816817},
|
||||
{74250000, 92812500, 4, 495, 1, 2,
|
||||
2, 1, 3, 3, 4, 0, 0},
|
||||
{148352000, 148352000, 1, 98, 1, 1,
|
||||
1, 1, 2, 2, 2, 0, 0xE6AE6B},
|
||||
{148500000, 148500000, 1, 99, 1, 1,
|
||||
1, 1, 2, 2, 2, 0, 0},
|
||||
{148352000, 185440000, 4, 494, 0, 2,
|
||||
2, 1, 3, 2, 2, 0, 0x816817},
|
||||
{148500000, 185625000, 4, 495, 0, 2,
|
||||
2, 1, 3, 2, 2, 0, 0},
|
||||
{296703000, 296703000, 1, 98, 0, 1,
|
||||
1, 1, 0, 2, 2, 0, 0xE6AE6B},
|
||||
{297000000, 297000000, 1, 99, 0, 1,
|
||||
1, 1, 0, 2, 2, 0, 0},
|
||||
{296703000, 370878750, 4, 494, 1, 2,
|
||||
0, 1, 3, 1, 1, 0, 0x816817},
|
||||
{297000000, 371250000, 4, 495, 1, 2,
|
||||
0, 1, 3, 1, 1, 0, 0},
|
||||
{593407000, 296703500, 1, 98, 0, 1,
|
||||
1, 1, 0, 2, 1, 0, 0xE6AE6B},
|
||||
{594000000, 297000000, 1, 99, 0, 1,
|
||||
1, 1, 0, 2, 1, 0, 0},
|
||||
{593407000, 370879375, 4, 494, 1, 2,
|
||||
0, 1, 3, 1, 1, 1, 0x816817},
|
||||
{594000000, 371250000, 4, 495, 1, 2,
|
||||
0, 1, 3, 1, 1, 1, 0},
|
||||
{593407000, 593407000, 1, 98, 0, 2,
|
||||
0, 1, 0, 1, 1, 0, 0xE6AE6B},
|
||||
{594000000, 594000000, 1, 99, 0, 2,
|
||||
0, 1, 0, 1, 1, 0, 0},
|
||||
{~0UL, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0},
|
||||
};
|
||||
|
||||
static const struct inno_post_pll_config inno_post_pll_cfg[] = {
|
||||
{33750000, 1, 40, 8, 1},
|
||||
{33750000, 1, 80, 8, 2},
|
||||
{74250000, 1, 40, 8, 1},
|
||||
{74250000, 18, 80, 8, 2},
|
||||
{148500000, 2, 40, 4, 3},
|
||||
{297000000, 4, 40, 2, 3},
|
||||
{371250000, 8, 40, 1, 3},
|
||||
{~0UL, 0, 0, 0, 0},
|
||||
};
|
||||
|
||||
static const struct inno_phy_config inno_phy_cfg[] = {
|
||||
{ 165000000, {
|
||||
0x07, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08,
|
||||
0x00, 0xac, 0xcc, 0xcc, 0xcc,
|
||||
},
|
||||
}, {
|
||||
340000000, {
|
||||
0x0b, 0x0d, 0x0d, 0x0d, 0x07, 0x15, 0x08, 0x08, 0x08,
|
||||
0x3f, 0xac, 0xcc, 0xcd, 0xdd,
|
||||
},
|
||||
}, {
|
||||
594000000, {
|
||||
0x10, 0x1a, 0x1a, 0x1a, 0x07, 0x15, 0x08, 0x08, 0x08,
|
||||
0x00, 0xac, 0xcc, 0xcc, 0xcc,
|
||||
},
|
||||
}, {
|
||||
~0UL, {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static void inno_phy_writel(struct rockchip_hdmi *hdmi, int offset, u8 val)
|
||||
{
|
||||
writel(val, hdmi->hdmiphy + (offset << 2));
|
||||
}
|
||||
|
||||
static u8 inno_phy_readl(struct rockchip_hdmi *hdmi, int offset)
|
||||
{
|
||||
return readl(hdmi->hdmiphy + (offset << 2));
|
||||
}
|
||||
|
||||
static void
|
||||
inno_phy_modeb(struct rockchip_hdmi *hdmi, u8 data, u8 mask, int offset)
|
||||
{
|
||||
u8 val = inno_phy_readl(hdmi, offset) & ~mask;
|
||||
|
||||
val |= data & mask;
|
||||
inno_phy_writel(hdmi, offset, val);
|
||||
}
|
||||
|
||||
static int inno_dw_hdmi_phy_read(struct dw_hdmi *hdmi, void *data, int offset)
|
||||
{
|
||||
struct rockchip_hdmi *rockchip_hdmi = (struct rockchip_hdmi *)data;
|
||||
|
||||
return inno_phy_readl(rockchip_hdmi, offset);
|
||||
}
|
||||
|
||||
static void
|
||||
inno_dw_hdmi_phy_write(struct dw_hdmi *hdmi, void *data, int val, int offset)
|
||||
{
|
||||
struct rockchip_hdmi *rockchip_hdmi = (struct rockchip_hdmi *)data;
|
||||
|
||||
inno_phy_writel(rockchip_hdmi, offset, val);
|
||||
}
|
||||
|
||||
static int
|
||||
inno_dw_hdmi_phy_init(struct dw_hdmi *dw_hdmi, void *data,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
const struct inno_post_pll_config *inno_pll_config = inno_post_pll_cfg;
|
||||
const struct inno_phy_config *inno_phy_config = inno_phy_cfg;
|
||||
u32 val, i, chipversion = 1;
|
||||
|
||||
if (hdmi->dev_type == RK3228_HDMI) {
|
||||
phy_power_on(hdmi->phy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rockchip_get_cpu_version())
|
||||
chipversion = 2;
|
||||
|
||||
for (; inno_pll_config->tmdsclock != ~0UL; inno_pll_config++)
|
||||
if (inno_pll_config->tmdsclock >= (mode->crtc_clock * 1000) &&
|
||||
inno_pll_config->version & chipversion)
|
||||
break;
|
||||
for (; inno_phy_config->tmdsclock != ~0UL; inno_phy_config++)
|
||||
if (inno_phy_config->tmdsclock >= (mode->crtc_clock * 1000))
|
||||
break;
|
||||
if (inno_pll_config->tmdsclock == ~0UL &&
|
||||
inno_phy_config->tmdsclock == ~0UL)
|
||||
return -EINVAL;
|
||||
|
||||
/* set pdata_en to 0 */
|
||||
inno_phy_modeb(hdmi, 0, 1, 0x02);
|
||||
/* Power off post PLL */
|
||||
inno_phy_modeb(hdmi, 1, 1, 0xaa);
|
||||
|
||||
val = inno_pll_config->nf & 0xff;
|
||||
inno_phy_writel(hdmi, 0xac, val);
|
||||
if (inno_pll_config->no == 1) {
|
||||
inno_phy_writel(hdmi, 0xaa, 2);
|
||||
val = (inno_pll_config->nf >> 8) |
|
||||
inno_pll_config->nd;
|
||||
inno_phy_writel(hdmi, 0xab, val);
|
||||
} else {
|
||||
val = (inno_pll_config->no / 2) - 1;
|
||||
inno_phy_writel(hdmi, 0xad, val);
|
||||
val = (inno_pll_config->nf >> 8) |
|
||||
inno_pll_config->nd;
|
||||
inno_phy_writel(hdmi, 0xab, val);
|
||||
inno_phy_writel(hdmi, 0xaa, 0x0e);
|
||||
}
|
||||
|
||||
for (i = 0; i < 14; i++)
|
||||
inno_phy_writel(hdmi, 0xb5 + i, inno_phy_config->regs[i]);
|
||||
|
||||
/* bit[7:6] of reg c8/c9/ca/c8 is ESD detect threshold:
|
||||
* 00 - 340mV
|
||||
* 01 - 280mV
|
||||
* 10 - 260mV
|
||||
* 11 - 240mV
|
||||
* default is 240mV, now we set it to 340mV
|
||||
*/
|
||||
inno_phy_writel(hdmi, 0xc8, 0);
|
||||
inno_phy_writel(hdmi, 0xc9, 0);
|
||||
inno_phy_writel(hdmi, 0xca, 0);
|
||||
inno_phy_writel(hdmi, 0xcb, 0);
|
||||
|
||||
if (inno_phy_config->tmdsclock > 340000000) {
|
||||
/* Set termination resistor to 100ohm */
|
||||
val = clk_get_rate(hdmi->hdmiphy_pclk) / 100000;
|
||||
inno_phy_writel(hdmi, 0xc5, ((val >> 8) & 0xff) | 0x80);
|
||||
inno_phy_writel(hdmi, 0xc6, val & 0xff);
|
||||
inno_phy_writel(hdmi, 0xc7, 3 << 1);
|
||||
inno_phy_writel(hdmi, 0xc5, ((val >> 8) & 0xff));
|
||||
} else if (inno_phy_config->tmdsclock > 165000000) {
|
||||
inno_phy_writel(hdmi, 0xc5, 0x81);
|
||||
/* clk termination resistor is 50ohm
|
||||
* data termination resistor is 150ohm
|
||||
*/
|
||||
inno_phy_writel(hdmi, 0xc8, 0x30);
|
||||
inno_phy_writel(hdmi, 0xc9, 0x10);
|
||||
inno_phy_writel(hdmi, 0xca, 0x10);
|
||||
inno_phy_writel(hdmi, 0xcb, 0x10);
|
||||
} else {
|
||||
inno_phy_writel(hdmi, 0xc5, 0x81);
|
||||
}
|
||||
|
||||
/* Power up post PLL */
|
||||
inno_phy_modeb(hdmi, 0, 1, 0xaa);
|
||||
/* Power up tmds driver */
|
||||
inno_phy_modeb(hdmi, 4, 4, 0xb0);
|
||||
inno_phy_writel(hdmi, 0xb2, 0x0f);
|
||||
|
||||
/* Wait for post PLL lock */
|
||||
for (i = 0; i < 5; ++i) {
|
||||
if (inno_phy_readl(hdmi, 0xaf) & 1)
|
||||
break;
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
if (!(inno_phy_readl(hdmi, 0xaf) & 1)) {
|
||||
dev_err(hdmi->dev, "HDMI PHY Post PLL unlock\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (inno_phy_config->tmdsclock > 340000000)
|
||||
msleep(100);
|
||||
/* set pdata_en to 1 */
|
||||
inno_phy_modeb(hdmi, 1, 1, 0x02);
|
||||
return 0;
|
||||
return phy_power_on(hdmi->phy);
|
||||
}
|
||||
|
||||
static void inno_dw_hdmi_phy_disable(struct dw_hdmi *dw_hdmi, void *data)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
|
||||
if (hdmi->dev_type == RK3228_HDMI) {
|
||||
phy_power_off(hdmi->phy);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Power off driver */
|
||||
inno_phy_writel(hdmi, 0xb2, 0);
|
||||
/* Power off band gap */
|
||||
inno_phy_modeb(hdmi, 0, 4, 0xb0);
|
||||
/* Power off post pll */
|
||||
inno_phy_modeb(hdmi, 1, 1, 0xaa);
|
||||
phy_power_off(hdmi->phy);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
@@ -384,210 +101,7 @@ inno_dw_hdmi_phy_read_hpd(struct dw_hdmi *dw_hdmi, void *data)
|
||||
return status;
|
||||
}
|
||||
|
||||
static int inno_dw_hdmi_phy_pll_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi =
|
||||
container_of(hw, struct rockchip_hdmi, hdmiphy_clkhw);
|
||||
|
||||
if (inno_phy_readl(hdmi, 0xa0) & 1)
|
||||
inno_phy_modeb(hdmi, 0, 1, 0xa0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void inno_dw_hdmi_phy_pll_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi =
|
||||
container_of(hw, struct rockchip_hdmi, hdmiphy_clkhw);
|
||||
|
||||
if ((inno_phy_readl(hdmi, 0xa0) & 1) == 0)
|
||||
inno_phy_modeb(hdmi, 1, 1, 0xa0);
|
||||
}
|
||||
|
||||
static int inno_dw_hdmi_phy_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi =
|
||||
container_of(hw, struct rockchip_hdmi, hdmiphy_clkhw);
|
||||
|
||||
return (inno_phy_readl(hdmi, 0xa0) & 1) ? 0 : 1;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
inno_dw_hdmi_phy_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi =
|
||||
container_of(hw, struct rockchip_hdmi, hdmiphy_clkhw);
|
||||
unsigned long rate, vco, frac;
|
||||
u8 nd, no_a, no_b, no_c, no_d;
|
||||
u16 nf;
|
||||
|
||||
nd = inno_phy_readl(hdmi, 0xa1) & 0x3f;
|
||||
nf = ((inno_phy_readl(hdmi, 0xa2) & 0x0f) << 8) |
|
||||
inno_phy_readl(hdmi, 0xa3);
|
||||
vco = parent_rate * nf;
|
||||
if ((inno_phy_readl(hdmi, 0xa2) & 0x30) == 0) {
|
||||
frac = inno_phy_readl(hdmi, 0xd3) |
|
||||
(inno_phy_readl(hdmi, 0xd2) << 8) |
|
||||
(inno_phy_readl(hdmi, 0xd1) << 16);
|
||||
vco += DIV_ROUND_CLOSEST(parent_rate * frac, (1 << 24));
|
||||
}
|
||||
if (inno_phy_readl(hdmi, 0xa0) & 2) {
|
||||
rate = vco / (nd * 5);
|
||||
} else {
|
||||
no_a = inno_phy_readl(hdmi, 0xa5) & 0x1f;
|
||||
no_b = ((inno_phy_readl(hdmi, 0xa5) >> 5) & 7) + 2;
|
||||
no_c = (1 << ((inno_phy_readl(hdmi, 0xa6) >> 5) & 7));
|
||||
no_d = inno_phy_readl(hdmi, 0xa6) & 0x1f;
|
||||
if (no_a == 1)
|
||||
rate = vco / (nd * no_b * no_d * 2);
|
||||
else
|
||||
rate = vco / (nd * no_a * no_d * 2);
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static long
|
||||
inno_dw_hdmi_phy_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
const struct inno_pre_pll_config *inno_pll_config = inno_pre_pll_cfg;
|
||||
|
||||
for (; inno_pll_config->pixclock != ~0UL; inno_pll_config++)
|
||||
if (inno_pll_config->pixclock == rate)
|
||||
break;
|
||||
/* XXX: Limit pixel clock under 300MHz */
|
||||
if (inno_pll_config->pixclock < 300000000)
|
||||
return inno_pll_config->pixclock;
|
||||
else
|
||||
return ~0UL;
|
||||
}
|
||||
|
||||
static int
|
||||
inno_dw_hdmi_phy_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi =
|
||||
container_of(hw, struct rockchip_hdmi, hdmiphy_clkhw);
|
||||
const struct inno_pre_pll_config *inno_pll_config = inno_pre_pll_cfg;
|
||||
u32 val, i;
|
||||
|
||||
for (; inno_pll_config->pixclock != ~0UL; inno_pll_config++)
|
||||
if (inno_pll_config->pixclock == rate &&
|
||||
inno_pll_config->tmdsclock == rate)
|
||||
break;
|
||||
if (inno_pll_config->pixclock == ~0UL) {
|
||||
dev_err(hdmi->dev, "unsupported rate %lu\n", rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Power off PLL */
|
||||
inno_phy_modeb(hdmi, 1, 1, 0xa0);
|
||||
/* Configure pre-pll */
|
||||
inno_phy_modeb(hdmi, (inno_pll_config->vco_div_5 & 1) << 1, 2, 0xa0);
|
||||
inno_phy_writel(hdmi, 0xa1, inno_pll_config->nd);
|
||||
if (inno_pll_config->frac)
|
||||
val = ((inno_pll_config->nf >> 8) & 0x0f) | 0xc0;
|
||||
else
|
||||
val = ((inno_pll_config->nf >> 8) & 0x0f) | 0xf0;
|
||||
inno_phy_writel(hdmi, 0xa2, val);
|
||||
val = inno_pll_config->nf & 0xff;
|
||||
inno_phy_writel(hdmi, 0xa3, val);
|
||||
val = (inno_pll_config->pclk_div_a & 0x1f) |
|
||||
((inno_pll_config->pclk_div_b & 3) << 5);
|
||||
inno_phy_writel(hdmi, 0xa5, val);
|
||||
val = (inno_pll_config->pclk_div_d & 0x1f) |
|
||||
((inno_pll_config->pclk_div_c & 3) << 5);
|
||||
inno_phy_writel(hdmi, 0xa6, val);
|
||||
val = ((inno_pll_config->tmds_div_a & 3) << 4) |
|
||||
((inno_pll_config->tmds_div_b & 3) << 2) |
|
||||
(inno_pll_config->tmds_div_c & 3);
|
||||
inno_phy_writel(hdmi, 0xa4, val);
|
||||
|
||||
if (inno_pll_config->frac) {
|
||||
val = inno_pll_config->frac & 0xff;
|
||||
inno_phy_writel(hdmi, 0xd3, val);
|
||||
val = (inno_pll_config->frac >> 8) & 0xff;
|
||||
inno_phy_writel(hdmi, 0xd2, val);
|
||||
val = (inno_pll_config->frac >> 16) & 0xff;
|
||||
inno_phy_writel(hdmi, 0xd1, val);
|
||||
} else {
|
||||
inno_phy_writel(hdmi, 0xd3, 0);
|
||||
inno_phy_writel(hdmi, 0xd2, 0);
|
||||
inno_phy_writel(hdmi, 0xd1, 0);
|
||||
}
|
||||
|
||||
/* Power up PLL */
|
||||
inno_phy_modeb(hdmi, 0, 1, 0xa0);
|
||||
|
||||
/* Wait for PLL lock */
|
||||
for (i = 0; i < 5; ++i) {
|
||||
if (inno_phy_readl(hdmi, 0xa9) & 1)
|
||||
break;
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops inno_dw_hdmi_phy_pll_clk_norate_ops = {
|
||||
.prepare = inno_dw_hdmi_phy_pll_prepare,
|
||||
.unprepare = inno_dw_hdmi_phy_pll_unprepare,
|
||||
.is_prepared = inno_dw_hdmi_phy_is_prepared,
|
||||
.recalc_rate = inno_dw_hdmi_phy_pll_recalc_rate,
|
||||
.round_rate = inno_dw_hdmi_phy_pll_round_rate,
|
||||
.set_rate = inno_dw_hdmi_phy_pll_set_rate,
|
||||
};
|
||||
|
||||
static void inno_dw_hdmi_phy_clk_unregister(void *data)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = data;
|
||||
|
||||
of_clk_del_provider(hdmi->dev->of_node);
|
||||
clk_unregister(hdmi->hdmiphy_clk);
|
||||
}
|
||||
|
||||
static int inno_dw_hdmi_phy_clk_register(struct rockchip_hdmi *hdmi)
|
||||
{
|
||||
struct device_node *node = hdmi->dev->of_node;
|
||||
struct clk_init_data init;
|
||||
const char *clk_name = "xin24m";
|
||||
int ret;
|
||||
|
||||
init.flags = 0;
|
||||
init.name = "hdmi_phy";
|
||||
init.ops = &inno_dw_hdmi_phy_pll_clk_norate_ops;
|
||||
init.parent_names = &clk_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
/* optional override of the clockname */
|
||||
of_property_read_string(node, "clock-output-names", &init.name);
|
||||
|
||||
hdmi->hdmiphy_clkhw.init = &init;
|
||||
|
||||
/* register the clock */
|
||||
hdmi->hdmiphy_clk = clk_register(hdmi->dev, &hdmi->hdmiphy_clkhw);
|
||||
if (IS_ERR(hdmi->hdmiphy_clk)) {
|
||||
ret = PTR_ERR(hdmi->hdmiphy_clk);
|
||||
goto err_ret;
|
||||
}
|
||||
ret = of_clk_add_provider(node, of_clk_src_simple_get,
|
||||
hdmi->hdmiphy_clk);
|
||||
if (ret < 0)
|
||||
goto err_clk_provider;
|
||||
|
||||
ret = devm_add_action(hdmi->dev, inno_dw_hdmi_phy_clk_unregister,
|
||||
hdmi);
|
||||
if (ret < 0)
|
||||
goto err_unreg_action;
|
||||
return 0;
|
||||
err_unreg_action:
|
||||
of_clk_del_provider(node);
|
||||
err_clk_provider:
|
||||
clk_unregister(hdmi->hdmiphy_clk);
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rk3328_hdmi_init(struct rockchip_hdmi *hdmi)
|
||||
static int inno_dw_hdmi_init(struct rockchip_hdmi *hdmi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -596,26 +110,27 @@ static int rk3328_hdmi_init(struct rockchip_hdmi *hdmi)
|
||||
dev_err(hdmi->dev, "failed to enable grfclk %d\n", ret);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
/* Map HPD pin to 3V io */
|
||||
regmap_write(hdmi->regmap,
|
||||
RK3328_GRF_SOC_CON4,
|
||||
RK3328_IO_3V_DOMAIN |
|
||||
RK3328_HPD_3V);
|
||||
/* Map ddc pin to 5V io */
|
||||
regmap_write(hdmi->regmap,
|
||||
RK3328_GRF_SOC_CON3,
|
||||
RK3328_IO_CTRL_BY_HDMI);
|
||||
regmap_write(hdmi->regmap,
|
||||
RK3328_GRF_SOC_CON2,
|
||||
RK3328_DDC_MASK_EN |
|
||||
BIT(18));
|
||||
if (hdmi->dev_type == RK3328_HDMI) {
|
||||
/* Map HPD pin to 3V io */
|
||||
regmap_write(hdmi->regmap,
|
||||
RK3328_GRF_SOC_CON4,
|
||||
RK3328_IO_3V_DOMAIN |
|
||||
RK3328_HPD_3V);
|
||||
/* Map ddc pin to 5V io */
|
||||
regmap_write(hdmi->regmap,
|
||||
RK3328_GRF_SOC_CON3,
|
||||
RK3328_IO_CTRL_BY_HDMI);
|
||||
regmap_write(hdmi->regmap,
|
||||
RK3328_GRF_SOC_CON2,
|
||||
RK3328_DDC_MASK_EN |
|
||||
BIT(18));
|
||||
} else {
|
||||
regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON2,
|
||||
RK3228_DDC_MASK_EN);
|
||||
regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON6,
|
||||
RK3228_IO_3V_DOMAIN);
|
||||
}
|
||||
clk_disable_unprepare(hdmi->grf_clk);
|
||||
/*
|
||||
* Use phy internal register control
|
||||
* rxsense/poweron/pllpd/pdataen signal.
|
||||
*/
|
||||
inno_phy_writel(hdmi, 0x01, 0x07);
|
||||
inno_phy_writel(hdmi, 0x02, 0x91);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -816,16 +331,6 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||||
return PTR_ERR(hdmi->hclk_vio);
|
||||
}
|
||||
|
||||
hdmi->hdmiphy_pclk = devm_clk_get(hdmi->dev, "pclk_hdmiphy");
|
||||
if (PTR_ERR(hdmi->hdmiphy_pclk) == -ENOENT) {
|
||||
hdmi->hdmiphy_pclk = NULL;
|
||||
} else if (PTR_ERR(hdmi->hdmiphy_pclk) == -EPROBE_DEFER) {
|
||||
return -EPROBE_DEFER;
|
||||
} else if (IS_ERR(hdmi->hdmiphy_pclk)) {
|
||||
dev_err(hdmi->dev, "failed to get pclk_hdmiphy clock\n");
|
||||
return PTR_ERR(hdmi->hdmiphy_pclk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->vpll_clk);
|
||||
if (ret) {
|
||||
dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret);
|
||||
@@ -839,13 +344,6 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->hdmiphy_pclk);
|
||||
if (ret) {
|
||||
dev_err(hdmi->dev, "Failed to eanble HDMI pclk_hdmiphy: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (of_get_property(np, "rockchip,phy-table", &val)) {
|
||||
phy_config = kmalloc(val, GFP_KERNEL);
|
||||
if (!phy_config) {
|
||||
@@ -1018,8 +516,6 @@ static const struct dw_hdmi_phy_ops inno_dw_hdmi_phy_ops = {
|
||||
.init = inno_dw_hdmi_phy_init,
|
||||
.disable = inno_dw_hdmi_phy_disable,
|
||||
.read_hpd = inno_dw_hdmi_phy_read_hpd,
|
||||
.read = inno_dw_hdmi_phy_read,
|
||||
.write = inno_dw_hdmi_phy_write,
|
||||
};
|
||||
|
||||
static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
|
||||
@@ -1092,7 +588,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
struct drm_device *drm = data;
|
||||
struct drm_encoder *encoder;
|
||||
struct rockchip_hdmi *hdmi;
|
||||
struct resource *iores, *phyres;
|
||||
struct resource *iores;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
@@ -1133,17 +629,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (hdmi->dev_type == RK3328_HDMI) {
|
||||
phyres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
hdmi->hdmiphy = devm_ioremap_resource(dev, phyres);
|
||||
if (IS_ERR(hdmi->hdmiphy))
|
||||
return PTR_ERR(hdmi->hdmiphy);
|
||||
plat_data->phy_data = hdmi;
|
||||
ret = rk3328_hdmi_init(hdmi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
inno_dw_hdmi_phy_clk_register(hdmi);
|
||||
} else if (hdmi->dev_type == RK3228_HDMI) {
|
||||
if (hdmi->dev_type == RK3328_HDMI || hdmi->dev_type == RK3228_HDMI) {
|
||||
hdmi->phy = devm_phy_get(dev, "hdmi_phy");
|
||||
if (IS_ERR(hdmi->phy)) {
|
||||
ret = PTR_ERR(hdmi->phy);
|
||||
@@ -1151,10 +637,9 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
return ret;
|
||||
}
|
||||
plat_data->phy_data = hdmi;
|
||||
regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON2,
|
||||
RK3228_DDC_MASK_EN);
|
||||
regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON6,
|
||||
RK3228_IO_3V_DOMAIN);
|
||||
ret = inno_dw_hdmi_init(hdmi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
|
||||
|
||||
Reference in New Issue
Block a user