From: Marek Vasut <marex@denx.de>
To: dri-devel@lists.freedesktop.org
Cc: Marek Vasut <marex@denx.de>,
Neil Armstrong <narmstrong@baylibre.com>,
Jonas Karlman <jonas@kwiboo.se>,
Laurent Pinchart <Laurent.pinchart@ideasonboard.com>,
Sam Ravnborg <sam@ravnborg.org>,
Maxime Ripard <maxime@cerno.tech>
Subject: [PATCH 7/7] drm/bridge: tc358767: Add support for PLL clock derivation from DSI HS clock
Date: Sat, 19 Feb 2022 01:28:44 +0100 [thread overview]
Message-ID: <20220219002844.362157-8-marex@denx.de> (raw)
In-Reply-To: <20220219002844.362157-1-marex@denx.de>
The TC358767/TC358867/TC9595 are all capable of operating without RefClk
Xtal in DSI-to-DPI mode. In this mode of operation, the chip PLLs are fed
with clock derived from the DSI HS clock.
The SYSPLL is fed from DSI HS clock divided by 2*7 and the resulting clock
must match the allowed RefClk supported by the chip, which is either of
13 MHz, 26 MHz, 19.2 MHz, 38.4 MHz and no other RefClk clock are allowed.
That limits the allowed DSI HS clock as well. The PixelPLL is fed directly
with DSI HS clock SDR and derives DPI pixel clock from those clock.
In order to impose these very specific limitations on DSI HS clock, this
patch implements .atomic_get_input_bus_cfgs callback and calculates the
most suitable clock to be passed on to the DSI host bridge.
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Jonas Karlman <jonas@kwiboo.se>
Cc: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Neil Armstrong <narmstrong@baylibre.com>
Cc: Sam Ravnborg <sam@ravnborg.org>
---
drivers/gpu/drm/bridge/tc358767.c | 97 +++++++++++++++++++++++++------
1 file changed, 79 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index 2b9fceb1333fa..98ea07912f721 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -250,11 +250,13 @@
#define PLLEN BIT(0)
#define PXL_PLLPARAM 0x0914
#define IN_SEL_REFCLK (0 << 14)
+#define IN_SEL_DSI_HSCK (1 << 14)
#define SYS_PLLPARAM 0x0918
#define REF_FREQ_38M4 (0 << 8) /* 38.4 MHz */
#define REF_FREQ_19M2 (1 << 8) /* 19.2 MHz */
#define REF_FREQ_26M (2 << 8) /* 26 MHz */
#define REF_FREQ_13M (3 << 8) /* 13 MHz */
+#define SYSCLK_SEL_DSI_HSCLK (1 << 4)
#define SYSCLK_SEL_LSCLK (0 << 4)
#define LSCLK_DIV_1 (0 << 0)
#define LSCLK_DIV_2 (1 << 0)
@@ -492,6 +494,27 @@ static u32 tc_srcctrl(struct tc_data *tc)
return reg;
}
+static int tc_mode_to_refclk(struct tc_data *tc, struct drm_display_mode *mode)
+{
+ u32 clock;
+
+ /* Minimum DSI clock lane clock. */
+ clock = (mode->htotal * mode->vtotal * 24 * 60) / tc->dsi_lanes / 2;
+ clock /= 7; /* DSIClk/7 */
+
+ /* Round the DSI clock to nearest supported faster clock. */
+ if (clock < 13000000) /* DSIClk / 2 / 7 = 13 MHz RefClk */
+ return 13000000;
+ else if (clock < 19200000) /* DSIClk / 2 / 7 = 19.2 MHz RefClk */
+ return 19200000;
+ else if (clock < 26000000) /* DSIClk / 2 / 7 = 26 MHz RefClk */
+ return 26000000;
+ else if (clock < 38400000) /* DSIClk / 2 / 7 = 38.4 MHz RefClk */
+ return 38400000;
+ else
+ return 0;
+}
+
static int tc_pllupdate(struct tc_data *tc, unsigned int pllctrl)
{
int ret;
@@ -508,6 +531,7 @@ static int tc_pllupdate(struct tc_data *tc, unsigned int pllctrl)
static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock)
{
+ unsigned long rate;
int ret;
int i_pre, best_pre = 1;
int i_post, best_post = 1;
@@ -596,10 +620,17 @@ static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock)
pxl_pllparam = vco_hi << 24; /* For PLL VCO >= 300 MHz = 1 */
pxl_pllparam |= ext_div[best_pre] << 20; /* External Pre-divider */
pxl_pllparam |= ext_div[best_post] << 16; /* External Post-divider */
- pxl_pllparam |= IN_SEL_REFCLK; /* Use RefClk as PLL input */
pxl_pllparam |= best_div << 8; /* Divider for PLL RefClk */
pxl_pllparam |= best_mul; /* Multiplier for PLL */
+ rate = clk_get_rate(tc->refclk);
+
+ /* DSI-to-DPI mode with clock inferred from DSI HS */
+ if (rate == 0 && tc->bridge.type == DRM_MODE_CONNECTOR_DPI)
+ pxl_pllparam |= IN_SEL_DSI_HSCK; /* Use DSI HSCK as PLL input */
+ else
+ pxl_pllparam |= IN_SEL_REFCLK; /* Use RefClk as PLL input */
+
ret = regmap_write(tc->regmap, PXL_PLLPARAM, pxl_pllparam);
if (ret)
return ret;
@@ -637,9 +668,19 @@ static int tc_stream_clock_calc(struct tc_data *tc)
static int tc_set_syspllparam(struct tc_data *tc)
{
unsigned long rate;
- u32 pllparam = SYSCLK_SEL_LSCLK | LSCLK_DIV_2;
+ u32 pllparam = LSCLK_DIV_2;
rate = clk_get_rate(tc->refclk);
+
+ /* DSI-to-DPI mode with clock inferred from DSI HS */
+ if (rate == 0 && tc->bridge.type == DRM_MODE_CONNECTOR_DPI) {
+ pllparam |= SYSCLK_SEL_DSI_HSCLK;
+ /* Get RefClk rate from DSI HS clock. */
+ rate = tc_mode_to_refclk(tc, &tc->mode);
+ } else {
+ pllparam |= SYSCLK_SEL_LSCLK;
+ }
+
switch (rate) {
case 38400000:
pllparam |= REF_FREQ_38M4;
@@ -1266,9 +1307,18 @@ static int tc_dpi_stream_enable(struct tc_data *tc)
if (ret)
return ret;
+ value = clk_get_rate(tc->refclk);
+ if (!value) {
+ /*
+ * If RefClk Xtal is not connected, it is the DSI HS clock
+ * SDR that is directly fed into the Pixel PLL and used as
+ * input clock. Hence no multiplication of this value by 2.
+ */
+ value = 7 * tc_mode_to_refclk(tc, &tc->mode);
+ }
+
/* Pixel PLL must always be enabled for DPI mode */
- ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk),
- 1000 * tc->mode.clock);
+ ret = tc_pxl_pll_en(tc, value, 1000 * tc->mode.clock);
if (ret)
return ret;
@@ -1781,30 +1831,38 @@ static void tc_edp_bridge_detach(struct drm_bridge *bridge)
drm_dp_aux_unregister(&bridge_to_tc(bridge)->aux);
}
-#define MAX_INPUT_SEL_FORMATS 1
+#define MAX_INPUT_SEL_CONFIGS 1
-static u32 *
-tc_dpi_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+static struct drm_bus_cfg *
+tc_dpi_atomic_get_input_bus_cfgs(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
- u32 output_fmt,
- unsigned int *num_input_fmts)
+ struct drm_bus_cfg *out_cfg,
+ unsigned int *num_input_cfgs)
{
- u32 *input_fmts;
+ struct tc_data *tc = bridge_to_tc(bridge);
+ struct drm_bus_cfg *input_cfgs;
+ u32 clock;
- *num_input_fmts = 0;
+ /* Minimum DSI clock lane clock in kHz (DSIClk / 7 = RefClk). */
+ clock = 2 * 7 * tc_mode_to_refclk(tc, &crtc_state->adjusted_mode) / 1000;
+ if (clock == 0)
+ return NULL;
- input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
+ *num_input_cfgs = 0;
+
+ input_cfgs = kcalloc(MAX_INPUT_SEL_CONFIGS, sizeof(*input_cfgs),
GFP_KERNEL);
- if (!input_fmts)
+ if (!input_cfgs)
return NULL;
/* This is the DSI-end bus format */
- input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
- *num_input_fmts = 1;
+ input_cfgs[0].format = MEDIA_BUS_FMT_RGB888_1X24;
+ input_cfgs[0].clock = clock; /* kHz */
+ *num_input_cfgs = MAX_INPUT_SEL_CONFIGS;
- return input_fmts;
+ return input_cfgs;
}
static const struct drm_bridge_funcs tc_dpi_bridge_funcs = {
@@ -1817,7 +1875,7 @@ static const struct drm_bridge_funcs tc_dpi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
- .atomic_get_input_bus_fmts = tc_dpi_atomic_get_input_bus_fmts,
+ .atomic_get_input_bus_cfgs = tc_dpi_atomic_get_input_bus_cfgs,
};
static const struct drm_bridge_funcs tc_bridge_funcs = {
@@ -2107,7 +2165,10 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
usleep_range(5000, 10000);
}
- tc->refclk = devm_clk_get(dev, "ref");
+ if (tc->bridge.type == DRM_MODE_CONNECTOR_DPI)
+ tc->refclk = devm_clk_get_optional(dev, "ref");
+ else
+ tc->refclk = devm_clk_get(dev, "ref");
if (IS_ERR(tc->refclk)) {
ret = PTR_ERR(tc->refclk);
dev_err(dev, "Failed to get refclk: %d\n", ret);
--
2.34.1
next prev parent reply other threads:[~2022-02-19 0:29 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-02-19 0:28 [RFC][PATCH 0/7] drm/bridge: Add support for selecting DSI host HS clock from DSI bridge Marek Vasut
2022-02-19 0:28 ` [PATCH 1/7] drm/bridge: Pass struct drm_bus_cfg to select_bus_fmt_recursive() Marek Vasut
2022-02-19 0:28 ` [PATCH 2/7] drm/bridge: Add new atomic_get_input_bus_cfgs callback Marek Vasut
2022-02-19 0:28 ` [PATCH 3/7] drm/bridge: Extend struct drm_bus_cfg with clock field Marek Vasut
2022-02-24 15:19 ` Maxime Ripard
2022-02-24 20:07 ` Marek Vasut
2022-02-25 10:51 ` Maxime Ripard
2022-02-19 0:28 ` [PATCH 4/7] drm/bridge: dw-mipi-dsi: Move PLL setup into atomic_enable Marek Vasut
2022-02-19 0:28 ` [PATCH 5/7] drm/bridge: dw-mipi-dsi: Pass bridge state into dw_mipi_dsi_get_lane_mbps() Marek Vasut
2022-02-19 0:28 ` [PATCH 6/7] drm/bridge: dw-mipi-dsi: Prefer DSI bus clock settings from bridge_state Marek Vasut
2022-02-19 0:28 ` Marek Vasut [this message]
2022-02-24 15:40 ` [RFC][PATCH 0/7] drm/bridge: Add support for selecting DSI host HS clock from DSI bridge Maxime Ripard
2022-02-24 20:24 ` Marek Vasut
2022-02-25 8:40 ` Maxime Ripard
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220219002844.362157-8-marex@denx.de \
--to=marex@denx.de \
--cc=Laurent.pinchart@ideasonboard.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=jonas@kwiboo.se \
--cc=maxime@cerno.tech \
--cc=narmstrong@baylibre.com \
--cc=sam@ravnborg.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox