Message ID | 20180820214941.22236-1-laurent.pinchart+renesas@ideasonboard.com (mailing list archive) |
---|---|
State | Not Applicable |
Delegated to: | Geert Uytterhoeven |
Headers | show |
Series | drm: rcar-du: Improve non-DPLL clock selection | expand |
Hi Jacopo, On Tuesday, 21 August 2018 00:49:41 EEST Laurent Pinchart wrote: > From: Jacopo Mondi <jacopo@jmondi.org> > > DU channels not equipped with a DPLL use an SoC internal (provided by > the CPG) or external clock source combined with a DU internal divider to > generate the desired output dot clock frequency. > > The current clock selection procedure does not fully exploit the ability > of external clock sources to generate the exact dot clock frequency by > themselves, but relies instead on tuning the internal DU clock divider > only, resulting in a less precise clock generation process. > > When possible, and desirable, ask the external clock source for the > exact output dot clock frequency, and select the clock source that > produces the frequency closest to the desired output dot clock. > > This patch specifically targets platforms (like Salvator-X[S] and ULCBs) > where the DU's input dotclock.in is generated by the versaclock VC5 > clock source, which is capable of generating the exact rate the DU needs > as pixel clock output. > > This patch fixes higher resolution modes which requires an high pixel > clock output currently not working on non-HDMI DU channel (such as > 1920x1080@60Hz on the VGA output). Just for the record, with this patch the following modes (as printed by modetest) on the VGA output now produce correct result with my monitor: 1920x1080 60 1920 2008 2052 2200 1080 1084 1089 1125 flags: nhsync, nvsync 1600x1200 60 1600 1664 1856 2160 1200 1201 1204 1250 flags: phsync, pvsync 1360x768 60 1360 1424 1536 1792 768 771 777 795 flags: phsync, pvsync The second mode used to not display at all, with a message telling that timings were out of range, and the other two modes used to produce a displayed image partly shifted or scaled out of the screen boundaries. The following modes still produce an image partly out of the screen boundaries. 1600x900 60 1600 1624 1704 1800 900 901 904 1000 flags: phsync, pvsync 1280x960 60 1280 1376 1488 1800 960 961 964 1000 flags: phsync, pvsync 1366x768 60 1366 1436 1579 1792 768 771 774 798 flags: phsync, pvsync 1366x768 60 1366 1380 1436 1500 768 769 772 800 flags: phsync, pvsync 1280x720 60 1280 1390 1430 1650 720 725 730 750 flags: phsync, pvsync And this one is reported to be out of range. 1920x1200 60 1920 2056 2256 2592 1200 1203 1209 1245 flags: nhsync, pvsync There is thus still room for improvement (some of the issues are possibly due to my monitor though), but there's also an improvement, and no noticeable regression. > Fixes: 1b30dbde8596 ("drm: rcar-du: Add support for external pixel clock") > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> > [Factor out code to a helper function] > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> > --- > drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 86 ++++++++++++++++++++---------- > 1 file changed, 55 insertions(+), 31 deletions(-) > > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index f8068170905a..2c9405458bbf > 100644 > --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > @@ -165,6 +165,48 @@ static void rcar_du_dpll_divider(struct rcar_du_crtc > *rcrtc, best_diff); > } > > +struct du_clk_params { > + struct clk *clk; > + unsigned long rate; > + unsigned long diff; > + u32 escr; > +}; > + > +static void rcar_du_escr_divider(struct rcar_du_crtc *rcrtc, struct clk > *clk, + unsigned long target, u32 escr, > + struct du_clk_params *params) > +{ > + unsigned long rate; > + unsigned long diff; > + u32 div; > + > + /* > + * If the target rate has already been achieved perfectly we can't do > + * better. > + */ > + if (params->diff == 0) > + return; > + > + /* > + * Compute the input clock rate and internal divisor values to obtain > + * the clock rate closest to the target frequency. > + */ > + rate = clk_round_rate(clk, target); > + div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1; > + diff = abs(rate / (div + 1) - target); > + > + /* > + * If the resulting frequency is better than any previously obtained, > + * store the parameters. > + */ > + if (diff < params->diff) { > + params->clk = clk; > + params->rate = rate; > + params->diff = diff; > + params->escr = escr | div; > + } > +} > + > static const struct soc_device_attribute rcar_du_r8a7795_es1[] = { > { .soc_id = "r8a7795", .revision = "ES1.*" }, > { /* sentinel */ } > @@ -225,42 +267,24 @@ static void rcar_du_crtc_set_display_timing(struct > rcar_du_crtc *rcrtc) > > escr = ESCR_DCLKSEL_DCLKIN | div; > } else { > - unsigned long clk; > - u32 div; > - > - /* > - * Compute the clock divisor and select the internal or external > - * dot clock based on the requested frequency. > - */ > - clk = clk_get_rate(rcrtc->clock); > - div = DIV_ROUND_CLOSEST(clk, mode_clock); > - div = clamp(div, 1U, 64U) - 1; > - > - escr = ESCR_DCLKSEL_CLKS | div; > - > - if (rcrtc->extclock) { > - unsigned long extclk; > - unsigned long extrate; > - unsigned long rate; > - u32 extdiv; > + struct du_clk_params params = { .diff = (unsigned long)-1 }; > > - extclk = clk_get_rate(rcrtc->extclock); > - extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); > - extdiv = clamp(extdiv, 1U, 64U) - 1; > + rcar_du_escr_divider(rcrtc, rcrtc->clock, mode_clock, > + ESCR_DCLKSEL_CLKS, ¶ms); > + if (rcrtc->extclock) > + rcar_du_escr_divider(rcrtc, rcrtc->extclock, mode_clock, > + ESCR_DCLKSEL_DCLKIN, ¶ms); > > - extrate = extclk / (extdiv + 1); > - rate = clk / (div + 1); > + dev_dbg(rcrtc->group->dev->dev, "mode clock %lu %s rate %lu\n", > + mode_clock, params.clk == rcrtc->clock ? "cpg" : "ext", > + params.rate); > > - if (abs((long)extrate - (long)mode_clock) < > - abs((long)rate - (long)mode_clock)) > - escr = ESCR_DCLKSEL_DCLKIN | extdiv; > - > - dev_dbg(rcrtc->group->dev->dev, > - "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", > - mode_clock, extrate, rate, escr); > - } > + clk_set_rate(params.clk, params.rate); > + escr = params.escr; > } > > + dev_dbg(rcrtc->group->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr); > + > rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, > escr); > rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
HI Laurent, thanks for the patch rework On Tue, Aug 21, 2018 at 01:12:40AM +0300, Laurent Pinchart wrote: > Hi Jacopo, > > On Tuesday, 21 August 2018 00:49:41 EEST Laurent Pinchart wrote: > > From: Jacopo Mondi <jacopo@jmondi.org> > > > > DU channels not equipped with a DPLL use an SoC internal (provided by > > the CPG) or external clock source combined with a DU internal divider to > > generate the desired output dot clock frequency. > > > > The current clock selection procedure does not fully exploit the ability > > of external clock sources to generate the exact dot clock frequency by > > themselves, but relies instead on tuning the internal DU clock divider > > only, resulting in a less precise clock generation process. > > > > When possible, and desirable, ask the external clock source for the > > exact output dot clock frequency, and select the clock source that > > produces the frequency closest to the desired output dot clock. > > > > This patch specifically targets platforms (like Salvator-X[S] and ULCBs) > > where the DU's input dotclock.in is generated by the versaclock VC5 > > clock source, which is capable of generating the exact rate the DU needs > > as pixel clock output. > > > > This patch fixes higher resolution modes which requires an high pixel > > clock output currently not working on non-HDMI DU channel (such as > > 1920x1080@60Hz on the VGA output). > > Just for the record, with this patch the following modes (as printed by > modetest) on the VGA output now produce correct result with my monitor: > > 1920x1080 60 1920 2008 2052 2200 1080 1084 1089 1125 flags: nhsync, nvsync > 1600x1200 60 1600 1664 1856 2160 1200 1201 1204 1250 flags: phsync, pvsync > 1360x768 60 1360 1424 1536 1792 768 771 777 795 flags: phsync, pvsync > > The second mode used to not display at all, with a message telling that > timings were out of range, and the other two modes used to produce a displayed > image partly shifted or scaled out of the screen boundaries. > > The following modes still produce an image partly out of the screen > boundaries. > > 1600x900 60 1600 1624 1704 1800 900 901 904 1000 flags: phsync, pvsync > 1280x960 60 1280 1376 1488 1800 960 961 964 1000 flags: phsync, pvsync > 1366x768 60 1366 1436 1579 1792 768 771 774 798 flags: phsync, pvsync > 1366x768 60 1366 1380 1436 1500 768 769 772 800 flags: phsync, pvsync > 1280x720 60 1280 1390 1430 1650 720 725 730 750 flags: phsync, pvsync > > And this one is reported to be out of range. > > 1920x1200 60 1920 2056 2256 2592 1200 1203 1209 1245 flags: nhsync, pvsync > > There is thus still room for improvement (some of the issues are possibly due > to my monitor though), but there's also an improvement, and no noticeable > regression. > > > Fixes: 1b30dbde8596 ("drm: rcar-du: Add support for external pixel clock") > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> > > [Factor out code to a helper function] > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> > > --- > > drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 86 ++++++++++++++++++++---------- > > 1 file changed, 55 insertions(+), 31 deletions(-) > > > > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > > b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index f8068170905a..2c9405458bbf > > 100644 > > --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > > +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > > @@ -165,6 +165,48 @@ static void rcar_du_dpll_divider(struct rcar_du_crtc > > *rcrtc, best_diff); > > } > > > > +struct du_clk_params { > > + struct clk *clk; > > + unsigned long rate; > > + unsigned long diff; > > + u32 escr; > > +}; > > + > > +static void rcar_du_escr_divider(struct rcar_du_crtc *rcrtc, struct clk > > *clk, + unsigned long target, u32 escr, > > + struct du_clk_params *params) I don't see the rcrtc parameter ever being used in this function. Do you want to keep it anyhow? > > +{ > > + unsigned long rate; > > + unsigned long diff; > > + u32 div; > > + > > + /* > > + * If the target rate has already been achieved perfectly we can't do > > + * better. > > + */ > > + if (params->diff == 0) > > + return; > > + > > + /* > > + * Compute the input clock rate and internal divisor values to obtain > > + * the clock rate closest to the target frequency. > > + */ > > + rate = clk_round_rate(clk, target); > > + div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1; > > + diff = abs(rate / (div + 1) - target); > > + > > + /* > > + * If the resulting frequency is better than any previously obtained, s/obtained,/obtained one,/ ? Will get back with some testing results on a different VGA monitor... Thanks j > > + * store the parameters. > > + */ > > + if (diff < params->diff) { > > + params->clk = clk; > > + params->rate = rate; > > + params->diff = diff; > > + params->escr = escr | div; > > + } > > +} > > + > > static const struct soc_device_attribute rcar_du_r8a7795_es1[] = { > > { .soc_id = "r8a7795", .revision = "ES1.*" }, > > { /* sentinel */ } > > @@ -225,42 +267,24 @@ static void rcar_du_crtc_set_display_timing(struct > > rcar_du_crtc *rcrtc) > > > > escr = ESCR_DCLKSEL_DCLKIN | div; > > } else { > > - unsigned long clk; > > - u32 div; > > - > > - /* > > - * Compute the clock divisor and select the internal or external > > - * dot clock based on the requested frequency. > > - */ > > - clk = clk_get_rate(rcrtc->clock); > > - div = DIV_ROUND_CLOSEST(clk, mode_clock); > > - div = clamp(div, 1U, 64U) - 1; > > - > > - escr = ESCR_DCLKSEL_CLKS | div; > > - > > - if (rcrtc->extclock) { > > - unsigned long extclk; > > - unsigned long extrate; > > - unsigned long rate; > > - u32 extdiv; > > + struct du_clk_params params = { .diff = (unsigned long)-1 }; > > > > - extclk = clk_get_rate(rcrtc->extclock); > > - extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); > > - extdiv = clamp(extdiv, 1U, 64U) - 1; > > + rcar_du_escr_divider(rcrtc, rcrtc->clock, mode_clock, > > + ESCR_DCLKSEL_CLKS, ¶ms); > > + if (rcrtc->extclock) > > + rcar_du_escr_divider(rcrtc, rcrtc->extclock, mode_clock, > > + ESCR_DCLKSEL_DCLKIN, ¶ms); > > > > - extrate = extclk / (extdiv + 1); > > - rate = clk / (div + 1); > > + dev_dbg(rcrtc->group->dev->dev, "mode clock %lu %s rate %lu\n", > > + mode_clock, params.clk == rcrtc->clock ? "cpg" : "ext", > > + params.rate); > > > > - if (abs((long)extrate - (long)mode_clock) < > > - abs((long)rate - (long)mode_clock)) > > - escr = ESCR_DCLKSEL_DCLKIN | extdiv; > > - > > - dev_dbg(rcrtc->group->dev->dev, > > - "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", > > - mode_clock, extrate, rate, escr); > > - } > > + clk_set_rate(params.clk, params.rate); > > + escr = params.escr; > > } > > > > + dev_dbg(rcrtc->group->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr); > > + > > rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, > > escr); > > rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); > > -- > Regards, > > Laurent Pinchart > > >
Hi Jacopo, (CC'ing Kieran) On Tuesday, 21 August 2018 10:33:57 EEST jacopo mondi wrote: > On Tue, Aug 21, 2018 at 01:12:40AM +0300, Laurent Pinchart wrote: > > On Tuesday, 21 August 2018 00:49:41 EEST Laurent Pinchart wrote: > >> From: Jacopo Mondi <jacopo@jmondi.org> > >> > >> DU channels not equipped with a DPLL use an SoC internal (provided by > >> the CPG) or external clock source combined with a DU internal divider to > >> generate the desired output dot clock frequency. > >> > >> The current clock selection procedure does not fully exploit the ability > >> of external clock sources to generate the exact dot clock frequency by > >> themselves, but relies instead on tuning the internal DU clock divider > >> only, resulting in a less precise clock generation process. > >> > >> When possible, and desirable, ask the external clock source for the > >> exact output dot clock frequency, and select the clock source that > >> produces the frequency closest to the desired output dot clock. > >> > >> This patch specifically targets platforms (like Salvator-X[S] and ULCBs) > >> where the DU's input dotclock.in is generated by the versaclock VC5 > >> clock source, which is capable of generating the exact rate the DU needs > >> as pixel clock output. > >> > >> This patch fixes higher resolution modes which requires an high pixel > >> clock output currently not working on non-HDMI DU channel (such as > >> 1920x1080@60Hz on the VGA output). > > > > Just for the record, with this patch the following modes (as printed by > > > > modetest) on the VGA output now produce correct result with my monitor: > > 1920x1080 60 1920 2008 2052 2200 1080 1084 1089 1125 flags: nhsync, > > nvsync > > 1600x1200 60 1600 1664 1856 2160 1200 1201 1204 1250 flags: phsync, > > pvsync > > 1360x768 60 1360 1424 1536 1792 768 771 777 795 flags: phsync, pvsync > > > > The second mode used to not display at all, with a message telling that > > timings were out of range, and the other two modes used to produce a > > displayed image partly shifted or scaled out of the screen boundaries. > > > > The following modes still produce an image partly out of the screen > > boundaries. > > > > 1600x900 60 1600 1624 1704 1800 900 901 904 1000 flags: phsync, pvsync > > 1280x960 60 1280 1376 1488 1800 960 961 964 1000 flags: phsync, pvsync > > 1366x768 60 1366 1436 1579 1792 768 771 774 798 flags: phsync, pvsync > > 1366x768 60 1366 1380 1436 1500 768 769 772 800 flags: phsync, pvsync > > 1280x720 60 1280 1390 1430 1650 720 725 730 750 flags: phsync, pvsync > > > > And this one is reported to be out of range. > > > > 1920x1200 60 1920 2056 2256 2592 1200 1203 1209 1245 flags: nhsync, > > pvsync > > > > There is thus still room for improvement (some of the issues are possibly > > due to my monitor though), but there's also an improvement, and no > > noticeable regression. > > > >> Fixes: 1b30dbde8596 ("drm: rcar-du: Add support for external pixel > >> clock") > >> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> > >> [Factor out code to a helper function] > >> Signed-off-by: Laurent Pinchart > >> <laurent.pinchart+renesas@ideasonboard.com> > >> --- > >> > >> drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 86 +++++++++++++++++++--------- > >> 1 file changed, 55 insertions(+), 31 deletions(-) > >> > >> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > >> b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index > >> f8068170905a..2c9405458bbf > >> 100644 > >> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > >> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > >> @@ -165,6 +165,48 @@ static void rcar_du_dpll_divider(struct > >> rcar_du_crtc *rcrtc, > >> best_diff); > >> } > >> > >> +struct du_clk_params { > >> + struct clk *clk; > >> + unsigned long rate; > >> + unsigned long diff; > >> + u32 escr; > >> +}; > >> + > >> +static void rcar_du_escr_divider(struct rcar_du_crtc *rcrtc, struct clk > >> *clk, > >> + unsigned long target, u32 escr, > >> + struct du_clk_params *params) > > I don't see the rcrtc parameter ever being used in this function. > Do you want to keep it anyhow? You're right, I'll remove it. > >> +{ > >> + unsigned long rate; > >> + unsigned long diff; > >> + u32 div; > >> + > >> + /* > >> + * If the target rate has already been achieved perfectly we can't do > >> + * better. > >> + */ > >> + if (params->diff == 0) > >> + return; > >> + > >> + /* > >> + * Compute the input clock rate and internal divisor values to obtain > >> + * the clock rate closest to the target frequency. > >> + */ > >> + rate = clk_round_rate(clk, target); > >> + div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1; > >> + diff = abs(rate / (div + 1) - target); > >> + > >> + /* > >> + * If the resulting frequency is better than any previously obtained, > > s/obtained,/obtained one,/ ? Any opinion from a native English speaker ? :-) > Will get back with some testing results on a different VGA monitor... Thank you. > >> + * store the parameters. > >> + */ > >> + if (diff < params->diff) { > >> + params->clk = clk; > >> + params->rate = rate; > >> + params->diff = diff; > >> + params->escr = escr | div; > >> + } > >> +} [snip]
Hi Laurent, I run some tests, and here below there's a summary of what I see On Tue, Aug 21, 2018 at 01:12:40AM +0300, Laurent Pinchart wrote: > Hi Jacopo, > > On Tuesday, 21 August 2018 00:49:41 EEST Laurent Pinchart wrote: > > From: Jacopo Mondi <jacopo@jmondi.org> > > > > DU channels not equipped with a DPLL use an SoC internal (provided by > > the CPG) or external clock source combined with a DU internal divider to > > generate the desired output dot clock frequency. > > > > The current clock selection procedure does not fully exploit the ability > > of external clock sources to generate the exact dot clock frequency by > > themselves, but relies instead on tuning the internal DU clock divider > > only, resulting in a less precise clock generation process. > > > > When possible, and desirable, ask the external clock source for the > > exact output dot clock frequency, and select the clock source that > > produces the frequency closest to the desired output dot clock. > > > > This patch specifically targets platforms (like Salvator-X[S] and ULCBs) > > where the DU's input dotclock.in is generated by the versaclock VC5 > > clock source, which is capable of generating the exact rate the DU needs > > as pixel clock output. > > > > This patch fixes higher resolution modes which requires an high pixel > > clock output currently not working on non-HDMI DU channel (such as > > 1920x1080@60Hz on the VGA output). > > Just for the record, with this patch the following modes (as printed by > modetest) on the VGA output now produce correct result with my monitor: > > 1920x1080 60 1920 2008 2052 2200 1080 1084 1089 1125 flags: nhsync, nvsync > 1600x1200 60 1600 1664 1856 2160 1200 1201 1204 1250 flags: phsync, pvsync > 1360x768 60 1360 1424 1536 1792 768 771 777 795 flags: phsync, pvsync > > The second mode used to not display at all, with a message telling that > timings were out of range, and the other two modes used to produce a displayed > image partly shifted or scaled out of the screen boundaries. > > The following modes still produce an image partly out of the screen > boundaries. > > 1600x900 60 1600 1624 1704 1800 900 901 904 1000 flags: phsync, pvsync > 1280x960 60 1280 1376 1488 1800 960 961 964 1000 flags: phsync, pvsync > 1366x768 60 1366 1436 1579 1792 768 771 774 798 flags: phsync, pvsync > 1366x768 60 1366 1380 1436 1500 768 769 772 800 flags: phsync, pvsync > 1280x720 60 1280 1390 1430 1650 720 725 730 750 flags: phsync, pvsync > > And this one is reported to be out of range. > > 1920x1200 60 1920 2056 2256 2592 1200 1203 1209 1245 flags: nhsync, pvsync > > There is thus still room for improvement (some of the issues are possibly due > to my monitor though), but there's also an improvement, and no noticeable > regression. The following table compares results obtained with the latest renesas-drivers, with and without this series applied on top. ------------------------------------------------------------------------------- Legend: A = image badly aligned: not all 4 blue/red borders visible F = flickering: disturbance in the shown image B = broken: mode is not displayed Results: I = improvement R = regression renesas-drivers du_clk result 1024x768 1920x1200 F A I 1920x1200 A A 1920x1080 A A 1600x1200 B I 1680x1050 1680x1050 1400x1050 1400x1050 1600x900 A A 1280x1024 1440x900 1440x900 1280x960 1366x768 A A 1366x768 A A 1360x768 A R 1280x800 1280x768 A A 1280x768 A A 1280x768 A A 1280x720 A A 800x600 800x600 848x480 640x480 ------------------------------------------------------------------------------- Overall I see two modes that were broken or unusable due to flickering (1600x1200 and 1920x1200 respectively) to be now (almost) fixed. There are visible alignement problems on some modes on both versions, but I only see one 'regression' (the last 1360x768 that is now slightly not aligned). I guess monitors play a role here, with each one being different, but overall I guess our test results match. > > > Fixes: 1b30dbde8596 ("drm: rcar-du: Add support for external pixel clock") > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> > > [Factor out code to a helper function] > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> > > --- > > drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 86 ++++++++++++++++++++---------- > > 1 file changed, 55 insertions(+), 31 deletions(-) > > > > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > > b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index f8068170905a..2c9405458bbf > > 100644 > > --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > > +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > > @@ -165,6 +165,48 @@ static void rcar_du_dpll_divider(struct rcar_du_crtc > > *rcrtc, best_diff); > > } > > > > +struct du_clk_params { > > + struct clk *clk; > > + unsigned long rate; > > + unsigned long diff; > > + u32 escr; > > +}; > > + > > +static void rcar_du_escr_divider(struct rcar_du_crtc *rcrtc, struct clk > > *clk, + unsigned long target, u32 escr, > > + struct du_clk_params *params) > > +{ > > + unsigned long rate; > > + unsigned long diff; > > + u32 div; > > + > > + /* > > + * If the target rate has already been achieved perfectly we can't do > > + * better. > > + */ > > + if (params->diff == 0) > > + return; > > + > > + /* > > + * Compute the input clock rate and internal divisor values to obtain > > + * the clock rate closest to the target frequency. > > + */ > > + rate = clk_round_rate(clk, target); > > + div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1; > > + diff = abs(rate / (div + 1) - target); > > + > > + /* > > + * If the resulting frequency is better than any previously obtained, > > + * store the parameters. > > + */ > > + if (diff < params->diff) { > > + params->clk = clk; > > + params->rate = rate; > > + params->diff = diff; > > + params->escr = escr | div; > > + } > > +} > > + > > static const struct soc_device_attribute rcar_du_r8a7795_es1[] = { > > { .soc_id = "r8a7795", .revision = "ES1.*" }, > > { /* sentinel */ } > > @@ -225,42 +267,24 @@ static void rcar_du_crtc_set_display_timing(struct > > rcar_du_crtc *rcrtc) > > > > escr = ESCR_DCLKSEL_DCLKIN | div; > > } else { > > - unsigned long clk; > > - u32 div; > > - > > - /* > > - * Compute the clock divisor and select the internal or external > > - * dot clock based on the requested frequency. > > - */ > > - clk = clk_get_rate(rcrtc->clock); > > - div = DIV_ROUND_CLOSEST(clk, mode_clock); > > - div = clamp(div, 1U, 64U) - 1; > > - > > - escr = ESCR_DCLKSEL_CLKS | div; > > - > > - if (rcrtc->extclock) { > > - unsigned long extclk; > > - unsigned long extrate; > > - unsigned long rate; > > - u32 extdiv; > > + struct du_clk_params params = { .diff = (unsigned long)-1 }; > > > > - extclk = clk_get_rate(rcrtc->extclock); > > - extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); > > - extdiv = clamp(extdiv, 1U, 64U) - 1; > > + rcar_du_escr_divider(rcrtc, rcrtc->clock, mode_clock, > > + ESCR_DCLKSEL_CLKS, ¶ms); > > + if (rcrtc->extclock) > > + rcar_du_escr_divider(rcrtc, rcrtc->extclock, mode_clock, > > + ESCR_DCLKSEL_DCLKIN, ¶ms); > > > > - extrate = extclk / (extdiv + 1); > > - rate = clk / (div + 1); > > + dev_dbg(rcrtc->group->dev->dev, "mode clock %lu %s rate %lu\n", > > + mode_clock, params.clk == rcrtc->clock ? "cpg" : "ext", > > + params.rate); > > > > - if (abs((long)extrate - (long)mode_clock) < > > - abs((long)rate - (long)mode_clock)) > > - escr = ESCR_DCLKSEL_DCLKIN | extdiv; > > - > > - dev_dbg(rcrtc->group->dev->dev, > > - "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", > > - mode_clock, extrate, rate, escr); > > - } > > + clk_set_rate(params.clk, params.rate); > > + escr = params.escr; > > } > > > > + dev_dbg(rcrtc->group->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr); > > + > > rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, > > escr); > > rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); > > -- > Regards, > > Laurent Pinchart > > >
And for the records, I forgot to add On Tue, Aug 21, 2018 at 12:35:48PM +0200, jacopo mondi wrote: > Hi Laurent, > I run some tests, and here below there's a summary of what I see > > On Tue, Aug 21, 2018 at 01:12:40AM +0300, Laurent Pinchart wrote: > > Hi Jacopo, > > > > On Tuesday, 21 August 2018 00:49:41 EEST Laurent Pinchart wrote: > > > From: Jacopo Mondi <jacopo@jmondi.org> > > > > > > DU channels not equipped with a DPLL use an SoC internal (provided by > > > the CPG) or external clock source combined with a DU internal divider to > > > generate the desired output dot clock frequency. > > > > > > The current clock selection procedure does not fully exploit the ability > > > of external clock sources to generate the exact dot clock frequency by > > > themselves, but relies instead on tuning the internal DU clock divider > > > only, resulting in a less precise clock generation process. > > > > > > When possible, and desirable, ask the external clock source for the > > > exact output dot clock frequency, and select the clock source that > > > produces the frequency closest to the desired output dot clock. > > > > > > This patch specifically targets platforms (like Salvator-X[S] and ULCBs) > > > where the DU's input dotclock.in is generated by the versaclock VC5 > > > clock source, which is capable of generating the exact rate the DU needs > > > as pixel clock output. > > > > > > This patch fixes higher resolution modes which requires an high pixel > > > clock output currently not working on non-HDMI DU channel (such as > > > 1920x1080@60Hz on the VGA output). > > > > Just for the record, with this patch the following modes (as printed by > > modetest) on the VGA output now produce correct result with my monitor: > > > > 1920x1080 60 1920 2008 2052 2200 1080 1084 1089 1125 flags: nhsync, nvsync > > 1600x1200 60 1600 1664 1856 2160 1200 1201 1204 1250 flags: phsync, pvsync > > 1360x768 60 1360 1424 1536 1792 768 771 777 795 flags: phsync, pvsync > > > > The second mode used to not display at all, with a message telling that > > timings were out of range, and the other two modes used to produce a displayed > > image partly shifted or scaled out of the screen boundaries. > > > > The following modes still produce an image partly out of the screen > > boundaries. > > > > 1600x900 60 1600 1624 1704 1800 900 901 904 1000 flags: phsync, pvsync > > 1280x960 60 1280 1376 1488 1800 960 961 964 1000 flags: phsync, pvsync > > 1366x768 60 1366 1436 1579 1792 768 771 774 798 flags: phsync, pvsync > > 1366x768 60 1366 1380 1436 1500 768 769 772 800 flags: phsync, pvsync > > 1280x720 60 1280 1390 1430 1650 720 725 730 750 flags: phsync, pvsync > > > > And this one is reported to be out of range. > > > > 1920x1200 60 1920 2056 2256 2592 1200 1203 1209 1245 flags: nhsync, pvsync > > > > There is thus still room for improvement (some of the issues are possibly due > > to my monitor though), but there's also an improvement, and no noticeable > > regression. > > The following table compares results obtained with the latest > renesas-drivers, with and without this series applied on top. > > ------------------------------------------------------------------------------- > Legend: > A = image badly aligned: not all 4 blue/red borders visible > F = flickering: disturbance in the shown image > B = broken: mode is not displayed > > Results: I = improvement > R = regression > > renesas-drivers du_clk result > 1024x768 > 1920x1200 F A I > 1920x1200 A A > 1920x1080 A A > 1600x1200 B I > 1680x1050 > 1680x1050 > 1400x1050 > 1400x1050 > 1600x900 A A > 1280x1024 > 1440x900 > 1440x900 > 1280x960 > 1366x768 A A > 1366x768 A A > 1360x768 A R > 1280x800 > 1280x768 A A > 1280x768 A A > 1280x768 A A > 1280x720 A A > 800x600 > 800x600 > 848x480 > 640x480 > ------------------------------------------------------------------------------- > > Overall I see two modes that were broken or unusable due to flickering > (1600x1200 and 1920x1200 respectively) to be now (almost) fixed. > > There are visible alignement problems on some modes on both versions, > but I only see one 'regression' (the last 1360x768 that is now > slightly not aligned). > > I guess monitors play a role here, with each one being different, but > overall I guess our test results match. > > > > > > > Fixes: 1b30dbde8596 ("drm: rcar-du: Add support for external pixel clock") > > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> > > > [Factor out code to a helper function] > > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Acked-by: Jacopo Mondi <jacopo+renesas@jmondi.org> To your re-worked version of the patch! Thanks j > > > --- > > > drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 86 ++++++++++++++++++++---------- > > > 1 file changed, 55 insertions(+), 31 deletions(-) > > > > > > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > > > b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index f8068170905a..2c9405458bbf > > > 100644 > > > --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > > > +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c > > > @@ -165,6 +165,48 @@ static void rcar_du_dpll_divider(struct rcar_du_crtc > > > *rcrtc, best_diff); > > > } > > > > > > +struct du_clk_params { > > > + struct clk *clk; > > > + unsigned long rate; > > > + unsigned long diff; > > > + u32 escr; > > > +}; > > > + > > > +static void rcar_du_escr_divider(struct rcar_du_crtc *rcrtc, struct clk > > > *clk, + unsigned long target, u32 escr, > > > + struct du_clk_params *params) > > > +{ > > > + unsigned long rate; > > > + unsigned long diff; > > > + u32 div; > > > + > > > + /* > > > + * If the target rate has already been achieved perfectly we can't do > > > + * better. > > > + */ > > > + if (params->diff == 0) > > > + return; > > > + > > > + /* > > > + * Compute the input clock rate and internal divisor values to obtain > > > + * the clock rate closest to the target frequency. > > > + */ > > > + rate = clk_round_rate(clk, target); > > > + div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1; > > > + diff = abs(rate / (div + 1) - target); > > > + > > > + /* > > > + * If the resulting frequency is better than any previously obtained, > > > + * store the parameters. > > > + */ > > > + if (diff < params->diff) { > > > + params->clk = clk; > > > + params->rate = rate; > > > + params->diff = diff; > > > + params->escr = escr | div; > > > + } > > > +} > > > + > > > static const struct soc_device_attribute rcar_du_r8a7795_es1[] = { > > > { .soc_id = "r8a7795", .revision = "ES1.*" }, > > > { /* sentinel */ } > > > @@ -225,42 +267,24 @@ static void rcar_du_crtc_set_display_timing(struct > > > rcar_du_crtc *rcrtc) > > > > > > escr = ESCR_DCLKSEL_DCLKIN | div; > > > } else { > > > - unsigned long clk; > > > - u32 div; > > > - > > > - /* > > > - * Compute the clock divisor and select the internal or external > > > - * dot clock based on the requested frequency. > > > - */ > > > - clk = clk_get_rate(rcrtc->clock); > > > - div = DIV_ROUND_CLOSEST(clk, mode_clock); > > > - div = clamp(div, 1U, 64U) - 1; > > > - > > > - escr = ESCR_DCLKSEL_CLKS | div; > > > - > > > - if (rcrtc->extclock) { > > > - unsigned long extclk; > > > - unsigned long extrate; > > > - unsigned long rate; > > > - u32 extdiv; > > > + struct du_clk_params params = { .diff = (unsigned long)-1 }; > > > > > > - extclk = clk_get_rate(rcrtc->extclock); > > > - extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); > > > - extdiv = clamp(extdiv, 1U, 64U) - 1; > > > + rcar_du_escr_divider(rcrtc, rcrtc->clock, mode_clock, > > > + ESCR_DCLKSEL_CLKS, ¶ms); > > > + if (rcrtc->extclock) > > > + rcar_du_escr_divider(rcrtc, rcrtc->extclock, mode_clock, > > > + ESCR_DCLKSEL_DCLKIN, ¶ms); > > > > > > - extrate = extclk / (extdiv + 1); > > > - rate = clk / (div + 1); > > > + dev_dbg(rcrtc->group->dev->dev, "mode clock %lu %s rate %lu\n", > > > + mode_clock, params.clk == rcrtc->clock ? "cpg" : "ext", > > > + params.rate); > > > > > > - if (abs((long)extrate - (long)mode_clock) < > > > - abs((long)rate - (long)mode_clock)) > > > - escr = ESCR_DCLKSEL_DCLKIN | extdiv; > > > - > > > - dev_dbg(rcrtc->group->dev->dev, > > > - "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", > > > - mode_clock, extrate, rate, escr); > > > - } > > > + clk_set_rate(params.clk, params.rate); > > > + escr = params.escr; > > > } > > > > > > + dev_dbg(rcrtc->group->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr); > > > + > > > rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, > > > escr); > > > rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); > > > > -- > > Regards, > > > > Laurent Pinchart > > > > > >
Hi Laurent, Jacopo, On 21/08/18 09:08, Laurent Pinchart wrote: > Hi Jacopo, > > (CC'ing Kieran) > > On Tuesday, 21 August 2018 10:33:57 EEST jacopo mondi wrote: >> On Tue, Aug 21, 2018 at 01:12:40AM +0300, Laurent Pinchart wrote: >>> On Tuesday, 21 August 2018 00:49:41 EEST Laurent Pinchart wrote: >>>> From: Jacopo Mondi <jacopo@jmondi.org> >>>> >>>> DU channels not equipped with a DPLL use an SoC internal (provided by >>>> the CPG) or external clock source combined with a DU internal divider to >>>> generate the desired output dot clock frequency. >>>> >>>> The current clock selection procedure does not fully exploit the ability >>>> of external clock sources to generate the exact dot clock frequency by >>>> themselves, but relies instead on tuning the internal DU clock divider >>>> only, resulting in a less precise clock generation process. >>>> >>>> When possible, and desirable, ask the external clock source for the >>>> exact output dot clock frequency, and select the clock source that >>>> produces the frequency closest to the desired output dot clock. >>>> >>>> This patch specifically targets platforms (like Salvator-X[S] and ULCBs) >>>> where the DU's input dotclock.in is generated by the versaclock VC5 >>>> clock source, which is capable of generating the exact rate the DU needs >>>> as pixel clock output. >>>> >>>> This patch fixes higher resolution modes which requires an high pixel >>>> clock output currently not working on non-HDMI DU channel (such as >>>> 1920x1080@60Hz on the VGA output). >>> >>> Just for the record, with this patch the following modes (as printed by >>> >>> modetest) on the VGA output now produce correct result with my monitor: >>> 1920x1080 60 1920 2008 2052 2200 1080 1084 1089 1125 flags: nhsync, >>> nvsync >>> 1600x1200 60 1600 1664 1856 2160 1200 1201 1204 1250 flags: phsync, >>> pvsync >>> 1360x768 60 1360 1424 1536 1792 768 771 777 795 flags: phsync, pvsync >>> >>> The second mode used to not display at all, with a message telling that >>> timings were out of range, and the other two modes used to produce a >>> displayed image partly shifted or scaled out of the screen boundaries. >>> >>> The following modes still produce an image partly out of the screen >>> boundaries. >>> >>> 1600x900 60 1600 1624 1704 1800 900 901 904 1000 flags: phsync, pvsync >>> 1280x960 60 1280 1376 1488 1800 960 961 964 1000 flags: phsync, pvsync >>> 1366x768 60 1366 1436 1579 1792 768 771 774 798 flags: phsync, pvsync >>> 1366x768 60 1366 1380 1436 1500 768 769 772 800 flags: phsync, pvsync >>> 1280x720 60 1280 1390 1430 1650 720 725 730 750 flags: phsync, pvsync >>> >>> And this one is reported to be out of range. >>> >>> 1920x1200 60 1920 2056 2256 2592 1200 1203 1209 1245 flags: nhsync, >>> pvsync >>> >>> There is thus still room for improvement (some of the issues are possibly >>> due to my monitor though), but there's also an improvement, and no >>> noticeable regression. >>> >>>> Fixes: 1b30dbde8596 ("drm: rcar-du: Add support for external pixel >>>> clock") >>>> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> >>>> [Factor out code to a helper function] >>>> Signed-off-by: Laurent Pinchart >>>> <laurent.pinchart+renesas@ideasonboard.com> >>>> --- >>>> >>>> drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 86 +++++++++++++++++++--------- >>>> 1 file changed, 55 insertions(+), 31 deletions(-) >>>> >>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c >>>> b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index >>>> f8068170905a..2c9405458bbf >>>> 100644 >>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c >>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c >>>> @@ -165,6 +165,48 @@ static void rcar_du_dpll_divider(struct >>>> rcar_du_crtc *rcrtc, >>>> best_diff); >>>> } >>>> >>>> +struct du_clk_params { >>>> + struct clk *clk; >>>> + unsigned long rate; >>>> + unsigned long diff; >>>> + u32 escr; >>>> +}; >>>> + >>>> +static void rcar_du_escr_divider(struct rcar_du_crtc *rcrtc, struct clk >>>> *clk, >>>> + unsigned long target, u32 escr, >>>> + struct du_clk_params *params) >> >> I don't see the rcrtc parameter ever being used in this function. >> Do you want to keep it anyhow? > > You're right, I'll remove it. > >>>> +{ >>>> + unsigned long rate; >>>> + unsigned long diff; >>>> + u32 div; >>>> + >>>> + /* >>>> + * If the target rate has already been achieved perfectly we can't do >>>> + * better. >>>> + */ >>>> + if (params->diff == 0) >>>> + return; >>>> + >>>> + /* >>>> + * Compute the input clock rate and internal divisor values to obtain >>>> + * the clock rate closest to the target frequency. >>>> + */ >>>> + rate = clk_round_rate(clk, target); >>>> + div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1; >>>> + diff = abs(rate / (div + 1) - target); >>>> + >>>> + /* >>>> + * If the resulting frequency is better than any previously obtained, >> >> s/obtained,/obtained one,/ ? > > Any opinion from a native English speaker ? :-) I'd probably write: Store the parameters if the resulting frequency is better than any previously calculated value. >> Will get back with some testing results on a different VGA monitor... > > Thank you. > >>>> + * store the parameters. >>>> + */ >>>> + if (diff < params->diff) { >>>> + params->clk = clk; >>>> + params->rate = rate; >>>> + params->diff = diff; >>>> + params->escr = escr | div; >>>> + } >>>> +} > > [snip] >
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index f8068170905a..2c9405458bbf 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -165,6 +165,48 @@ static void rcar_du_dpll_divider(struct rcar_du_crtc *rcrtc, best_diff); } +struct du_clk_params { + struct clk *clk; + unsigned long rate; + unsigned long diff; + u32 escr; +}; + +static void rcar_du_escr_divider(struct rcar_du_crtc *rcrtc, struct clk *clk, + unsigned long target, u32 escr, + struct du_clk_params *params) +{ + unsigned long rate; + unsigned long diff; + u32 div; + + /* + * If the target rate has already been achieved perfectly we can't do + * better. + */ + if (params->diff == 0) + return; + + /* + * Compute the input clock rate and internal divisor values to obtain + * the clock rate closest to the target frequency. + */ + rate = clk_round_rate(clk, target); + div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1; + diff = abs(rate / (div + 1) - target); + + /* + * If the resulting frequency is better than any previously obtained, + * store the parameters. + */ + if (diff < params->diff) { + params->clk = clk; + params->rate = rate; + params->diff = diff; + params->escr = escr | div; + } +} + static const struct soc_device_attribute rcar_du_r8a7795_es1[] = { { .soc_id = "r8a7795", .revision = "ES1.*" }, { /* sentinel */ } @@ -225,42 +267,24 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) escr = ESCR_DCLKSEL_DCLKIN | div; } else { - unsigned long clk; - u32 div; - - /* - * Compute the clock divisor and select the internal or external - * dot clock based on the requested frequency. - */ - clk = clk_get_rate(rcrtc->clock); - div = DIV_ROUND_CLOSEST(clk, mode_clock); - div = clamp(div, 1U, 64U) - 1; - - escr = ESCR_DCLKSEL_CLKS | div; - - if (rcrtc->extclock) { - unsigned long extclk; - unsigned long extrate; - unsigned long rate; - u32 extdiv; + struct du_clk_params params = { .diff = (unsigned long)-1 }; - extclk = clk_get_rate(rcrtc->extclock); - extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); - extdiv = clamp(extdiv, 1U, 64U) - 1; + rcar_du_escr_divider(rcrtc, rcrtc->clock, mode_clock, + ESCR_DCLKSEL_CLKS, ¶ms); + if (rcrtc->extclock) + rcar_du_escr_divider(rcrtc, rcrtc->extclock, mode_clock, + ESCR_DCLKSEL_DCLKIN, ¶ms); - extrate = extclk / (extdiv + 1); - rate = clk / (div + 1); + dev_dbg(rcrtc->group->dev->dev, "mode clock %lu %s rate %lu\n", + mode_clock, params.clk == rcrtc->clock ? "cpg" : "ext", + params.rate); - if (abs((long)extrate - (long)mode_clock) < - abs((long)rate - (long)mode_clock)) - escr = ESCR_DCLKSEL_DCLKIN | extdiv; - - dev_dbg(rcrtc->group->dev->dev, - "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", - mode_clock, extrate, rate, escr); - } + clk_set_rate(params.clk, params.rate); + escr = params.escr; } + dev_dbg(rcrtc->group->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr); + rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, escr); rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);