diff mbox

[v3,3/6] drm/rockchip/dsi: correct Feedback divider setting

Message ID 1508903463-7254-3-git-send-email-nickey.yang@rock-chips.com (mailing list archive)
State New, archived
Headers show

Commit Message

Nickey Yang Oct. 25, 2017, 3:51 a.m. UTC
This patch correct Feedback divider setting:
1、Set Feedback divider [8:5] when HIGH_PROGRAM_EN
2、Due to the use of a "by 2 pre-scaler," the range of the
feedback multiplication Feedback divider is limited to even
division numbers, and Feedback divider must be greater than
12, less than 1000.
3、Make the previously configured Feedback divider(LSB)
factors effective

Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
---
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 93 ++++++++++++++++++++++------------
 1 file changed, 62 insertions(+), 31 deletions(-)

Comments

Sean Paul Oct. 25, 2017, 7:57 a.m. UTC | #1
On Wed, Oct 25, 2017 at 11:51:00AM +0800, Nickey Yang wrote:
> This patch correct Feedback divider setting:
> 1、Set Feedback divider [8:5] when HIGH_PROGRAM_EN
> 2、Due to the use of a "by 2 pre-scaler," the range of the
> feedback multiplication Feedback divider is limited to even
> division numbers, and Feedback divider must be greater than
> 12, less than 1000.
> 3、Make the previously configured Feedback divider(LSB)
> factors effective
> 
> Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
> ---

You don't list the changes between this version and the previous ones, so I looked
at the feedback from the last time. Archit asked a question about moving to
dw-mipi-dsi, and Kristian asked you to split the patch for each line item in the
above list. I know you split out the register definitions, but an explanation
about why you didn't split the rest would be helpful.

In future, list the changes between patches and Cc people who have given you
review on your previous versions (I've cc'd Archit and Kristian).

Sean


>  drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 93 ++++++++++++++++++++++------------
>  1 file changed, 62 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> index 09e7bfe..589b420 100644
> --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> @@ -239,7 +239,7 @@
>  #define LOW_PROGRAM_EN		0
>  #define HIGH_PROGRAM_EN		BIT(7)
>  #define LOOP_DIV_LOW_SEL(val)	(((val) - 1) & 0x1f)
> -#define LOOP_DIV_HIGH_SEL(val)	((((val) - 1) >> 5) & 0x1f)
> +#define LOOP_DIV_HIGH_SEL(val)	((((val) - 1) >> 5) & 0xf)
>  #define PLL_LOOP_DIV_EN		BIT(5)
>  #define PLL_INPUT_DIV_EN	BIT(4)
>  
> @@ -531,6 +531,14 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
>  	dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
>  			      LOOP_DIV_LOW_SEL(dsi->feedback_div) |
>  			      LOW_PROGRAM_EN);
> +	/*
> +	 * we need set PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL immediately
> +	 * to make the configrued LSB effective according to IP simulation
> +	 * and lab test results.
> +	 * Only in this way can we get correct mipi phy pll frequency.
> +	 */
> +	dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
> +			      PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
>  	dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
>  			      LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
>  			      HIGH_PROGRAM_EN);
> @@ -604,11 +612,16 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
>  static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi,
>  				    struct drm_display_mode *mode)
>  {
> -	unsigned int i, pre;
> -	unsigned long mpclk, pllref, tmp;
> -	unsigned int m = 1, n = 1, target_mbps = 1000;
> +	unsigned long mpclk, tmp;
> +	unsigned int target_mbps = 1000;
>  	unsigned int max_mbps = dppa_map[ARRAY_SIZE(dppa_map) - 1].max_mbps;
>  	int bpp;
> +	unsigned long best_freq = 0;
> +	unsigned long fvco_min, fvco_max, fin, fout;
> +	unsigned int min_prediv, max_prediv;
> +	unsigned int _prediv, uninitialized_var(best_prediv);
> +	unsigned long _fbdiv, uninitialized_var(best_fbdiv);
> +	unsigned long min_delta = ULONG_MAX;
>  
>  	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
>  	if (bpp < 0) {
> @@ -629,35 +642,53 @@ static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi,
>  				      "DPHY clock frequency is out of range\n");
>  	}
>  
> -	pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC);
> -	tmp = pllref;
> -
> -	/*
> -	 * The limits on the PLL divisor are:
> -	 *
> -	 *	5MHz <= (pllref / n) <= 40MHz
> -	 *
> -	 * we walk over these values in descreasing order so that if we hit
> -	 * an exact match for target_mbps it is more likely that "m" will be
> -	 * even.
> -	 *
> -	 * TODO: ensure that "m" is even after this loop.
> -	 */
> -	for (i = pllref / 5; i > (pllref / 40); i--) {
> -		pre = pllref / i;
> -		if ((tmp > (target_mbps % pre)) && (target_mbps / pre < 512)) {
> -			tmp = target_mbps % pre;
> -			n = i;
> -			m = target_mbps / pre;
> +	fin = clk_get_rate(dsi->pllref_clk);
> +	fout = target_mbps * USEC_PER_SEC;
> +
> +	/* constraint: 5Mhz <= Fref / N <= 40MHz */
> +	min_prediv = DIV_ROUND_UP(fin, 40 * USEC_PER_SEC);
> +	max_prediv = fin / (5 * USEC_PER_SEC);
> +
> +	/* constraint: 80MHz <= Fvco <= 1500Mhz */
> +	fvco_min = 80 * USEC_PER_SEC;
> +	fvco_max = 1500 * USEC_PER_SEC;
> +
> +	for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
> +		u64 tmp;
> +		u32 delta;
> +		/* Fvco = Fref * M / N */
> +		tmp = (u64)fout * _prediv;
> +		do_div(tmp, fin);
> +		_fbdiv = tmp;
> +		/*
> +		 * Due to the use of a "by 2 pre-scaler," the range of the
> +		 * feedback multiplication value M is limited to even division
> +		 * numbers, and m must be greater than 12, less than 1000.
> +		 */
> +		if (_fbdiv <= 12 || _fbdiv >= 1000)
> +			continue;
> +
> +		_fbdiv += _fbdiv % 2;
> +
> +		tmp = (u64)_fbdiv * fin;
> +		do_div(tmp, _prediv);
> +		if (tmp < fvco_min || tmp > fvco_max)
> +			continue;
> +
> +		delta = abs(fout - tmp);
> +		if (delta < min_delta) {
> +			best_prediv = _prediv;
> +			best_fbdiv = _fbdiv;
> +			min_delta = delta;
> +			best_freq = tmp;
>  		}
> -		if (tmp == 0)
> -			break;
>  	}
> -
> -	dsi->lane_mbps = pllref / n * m;
> -	dsi->input_div = n;
> -	dsi->feedback_div = m;
> -
> +	if (best_freq) {
> +		dsi->lane_mbps = DIV_ROUND_UP(best_freq, USEC_PER_SEC);
> +		dsi->input_div = best_prediv;
> +		dsi->feedback_div = best_fbdiv;
> +	} else
> +		DRM_DEV_ERROR(dsi->dev, "Can not find best_freq for DPHY\n");
>  	return 0;
>  }
>  
> -- 
> 1.9.1
>
Brian Norris Oct. 26, 2017, 1:09 a.m. UTC | #2
On Wed, Oct 25, 2017 at 03:57:19AM -0400, Sean Paul wrote:
> Archit asked a question about moving to
> dw-mipi-dsi

That question made me think though: this approach seems backwards. It
seems like someone did copy/paste/fork, and then we're asking the
authors of the original driver to un-fork? It seems like this should
happen the other way around -- those trying to support a new incarnation
should have looked to try to abstract the original driver for their
uses first.

IIUC, that's exactly what Rockchip did for much of their Analogix eDP
code -- they reworked the Exynos DP driver to split common Analogix code
from any Exynos-specific bits.

And actually, the current stuff in
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c is completely unused. It
exports some functions, but I see no users of it. Is that intended? Is
somebody already working on refactoring existing Rockchip code to use
this?

Brian
Archit Taneja Oct. 26, 2017, 4:13 a.m. UTC | #3
Hi,

On 10/26/2017 06:39 AM, Brian Norris wrote:
> On Wed, Oct 25, 2017 at 03:57:19AM -0400, Sean Paul wrote:
>> Archit asked a question about moving to
>> dw-mipi-dsi
> 
> That question made me think though: this approach seems backwards. It
> seems like someone did copy/paste/fork, and then we're asking the
> authors of the original driver to un-fork? It seems like this should
> happen the other way around -- those trying to support a new incarnation
> should have looked to try to abstract the original driver for their
> uses first.

Yes, ST wanted to replicate rockchip's version of the mipi DSI driver and
put it in their folder. If they did that, their KMS driver would have been
the third driver to implement a third instance of the DW DSI controller driver.
Hisilicon and Rockchip being the other 2.

It was either that or attempt at a common DSI DW bridge driver. I suggested
the latter.

The ST guys have abstracted out the PHY pieces, which they knew varied between
rockchip and ST. Ideally, they should have also tried to create a RFC patch to
make the rockchip driver use the bridge too. But they didn't do that, and
the rockchip or hisilicon people were interested in even looking at it,
even after I CC'ed them.

> 
> IIUC, that's exactly what Rockchip did for much of their Analogix eDP
> code -- they reworked the Exynos DP driver to split common Analogix code
> from any Exynos-specific bits.

I get that. I had hoped either ST or Rockchip guys would have done the similar
thing, but no one volunteered.

> 
> And actually, the current stuff in
> drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c is completely unused. It
> exports some functions, but I see no users of it. Is that intended? Is

The ST kms driver uses it:

drivers/gpu/drm/stm/dw_mipi_dsi-stm.c

> somebody already working on refactoring existing Rockchip code to use
> this?

I don't know. If rockchip isn't interested in doing it, we can check with
Philippe from ST if he can try creating a RFC that converts the rockchip
driver to use the dw-mipi-dsi driver.

Thanks,
Archit
Philippe CORNU Oct. 26, 2017, 9:44 a.m. UTC | #4
Hi,

On 10/26/2017 06:13 AM, Archit Taneja wrote:
> Hi,
> 
> On 10/26/2017 06:39 AM, Brian Norris wrote:
>> On Wed, Oct 25, 2017 at 03:57:19AM -0400, Sean Paul wrote:
>>> Archit asked a question about moving to
>>> dw-mipi-dsi
>>
>> That question made me think though: this approach seems backwards. It
>> seems like someone did copy/paste/fork, and then we're asking the
>> authors of the original driver to un-fork? It seems like this should
>> happen the other way around -- those trying to support a new incarnation
>> should have looked to try to abstract the original driver for their
>> uses first.
> 
> Yes, ST wanted to replicate rockchip's version of the mipi DSI driver and
> put it in their folder. If they did that, their KMS driver would have been
> the third driver to implement a third instance of the DW DSI controller 
> driver.
> Hisilicon and Rockchip being the other 2.
> 
> It was either that or attempt at a common DSI DW bridge driver. I suggested
> the latter.
> 
> The ST guys have abstracted out the PHY pieces, which they knew varied 
> between
> rockchip and ST. Ideally, they should have also tried to create a RFC 
> patch to
> make the rockchip driver use the bridge too. But they didn't do that, and
> the rockchip or hisilicon people were interested in even looking at it,
> even after I CC'ed them.
> 
>>
>> IIUC, that's exactly what Rockchip did for much of their Analogix eDP
>> code -- they reworked the Exynos DP driver to split common Analogix code
>> from any Exynos-specific bits.
> 
> I get that. I had hoped either ST or Rockchip guys would have done the 
> similar
> thing, but no one volunteered.
> 
>>
>> And actually, the current stuff in
>> drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c is completely unused. It
>> exports some functions, but I see no users of it. Is that intended? Is
> 
> The ST kms driver uses it:
> 
> drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
> 

I confirm STM32 chipsets use the Synopsys dw dsi bridge driver.

I plan to improve this bridge driver by adding new features (see todos + 
dsi read, command mode with bta & gpio...).

For the first commit, I did my best to keep the source code as close as 
possible to the Rockchip version, in order to ease the port for Rockchip 
guys.

>> somebody already working on refactoring existing Rockchip code to use
>> this?
> 
> I don't know. If rockchip isn't interested in doing it, we can check with
> Philippe from ST if he can try creating a RFC that converts the rockchip
> driver to use the dw-mipi-dsi driver.

I am not really interested in doing this port for Rockchip (or Hisilicon 
or i.MX...) but happy to help anyone that wants to use the dw-mipi-dsi 
bridge driver :)

Many thanks,
Philippe :)

> 
> Thanks,
> Archit
>
Brian Norris Oct. 26, 2017, 9:32 p.m. UTC | #5
(correction zyw's email ".comg" is not a TLD!)

Hi,

On Thu, Oct 26, 2017 at 09:44:14AM +0000, Philippe CORNU wrote:
> On 10/26/2017 06:13 AM, Archit Taneja wrote:
> > On 10/26/2017 06:39 AM, Brian Norris wrote:
> >> On Wed, Oct 25, 2017 at 03:57:19AM -0400, Sean Paul wrote:
> >>> Archit asked a question about moving to
> >>> dw-mipi-dsi
> >>
> >> That question made me think though: this approach seems backwards. It
> >> seems like someone did copy/paste/fork, and then we're asking the
> >> authors of the original driver to un-fork? It seems like this should
> >> happen the other way around -- those trying to support a new incarnation
> >> should have looked to try to abstract the original driver for their
> >> uses first.
> > 
> > Yes, ST wanted to replicate rockchip's version of the mipi DSI driver and
> > put it in their folder. If they did that, their KMS driver would have been
> > the third driver to implement a third instance of the DW DSI controller 
> > driver.
> > Hisilicon and Rockchip being the other 2.

I hadn't noticed Hisilicon's version. That's an unfortunate start :(

> > It was either that or attempt at a common DSI DW bridge driver. I suggested
> > the latter.
> > 
> > The ST guys have abstracted out the PHY pieces, which they knew varied 
> > between
> > rockchip and ST. Ideally, they should have also tried to create a RFC 
> > patch to
> > make the rockchip driver use the bridge too. But they didn't do that, and
> > the rockchip or hisilicon people were interested in even looking at it,
> > even after I CC'ed them.

I see. At least the current code from Philippe isn't that big, and it is
indeed fairly similar.

But I still think the way to get cooperation upstream is to enforce it;
so there was a 3rd option to the above -- don't merge *any* new driver
without posting at least an attempt to unify the existing drivers.

> >> IIUC, that's exactly what Rockchip did for much of their Analogix eDP
> >> code -- they reworked the Exynos DP driver to split common Analogix code
> >> from any Exynos-specific bits.
> > 
> > I get that. I had hoped either ST or Rockchip guys would have done the 
> > similar
> > thing, but no one volunteered.

:(

Nickey, can you confirm that you or someone on your team will look at
utilizing the common driver? Please reply to this email thread before
sending another version of this series.

> >> And actually, the current stuff in
> >> drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c is completely unused. It
> >> exports some functions, but I see no users of it. Is that intended? Is
> > 
> > The ST kms driver uses it:
> > 
> > drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
> > 
> 
> I confirm STM32 chipsets use the Synopsys dw dsi bridge driver.
> 
> I plan to improve this bridge driver by adding new features (see todos + 
> dsi read, command mode with bta & gpio...).
> 
> For the first commit, I did my best to keep the source code as close as 
> possible to the Rockchip version, in order to ease the port for Rockchip 
> guys.

That's nice to see, even if it still isn't ideal.

> >> somebody already working on refactoring existing Rockchip code to use
> >> this?
> > 
> > I don't know. If rockchip isn't interested in doing it, we can check with
> > Philippe from ST if he can try creating a RFC that converts the rockchip
> > driver to use the dw-mipi-dsi driver.
> 
> I am not really interested in doing this port for Rockchip (or Hisilicon 
> or i.MX...) but happy to help anyone that wants to use the dw-mipi-dsi 
> bridge driver :)

Well, see my comments above. Your "interest" is obviously in merging
code to support your own IP, but the community can *make* it your
interest by requiring you do the unification before your code goes
upstream. Apparently that's not the policy here though.

Brian
Brian Norris Nov. 28, 2017, 12:29 a.m. UTC | #6
On Thu, Oct 26, 2017 at 09:44:14AM +0000, Philippe CORNU wrote:
> On 10/26/2017 06:13 AM, Archit Taneja wrote:
> > On 10/26/2017 06:39 AM, Brian Norris wrote:
> >> somebody already working on refactoring existing Rockchip code to use
> >> this?
> > 
> > I don't know. If rockchip isn't interested in doing it, we can check with
> > Philippe from ST if he can try creating a RFC that converts the rockchip
> > driver to use the dw-mipi-dsi driver.
> 
> I am not really interested in doing this port for Rockchip (or Hisilicon 
> or i.MX...) but happy to help anyone that wants to use the dw-mipi-dsi 
> bridge driver :)

Ugh, this stuff is worse than I expected. I'm helping Rockchip along
with getting this rewritten, but there are some hiccups.

Among other things, the bridge driver is assuming it can set the
device's drvdata itself. This works because the STM MIPI driver is
simple, but the Rockchip one registers stuff via component_add(), and so
it *needs* to handle drvdata between probe() and bind()...but then the
"common" bridge driver is going to clobber it (dev_set_drvdata()).

Along the way, I'm noticing that the STM driver just steps around this
at times by referencing a static (!!) instance of its priv_data. See:

static int dw_mipi_dsi_stm_remove(struct platform_device *pdev)
{
	struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
...


I might rewrite this, but it's not fun to have to fix somebody else's
fork for them...

Brian
Brian Norris Nov. 28, 2017, 12:34 a.m. UTC | #7
(Dropping Mark, whose Rockchip address is dead; and fixing Chris's email again)

On Mon, Nov 27, 2017 at 4:29 PM, Brian Norris <briannorris@chromium.org> wrote:
> On Thu, Oct 26, 2017 at 09:44:14AM +0000, Philippe CORNU wrote:
>> On 10/26/2017 06:13 AM, Archit Taneja wrote:
>> > On 10/26/2017 06:39 AM, Brian Norris wrote:
>> >> somebody already working on refactoring existing Rockchip code to use
>> >> this?
>> >
>> > I don't know. If rockchip isn't interested in doing it, we can check with
>> > Philippe from ST if he can try creating a RFC that converts the rockchip
>> > driver to use the dw-mipi-dsi driver.
>>
>> I am not really interested in doing this port for Rockchip (or Hisilicon
>> or i.MX...) but happy to help anyone that wants to use the dw-mipi-dsi
>> bridge driver :)
>
> Ugh, this stuff is worse than I expected. I'm helping Rockchip along
> with getting this rewritten, but there are some hiccups.
>
> Among other things, the bridge driver is assuming it can set the
> device's drvdata itself. This works because the STM MIPI driver is
> simple, but the Rockchip one registers stuff via component_add(), and so
> it *needs* to handle drvdata between probe() and bind()...but then the
> "common" bridge driver is going to clobber it (dev_set_drvdata()).
>
> Along the way, I'm noticing that the STM driver just steps around this
> at times by referencing a static (!!) instance of its priv_data. See:
>
> static int dw_mipi_dsi_stm_remove(struct platform_device *pdev)
> {
>         struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
> ...
>
>
> I might rewrite this, but it's not fun to have to fix somebody else's
> fork for them...
>
> Brian
diff mbox

Patch

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index 09e7bfe..589b420 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -239,7 +239,7 @@ 
 #define LOW_PROGRAM_EN		0
 #define HIGH_PROGRAM_EN		BIT(7)
 #define LOOP_DIV_LOW_SEL(val)	(((val) - 1) & 0x1f)
-#define LOOP_DIV_HIGH_SEL(val)	((((val) - 1) >> 5) & 0x1f)
+#define LOOP_DIV_HIGH_SEL(val)	((((val) - 1) >> 5) & 0xf)
 #define PLL_LOOP_DIV_EN		BIT(5)
 #define PLL_INPUT_DIV_EN	BIT(4)
 
@@ -531,6 +531,14 @@  static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
 	dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
 			      LOOP_DIV_LOW_SEL(dsi->feedback_div) |
 			      LOW_PROGRAM_EN);
+	/*
+	 * we need set PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL immediately
+	 * to make the configrued LSB effective according to IP simulation
+	 * and lab test results.
+	 * Only in this way can we get correct mipi phy pll frequency.
+	 */
+	dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
+			      PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
 	dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
 			      LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
 			      HIGH_PROGRAM_EN);
@@ -604,11 +612,16 @@  static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
 static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi,
 				    struct drm_display_mode *mode)
 {
-	unsigned int i, pre;
-	unsigned long mpclk, pllref, tmp;
-	unsigned int m = 1, n = 1, target_mbps = 1000;
+	unsigned long mpclk, tmp;
+	unsigned int target_mbps = 1000;
 	unsigned int max_mbps = dppa_map[ARRAY_SIZE(dppa_map) - 1].max_mbps;
 	int bpp;
+	unsigned long best_freq = 0;
+	unsigned long fvco_min, fvco_max, fin, fout;
+	unsigned int min_prediv, max_prediv;
+	unsigned int _prediv, uninitialized_var(best_prediv);
+	unsigned long _fbdiv, uninitialized_var(best_fbdiv);
+	unsigned long min_delta = ULONG_MAX;
 
 	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
 	if (bpp < 0) {
@@ -629,35 +642,53 @@  static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi,
 				      "DPHY clock frequency is out of range\n");
 	}
 
-	pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC);
-	tmp = pllref;
-
-	/*
-	 * The limits on the PLL divisor are:
-	 *
-	 *	5MHz <= (pllref / n) <= 40MHz
-	 *
-	 * we walk over these values in descreasing order so that if we hit
-	 * an exact match for target_mbps it is more likely that "m" will be
-	 * even.
-	 *
-	 * TODO: ensure that "m" is even after this loop.
-	 */
-	for (i = pllref / 5; i > (pllref / 40); i--) {
-		pre = pllref / i;
-		if ((tmp > (target_mbps % pre)) && (target_mbps / pre < 512)) {
-			tmp = target_mbps % pre;
-			n = i;
-			m = target_mbps / pre;
+	fin = clk_get_rate(dsi->pllref_clk);
+	fout = target_mbps * USEC_PER_SEC;
+
+	/* constraint: 5Mhz <= Fref / N <= 40MHz */
+	min_prediv = DIV_ROUND_UP(fin, 40 * USEC_PER_SEC);
+	max_prediv = fin / (5 * USEC_PER_SEC);
+
+	/* constraint: 80MHz <= Fvco <= 1500Mhz */
+	fvco_min = 80 * USEC_PER_SEC;
+	fvco_max = 1500 * USEC_PER_SEC;
+
+	for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
+		u64 tmp;
+		u32 delta;
+		/* Fvco = Fref * M / N */
+		tmp = (u64)fout * _prediv;
+		do_div(tmp, fin);
+		_fbdiv = tmp;
+		/*
+		 * Due to the use of a "by 2 pre-scaler," the range of the
+		 * feedback multiplication value M is limited to even division
+		 * numbers, and m must be greater than 12, less than 1000.
+		 */
+		if (_fbdiv <= 12 || _fbdiv >= 1000)
+			continue;
+
+		_fbdiv += _fbdiv % 2;
+
+		tmp = (u64)_fbdiv * fin;
+		do_div(tmp, _prediv);
+		if (tmp < fvco_min || tmp > fvco_max)
+			continue;
+
+		delta = abs(fout - tmp);
+		if (delta < min_delta) {
+			best_prediv = _prediv;
+			best_fbdiv = _fbdiv;
+			min_delta = delta;
+			best_freq = tmp;
 		}
-		if (tmp == 0)
-			break;
 	}
-
-	dsi->lane_mbps = pllref / n * m;
-	dsi->input_div = n;
-	dsi->feedback_div = m;
-
+	if (best_freq) {
+		dsi->lane_mbps = DIV_ROUND_UP(best_freq, USEC_PER_SEC);
+		dsi->input_div = best_prediv;
+		dsi->feedback_div = best_fbdiv;
+	} else
+		DRM_DEV_ERROR(dsi->dev, "Can not find best_freq for DPHY\n");
 	return 0;
 }