From nobody Wed Dec 17 17:25:10 2025 Received: from mx07-00178001.pphosted.com (mx08-00178001.pphosted.com [91.207.212.93]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 46C145820C for ; Mon, 29 Jan 2024 10:42:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.207.212.93 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706524932; cv=none; b=riQlBTysgDYMsreRfQKLDxIZTcCzdYu/BM0XlC+kD2eyFnQQr9H3xYSrI4Ic4r96nJrPPCRWlyc7QKfKiDzkCkJxU1oCEdRhjAAIEFaQRBzcTSv39+TYq1AeRHTcdBRn2PBLqvHxGONWBxd6+sUyq4v6YydC7RDFXpcJ1RRJQlQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706524932; c=relaxed/simple; bh=kl/4ovMtYNFXC8LbdOh8xUDpQ2m6FDCerkaDyKatHbY=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=PY7S1UfdjHsCi/GIEooUXfBjAYRsQ2dQUevZXY00Xireufmkg54FS44uMVesqiGoy/i+6uG/duAJhoFard8KPhYsEWhQTfEmxmZ8OzbavNvDnOz3pVrOSelFPaxQoK4mBEGGveTjqyMJdny2jUmI1Iqs9gx8awI6r9hOGkHc1gE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=foss.st.com; spf=pass smtp.mailfrom=foss.st.com; dkim=pass (2048-bit key) header.d=foss.st.com header.i=@foss.st.com header.b=nDc+/c+f; arc=none smtp.client-ip=91.207.212.93 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=foss.st.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=foss.st.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=foss.st.com header.i=@foss.st.com header.b="nDc+/c+f" Received: from pps.filterd (m0046661.ppops.net [127.0.0.1]) by mx07-00178001.pphosted.com (8.17.1.24/8.17.1.24) with ESMTP id 40T61GZE019758; Mon, 29 Jan 2024 11:41:21 +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=6hfGw0RhT4qoAVlq5UYImYdoxzmaiZjeqx1NKe8hFj0=; b=nD c+/c+fap2l4P1iePY+fN3rwAIywWlNd6rrbO2n3fv5oTA1gTt4odUX9rw2ouqcmh 84bOHj4zPTOsfsnG4y39vVW1hbCZ62J/qmuivuhHDx27l7v4IONnx+0W8uq+YlM+ qjh6HEWgs753kY0nkW5RuLmaiZkzuwLeZLNpxsGRqmufuG45u+ewu+shjJg4ev+T PhX5rPMyX6JorVUGJn8lWinpYJvQpkXVKIlPeEz0oMhbmiPzYQB9yP5EcRrCLYbp u83EW7KCVcIVYngvYI4DAfcyjdBK1IrZu3oouhLrA5FGJAoKZpi425V4lgSZLgjR XpxKlMAUoSrAclYgxhjQ== 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 3vvskgpact-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 29 Jan 2024 11:41:21 +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 7D587100063; Mon, 29 Jan 2024 11:41:18 +0100 (CET) Received: from Webmail-eu.st.com (shfdag1node2.st.com [10.75.129.70]) by euls16034.sgp.st.com (STMicroelectronics) with ESMTP id 75044216838; Mon, 29 Jan 2024 11:41:18 +0100 (CET) Received: from localhost (10.252.28.37) 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; Mon, 29 Jan 2024 11:41:18 +0100 From: Raphael Gallais-Pou To: Yannick Fertre , Raphael Gallais-Pou , Philippe Cornu , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , Maxime Coquelin , Alexandre Torgue CC: , , , Subject: [PATCH RESEND v3 3/3] drm/stm: dsi: expose DSI PHY internal clock Date: Mon, 29 Jan 2024 11:41:06 +0100 Message-ID: <20240129104106.43141-4-raphael.gallais-pou@foss.st.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240129104106.43141-1-raphael.gallais-pou@foss.st.com> References: <20240129104106.43141-1-raphael.gallais-pou@foss.st.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SHFCAS1NODE2.st.com (10.75.129.73) To SHFDAG1NODE2.st.com (10.75.129.70) X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.1011,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2024-01-29_06,2024-01-29_01,2023-05-22_02 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 Acked-by: Yannick Fertre Tested-by: Yannick Fertre --- Changes in v3: - Fix smatch warning: .../dw_mipi_dsi-stm.c:719 dw_mipi_dsi_stm_probe() warn: 'dsi->pclk' from clk_prepare_enable() not released on lines: 719. --- drivers/gpu/drm/stm/dw_mipi_dsi-stm.c | 247 ++++++++++++++++++++++---- 1 file changed, 216 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 82fff9e84345..b20123854c4a 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,41 @@ 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); + clk_disable_unprepare(dsi->pclk); + goto err_dsi_probe; + } + + clk_disable_unprepare(dsi->pclk); + return 0; =20 err_dsi_probe: @@ -542,12 +726,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 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 +745,7 @@ static int dw_mipi_dsi_stm_suspend(struct device *dev) =20 static int 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