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:
Zhibin Huang
2024-08-08 09:41:23 +08:00
parent 918d45834b
commit efb2e22117
2 changed files with 83 additions and 70 deletions

View File

@@ -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;

View File

@@ -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;