* [PATCH 1/5] drm/bridge: nwl-dsi: Add support for video_pll
2020-08-28 11:13 [PATCH 0/5] Add new features to nwl-dsi driver Robert Chiras (OSS)
@ 2020-08-28 11:13 ` Robert Chiras (OSS)
2020-09-04 17:04 ` Guido Günther
2020-08-28 11:13 ` [PATCH 2/5] dt-bindings: display/bridge: nwl-dsi: Document video_pll clock Robert Chiras (OSS)
` (4 subsequent siblings)
5 siblings, 1 reply; 14+ messages in thread
From: Robert Chiras (OSS) @ 2020-08-28 11:13 UTC (permalink / raw)
To: Rob Herring, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sam Ravnborg, Guido Günther,
Fabio Estevam, Ondrej Jirman
Cc: David Airlie, Daniel Vetter, dri-devel, devicetree, linux-kernel,
linux-imx
From: Robert Chiras <robert.chiras@nxp.com>
This patch adds support for a new clock 'video_pll' in order to better
set the video_pll clock to a clock-rate that satisfies a mode's clock.
The video PLL, on i.MX8MQ, can drive both DC pixel-clock and DSI phy_ref
clock. When used with a bridge that can drive multiple modes, like a DSI
to HDMI bridge, the DSI can't be statically configured for a specific
mode in the DTS file.
So, this patch adds access to the video PLL inside the DSI driver, so
that modes can be filtered-out if the required combination of phy_ref
and pixel-clock can't be achieved using the video PLL.
Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
---
drivers/gpu/drm/bridge/nwl-dsi.c | 318 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 313 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
index ce94f79..1228466 100644
--- a/drivers/gpu/drm/bridge/nwl-dsi.c
+++ b/drivers/gpu/drm/bridge/nwl-dsi.c
@@ -39,6 +39,20 @@
#define NWL_DSI_MIPI_FIFO_TIMEOUT msecs_to_jiffies(500)
+/* Maximum Video PLL frequency: 1.2GHz */
+#define MAX_PLL_FREQ 1200000000
+
+#define MBPS(x) ((x) * 1000000)
+#define MIN_PHY_RATE MBPS(24)
+#define MAX_PHY_RATE MBPS(30)
+
+/* Possible valid PHY reference clock rates*/
+static u32 phyref_rates[] = {
+ 27000000,
+ 25000000,
+ 24000000,
+};
+
enum transfer_direction {
DSI_PACKET_SEND,
DSI_PACKET_RECEIVE,
@@ -67,6 +81,17 @@ struct nwl_dsi_transfer {
size_t rx_len; /* in bytes */
};
+struct mode_config {
+ int clock;
+ int crtc_clock;
+ unsigned int lanes;
+ unsigned long bitclock;
+ unsigned long phy_rates[3];
+ unsigned long pll_rates[3];
+ int phy_rate_idx;
+ struct list_head list;
+};
+
struct nwl_dsi {
struct drm_bridge bridge;
struct mipi_dsi_host dsi_host;
@@ -101,6 +126,7 @@ struct nwl_dsi {
struct clk *rx_esc_clk;
struct clk *tx_esc_clk;
struct clk *core_clk;
+ struct clk *pll_clk;
/*
* hardware bug: the i.MX8MQ needs this clock on during reset
* even when not using LCDIF.
@@ -115,6 +141,7 @@ struct nwl_dsi {
int error;
struct nwl_dsi_transfer *xfer;
+ struct list_head valid_modes;
};
static const struct regmap_config nwl_dsi_regmap_config = {
@@ -778,6 +805,207 @@ static void nwl_dsi_bridge_disable(struct drm_bridge *bridge)
pm_runtime_put(dsi->dev);
}
+static unsigned long nwl_dsi_get_bit_clock(struct nwl_dsi *dsi,
+ unsigned long pixclock, u32 lanes)
+{
+ int bpp;
+
+ if (lanes < 1 || lanes > 4)
+ return 0;
+
+ bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+
+ return (pixclock * bpp) / lanes;
+}
+
+/*
+ * Utility function to calculate least commom multiple, using an improved
+ * version of the Euclidean algorithm for greatest common factor.
+ */
+static unsigned long nwl_dsi_get_lcm(unsigned long a, unsigned long b)
+{
+ u32 gcf = 0; /* greatest common factor */
+ unsigned long tmp_a = a;
+ unsigned long tmp_b = b;
+
+ if (!a || !b)
+ return 0;
+
+ while (tmp_a % tmp_b) {
+ gcf = tmp_a % tmp_b;
+ tmp_a = tmp_b;
+ tmp_b = gcf;
+ }
+
+ if (!gcf)
+ return a;
+
+ return ((unsigned long long)a * b) / gcf;
+}
+
+/*
+ * This function tries to adjust the crtc_clock for a DSI device in such a way
+ * that the video pll will be able to satisfy both Display Controller pixel
+ * clock (feeding out DPI interface) and our input phy_ref clock.
+ */
+static void nwl_dsi_setup_pll_config(struct mode_config *config)
+{
+ unsigned long pll_rate;
+ int div;
+ size_t i, num_rates = ARRAY_SIZE(config->phy_rates);
+
+ config->crtc_clock = 0;
+
+ for (i = 0; i < num_rates; i++) {
+ int crtc_clock;
+
+ if (!config->phy_rates[i])
+ break;
+ /*
+ * First, we need to check if phy_ref can actually be obtained
+ * from pixel clock. To do this, we check their lowest common
+ * multiple, which has to be in PLL range.
+ */
+ pll_rate = nwl_dsi_get_lcm(config->clock, config->phy_rates[i]);
+ if (pll_rate > MAX_PLL_FREQ) {
+ /* Drop pll_rate to a realistic value */
+ while (pll_rate > MAX_PLL_FREQ)
+ pll_rate >>= 1;
+ /* Make sure pll_rate can provide phy_ref rate */
+ div = DIV_ROUND_UP(pll_rate, config->phy_rates[i]);
+ pll_rate = config->phy_rates[i] * div;
+ } else {
+ /*
+ * Increase the pll rate to highest possible rate for
+ * better accuracy.
+ */
+ while (pll_rate <= MAX_PLL_FREQ)
+ pll_rate <<= 1;
+ pll_rate >>= 1;
+ }
+
+ /*
+ * Next, we need to tweak the pll_rate to a value that can also
+ * satisfy the crtc_clock.
+ */
+ div = DIV_ROUND_CLOSEST(pll_rate, config->clock);
+ if (lvl)
+ pll_rate -= config->phy_rates[i] * lvl;
+ crtc_clock = pll_rate / div;
+ config->pll_rates[i] = pll_rate;
+
+ /*
+ * Pick a crtc_clock which is closest to pixel clock.
+ * Also, make sure that the pixel clock is a multiply of
+ * 50Hz.
+ */
+ if (!(crtc_clock % 50) &&
+ abs(config->clock - crtc_clock) <
+ abs(config->clock - config->crtc_clock)) {
+ config->crtc_clock = crtc_clock;
+ config->phy_rate_idx = i;
+ }
+ }
+}
+
+
+/*
+ * This function will try the required phy speed for current mode
+ * If the phy speed can be achieved, the phy will save the speed
+ * configuration
+ */
+static struct mode_config *nwl_dsi_mode_probe(struct nwl_dsi *dsi,
+ const struct drm_display_mode *mode)
+{
+ struct device *dev = dsi->dev;
+ struct mode_config *config;
+ union phy_configure_opts phy_opts;
+ unsigned long clock = mode->clock * 1000;
+ unsigned long bit_clk = 0;
+ unsigned long phy_rates[3] = {0};
+ int match_rates = 0;
+ u32 lanes = dsi->lanes;
+ size_t i = 0, num_rates = ARRAY_SIZE(phyref_rates);
+
+ list_for_each_entry(config, &dsi->valid_modes, list)
+ if (config->clock == clock)
+ return config;
+
+ phy_mipi_dphy_get_default_config(clock,
+ mipi_dsi_pixel_format_to_bpp(dsi->format),
+ lanes, &phy_opts.mipi_dphy);
+ phy_opts.mipi_dphy.lp_clk_rate = clk_get_rate(dsi->tx_esc_clk);
+
+ while (i < num_rates) {
+ int ret;
+
+ bit_clk = nwl_dsi_get_bit_clock(dsi, clock, lanes);
+
+ clk_set_rate(dsi->pll_clk, phyref_rates[i] * 32);
+ clk_set_rate(dsi->phy_ref_clk, phyref_rates[i]);
+ ret = phy_validate(dsi->phy, PHY_MODE_MIPI_DPHY, 0, &phy_opts);
+
+ /* Pick the non-failing rate, and search for more */
+ if (!ret) {
+ phy_rates[match_rates++] = phyref_rates[i++];
+ continue;
+ }
+
+ if (match_rates)
+ break;
+
+ /* Reached the end of phyref_rates, try another lane config */
+ if ((i++ == num_rates - 1) && (--lanes > 2)) {
+ i = 0;
+ continue;
+ }
+ }
+
+ /*
+ * Try swinging between min and max pll rates and see what rate (in terms
+ * of kHz) we can custom use to get the required bit-clock.
+ */
+ if (!match_rates) {
+ int min_div, max_div;
+ int bit_clk_khz;
+
+ lanes = dsi->lanes;
+ bit_clk = nwl_dsi_get_bit_clock(dsi, clock, lanes);
+
+ min_div = DIV_ROUND_UP(bit_clk, MAX_PHY_RATE);
+ max_div = DIV_ROUND_DOWN_ULL(bit_clk, MIN_PHY_RATE);
+ bit_clk_khz = bit_clk / 1000;
+
+ for (i = max_div; i > min_div; i--) {
+ if (!(bit_clk_khz % i)) {
+ phy_rates[0] = bit_clk / i;
+ match_rates = 1;
+ break;
+ }
+ }
+ }
+
+ if (!match_rates) {
+ DRM_DEV_DEBUG_DRIVER(dev,
+ "Cannot setup PHY for mode: %ux%u @%d kHz\n",
+ mode->hdisplay,
+ mode->vdisplay,
+ mode->clock);
+
+ return NULL;
+ }
+
+ config = devm_kzalloc(dsi->dev, sizeof(struct mode_config), GFP_KERNEL);
+ config->clock = clock;
+ config->lanes = lanes;
+ config->bitclock = bit_clk;
+ memcpy(&config->phy_rates, &phy_rates, sizeof(phy_rates));
+ list_add(&config->list, &dsi->valid_modes);
+
+ return config;
+}
+
+
static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi,
const struct drm_display_mode *mode,
union phy_configure_opts *phy_opts)
@@ -809,6 +1037,38 @@ static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
+ struct nwl_dsi *dsi = bridge_to_dsi(bridge);
+ struct mode_config *config;
+ unsigned long pll_rate;
+
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "Fixup mode:\n");
+ drm_mode_debug_printmodeline(adjusted_mode);
+
+ config = nwl_dsi_mode_probe(dsi, adjusted_mode);
+ if (!config)
+ return false;
+
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "lanes=%u, data_rate=%lu\n",
+ config->lanes, config->bitclock);
+ if (config->lanes < 2 || config->lanes > 4)
+ return false;
+
+ /* Max data rate for this controller is 1.5Gbps */
+ if (config->bitclock > 1500000000)
+ return false;
+
+ pll_rate = config->pll_rates[config->phy_rate_idx];
+ if (dsi->pll_clk && pll_rate) {
+ clk_set_rate(dsi->pll_clk, pll_rate);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev,
+ "Video pll rate: %lu (actual: %lu)",
+ pll_rate, clk_get_rate(dsi->pll_clk));
+ }
+ /* Update the crtc_clock to be used by display controller */
+ if (config->crtc_clock)
+ adjusted_mode->crtc_clock = config->crtc_clock / 1000;
+
+
/* At least LCDIF + NWL needs active high sync */
adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
@@ -822,14 +1082,29 @@ nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode)
{
struct nwl_dsi *dsi = bridge_to_dsi(bridge);
- int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+ struct mode_config *config;
+ unsigned long pll_rate;
+ int bit_rate;
- if (mode->clock * bpp > 15000000 * dsi->lanes)
+ bit_rate = nwl_dsi_get_bit_clock(dsi, mode->clock * 1000, dsi->lanes);
+
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "Validating mode:");
+ drm_mode_debug_printmodeline(mode);
+
+ if (bit_rate > MBPS(1500))
return MODE_CLOCK_HIGH;
- if (mode->clock * bpp < 80000 * dsi->lanes)
+ if (bit_rate < MBPS(80))
return MODE_CLOCK_LOW;
+ config = nwl_dsi_mode_probe(dsi, mode);
+ if (!config)
+ return MODE_NOCLOCK;
+
+ pll_rate = config->pll_rates[config->phy_rate_idx];
+ if (dsi->pll_clk && !pll_rate)
+ nwl_dsi_setup_pll_config(config);
+
return MODE_OK;
}
@@ -842,8 +1117,22 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
struct device *dev = dsi->dev;
union phy_configure_opts new_cfg;
unsigned long phy_ref_rate;
+ struct mode_config *config;
int ret;
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "Setting mode:\n");
+ drm_mode_debug_printmodeline(adjusted_mode);
+
+ config = nwl_dsi_mode_probe(dsi, adjusted_mode);
+ /* New mode? This should NOT happen */
+ if (!config) {
+ DRM_DEV_ERROR(dsi->dev, "Unsupported mode provided:\n");
+ drm_mode_debug_printmodeline(adjusted_mode);
+ return;
+ }
+
+ phy_ref_rate = config->phy_rates[config->phy_rate_idx];
+ clk_set_rate(dsi->phy_ref_clk, phy_ref_rate);
ret = nwl_dsi_get_dphy_params(dsi, adjusted_mode, &new_cfg);
if (ret < 0)
return;
@@ -855,8 +1144,10 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
if (new_cfg.mipi_dphy.hs_clk_rate == dsi->phy_cfg.mipi_dphy.hs_clk_rate)
return;
- phy_ref_rate = clk_get_rate(dsi->phy_ref_clk);
- DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n", phy_ref_rate);
+ DRM_DEV_DEBUG_DRIVER(dev,
+ "PHY at ref rate: %lu (actual: %lu)\n",
+ phy_ref_rate, clk_get_rate(dsi->phy_ref_clk));
+
/* Save the new desired phy config */
memcpy(&dsi->phy_cfg, &new_cfg, sizeof(new_cfg));
@@ -1014,6 +1305,12 @@ static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
}
dsi->tx_esc_clk = clk;
+ /* The video_pll clock is optional */
+ clk = devm_clk_get(dsi->dev, "video_pll");
+ if (!IS_ERR(clk))
+ dsi->pll_clk = clk;
+
+
dsi->mux = devm_mux_control_get(dsi->dev, NULL);
if (IS_ERR(dsi->mux)) {
ret = PTR_ERR(dsi->mux);
@@ -1066,6 +1363,9 @@ static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
PTR_ERR(dsi->rst_dpi));
return PTR_ERR(dsi->rst_dpi);
}
+
+ INIT_LIST_HEAD(&dsi->valid_modes);
+
return 0;
}
@@ -1184,6 +1484,14 @@ static int nwl_dsi_probe(struct platform_device *pdev)
static int nwl_dsi_remove(struct platform_device *pdev)
{
struct nwl_dsi *dsi = platform_get_drvdata(pdev);
+ struct mode_config *config;
+ struct list_head *pos, *tmp;
+
+ list_for_each_safe(pos, tmp, &dsi->valid_modes) {
+ config = list_entry(pos, struct mode_config, list);
+ list_del(pos);
+ devm_kfree(dsi->dev, config);
+ }
nwl_dsi_deselect_input(dsi);
mipi_dsi_host_unregister(&dsi->dsi_host);
--
2.7.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 1/5] drm/bridge: nwl-dsi: Add support for video_pll
2020-08-28 11:13 ` [PATCH 1/5] drm/bridge: nwl-dsi: Add support for video_pll Robert Chiras (OSS)
@ 2020-09-04 17:04 ` Guido Günther
0 siblings, 0 replies; 14+ messages in thread
From: Guido Günther @ 2020-09-04 17:04 UTC (permalink / raw)
To: Robert Chiras (OSS)
Cc: Rob Herring, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sam Ravnborg, Fabio Estevam,
Ondrej Jirman, David Airlie, Daniel Vetter, dri-devel,
devicetree, linux-kernel, linux-imx
Hi Robert,
On Fri, Aug 28, 2020 at 02:13:28PM +0300, Robert Chiras (OSS) wrote:
> From: Robert Chiras <robert.chiras@nxp.com>
>
> This patch adds support for a new clock 'video_pll' in order to better
> set the video_pll clock to a clock-rate that satisfies a mode's clock.
> The video PLL, on i.MX8MQ, can drive both DC pixel-clock and DSI phy_ref
> clock. When used with a bridge that can drive multiple modes, like a DSI
> to HDMI bridge, the DSI can't be statically configured for a specific
> mode in the DTS file.
> So, this patch adds access to the video PLL inside the DSI driver, so
> that modes can be filtered-out if the required combination of phy_ref
> and pixel-clock can't be achieved using the video PLL.
>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
> drivers/gpu/drm/bridge/nwl-dsi.c | 318 ++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 313 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
> index ce94f79..1228466 100644
> --- a/drivers/gpu/drm/bridge/nwl-dsi.c
> +++ b/drivers/gpu/drm/bridge/nwl-dsi.c
> @@ -39,6 +39,20 @@
>
> #define NWL_DSI_MIPI_FIFO_TIMEOUT msecs_to_jiffies(500)
>
> +/* Maximum Video PLL frequency: 1.2GHz */
> +#define MAX_PLL_FREQ 1200000000
> +
> +#define MBPS(x) ((x) * 1000000)
> +#define MIN_PHY_RATE MBPS(24)
> +#define MAX_PHY_RATE MBPS(30)
> +
> +/* Possible valid PHY reference clock rates*/
nitpick: missing ' ' at end of comment.
> +static u32 phyref_rates[] = {
> + 27000000,
> + 25000000,
> + 24000000,
> +};
> +
> enum transfer_direction {
> DSI_PACKET_SEND,
> DSI_PACKET_RECEIVE,
> @@ -67,6 +81,17 @@ struct nwl_dsi_transfer {
> size_t rx_len; /* in bytes */
> };
>
> +struct mode_config {
Maybe use nwl_mode_config ? There's so much other mode_config around an
this makes it obvious it's driver specific.
> + int clock;
> + int crtc_clock;
> + unsigned int lanes;
> + unsigned long bitclock;
> + unsigned long phy_rates[3];
> + unsigned long pll_rates[3];
> + int phy_rate_idx;
> + struct list_head list;
> +};
> +
> struct nwl_dsi {
> struct drm_bridge bridge;
> struct mipi_dsi_host dsi_host;
> @@ -101,6 +126,7 @@ struct nwl_dsi {
> struct clk *rx_esc_clk;
> struct clk *tx_esc_clk;
> struct clk *core_clk;
> + struct clk *pll_clk;
> /*
> * hardware bug: the i.MX8MQ needs this clock on during reset
> * even when not using LCDIF.
> @@ -115,6 +141,7 @@ struct nwl_dsi {
> int error;
>
> struct nwl_dsi_transfer *xfer;
> + struct list_head valid_modes;
> };
>
> static const struct regmap_config nwl_dsi_regmap_config = {
> @@ -778,6 +805,207 @@ static void nwl_dsi_bridge_disable(struct drm_bridge *bridge)
> pm_runtime_put(dsi->dev);
> }
>
> +static unsigned long nwl_dsi_get_bit_clock(struct nwl_dsi *dsi,
> + unsigned long pixclock, u32 lanes)
> +{
> + int bpp;
> +
> + if (lanes < 1 || lanes > 4)
> + return 0;
> +
> + bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +
> + return (pixclock * bpp) / lanes;
> +}
> +
> +/*
> + * Utility function to calculate least commom multiple, using an
> improved
s/commom/common/
> + * version of the Euclidean algorithm for greatest common factor.
> + */
> +static unsigned long nwl_dsi_get_lcm(unsigned long a, unsigned long b)
> +{
> + u32 gcf = 0; /* greatest common factor */
> + unsigned long tmp_a = a;
> + unsigned long tmp_b = b;
> +
> + if (!a || !b)
> + return 0;
> +
> + while (tmp_a % tmp_b) {
> + gcf = tmp_a % tmp_b;
> + tmp_a = tmp_b;
> + tmp_b = gcf;
> + }
> +
> + if (!gcf)
> + return a;
> +
> + return ((unsigned long long)a * b) / gcf;
> +}
> +
> +/*
> + * This function tries to adjust the crtc_clock for a DSI device in such a way
> + * that the video pll will be able to satisfy both Display Controller pixel
> + * clock (feeding out DPI interface) and our input phy_ref clock.
> + */
> +static void nwl_dsi_setup_pll_config(struct mode_config *config)
> +{
> + unsigned long pll_rate;
> + int div;
> + size_t i, num_rates = ARRAY_SIZE(config->phy_rates);
> +
> + config->crtc_clock = 0;
> +
> + for (i = 0; i < num_rates; i++) {
> + int crtc_clock;
> +
> + if (!config->phy_rates[i])
> + break;
> + /*
> + * First, we need to check if phy_ref can actually be obtained
> + * from pixel clock. To do this, we check their lowest common
> + * multiple, which has to be in PLL range.
> + */
> + pll_rate = nwl_dsi_get_lcm(config->clock, config->phy_rates[i]);
> + if (pll_rate > MAX_PLL_FREQ) {
> + /* Drop pll_rate to a realistic value */
> + while (pll_rate > MAX_PLL_FREQ)
> + pll_rate >>= 1;
> + /* Make sure pll_rate can provide phy_ref rate */
> + div = DIV_ROUND_UP(pll_rate, config->phy_rates[i]);
> + pll_rate = config->phy_rates[i] * div;
> + } else {
> + /*
> + * Increase the pll rate to highest possible rate for
> + * better accuracy.
> + */
> + while (pll_rate <= MAX_PLL_FREQ)
> + pll_rate <<= 1;
> + pll_rate >>= 1;
> + }
> +
> + /*
> + * Next, we need to tweak the pll_rate to a value that can also
> + * satisfy the crtc_clock.
> + */
> + div = DIV_ROUND_CLOSEST(pll_rate, config->clock);
> + if (lvl)
> + pll_rate -= config->phy_rates[i] * lvl;
lvl gets never defined so it doesn't compile breaking bisection.
> + crtc_clock = pll_rate / div;
> + config->pll_rates[i] = pll_rate;
> +
> + /*
> + * Pick a crtc_clock which is closest to pixel clock.
> + * Also, make sure that the pixel clock is a multiply of
> + * 50Hz.
> + */
> + if (!(crtc_clock % 50) &&
> + abs(config->clock - crtc_clock) <
> + abs(config->clock - config->crtc_clock)) {
> + config->crtc_clock = crtc_clock;
> + config->phy_rate_idx = i;
> + }
> + }
> +}
> +
> +
> +/*
> + * This function will try the required phy speed for current mode
> + * If the phy speed can be achieved, the phy will save the speed
> + * configuration
> + */
> +static struct mode_config *nwl_dsi_mode_probe(struct nwl_dsi *dsi,
> + const struct drm_display_mode *mode)
> +{
> + struct device *dev = dsi->dev;
> + struct mode_config *config;
> + union phy_configure_opts phy_opts;
> + unsigned long clock = mode->clock * 1000;
> + unsigned long bit_clk = 0;
> + unsigned long phy_rates[3] = {0};
> + int match_rates = 0;
> + u32 lanes = dsi->lanes;
> + size_t i = 0, num_rates = ARRAY_SIZE(phyref_rates);
> +
> + list_for_each_entry(config, &dsi->valid_modes, list)
> + if (config->clock == clock)
> + return config;
> +
> + phy_mipi_dphy_get_default_config(clock,
> + mipi_dsi_pixel_format_to_bpp(dsi->format),
> + lanes, &phy_opts.mipi_dphy);
> + phy_opts.mipi_dphy.lp_clk_rate = clk_get_rate(dsi->tx_esc_clk);
> +
> + while (i < num_rates) {
> + int ret;
> +
> + bit_clk = nwl_dsi_get_bit_clock(dsi, clock, lanes);
> +
> + clk_set_rate(dsi->pll_clk, phyref_rates[i] * 32);
> + clk_set_rate(dsi->phy_ref_clk, phyref_rates[i]);
> + ret = phy_validate(dsi->phy, PHY_MODE_MIPI_DPHY, 0, &phy_opts);
> +
> + /* Pick the non-failing rate, and search for more */
> + if (!ret) {
> + phy_rates[match_rates++] = phyref_rates[i++];
> + continue;
> + }
> +
> + if (match_rates)
> + break;
> +
> + /* Reached the end of phyref_rates, try another lane config */
> + if ((i++ == num_rates - 1) && (--lanes > 2)) {
> + i = 0;
> + continue;
> + }
> + }
> +
> + /*
> + * Try swinging between min and max pll rates and see what rate (in terms
> + * of kHz) we can custom use to get the required bit-clock.
> + */
> + if (!match_rates) {
> + int min_div, max_div;
> + int bit_clk_khz;
> +
> + lanes = dsi->lanes;
> + bit_clk = nwl_dsi_get_bit_clock(dsi, clock, lanes);
> +
> + min_div = DIV_ROUND_UP(bit_clk, MAX_PHY_RATE);
> + max_div = DIV_ROUND_DOWN_ULL(bit_clk, MIN_PHY_RATE);
> + bit_clk_khz = bit_clk / 1000;
> +
> + for (i = max_div; i > min_div; i--) {
> + if (!(bit_clk_khz % i)) {
> + phy_rates[0] = bit_clk / i;
> + match_rates = 1;
> + break;
> + }
> + }
> + }
> +
> + if (!match_rates) {
> + DRM_DEV_DEBUG_DRIVER(dev,
> + "Cannot setup PHY for mode: %ux%u @%d kHz\n",
> + mode->hdisplay,
> + mode->vdisplay,
> + mode->clock);
> +
> + return NULL;
> + }
> +
> + config = devm_kzalloc(dsi->dev, sizeof(struct mode_config), GFP_KERNEL);
> + config->clock = clock;
> + config->lanes = lanes;
> + config->bitclock = bit_clk;
> + memcpy(&config->phy_rates, &phy_rates, sizeof(phy_rates));
> + list_add(&config->list, &dsi->valid_modes);
> +
> + return config;
> +}
> +
> +
> static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi,
> const struct drm_display_mode *mode,
> union phy_configure_opts *phy_opts)
> @@ -809,6 +1037,38 @@ static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
> const struct drm_display_mode *mode,
> struct drm_display_mode *adjusted_mode)
> {
> + struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> + struct mode_config *config;
> + unsigned long pll_rate;
> +
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "Fixup mode:\n");
> + drm_mode_debug_printmodeline(adjusted_mode);
> +
> + config = nwl_dsi_mode_probe(dsi, adjusted_mode);
Since this ends up calling `clk_set_rate` doesn't this violate the
`Drivers MUST NOT touch any persistent state` rule of the fixup
function? So would it be better to teach the phy about it's possible
rates rather then NWL? I guess that get's tricky your clock-drop-level
is dependent on the phy rate later on.
> + if (!config)
> + return false;
> +
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "lanes=%u, data_rate=%lu\n",
> + config->lanes, config->bitclock);
> + if (config->lanes < 2 || config->lanes > 4)
> + return false;
> +
> + /* Max data rate for this controller is 1.5Gbps */
> + if (config->bitclock > 1500000000)
> + return false;
> +
> + pll_rate = config->pll_rates[config->phy_rate_idx];
> + if (dsi->pll_clk && pll_rate) {
> + clk_set_rate(dsi->pll_clk, pll_rate);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev,
> + "Video pll rate: %lu (actual: %lu)",
> + pll_rate, clk_get_rate(dsi->pll_clk));
> + }
> + /* Update the crtc_clock to be used by display controller */
> + if (config->crtc_clock)
> + adjusted_mode->crtc_clock = config->crtc_clock / 1000;
> +
> +
> /* At least LCDIF + NWL needs active high sync */
> adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
> adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
> @@ -822,14 +1082,29 @@ nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
> const struct drm_display_mode *mode)
> {
> struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> - int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> + struct mode_config *config;
> + unsigned long pll_rate;
> + int bit_rate;
>
> - if (mode->clock * bpp > 15000000 * dsi->lanes)
> + bit_rate = nwl_dsi_get_bit_clock(dsi, mode->clock * 1000, dsi->lanes);
> +
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "Validating mode:");
> + drm_mode_debug_printmodeline(mode);
These (and other locations) are a bit confusings since
`drm_mode_debug_printmodeline` uses DRM_DEBUG_KMS so if one only enabled
driver debugging (0x2) one gets: `Validating mode:` but then nothing.
> +
> + if (bit_rate > MBPS(1500))
> return MODE_CLOCK_HIGH;
>
> - if (mode->clock * bpp < 80000 * dsi->lanes)
> + if (bit_rate < MBPS(80))
> return MODE_CLOCK_LOW;
>
> + config = nwl_dsi_mode_probe(dsi, mode);
> + if (!config)
> + return MODE_NOCLOCK;
> +
> + pll_rate = config->pll_rates[config->phy_rate_idx];
> + if (dsi->pll_clk && !pll_rate)
> + nwl_dsi_setup_pll_config(config);
> +
> return MODE_OK;
> }
>
> @@ -842,8 +1117,22 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
> struct device *dev = dsi->dev;
> union phy_configure_opts new_cfg;
> unsigned long phy_ref_rate;
> + struct mode_config *config;
> int ret;
>
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "Setting mode:\n");
> + drm_mode_debug_printmodeline(adjusted_mode);
> +
> + config = nwl_dsi_mode_probe(dsi, adjusted_mode);
> + /* New mode? This should NOT happen */
> + if (!config) {
> + DRM_DEV_ERROR(dsi->dev, "Unsupported mode provided:\n");
> + drm_mode_debug_printmodeline(adjusted_mode);
> + return;
> + }
> +
> + phy_ref_rate = config->phy_rates[config->phy_rate_idx];
> + clk_set_rate(dsi->phy_ref_clk, phy_ref_rate);
> ret = nwl_dsi_get_dphy_params(dsi, adjusted_mode, &new_cfg);
> if (ret < 0)
> return;
> @@ -855,8 +1144,10 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
> if (new_cfg.mipi_dphy.hs_clk_rate == dsi->phy_cfg.mipi_dphy.hs_clk_rate)
> return;
>
> - phy_ref_rate = clk_get_rate(dsi->phy_ref_clk);
> - DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n", phy_ref_rate);
> + DRM_DEV_DEBUG_DRIVER(dev,
> + "PHY at ref rate: %lu (actual: %lu)\n",
> + phy_ref_rate, clk_get_rate(dsi->phy_ref_clk));
> +
> /* Save the new desired phy config */
> memcpy(&dsi->phy_cfg, &new_cfg, sizeof(new_cfg));
>
> @@ -1014,6 +1305,12 @@ static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
> }
> dsi->tx_esc_clk = clk;
>
> + /* The video_pll clock is optional */
> + clk = devm_clk_get(dsi->dev, "video_pll");
Using devm_clk_get_optional return NULL so we can return a proper error
on other failures.
> + if (!IS_ERR(clk))
> + dsi->pll_clk = clk;
> +
> +
Drop one empty line.
Cheers,
-- Guido
> dsi->mux = devm_mux_control_get(dsi->dev, NULL);
> if (IS_ERR(dsi->mux)) {
> ret = PTR_ERR(dsi->mux);
> @@ -1066,6 +1363,9 @@ static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
> PTR_ERR(dsi->rst_dpi));
> return PTR_ERR(dsi->rst_dpi);
> }
> +
> + INIT_LIST_HEAD(&dsi->valid_modes);
> +
> return 0;
> }
>
> @@ -1184,6 +1484,14 @@ static int nwl_dsi_probe(struct platform_device *pdev)
> static int nwl_dsi_remove(struct platform_device *pdev)
> {
> struct nwl_dsi *dsi = platform_get_drvdata(pdev);
> + struct mode_config *config;
> + struct list_head *pos, *tmp;
> +
> + list_for_each_safe(pos, tmp, &dsi->valid_modes) {
> + config = list_entry(pos, struct mode_config, list);
> + list_del(pos);
> + devm_kfree(dsi->dev, config);
> + }
>
> nwl_dsi_deselect_input(dsi);
> mipi_dsi_host_unregister(&dsi->dsi_host);
> --
> 2.7.4
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 2/5] dt-bindings: display/bridge: nwl-dsi: Document video_pll clock
2020-08-28 11:13 [PATCH 0/5] Add new features to nwl-dsi driver Robert Chiras (OSS)
2020-08-28 11:13 ` [PATCH 1/5] drm/bridge: nwl-dsi: Add support for video_pll Robert Chiras (OSS)
@ 2020-08-28 11:13 ` Robert Chiras (OSS)
2020-08-28 21:40 ` Rob Herring
2020-08-28 11:13 ` [PATCH 3/5] drm/bridge: nwl-dsi: Add support for clock-drop-level Robert Chiras (OSS)
` (3 subsequent siblings)
5 siblings, 1 reply; 14+ messages in thread
From: Robert Chiras (OSS) @ 2020-08-28 11:13 UTC (permalink / raw)
To: Rob Herring, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sam Ravnborg, Guido Günther,
Fabio Estevam, Ondrej Jirman
Cc: David Airlie, Daniel Vetter, dri-devel, devicetree, linux-kernel,
linux-imx
From: Robert Chiras <robert.chiras@nxp.com>
Add documentation for a new clock 'video_pll'.
Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
---
Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
index 04099f5..8b5741b 100644
--- a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
@@ -37,6 +37,8 @@ properties:
- description: TX_ESC clock (used in escape mode)
- description: PHY_REF clock
- description: LCDIF clock
+ - description: VIDEO_PLL clock (used to set the the PLL clock feeding both
+ phy_ref and DC pixel clock to a convenient rate)
clock-names:
items:
@@ -45,6 +47,7 @@ properties:
- const: tx_esc
- const: phy_ref
- const: lcdif
+ - const: video_pll
mux-controls:
description:
--
2.7.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 2/5] dt-bindings: display/bridge: nwl-dsi: Document video_pll clock
2020-08-28 11:13 ` [PATCH 2/5] dt-bindings: display/bridge: nwl-dsi: Document video_pll clock Robert Chiras (OSS)
@ 2020-08-28 21:40 ` Rob Herring
0 siblings, 0 replies; 14+ messages in thread
From: Rob Herring @ 2020-08-28 21:40 UTC (permalink / raw)
To: Robert Chiras (OSS)
Cc: Jernej Skrabec, David Airlie, dri-devel, Ondrej Jirman,
Sam Ravnborg, devicetree, linux-kernel, linux-imx, Fabio Estevam,
Andrzej Hajda, Jonas Karlman, Guido Günther, Daniel Vetter,
Neil Armstrong, Rob Herring, Laurent Pinchart
On Fri, 28 Aug 2020 14:13:29 +0300, Robert Chiras (OSS) wrote:
> From: Robert Chiras <robert.chiras@nxp.com>
>
> Add documentation for a new clock 'video_pll'.
>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
> Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml | 3 +++
> 1 file changed, 3 insertions(+)
>
My bot found errors running 'make dt_binding_check' on your patch:
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/display/bridge/nwl-dsi.example.dt.yaml: mipi_dsi@30a00000: clocks: [[4294967295, 163], [4294967295, 244], [4294967295, 245], [4294967295, 164], [4294967295, 128]] is too short
From schema: /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/display/bridge/nwl-dsi.example.dt.yaml: mipi_dsi@30a00000: clock-names: ['core', 'rx_esc', 'tx_esc', 'phy_ref', 'lcdif'] is too short
From schema: /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
See https://patchwork.ozlabs.org/patch/1353197
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure dt-schema is up to date:
pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade
Please check and re-submit.
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 3/5] drm/bridge: nwl-dsi: Add support for clock-drop-level
2020-08-28 11:13 [PATCH 0/5] Add new features to nwl-dsi driver Robert Chiras (OSS)
2020-08-28 11:13 ` [PATCH 1/5] drm/bridge: nwl-dsi: Add support for video_pll Robert Chiras (OSS)
2020-08-28 11:13 ` [PATCH 2/5] dt-bindings: display/bridge: nwl-dsi: Document video_pll clock Robert Chiras (OSS)
@ 2020-08-28 11:13 ` Robert Chiras (OSS)
2020-09-06 10:28 ` Guido Günther
2020-08-28 11:13 ` [PATCH 4/5] dt-bindings: display/bridge: nwl-dsi: Document fsl,clock-drop-level property Robert Chiras (OSS)
` (2 subsequent siblings)
5 siblings, 1 reply; 14+ messages in thread
From: Robert Chiras (OSS) @ 2020-08-28 11:13 UTC (permalink / raw)
To: Rob Herring, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sam Ravnborg, Guido Günther,
Fabio Estevam, Ondrej Jirman
Cc: David Airlie, Daniel Vetter, dri-devel, devicetree, linux-kernel,
linux-imx
From: Robert Chiras <robert.chiras@nxp.com>
The clock-drop-level is needed in order to add more blanking space needed
by DSI panels when sending DSI commands. One level is the equivalent of
phy_ref rate from the PLL rate. Since the PLL rate is targeted as highest
possible, each level should not get the crtc_clock too low, compared to
the actual clock.
Example for a clock of 132M, with "clock-drop-level = <1>" in dts file
will result in a crtc_clock of 129M, using the following logic:
- video_pll rate to provide both phy_ref rate of 24M and pixel-clock
of 132M is 1056M (divisor /43 for phy_ref and /8 for pixel-clock)
- from this rate, we subtract the equivalent of phy_ref (24M) but
keep the same divisor. This way, the video_pll rate will be 1056 - 24
= 1032M.
- new pixel-clock will be: 1032 / 8 = 129M
For a "clock-drop-level = <2>", new pixel-clock will be:
(1056 - (24 * 2)) / 8 = 1008 / 8 = 126M
Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
---
drivers/gpu/drm/bridge/nwl-dsi.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
index 1228466..ac4aa0a 100644
--- a/drivers/gpu/drm/bridge/nwl-dsi.c
+++ b/drivers/gpu/drm/bridge/nwl-dsi.c
@@ -142,6 +142,7 @@ struct nwl_dsi {
struct nwl_dsi_transfer *xfer;
struct list_head valid_modes;
+ u32 clk_drop_lvl;
};
static const struct regmap_config nwl_dsi_regmap_config = {
@@ -842,13 +843,14 @@ static unsigned long nwl_dsi_get_lcm(unsigned long a, unsigned long b)
return ((unsigned long long)a * b) / gcf;
}
-
/*
* This function tries to adjust the crtc_clock for a DSI device in such a way
* that the video pll will be able to satisfy both Display Controller pixel
* clock (feeding out DPI interface) and our input phy_ref clock.
+ * Also, the DC pixel clock must be lower than the actual clock in order to
+ * have enough blanking space to send DSI commands, if the device is a panel.
*/
-static void nwl_dsi_setup_pll_config(struct mode_config *config)
+static void nwl_dsi_setup_pll_config(struct mode_config *config, u32 lvl)
{
unsigned long pll_rate;
int div;
@@ -908,7 +910,6 @@ static void nwl_dsi_setup_pll_config(struct mode_config *config)
}
}
-
/*
* This function will try the required phy speed for current mode
* If the phy speed can be achieved, the phy will save the speed
@@ -1103,7 +1104,7 @@ nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
pll_rate = config->pll_rates[config->phy_rate_idx];
if (dsi->pll_clk && !pll_rate)
- nwl_dsi_setup_pll_config(config);
+ nwl_dsi_setup_pll_config(config, dsi->clk_drop_lvl);
return MODE_OK;
}
@@ -1248,6 +1249,7 @@ static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
{
struct platform_device *pdev = to_platform_device(dsi->dev);
+ struct device_node *np = dsi->dev->of_node;
struct clk *clk;
void __iomem *base;
int ret;
@@ -1364,6 +1366,8 @@ static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
return PTR_ERR(dsi->rst_dpi);
}
+ of_property_read_u32(np, "fsl,clock-drop-level", &dsi->clk_drop_lvl);
+
INIT_LIST_HEAD(&dsi->valid_modes);
return 0;
--
2.7.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 3/5] drm/bridge: nwl-dsi: Add support for clock-drop-level
2020-08-28 11:13 ` [PATCH 3/5] drm/bridge: nwl-dsi: Add support for clock-drop-level Robert Chiras (OSS)
@ 2020-09-06 10:28 ` Guido Günther
0 siblings, 0 replies; 14+ messages in thread
From: Guido Günther @ 2020-09-06 10:28 UTC (permalink / raw)
To: Robert Chiras (OSS)
Cc: Rob Herring, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sam Ravnborg, Fabio Estevam,
Ondrej Jirman, David Airlie, Daniel Vetter, dri-devel,
devicetree, linux-kernel, linux-imx
Hi Robert,
On Fri, Aug 28, 2020 at 02:13:30PM +0300, Robert Chiras (OSS) wrote:
> From: Robert Chiras <robert.chiras@nxp.com>
>
> The clock-drop-level is needed in order to add more blanking space needed
> by DSI panels when sending DSI commands. One level is the equivalent of
> phy_ref rate from the PLL rate. Since the PLL rate is targeted as highest
> possible, each level should not get the crtc_clock too low, compared to
> the actual clock.
Did you check whether this is only needed during panel prepare (when the
image sequence is being sent)? I wonder if this is an artifact of the
driver sending pixel data too early - and if it's not whether we have
something else wrong so that we need to have a longer blanking period
with some panels?
Cheers,
-- Guido
>
> Example for a clock of 132M, with "clock-drop-level = <1>" in dts file
> will result in a crtc_clock of 129M, using the following logic:
> - video_pll rate to provide both phy_ref rate of 24M and pixel-clock
> of 132M is 1056M (divisor /43 for phy_ref and /8 for pixel-clock)
> - from this rate, we subtract the equivalent of phy_ref (24M) but
> keep the same divisor. This way, the video_pll rate will be 1056 - 24
> = 1032M.
> - new pixel-clock will be: 1032 / 8 = 129M
>
> For a "clock-drop-level = <2>", new pixel-clock will be:
> (1056 - (24 * 2)) / 8 = 1008 / 8 = 126M
>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
> drivers/gpu/drm/bridge/nwl-dsi.c | 12 ++++++++----
> 1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
> index 1228466..ac4aa0a 100644
> --- a/drivers/gpu/drm/bridge/nwl-dsi.c
> +++ b/drivers/gpu/drm/bridge/nwl-dsi.c
> @@ -142,6 +142,7 @@ struct nwl_dsi {
>
> struct nwl_dsi_transfer *xfer;
> struct list_head valid_modes;
> + u32 clk_drop_lvl;
> };
>
> static const struct regmap_config nwl_dsi_regmap_config = {
> @@ -842,13 +843,14 @@ static unsigned long nwl_dsi_get_lcm(unsigned long a, unsigned long b)
>
> return ((unsigned long long)a * b) / gcf;
> }
> -
> /*
> * This function tries to adjust the crtc_clock for a DSI device in such a way
> * that the video pll will be able to satisfy both Display Controller pixel
> * clock (feeding out DPI interface) and our input phy_ref clock.
> + * Also, the DC pixel clock must be lower than the actual clock in order to
> + * have enough blanking space to send DSI commands, if the device is a panel.
> */
> -static void nwl_dsi_setup_pll_config(struct mode_config *config)
> +static void nwl_dsi_setup_pll_config(struct mode_config *config, u32 lvl)
> {
> unsigned long pll_rate;
> int div;
> @@ -908,7 +910,6 @@ static void nwl_dsi_setup_pll_config(struct mode_config *config)
> }
> }
>
> -
> /*
> * This function will try the required phy speed for current mode
> * If the phy speed can be achieved, the phy will save the speed
> @@ -1103,7 +1104,7 @@ nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>
> pll_rate = config->pll_rates[config->phy_rate_idx];
> if (dsi->pll_clk && !pll_rate)
> - nwl_dsi_setup_pll_config(config);
> + nwl_dsi_setup_pll_config(config, dsi->clk_drop_lvl);
>
> return MODE_OK;
> }
> @@ -1248,6 +1249,7 @@ static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
> static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
> {
> struct platform_device *pdev = to_platform_device(dsi->dev);
> + struct device_node *np = dsi->dev->of_node;
> struct clk *clk;
> void __iomem *base;
> int ret;
> @@ -1364,6 +1366,8 @@ static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
> return PTR_ERR(dsi->rst_dpi);
> }
>
> + of_property_read_u32(np, "fsl,clock-drop-level", &dsi->clk_drop_lvl);
> +
> INIT_LIST_HEAD(&dsi->valid_modes);
>
> return 0;
> --
> 2.7.4
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 4/5] dt-bindings: display/bridge: nwl-dsi: Document fsl,clock-drop-level property
2020-08-28 11:13 [PATCH 0/5] Add new features to nwl-dsi driver Robert Chiras (OSS)
` (2 preceding siblings ...)
2020-08-28 11:13 ` [PATCH 3/5] drm/bridge: nwl-dsi: Add support for clock-drop-level Robert Chiras (OSS)
@ 2020-08-28 11:13 ` Robert Chiras (OSS)
2020-09-04 17:05 ` Guido Günther
` (2 more replies)
2020-08-28 11:13 ` [PATCH 5/5] drm/bridge: nwl-dsi: add support for DCSS Robert Chiras (OSS)
2020-09-04 17:08 ` [PATCH 0/5] Add new features to nwl-dsi driver Guido Günther
5 siblings, 3 replies; 14+ messages in thread
From: Robert Chiras (OSS) @ 2020-08-28 11:13 UTC (permalink / raw)
To: Rob Herring, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sam Ravnborg, Guido Günther,
Fabio Estevam, Ondrej Jirman
Cc: David Airlie, Daniel Vetter, dri-devel, devicetree, linux-kernel,
linux-imx
From: Robert Chiras <robert.chiras@nxp.com>
Add documentation for a new property: 'fsl,clock-drop-level'.
Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
---
Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
index 8b5741b..b415f4e 100644
--- a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
@@ -143,6 +143,10 @@ properties:
additionalProperties: false
+ clock-drop-level:
+ description:
+ Specifies the level at wich the crtc_clock should be dropped
+
patternProperties:
"^panel@[0-9]+$":
type: object
--
2.7.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 4/5] dt-bindings: display/bridge: nwl-dsi: Document fsl,clock-drop-level property
2020-08-28 11:13 ` [PATCH 4/5] dt-bindings: display/bridge: nwl-dsi: Document fsl,clock-drop-level property Robert Chiras (OSS)
@ 2020-09-04 17:05 ` Guido Günther
2020-09-05 16:07 ` Laurent Pinchart
2020-09-09 20:43 ` Rob Herring
2 siblings, 0 replies; 14+ messages in thread
From: Guido Günther @ 2020-09-04 17:05 UTC (permalink / raw)
To: Robert Chiras (OSS)
Cc: Rob Herring, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sam Ravnborg, Fabio Estevam,
Ondrej Jirman, David Airlie, Daniel Vetter, dri-devel,
devicetree, linux-kernel, linux-imx
Hi,
On Fri, Aug 28, 2020 at 02:13:31PM +0300, Robert Chiras (OSS) wrote:
> From: Robert Chiras <robert.chiras@nxp.com>
>
> Add documentation for a new property: 'fsl,clock-drop-level'.
>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
> Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> index 8b5741b..b415f4e 100644
> --- a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> +++ b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> @@ -143,6 +143,10 @@ properties:
>
> additionalProperties: false
>
> + clock-drop-level:
Property is called fsl,clock-drop-level.
> + description:
> + Specifies the level at wich the crtc_clock should be dropped
> +
> patternProperties:
> "^panel@[0-9]+$":
> type: object
> --
> 2.7.4
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 4/5] dt-bindings: display/bridge: nwl-dsi: Document fsl,clock-drop-level property
2020-08-28 11:13 ` [PATCH 4/5] dt-bindings: display/bridge: nwl-dsi: Document fsl,clock-drop-level property Robert Chiras (OSS)
2020-09-04 17:05 ` Guido Günther
@ 2020-09-05 16:07 ` Laurent Pinchart
2020-09-09 20:43 ` Rob Herring
2 siblings, 0 replies; 14+ messages in thread
From: Laurent Pinchart @ 2020-09-05 16:07 UTC (permalink / raw)
To: Robert Chiras (OSS)
Cc: Rob Herring, Andrzej Hajda, Neil Armstrong, Jonas Karlman,
Jernej Skrabec, Sam Ravnborg, Guido Günther, Fabio Estevam,
Ondrej Jirman, David Airlie, Daniel Vetter, dri-devel,
devicetree, linux-kernel, linux-imx
Hi Robert,
Thank you for the patch.
On Fri, Aug 28, 2020 at 02:13:31PM +0300, Robert Chiras (OSS) wrote:
> From: Robert Chiras <robert.chiras@nxp.com>
>
> Add documentation for a new property: 'fsl,clock-drop-level'.
>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
> Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> index 8b5741b..b415f4e 100644
> --- a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> +++ b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> @@ -143,6 +143,10 @@ properties:
>
> additionalProperties: false
>
> + clock-drop-level:
> + description:
> + Specifies the level at wich the crtc_clock should be dropped
> +
There's no "crtc_clock" defined in the bindings. As DT bindings
shouldn't be tied to a particular driver implementation, could you
document this property without referring to concepts specific to the
driver ? I think the documentation should also be extended, looking at
this patch I have no idea what this does and how to compute the value
that should be set.
> patternProperties:
> "^panel@[0-9]+$":
> type: object
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 4/5] dt-bindings: display/bridge: nwl-dsi: Document fsl,clock-drop-level property
2020-08-28 11:13 ` [PATCH 4/5] dt-bindings: display/bridge: nwl-dsi: Document fsl,clock-drop-level property Robert Chiras (OSS)
2020-09-04 17:05 ` Guido Günther
2020-09-05 16:07 ` Laurent Pinchart
@ 2020-09-09 20:43 ` Rob Herring
2 siblings, 0 replies; 14+ messages in thread
From: Rob Herring @ 2020-09-09 20:43 UTC (permalink / raw)
To: Robert Chiras (OSS)
Cc: Andrzej Hajda, Neil Armstrong, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Sam Ravnborg, Guido Günther, Fabio Estevam,
Ondrej Jirman, David Airlie, Daniel Vetter, dri-devel,
devicetree, linux-kernel, linux-imx
On Fri, Aug 28, 2020 at 02:13:31PM +0300, Robert Chiras (OSS) wrote:
> From: Robert Chiras <robert.chiras@nxp.com>
>
> Add documentation for a new property: 'fsl,clock-drop-level'.
>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
> Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> index 8b5741b..b415f4e 100644
> --- a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> +++ b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> @@ -143,6 +143,10 @@ properties:
>
> additionalProperties: false
>
> + clock-drop-level:
fsl, ?
> + description:
> + Specifies the level at wich the crtc_clock should be dropped
Needs a type $ref.
> +
> patternProperties:
> "^panel@[0-9]+$":
> type: object
> --
> 2.7.4
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 5/5] drm/bridge: nwl-dsi: add support for DCSS
2020-08-28 11:13 [PATCH 0/5] Add new features to nwl-dsi driver Robert Chiras (OSS)
` (3 preceding siblings ...)
2020-08-28 11:13 ` [PATCH 4/5] dt-bindings: display/bridge: nwl-dsi: Document fsl,clock-drop-level property Robert Chiras (OSS)
@ 2020-08-28 11:13 ` Robert Chiras (OSS)
2020-09-04 17:05 ` Guido Günther
2020-09-04 17:08 ` [PATCH 0/5] Add new features to nwl-dsi driver Guido Günther
5 siblings, 1 reply; 14+ messages in thread
From: Robert Chiras (OSS) @ 2020-08-28 11:13 UTC (permalink / raw)
To: Rob Herring, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sam Ravnborg, Guido Günther,
Fabio Estevam, Ondrej Jirman
Cc: David Airlie, Daniel Vetter, dri-devel, devicetree, linux-kernel,
linux-imx
From: Laurentiu Palcu <laurentiu.palcu@nxp.com>
DCSS needs active low VSYNC and HSYNC. Also, move the input selection in
the probe function, as this will not change at runtime.
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
---
drivers/gpu/drm/bridge/nwl-dsi.c | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
index ac4aa0a..c30f7a8 100644
--- a/drivers/gpu/drm/bridge/nwl-dsi.c
+++ b/drivers/gpu/drm/bridge/nwl-dsi.c
@@ -143,6 +143,7 @@ struct nwl_dsi {
struct nwl_dsi_transfer *xfer;
struct list_head valid_modes;
u32 clk_drop_lvl;
+ bool use_dcss;
};
static const struct regmap_config nwl_dsi_regmap_config = {
@@ -1036,16 +1037,16 @@ static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi,
static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+ struct drm_display_mode *adjusted)
{
struct nwl_dsi *dsi = bridge_to_dsi(bridge);
struct mode_config *config;
unsigned long pll_rate;
DRM_DEV_DEBUG_DRIVER(dsi->dev, "Fixup mode:\n");
- drm_mode_debug_printmodeline(adjusted_mode);
+ drm_mode_debug_printmodeline(adjusted);
- config = nwl_dsi_mode_probe(dsi, adjusted_mode);
+ config = nwl_dsi_mode_probe(dsi, adjusted);
if (!config)
return false;
@@ -1067,12 +1068,16 @@ static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
}
/* Update the crtc_clock to be used by display controller */
if (config->crtc_clock)
- adjusted_mode->crtc_clock = config->crtc_clock / 1000;
+ adjusted->crtc_clock = config->crtc_clock / 1000;
-
- /* At least LCDIF + NWL needs active high sync */
- adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
- adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
+ if (!dsi->use_dcss) {
+ /* At least LCDIF + NWL needs active high sync */
+ adjusted->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
+ adjusted->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
+ } else {
+ adjusted->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
+ adjusted->flags |= (DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
+ }
return true;
}
@@ -1400,6 +1405,9 @@ static int nwl_dsi_select_input(struct nwl_dsi *dsi)
DRM_DEV_ERROR(dsi->dev, "Failed to select input: %d\n", ret);
of_node_put(remote);
+
+ dsi->use_dcss = use_dcss;
+
return ret;
}
--
2.7.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 5/5] drm/bridge: nwl-dsi: add support for DCSS
2020-08-28 11:13 ` [PATCH 5/5] drm/bridge: nwl-dsi: add support for DCSS Robert Chiras (OSS)
@ 2020-09-04 17:05 ` Guido Günther
0 siblings, 0 replies; 14+ messages in thread
From: Guido Günther @ 2020-09-04 17:05 UTC (permalink / raw)
To: Robert Chiras (OSS)
Cc: Rob Herring, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sam Ravnborg, Fabio Estevam,
Ondrej Jirman, David Airlie, Daniel Vetter, dri-devel,
devicetree, linux-kernel, linux-imx
Hi,
On Fri, Aug 28, 2020 at 02:13:32PM +0300, Robert Chiras (OSS) wrote:
> From: Laurentiu Palcu <laurentiu.palcu@nxp.com>
>
> DCSS needs active low VSYNC and HSYNC. Also, move the input selection in
> the probe function, as this will not change at runtime.
>
> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
> drivers/gpu/drm/bridge/nwl-dsi.c | 24 ++++++++++++++++--------
> 1 file changed, 16 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
> index ac4aa0a..c30f7a8 100644
> --- a/drivers/gpu/drm/bridge/nwl-dsi.c
> +++ b/drivers/gpu/drm/bridge/nwl-dsi.c
> @@ -143,6 +143,7 @@ struct nwl_dsi {
> struct nwl_dsi_transfer *xfer;
> struct list_head valid_modes;
> u32 clk_drop_lvl;
> + bool use_dcss;
> };
>
> static const struct regmap_config nwl_dsi_regmap_config = {
> @@ -1036,16 +1037,16 @@ static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi,
>
> static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
> const struct drm_display_mode *mode,
> - struct drm_display_mode *adjusted_mode)
> + struct drm_display_mode *adjusted)
why the rename?
> {
> struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> struct mode_config *config;
> unsigned long pll_rate;
>
> DRM_DEV_DEBUG_DRIVER(dsi->dev, "Fixup mode:\n");
> - drm_mode_debug_printmodeline(adjusted_mode);
> + drm_mode_debug_printmodeline(adjusted);
>
> - config = nwl_dsi_mode_probe(dsi, adjusted_mode);
> + config = nwl_dsi_mode_probe(dsi, adjusted);
> if (!config)
> return false;
>
> @@ -1067,12 +1068,16 @@ static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
> }
> /* Update the crtc_clock to be used by display controller */
> if (config->crtc_clock)
> - adjusted_mode->crtc_clock = config->crtc_clock / 1000;
> + adjusted->crtc_clock = config->crtc_clock / 1000;
>
> -
> - /* At least LCDIF + NWL needs active high sync */
> - adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
> - adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
> + if (!dsi->use_dcss) {
> + /* At least LCDIF + NWL needs active high sync */
> + adjusted->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
> + adjusted->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
> + } else {
> + adjusted->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
> + adjusted->flags |= (DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
> + }
>
> return true;
> }
> @@ -1400,6 +1405,9 @@ static int nwl_dsi_select_input(struct nwl_dsi *dsi)
> DRM_DEV_ERROR(dsi->dev, "Failed to select input: %d\n", ret);
>
> of_node_put(remote);
> +
> + dsi->use_dcss = use_dcss;
If we need to preserve it we can assign `dsi->use_dcss` directly and
drop `use_dcss`.
Cheers,
-- Guido
> +
> return ret;
> }
>
> --
> 2.7.4
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 0/5] Add new features to nwl-dsi driver
2020-08-28 11:13 [PATCH 0/5] Add new features to nwl-dsi driver Robert Chiras (OSS)
` (4 preceding siblings ...)
2020-08-28 11:13 ` [PATCH 5/5] drm/bridge: nwl-dsi: add support for DCSS Robert Chiras (OSS)
@ 2020-09-04 17:08 ` Guido Günther
5 siblings, 0 replies; 14+ messages in thread
From: Guido Günther @ 2020-09-04 17:08 UTC (permalink / raw)
To: Robert Chiras (OSS)
Cc: Rob Herring, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sam Ravnborg, Fabio Estevam,
Ondrej Jirman, David Airlie, Daniel Vetter, dri-devel,
devicetree, linux-kernel, linux-imx
Hi Robert,
On Fri, Aug 28, 2020 at 02:13:27PM +0300, Robert Chiras (OSS) wrote:
> From: Robert Chiras <robert.chiras@nxp.com>
>
> This patch-set adds the new following features to the nwl-dsi bridge driver:
>
> 1. Control Video PLL from nwl-dsi driver
>
> Add support for the Video PLL into the nwl-dsi driver, in order
> to better control it's rate, depending on the requested video mode.
> Controlling the Video PLL from nwl-dsi is usefull, since it both drives the DC
> pixel-clock and DPHY phy_ref clock.
> On i.MX8MQ, the DC can be either DCSS or LCDIF.
>
> 2. Add new property to nwl-dsi: clock-drop-level
>
> This new property is usefull in order to use DSI panels with the nwl-dsi
> driver which require a higher overhead to the pixel-clock.
> For example, the Raydium RM67191 DSI Panel works with 132M pixel-clock,
> but it needs an overhead in order to work properly. So, the actual pixel-clock
> fed into the DSI DPI interface needs to be lower than the one used ad DSI output.
> This new property addresses this matter.
>
> 3. Add support to handle both inputs for nwl-dsi: DCSS and LCDIF
Thanks. I've tested the drop-clock-level part with mxsfb on a Librem 5
devkit and it removes the slight flickering we've seen before (and which
could be worked around by reducing the input pixel clock so 1 and 3 are
Tested-by: Guido Günther <agx@sigxcpu.org>
I've have added some comments to the individual patches and try to get
around to check out the DCSS part too.
Cheers,
-- Guido
>
> Laurentiu Palcu (1):
> drm/bridge: nwl-dsi: add support for DCSS
>
> Robert Chiras (4):
> drm/bridge: nwl-dsi: Add support for video_pll
> dt-bindings: display/bridge: nwl-dsi: Document video_pll clock
> drm/bridge: nwl-dsi: Add support for clock-drop-level
> dt-bindings: display/bridge: nwl-dsi: Document fsl,clock-drop-level
> property
>
> .../bindings/display/bridge/nwl-dsi.yaml | 7 +
> drivers/gpu/drm/bridge/nwl-dsi.c | 338 ++++++++++++++++++++-
> 2 files changed, 336 insertions(+), 9 deletions(-)
>
> --
> 2.7.4
>
^ permalink raw reply [flat|nested] 14+ messages in thread