mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 18:41:58 +09:00
phy: rockchip: mipi-dcphy: optimize pll and ssc calculation
Type: Fix Redmine ID: N/A Associated modifications: N/A Test: N/A Signed-off-by: Zhibin Huang <zhibin.huang@rock-chips.com> Change-Id: If77c1e6526041fdeae07bdc174cddf01bbee4f49
This commit is contained in:
@@ -1373,19 +1373,7 @@ static void samsung_mipi_dcphy_pll_configure(struct samsung_mipi_dcphy *samsung)
|
||||
{
|
||||
regmap_update_bits(samsung->regmap, PLL_CON0, S_MASK | P_MASK,
|
||||
S(samsung->pll.scaler) | P(samsung->pll.prediv));
|
||||
|
||||
if (samsung->pll.dsm < 0) {
|
||||
u16 dsm_tmp;
|
||||
|
||||
/* Using opposite number subtraction to find complement */
|
||||
dsm_tmp = abs(samsung->pll.dsm);
|
||||
dsm_tmp = dsm_tmp - 1;
|
||||
dsm_tmp ^= 0xffff;
|
||||
regmap_write(samsung->regmap, PLL_CON1, dsm_tmp);
|
||||
} else {
|
||||
regmap_write(samsung->regmap, PLL_CON1, samsung->pll.dsm);
|
||||
}
|
||||
|
||||
regmap_write(samsung->regmap, PLL_CON1, samsung->pll.dsm);
|
||||
regmap_update_bits(samsung->regmap, PLL_CON2,
|
||||
M_MASK, M(samsung->pll.fbdiv));
|
||||
|
||||
@@ -1505,28 +1493,36 @@ static void samsung_mipi_cphy_timing_init(struct samsung_mipi_dcphy *samsung)
|
||||
/* set T_ERR_SOT_SYNC default value */
|
||||
}
|
||||
|
||||
static int samsung_mipi_dcphy_pll_ssc_modulation_calc(struct samsung_mipi_dcphy *samsung,
|
||||
u16 prediv, u16 fbdiv, u8 *mfr, u8 *mrr);
|
||||
static unsigned long
|
||||
samsung_mipi_dcphy_pll_round_rate(struct samsung_mipi_dcphy *samsung,
|
||||
unsigned long prate, unsigned long rate,
|
||||
u8 *prediv, u16 *fbdiv, int *dsm, u8 *scaler)
|
||||
u8 *prediv, u16 *fbdiv, u16 *dsm, u8 *scaler, u8 *mfr, u8 *mrr)
|
||||
{
|
||||
u32 max_fout = samsung->c_option ?
|
||||
samsung->pdata->cphy_tx_max_ksps_per_lane :
|
||||
samsung->pdata->dphy_tx_max_kbps_per_lane;
|
||||
u64 best_freq = 0;
|
||||
u64 _freq, best_freq = 0;
|
||||
u64 fin, fvco, fout;
|
||||
u8 min_prediv, max_prediv;
|
||||
u8 _mfr = 0, best_mfr = 0;
|
||||
u8 _mrr = 0, best_mrr = 0;
|
||||
u8 _prediv, best_prediv = 1;
|
||||
u16 _fbdiv, best_fbdiv = 1;
|
||||
u8 _scaler, best_scaler = 0;
|
||||
u32 min_delta = UINT_MAX;
|
||||
long _dsm, best_dsm = 0;
|
||||
long long _dsm, best_dsm = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!prate) {
|
||||
dev_err(samsung->dev, "prate of pll can not be set zero\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(samsung->dev, "%s: fin=%lu, req_rate=%lu\n",
|
||||
__func__, prate, rate);
|
||||
|
||||
/*
|
||||
* The PLL output frequency can be calculated using a simple formula:
|
||||
* Fvco = ((m+k/65536) x 2 x Fin) / p
|
||||
@@ -1562,23 +1558,39 @@ samsung_mipi_dcphy_pll_round_rate(struct samsung_mipi_dcphy *samsung,
|
||||
if ((_fbdiv < 64) || (_fbdiv > 1023))
|
||||
continue;
|
||||
|
||||
/* -32767 ≤ K[15:0] ≤ 32767 */
|
||||
_dsm = ((_prediv * fvco) - (2 * _fbdiv * fin));
|
||||
_dsm = DIV_ROUND_UP_ULL(_dsm << 15, fin);
|
||||
if (abs(_dsm) > 32767)
|
||||
/* -32768 ≤ K[15:0] ≤ 32767 */
|
||||
_dsm = _prediv * fvco - 2 * _fbdiv * fin;
|
||||
_dsm = _dsm / abs(_dsm) * DIV_ROUND_UP_ULL(abs(_dsm) << 15, fin);
|
||||
if (_dsm < -32768 || _dsm > 32767)
|
||||
continue;
|
||||
|
||||
tmp = DIV_ROUND_CLOSEST_ULL((_fbdiv * fin * 2 * 1000), _prediv);
|
||||
tmp += DIV_ROUND_CLOSEST_ULL((_dsm * fin * 1000), _prediv << 15);
|
||||
tmp = DIV_ROUND_CLOSEST_ULL(_fbdiv * fin * 2 * 1000, _prediv);
|
||||
tmp += (_dsm / abs(_dsm) *
|
||||
DIV_ROUND_CLOSEST_ULL(abs(_dsm) * fin * 1000,
|
||||
_prediv << 15));
|
||||
_freq = (DIV_ROUND_CLOSEST_ULL(tmp, 1000) * MSEC_PER_SEC);
|
||||
|
||||
/*
|
||||
* All DPHY 2.0 compliant Transmitters shall support SSC
|
||||
* operating above 2.5 Gbps
|
||||
*/
|
||||
if ((_freq >> _scaler) > 2500000000LL)
|
||||
ret = samsung_mipi_dcphy_pll_ssc_modulation_calc(samsung,
|
||||
_prediv,
|
||||
_fbdiv,
|
||||
&_mfr,
|
||||
&_mrr);
|
||||
|
||||
delta = abs(fvco * MSEC_PER_SEC - tmp);
|
||||
if (delta < min_delta) {
|
||||
if (!ret && delta <= min_delta) {
|
||||
best_prediv = _prediv;
|
||||
best_fbdiv = _fbdiv;
|
||||
best_dsm = _dsm;
|
||||
best_mfr = _mfr;
|
||||
best_mrr = _mrr;
|
||||
best_scaler = _scaler;
|
||||
min_delta = delta;
|
||||
best_freq = DIV_ROUND_CLOSEST_ULL(tmp, 1000) * MSEC_PER_SEC;
|
||||
best_freq = _freq;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1590,8 +1602,10 @@ samsung_mipi_dcphy_pll_round_rate(struct samsung_mipi_dcphy *samsung,
|
||||
*fbdiv = best_fbdiv;
|
||||
*dsm = (int)best_dsm & 0xffff;
|
||||
*scaler = best_scaler;
|
||||
dev_dbg(samsung->dev, "p: %d, m: %d, dsm:%ld, scaler: %d\n",
|
||||
best_prediv, best_fbdiv, best_dsm, best_scaler);
|
||||
*mfr = best_mfr;
|
||||
*mrr = best_mrr;
|
||||
dev_dbg(samsung->dev, "%s: fout=%llu, prediv=%u, fbdiv=%u, dsm=%lld, scaler=%u\n",
|
||||
__func__, best_freq >> best_scaler, best_prediv, best_fbdiv, best_dsm, best_scaler);
|
||||
|
||||
return best_freq >> best_scaler;
|
||||
}
|
||||
@@ -1827,47 +1841,58 @@ static int samsung_mipi_dcphy_set_mode(struct phy *phy, enum phy_mode mode,
|
||||
|
||||
static int
|
||||
samsung_mipi_dcphy_pll_ssc_modulation_calc(struct samsung_mipi_dcphy *samsung,
|
||||
u8 *mfr, u8 *mrr)
|
||||
u16 prediv, u16 fbdiv, u8 *mfr, u8 *mrr)
|
||||
{
|
||||
unsigned long fin = div64_ul(clk_get_rate(samsung->ref_clk), MSEC_PER_SEC);
|
||||
u16 prediv = samsung->pll.prediv;
|
||||
u16 fbdiv = samsung->pll.fbdiv;
|
||||
u16 min_mfr, max_mfr;
|
||||
u16 min_mfr, max_mfr, mid_mfr, mfr_end;
|
||||
u16 _mfr, best_mfr = 0;
|
||||
u16 mr, _mrr, best_mrr = 0;
|
||||
u16 _mrr, best_mrr = 0;
|
||||
|
||||
/* 20KHz ≤ MF ≤ 150KHz */
|
||||
max_mfr = DIV_ROUND_UP(fin, (20 * prediv) << 5);
|
||||
min_mfr = div64_ul(fin, ((150 * prediv) << 5));
|
||||
/*0 ≤ mfr ≤ 255 */
|
||||
/* MF(MHz) = Fref / p / mfr / 32 */
|
||||
/* 30MHz ≤ MF ≤ 33MHz, default 31 */
|
||||
max_mfr = DIV_ROUND_UP(fin, (30 * prediv) << 5);
|
||||
min_mfr = div64_ul(fin, ((33 * prediv) << 5));
|
||||
mid_mfr = div64_ul(fin, (31 * prediv) << 5);
|
||||
/* 0 ≤ mfr ≤ 255 */
|
||||
if (max_mfr > 256)
|
||||
max_mfr = 256;
|
||||
|
||||
for (_mfr = min_mfr; _mfr < max_mfr; _mfr++) {
|
||||
/* 1 ≤ mrr ≤ 31 */
|
||||
for (_mrr = 1; _mrr < 32; _mrr++) {
|
||||
mr = DIV_ROUND_UP(_mfr * _mrr * 100, fbdiv << 6);
|
||||
/* 0 ≤ MR ≤ 5% */
|
||||
if (mr > 5)
|
||||
continue;
|
||||
|
||||
if (_mfr * _mrr < 513) {
|
||||
mfr_end = max_mfr;
|
||||
for (_mfr = mid_mfr; _mfr < mfr_end; _mfr++) {
|
||||
/* MR(%) = mfr * mrr * 100 / m / 64 */
|
||||
/* 0 ≤ MR ≤ 5000ppm(0.5%), default is reduced from 0.25%. */
|
||||
_mrr = (25 * fbdiv << 6) / (_mfr * 100 * 100);
|
||||
for (; _mrr > 0; _mrr--) {
|
||||
/* 0 ≤ mrr * mfr ≤ 512 */
|
||||
if (_mfr * _mrr <= 512) {
|
||||
best_mfr = _mfr;
|
||||
best_mrr = _mrr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_mrr)
|
||||
break;
|
||||
|
||||
if (_mfr == mfr_end - 1 && _mfr > mid_mfr) {
|
||||
_mfr = min_mfr - 1;
|
||||
mfr_end = mid_mfr;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_mrr) {
|
||||
*mfr = best_mfr & 0xff;
|
||||
*mrr = best_mrr & 0x3f;
|
||||
} else {
|
||||
dev_err(samsung->dev, "failed to calc ssc parameter mfr and mrr\n");
|
||||
return -EINVAL;
|
||||
dev_dbg(samsung->dev, "mfr=%d, mrr=%d, MF=%llukHz, MR=%lluppm\n",
|
||||
*mfr, *mrr, div64_ul(fin, (prediv * *mfr) << 5),
|
||||
div64_ul(*mfr * *mrr * 1000000, fbdiv << 6));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
dev_err(samsung->dev, "%s: failed to calc ssc parameter mfr and mrr\n", __func__);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1879,32 +1904,20 @@ samsung_mipi_dcphy_pll_calc_rate(struct samsung_mipi_dcphy *samsung,
|
||||
u8 scaler = 0, mfr = 0, mrr = 0;
|
||||
u16 fbdiv = 0;
|
||||
u8 prediv = 1;
|
||||
int dsm = 0;
|
||||
int ret;
|
||||
u16 dsm = 0;
|
||||
|
||||
fout = samsung_mipi_dcphy_pll_round_rate(samsung, prate, rate,
|
||||
&prediv, &fbdiv, &dsm,
|
||||
&scaler);
|
||||
&scaler, &mfr, &mrr);
|
||||
|
||||
dev_dbg(samsung->dev, "%s: fin=%lu, req_rate=%llu\n",
|
||||
__func__, prate, rate);
|
||||
dev_dbg(samsung->dev, "%s: fout=%lu, prediv=%u, fbdiv=%u\n",
|
||||
__func__, fout, prediv, fbdiv);
|
||||
if (fout != 0) {
|
||||
samsung->pll.prediv = prediv;
|
||||
samsung->pll.fbdiv = fbdiv;
|
||||
samsung->pll.dsm = dsm;
|
||||
samsung->pll.scaler = scaler;
|
||||
samsung->pll.rate = fout;
|
||||
|
||||
samsung->pll.prediv = prediv;
|
||||
samsung->pll.fbdiv = fbdiv;
|
||||
samsung->pll.dsm = dsm;
|
||||
samsung->pll.scaler = scaler;
|
||||
samsung->pll.rate = fout;
|
||||
|
||||
/*
|
||||
* All DPHY 2.0 compliant Transmitters shall support SSC operating above
|
||||
* 2.5 Gbps
|
||||
*/
|
||||
if (fout > 2500000000LL) {
|
||||
ret = samsung_mipi_dcphy_pll_ssc_modulation_calc(samsung,
|
||||
&mfr, &mrr);
|
||||
if (!ret) {
|
||||
if (fout > 2500000000LL) {
|
||||
samsung->pll.ssc_en = true;
|
||||
samsung->pll.mfr = mfr;
|
||||
samsung->pll.mrr = mrr;
|
||||
|
||||
@@ -65,7 +65,7 @@ struct samsung_mipi_dcphy {
|
||||
unsigned long long rate;
|
||||
u8 prediv;
|
||||
u16 fbdiv;
|
||||
long dsm;
|
||||
u16 dsm;
|
||||
u8 scaler;
|
||||
|
||||
bool ssc_en;
|
||||
|
||||
Reference in New Issue
Block a user