From nobody Wed Dec 17 17:23:53 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3615EC61DF4 for ; Fri, 24 Nov 2023 07:18:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344837AbjKXHSd (ORCPT ); Fri, 24 Nov 2023 02:18:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37316 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231334AbjKXHSQ (ORCPT ); Fri, 24 Nov 2023 02:18:16 -0500 Received: from mx07-00178001.pphosted.com (mx08-00178001.pphosted.com [91.207.212.93]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5588BD5E; Thu, 23 Nov 2023 23:18:22 -0800 (PST) Received: from pps.filterd (m0046660.ppops.net [127.0.0.1]) by mx07-00178001.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3ANMsBFZ021992; Fri, 24 Nov 2023 08:17:14 +0100 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=foss.st.com; h= from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:content-type; s= selector1; bh=SHgmO6NomtaIRhqk5gfHNSdYmangnboMcVbymiu6fk4=; b=GO CnsHIGVuBTYgcETotuMmtKLL7GyJfTWiNogDfK7VpkiDrZUjwr4EdWgbq2rGc4RN zjUd3K/drHOwq68+wr1y9IM2T2C3D9c423zJCoCaZY4cFBmxxIcZ9T2RzrRhOLsS pVkqLBXK+wPFIDgXVjABsPbBIaEjyAVpnddfgd9kf3SIEJcV75kjkQYZ/dycZyGT SW/X+BgMFJDDAFKP0iiHL/UGFzoPqb28j+CEFsRlTXe4yGynCmRlVcSTiuzZkAzG me7aevUehKf9uSqPvNoU6c1IcU19vNS7I9TaQJhH6fn7NXEC6PBOwZOpA6BHnHnr pE+1lqcM1cMhL+71C2xQ== Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx07-00178001.pphosted.com (PPS) with ESMTPS id 3uj46nnt8d-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 24 Nov 2023 08:17:14 +0100 (CET) Received: from euls16034.sgp.st.com (euls16034.sgp.st.com [10.75.44.20]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id E8EC510002A; Fri, 24 Nov 2023 08:17:13 +0100 (CET) Received: from Webmail-eu.st.com (shfdag1node2.st.com [10.75.129.70]) by euls16034.sgp.st.com (STMicroelectronics) with ESMTP id DE152215137; Fri, 24 Nov 2023 08:17:13 +0100 (CET) Received: from localhost (10.252.31.103) by SHFDAG1NODE2.st.com (10.75.129.70) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.27; Fri, 24 Nov 2023 08:17:13 +0100 From: Raphael Gallais-Pou To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maxime Coquelin , Alexandre Torgue , Yannick Fertre , Philippe Cornu , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter CC: , , , , Subject: [PATCH RESEND 2/3] drm/stm: dsi: expose DSI PHY internal clock Date: Fri, 24 Nov 2023 08:16:48 +0100 Message-ID: <20231124071649.372270-3-raphael.gallais-pou@foss.st.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231124071649.372270-1-raphael.gallais-pou@foss.st.com> References: <20231124071649.372270-1-raphael.gallais-pou@foss.st.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.252.31.103] X-ClientProxiedBy: EQNCAS1NODE3.st.com (10.75.129.80) To SHFDAG1NODE2.st.com (10.75.129.70) X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.987,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2023-11-23_15,2023-11-22_01,2023-05-22_02 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" DSISRC __________ __\_ | \ pll4_p_ck ->| 1 |____dsi_k ck_dsi_phy ->| 0 | |____/ A DSI clock is missing in the clock framework. Looking at the clk_summary, it appears that 'ck_dsi_phy' is not implemented. Since the DSI kernel clock is based on the internal DSI pll. The common clock driver can not directly expose this 'ck_dsi_phy' clock because it does not contain any common registers with the DSI. Thus it needs to be done directly within the DSI phy driver. Signed-off-by: Raphael Gallais-Pou --- drivers/gpu/drm/stm/dw_mipi_dsi-stm.c | 246 ++++++++++++++++++++++---- 1 file changed, 215 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw= _mipi_dsi-stm.c index a0ca4d7f3b8a..1a4a88ecffe0 100644 --- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c @@ -7,7 +7,9 @@ */ =20 #include +#include #include +#include #include #include #include @@ -77,9 +79,12 @@ enum dsi_color { =20 struct dw_mipi_dsi_stm { void __iomem *base; + struct device *dev; struct clk *pllref_clk; struct clk *pclk; + struct clk_hw txbyte_clk; struct dw_mipi_dsi *dsi; + struct dw_mipi_dsi_plat_data pdata; u32 hw_version; int lane_min_kbps; int lane_max_kbps; @@ -196,29 +201,198 @@ static int dsi_pll_get_params(struct dw_mipi_dsi_stm= *dsi, return 0; } =20 -static int dw_mipi_dsi_phy_init(void *priv_data) +#define clk_to_dw_mipi_dsi_stm(clk) \ + container_of(clk, struct dw_mipi_dsi_stm, txbyte_clk) + +static void dw_mipi_dsi_clk_disable(struct clk_hw *clk) { - struct dw_mipi_dsi_stm *dsi =3D priv_data; + struct dw_mipi_dsi_stm *dsi =3D clk_to_dw_mipi_dsi_stm(clk); + + DRM_DEBUG_DRIVER("\n"); + + /* Disable the DSI PLL */ + dsi_clear(dsi, DSI_WRPCR, WRPCR_PLLEN); + + /* Disable the regulator */ + dsi_clear(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN); +} + +static int dw_mipi_dsi_clk_enable(struct clk_hw *clk) +{ + struct dw_mipi_dsi_stm *dsi =3D clk_to_dw_mipi_dsi_stm(clk); u32 val; int ret; =20 + DRM_DEBUG_DRIVER("\n"); + /* Enable the regulator */ dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN); - ret =3D readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS, - SLEEP_US, TIMEOUT_US); + ret =3D readl_poll_timeout_atomic(dsi->base + DSI_WISR, val, val & WISR_R= RS, + SLEEP_US, TIMEOUT_US); if (ret) DRM_DEBUG_DRIVER("!TIMEOUT! waiting REGU, let's continue\n"); =20 /* Enable the DSI PLL & wait for its lock */ dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN); - ret =3D readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS, - SLEEP_US, TIMEOUT_US); + ret =3D readl_poll_timeout_atomic(dsi->base + DSI_WISR, val, val & WISR_P= LLLS, + SLEEP_US, TIMEOUT_US); if (ret) DRM_DEBUG_DRIVER("!TIMEOUT! waiting PLL, let's continue\n"); =20 return 0; } =20 +static int dw_mipi_dsi_clk_is_enabled(struct clk_hw *hw) +{ + struct dw_mipi_dsi_stm *dsi =3D clk_to_dw_mipi_dsi_stm(hw); + + return dsi_read(dsi, DSI_WRPCR) & WRPCR_PLLEN; +} + +static unsigned long dw_mipi_dsi_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct dw_mipi_dsi_stm *dsi =3D clk_to_dw_mipi_dsi_stm(hw); + unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; + u32 val; + + DRM_DEBUG_DRIVER("\n"); + + pll_in_khz =3D (unsigned int)(parent_rate / 1000); + + val =3D dsi_read(dsi, DSI_WRPCR); + + idf =3D (val & WRPCR_IDF) >> 11; + if (!idf) + idf =3D 1; + ndiv =3D (val & WRPCR_NDIV) >> 2; + odf =3D int_pow(2, (val & WRPCR_ODF) >> 16); + + /* Get the adjusted pll out value */ + pll_out_khz =3D dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); + + return (unsigned long)pll_out_khz * 1000; +} + +static long dw_mipi_dsi_clk_round_rate(struct clk_hw *hw, unsigned long ra= te, + unsigned long *parent_rate) +{ + struct dw_mipi_dsi_stm *dsi =3D clk_to_dw_mipi_dsi_stm(hw); + unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; + int ret; + + DRM_DEBUG_DRIVER("\n"); + + pll_in_khz =3D (unsigned int)(*parent_rate / 1000); + + /* Compute best pll parameters */ + idf =3D 0; + ndiv =3D 0; + odf =3D 0; + + ret =3D dsi_pll_get_params(dsi, pll_in_khz, rate / 1000, + &idf, &ndiv, &odf); + if (ret) + DRM_WARN("Warning dsi_pll_get_params(): bad params\n"); + + /* Get the adjusted pll out value */ + pll_out_khz =3D dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); + + return pll_out_khz * 1000; +} + +static int dw_mipi_dsi_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct dw_mipi_dsi_stm *dsi =3D clk_to_dw_mipi_dsi_stm(hw); + unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; + int ret; + u32 val; + + DRM_DEBUG_DRIVER("\n"); + + pll_in_khz =3D (unsigned int)(parent_rate / 1000); + + /* Compute best pll parameters */ + idf =3D 0; + ndiv =3D 0; + odf =3D 0; + + ret =3D dsi_pll_get_params(dsi, pll_in_khz, rate / 1000, &idf, &ndiv, &od= f); + if (ret) + DRM_WARN("Warning dsi_pll_get_params(): bad params\n"); + + /* Get the adjusted pll out value */ + pll_out_khz =3D dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); + + /* Set the PLL division factors */ + dsi_update_bits(dsi, DSI_WRPCR, WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF, + (ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16)); + + /* Compute uix4 & set the bit period in high-speed mode */ + val =3D 4000000 / pll_out_khz; + dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val); + + return 0; +} + +static void dw_mipi_dsi_clk_unregister(void *data) +{ + struct dw_mipi_dsi_stm *dsi =3D data; + + DRM_DEBUG_DRIVER("\n"); + + of_clk_del_provider(dsi->dev->of_node); + clk_hw_unregister(&dsi->txbyte_clk); +} + +static const struct clk_ops dw_mipi_dsi_stm_clk_ops =3D { + .enable =3D dw_mipi_dsi_clk_enable, + .disable =3D dw_mipi_dsi_clk_disable, + .is_enabled =3D dw_mipi_dsi_clk_is_enabled, + .recalc_rate =3D dw_mipi_dsi_clk_recalc_rate, + .round_rate =3D dw_mipi_dsi_clk_round_rate, + .set_rate =3D dw_mipi_dsi_clk_set_rate, +}; + +static struct clk_init_data cdata_init =3D { + .name =3D "ck_dsi_phy", + .ops =3D &dw_mipi_dsi_stm_clk_ops, + .parent_names =3D (const char * []) {"ck_hse"}, + .num_parents =3D 1, +}; + +static int dw_mipi_dsi_clk_register(struct dw_mipi_dsi_stm *dsi, + struct device *dev) +{ + struct device_node *node =3D dev->of_node; + int ret; + + DRM_DEBUG_DRIVER("Registering clk\n"); + + dsi->txbyte_clk.init =3D &cdata_init; + + ret =3D clk_hw_register(dev, &dsi->txbyte_clk); + if (ret) + return ret; + + ret =3D of_clk_add_hw_provider(node, of_clk_hw_simple_get, + &dsi->txbyte_clk); + if (ret) + clk_hw_unregister(&dsi->txbyte_clk); + + return ret; +} + +static int dw_mipi_dsi_phy_init(void *priv_data) +{ + struct dw_mipi_dsi_stm *dsi =3D priv_data; + int ret; + + ret =3D clk_prepare_enable(dsi->txbyte_clk.clk); + return ret; +} + static void dw_mipi_dsi_phy_power_on(void *priv_data) { struct dw_mipi_dsi_stm *dsi =3D priv_data; @@ -235,6 +409,8 @@ static void dw_mipi_dsi_phy_power_off(void *priv_data) =20 DRM_DEBUG_DRIVER("\n"); =20 + clk_disable_unprepare(dsi->txbyte_clk.clk); + /* Disable the DSI wrapper */ dsi_clear(dsi, DSI_WCR, WCR_DSIEN); } @@ -245,9 +421,8 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct= drm_display_mode *mode, unsigned int *lane_mbps) { struct dw_mipi_dsi_stm *dsi =3D priv_data; - unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; + unsigned int pll_in_khz, pll_out_khz; int ret, bpp; - u32 val; =20 pll_in_khz =3D (unsigned int)(clk_get_rate(dsi->pllref_clk) / 1000); =20 @@ -268,25 +443,10 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const stru= ct drm_display_mode *mode, DRM_WARN("Warning min phy mbps is used\n"); } =20 - /* Compute best pll parameters */ - idf =3D 0; - ndiv =3D 0; - odf =3D 0; - ret =3D dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz, - &idf, &ndiv, &odf); + ret =3D clk_set_rate((dsi->txbyte_clk.clk), pll_out_khz * 1000); if (ret) - DRM_WARN("Warning dsi_pll_get_params(): bad params\n"); - - /* Get the adjusted pll out value */ - pll_out_khz =3D dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); - - /* Set the PLL division factors */ - dsi_update_bits(dsi, DSI_WRPCR, WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF, - (ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16)); - - /* Compute uix4 & set the bit period in high-speed mode */ - val =3D 4000000 / pll_out_khz; - dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val); + DRM_DEBUG_DRIVER("ERROR Could not set rate of %d to %s clk->name", + pll_out_khz, clk_hw_get_name(&dsi->txbyte_clk)); =20 /* Select video mode by resetting DSIM bit */ dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM); @@ -445,6 +605,7 @@ static int dw_mipi_dsi_stm_probe(struct platform_device= *pdev) { struct device *dev =3D &pdev->dev; struct dw_mipi_dsi_stm *dsi; + const struct dw_mipi_dsi_plat_data *pdata =3D of_device_get_match_data(de= v); int ret; =20 dsi =3D devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); @@ -514,18 +675,40 @@ static int dw_mipi_dsi_stm_probe(struct platform_devi= ce *pdev) dsi->lane_max_kbps *=3D 2; } =20 - dw_mipi_dsi_stm_plat_data.base =3D dsi->base; - dw_mipi_dsi_stm_plat_data.priv_data =3D dsi; + dsi->pdata =3D *pdata; + dsi->pdata.base =3D dsi->base; + dsi->pdata.priv_data =3D dsi; + + dsi->pdata.max_data_lanes =3D 2; + dsi->pdata.phy_ops =3D &dw_mipi_dsi_stm_phy_ops; =20 platform_set_drvdata(pdev, dsi); =20 - dsi->dsi =3D dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data); + dsi->dsi =3D dw_mipi_dsi_probe(pdev, &dsi->pdata); if (IS_ERR(dsi->dsi)) { ret =3D PTR_ERR(dsi->dsi); dev_err_probe(dev, ret, "Failed to initialize mipi dsi host\n"); goto err_dsi_probe; } =20 + /* + * We need to wait for the generic bridge to probe before enabling and + * register the internal pixel clock. + */ + ret =3D clk_prepare_enable(dsi->pclk); + if (ret) { + DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__); + goto err_dsi_probe; + } + + ret =3D dw_mipi_dsi_clk_register(dsi, dev); + if (ret) { + DRM_ERROR("Failed to register DSI pixel clock: %d\n", ret); + goto err_dsi_probe; + } + + clk_disable_unprepare(dsi->pclk); + return 0; =20 err_dsi_probe: @@ -542,12 +725,13 @@ static void dw_mipi_dsi_stm_remove(struct platform_de= vice *pdev) =20 dw_mipi_dsi_remove(dsi->dsi); clk_disable_unprepare(dsi->pllref_clk); + dw_mipi_dsi_clk_unregister(dsi); regulator_disable(dsi->vdd_supply); } =20 static int __maybe_unused dw_mipi_dsi_stm_suspend(struct device *dev) { - struct dw_mipi_dsi_stm *dsi =3D dw_mipi_dsi_stm_plat_data.priv_data; + struct dw_mipi_dsi_stm *dsi =3D dev_get_drvdata(dev); =20 DRM_DEBUG_DRIVER("\n"); =20 @@ -560,7 +744,7 @@ static int __maybe_unused dw_mipi_dsi_stm_suspend(struc= t device *dev) =20 static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev) { - struct dw_mipi_dsi_stm *dsi =3D dw_mipi_dsi_stm_plat_data.priv_data; + struct dw_mipi_dsi_stm *dsi =3D dev_get_drvdata(dev); int ret; =20 DRM_DEBUG_DRIVER("\n"); --=20 2.25.1