mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
phy/rockchip: mipi_dphy: Add support for RK1808
The innoslicon GF22FDX MIPI D-PHY integrates a MIPI 1.2 compatible PHY that supports up to 2.5Gbps high speed data transmitter, plus a MIPI low-power low speed transceiver that support data transfer in the bi-directional mode. The IP supports the full specifications described in the D-PHY spec 1.2. The D-PHY is built in with a standard digital interface to talk to any third party host controller. Change-Id: Iad7b9dc8fedaa1a5741830e9c02a593f544c2423 Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
@@ -3,6 +3,7 @@ ROCKCHIP MIPI DPHY WITH INNO IP BLOCK
|
||||
Required properties:
|
||||
- compatible : must be one of:
|
||||
"rockchip,px30-mipi-dphy";
|
||||
"rockchip,rk1808-mipi-dphy";
|
||||
"rockchip,rk3128-mipi-dphy";
|
||||
"rockchip,rk3366-mipi-dphy";
|
||||
"rockchip,rk3368-mipi-dphy";
|
||||
|
||||
@@ -209,9 +209,21 @@ struct mipi_dphy_timing {
|
||||
unsigned int wakeup;
|
||||
};
|
||||
|
||||
struct inno_mipi_dphy_timing {
|
||||
unsigned int max_lane_mbps;
|
||||
u8 lpx;
|
||||
u8 hs_prepare;
|
||||
u8 clk_lane_hs_zero;
|
||||
u8 data_lane_hs_zero;
|
||||
u8 hs_trail;
|
||||
};
|
||||
|
||||
struct inno_video_phy_socdata {
|
||||
bool pinmux;
|
||||
bool has_h2p_clk;
|
||||
bool post_div_enable;
|
||||
const struct inno_mipi_dphy_timing *timings;
|
||||
unsigned int num_timings;
|
||||
};
|
||||
|
||||
struct inno_mipi_dphy {
|
||||
@@ -247,26 +259,41 @@ enum {
|
||||
REGISTER_PART_DATA3_LANE,
|
||||
};
|
||||
|
||||
struct inno_mipi_dphy_timing {
|
||||
unsigned int max_lane_mbps;
|
||||
u8 hs_prepare;
|
||||
u8 clk_lane_hs_zero;
|
||||
u8 data_lane_hs_zero;
|
||||
u8 hs_trail;
|
||||
static const struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table[] = {
|
||||
{ 110, 0x00, 0x20, 0x16, 0x02, 0x22},
|
||||
{ 150, 0x00, 0x06, 0x16, 0x03, 0x45},
|
||||
{ 200, 0x00, 0x18, 0x17, 0x04, 0x0b},
|
||||
{ 250, 0x00, 0x05, 0x17, 0x05, 0x16},
|
||||
{ 300, 0x00, 0x51, 0x18, 0x06, 0x2c},
|
||||
{ 400, 0x00, 0x64, 0x19, 0x07, 0x33},
|
||||
{ 500, 0x00, 0x20, 0x1b, 0x07, 0x4e},
|
||||
{ 600, 0x00, 0x6a, 0x1d, 0x08, 0x3a},
|
||||
{ 700, 0x00, 0x3e, 0x1e, 0x08, 0x6a},
|
||||
{ 800, 0x00, 0x21, 0x1f, 0x09, 0x29},
|
||||
{1000, 0x00, 0x09, 0x20, 0x09, 0x27},
|
||||
};
|
||||
|
||||
static const struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table[] = {
|
||||
{ 110, 0x20, 0x16, 0x02, 0x22},
|
||||
{ 150, 0x06, 0x16, 0x03, 0x45},
|
||||
{ 200, 0x18, 0x17, 0x04, 0x0b},
|
||||
{ 250, 0x05, 0x17, 0x05, 0x16},
|
||||
{ 300, 0x51, 0x18, 0x06, 0x2c},
|
||||
{ 400, 0x64, 0x19, 0x07, 0x33},
|
||||
{ 500, 0x20, 0x1b, 0x07, 0x4e},
|
||||
{ 600, 0x6a, 0x1d, 0x08, 0x3a},
|
||||
{ 700, 0x3e, 0x1e, 0x08, 0x6a},
|
||||
{ 800, 0x21, 0x1f, 0x09, 0x29},
|
||||
{1000, 0x09, 0x20, 0x09, 0x27},
|
||||
static const
|
||||
struct inno_mipi_dphy_timing inno_mipi_dphy_gf22fdx_timing_table[] = {
|
||||
{ 110, 0x02, 0x7f, 0x16, 0x02, 0x02},
|
||||
{ 150, 0x02, 0x7f, 0x16, 0x03, 0x02},
|
||||
{ 200, 0x02, 0x7f, 0x17, 0x04, 0x02},
|
||||
{ 250, 0x02, 0x7f, 0x17, 0x05, 0x04},
|
||||
{ 300, 0x02, 0x7f, 0x18, 0x06, 0x04},
|
||||
{ 400, 0x03, 0x7e, 0x19, 0x07, 0x04},
|
||||
{ 500, 0x03, 0x7c, 0x1b, 0x07, 0x08},
|
||||
{ 600, 0x03, 0x70, 0x1d, 0x08, 0x10},
|
||||
{ 700, 0x05, 0x40, 0x1e, 0x08, 0x30},
|
||||
{ 800, 0x05, 0x02, 0x1f, 0x09, 0x30},
|
||||
{1000, 0x05, 0x08, 0x20, 0x09, 0x30},
|
||||
{1200, 0x06, 0x03, 0x32, 0x14, 0x0f},
|
||||
{1400, 0x09, 0x03, 0x32, 0x14, 0x0f},
|
||||
{1600, 0x0d, 0x42, 0x36, 0x0e, 0x0f},
|
||||
{1800, 0x0e, 0x47, 0x7a, 0x0e, 0x0f},
|
||||
{2000, 0x11, 0x64, 0x7a, 0x0e, 0x0b},
|
||||
{2200, 0x13, 0x64, 0x7e, 0x15, 0x0b},
|
||||
{2400, 0x13, 0x33, 0x7f, 0x15, 0x6a},
|
||||
{2500, 0x15, 0x54, 0x7f, 0x15, 0x6a},
|
||||
};
|
||||
|
||||
static const struct pinctrl_pin_desc inno_video_phy_pins[] = {
|
||||
@@ -494,6 +521,9 @@ static void inno_mipi_dphy_pll_enable(struct inno_mipi_dphy *inno)
|
||||
REG_FBDIV_HI_MASK, REG_FBDIV_HI(inno->pll.fbdiv >> 8));
|
||||
inno_update_bits(inno, REGISTER_PART_ANALOG, 0x04,
|
||||
REG_FBDIV_LO_MASK, REG_FBDIV_LO(inno->pll.fbdiv));
|
||||
if (inno->socdata->post_div_enable)
|
||||
inno_update_bits(inno, REGISTER_PART_ANALOG, 0x08,
|
||||
PLL_POST_DIV_ENABLE_MASK, PLL_POST_DIV_ENABLE);
|
||||
inno_update_bits(inno, REGISTER_PART_ANALOG, 0x01,
|
||||
REG_LDOPD_MASK | REG_PLLPD_MASK,
|
||||
REG_LDOPD_POWER_ON | REG_PLLPD_POWER_ON);
|
||||
@@ -535,18 +565,19 @@ static void mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
|
||||
}
|
||||
|
||||
static const struct inno_mipi_dphy_timing *
|
||||
inno_mipi_dphy_get_timing(unsigned int lane_mbps)
|
||||
inno_mipi_dphy_get_timing(struct inno_mipi_dphy *inno)
|
||||
{
|
||||
unsigned int lane_mbps = inno->lane_rate / USEC_PER_SEC;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(inno_mipi_dphy_timing_table); i++)
|
||||
if (lane_mbps <= inno_mipi_dphy_timing_table[i].max_lane_mbps)
|
||||
for (i = 0; i < inno->socdata->num_timings; i++)
|
||||
if (lane_mbps <= inno->socdata->timings[i].max_lane_mbps)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(inno_mipi_dphy_timing_table))
|
||||
if (i == inno->socdata->num_timings)
|
||||
--i;
|
||||
|
||||
return &inno_mipi_dphy_timing_table[i];
|
||||
return &inno->socdata->timings[i];
|
||||
}
|
||||
|
||||
static void inno_mipi_dphy_timing_init(struct inno_mipi_dphy *inno)
|
||||
@@ -571,7 +602,7 @@ static void inno_mipi_dphy_timing_init(struct inno_mipi_dphy *inno)
|
||||
txbyteclk, ui, sys_clk);
|
||||
|
||||
mipi_dphy_timing_get_default(&gotp, ui);
|
||||
timing = inno_mipi_dphy_get_timing(inno->lane_rate / USEC_PER_SEC);
|
||||
timing = inno_mipi_dphy_get_timing(inno);
|
||||
|
||||
hs_exit = DIV_ROUND_UP(gotp.hsexit * txbyteclk, NSEC_PER_SEC);
|
||||
clk_post = DIV_ROUND_UP(gotp.clkpost * txbyteclk, NSEC_PER_SEC);
|
||||
@@ -581,9 +612,14 @@ static void inno_mipi_dphy_timing_init(struct inno_mipi_dphy *inno)
|
||||
if (wakeup > 0x3ff)
|
||||
wakeup = 0x3ff;
|
||||
|
||||
lpx = DIV_ROUND_UP(txbyteclk * gotp.lpx, NSEC_PER_SEC);
|
||||
if (lpx >= 2)
|
||||
lpx -= 2;
|
||||
if (timing->lpx) {
|
||||
lpx = timing->lpx;
|
||||
} else {
|
||||
/* Tlpx = Tpin_txbyteclkhs * (2 + value) */
|
||||
lpx = DIV_ROUND_UP(txbyteclk * gotp.lpx, NSEC_PER_SEC);
|
||||
if (lpx >= 2)
|
||||
lpx -= 2;
|
||||
}
|
||||
|
||||
ta_go = DIV_ROUND_UP(gotp.tago * txclkesc, NSEC_PER_SEC);
|
||||
ta_sure = DIV_ROUND_UP(gotp.tasure * txclkesc, NSEC_PER_SEC);
|
||||
@@ -640,12 +676,15 @@ static void inno_mipi_dphy_timing_init(struct inno_mipi_dphy *inno)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long inno_mipi_dphy_pll_rate_fixup(unsigned long fin,
|
||||
static unsigned long inno_mipi_dphy_pll_round_rate(struct inno_mipi_dphy *inno,
|
||||
unsigned long prate,
|
||||
unsigned long rate,
|
||||
u8 *prediv, u16 *fbdiv)
|
||||
{
|
||||
const struct inno_mipi_dphy_timing *timings = inno->socdata->timings;
|
||||
unsigned int num_timings = inno->socdata->num_timings;
|
||||
unsigned long best_freq = 0;
|
||||
unsigned long fout;
|
||||
unsigned int fin, fout, max_fout;
|
||||
u8 min_prediv, max_prediv;
|
||||
u8 _prediv, best_prediv = 1;
|
||||
u16 _fbdiv, best_fbdiv = 1;
|
||||
@@ -656,34 +695,34 @@ static unsigned long inno_mipi_dphy_pll_rate_fixup(unsigned long fin,
|
||||
* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2
|
||||
* PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2
|
||||
*/
|
||||
fout = 2 * rate;
|
||||
fin = prate / USEC_PER_SEC;
|
||||
fout = 2 * (rate / USEC_PER_SEC);
|
||||
max_fout = 2 * timings[num_timings - 1].max_lane_mbps;
|
||||
if (fout > max_fout)
|
||||
fout = max_fout;
|
||||
|
||||
/* constraint: 5Mhz < Fref / prediv < 40MHz */
|
||||
min_prediv = DIV_ROUND_UP(fin, 40000000);
|
||||
max_prediv = fin / 5000000;
|
||||
min_prediv = DIV_ROUND_UP(fin, 40);
|
||||
max_prediv = fin / 5;
|
||||
|
||||
for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
|
||||
u64 tmp;
|
||||
u32 delta;
|
||||
u32 delta, tmp;
|
||||
|
||||
tmp = (u64)fout * _prediv;
|
||||
do_div(tmp, fin);
|
||||
_fbdiv = tmp;
|
||||
_fbdiv = fout * _prediv / fin;
|
||||
/*
|
||||
* The all possible settings of feedback divider are
|
||||
* 12, 13, 14, 16, ~ 511
|
||||
*/
|
||||
if ((_fbdiv == 15) || (_fbdiv < 12) || (_fbdiv > 511))
|
||||
continue;
|
||||
tmp = (u64)_fbdiv * fin;
|
||||
do_div(tmp, _prediv);
|
||||
|
||||
tmp = _fbdiv * fin / _prediv;
|
||||
delta = abs(fout - tmp);
|
||||
if (delta < min_delta) {
|
||||
best_prediv = _prediv;
|
||||
best_fbdiv = _fbdiv;
|
||||
min_delta = delta;
|
||||
best_freq = tmp;
|
||||
best_freq = tmp * USEC_PER_SEC;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,7 +783,8 @@ static long inno_mipi_dphy_pll_clk_round_rate(struct clk_hw *hw,
|
||||
u16 fbdiv = 1;
|
||||
u8 prediv = 1;
|
||||
|
||||
fout = inno_mipi_dphy_pll_rate_fixup(fin, rate, &prediv, &fbdiv);
|
||||
fout = inno_mipi_dphy_pll_round_rate(inno, fin, rate,
|
||||
&prediv, &fbdiv);
|
||||
|
||||
dev_dbg(inno->dev, "%s: fin=%lu, req_rate=%lu\n",
|
||||
__func__, *prate, rate);
|
||||
@@ -965,25 +1005,46 @@ static int inno_mipi_dphy_remove(struct platform_device *pdev)
|
||||
static const struct inno_video_phy_socdata px30_socdata = {
|
||||
.has_h2p_clk = false,
|
||||
.pinmux = false,
|
||||
.post_div_enable = false,
|
||||
.timings = inno_mipi_dphy_timing_table,
|
||||
.num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table),
|
||||
};
|
||||
|
||||
static const struct inno_video_phy_socdata rk1808_socdata = {
|
||||
.has_h2p_clk = false,
|
||||
.pinmux = false,
|
||||
.post_div_enable = true,
|
||||
.timings = inno_mipi_dphy_gf22fdx_timing_table,
|
||||
.num_timings = ARRAY_SIZE(inno_mipi_dphy_gf22fdx_timing_table),
|
||||
};
|
||||
|
||||
static const struct inno_video_phy_socdata rk3128_socdata = {
|
||||
.has_h2p_clk = true,
|
||||
.pinmux = false,
|
||||
.post_div_enable = false,
|
||||
.timings = inno_mipi_dphy_timing_table,
|
||||
.num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table),
|
||||
};
|
||||
|
||||
static const struct inno_video_phy_socdata rk3366_socdata = {
|
||||
.has_h2p_clk = false,
|
||||
.pinmux = false,
|
||||
.post_div_enable = false,
|
||||
.timings = inno_mipi_dphy_timing_table,
|
||||
.num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table),
|
||||
};
|
||||
|
||||
static const struct inno_video_phy_socdata rk3368_socdata = {
|
||||
.has_h2p_clk = false,
|
||||
.pinmux = true,
|
||||
.post_div_enable = false,
|
||||
.timings = inno_mipi_dphy_timing_table,
|
||||
.num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table),
|
||||
};
|
||||
|
||||
static const struct of_device_id inno_mipi_dphy_of_match[] = {
|
||||
{ .compatible = "rockchip,px30-mipi-dphy", .data = &px30_socdata },
|
||||
{ .compatible = "rockchip,rk1808-mipi-dphy", .data = &rk1808_socdata },
|
||||
{ .compatible = "rockchip,rk3128-mipi-dphy", .data = &rk3128_socdata },
|
||||
{ .compatible = "rockchip,rk3366-mipi-dphy", .data = &rk3366_socdata },
|
||||
{ .compatible = "rockchip,rk3368-mipi-dphy", .data = &rk3368_socdata },
|
||||
|
||||
Reference in New Issue
Block a user