From 27f113e22c5c3ba87892404860f0e9af56f422ac Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Tue, 19 Mar 2024 14:09:05 +0800 Subject: [PATCH 01/17] ASoC: rk817: Fix compile warning Should use '%d' for type 'int'. Signed-off-by: Sugar Zhang Change-Id: I06482bcaf2e88516758450536c616652ce24ea91 --- sound/soc/codecs/rk817_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c index 400be6d5c3d4..f82c0d940257 100644 --- a/sound/soc/codecs/rk817_codec.c +++ b/sound/soc/codecs/rk817_codec.c @@ -931,7 +931,7 @@ static int rk817_resume_path_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); - DBG("%s : resume_path %ld\n", __func__, rk817->resume_path); + DBG("%s : resume_path %d\n", __func__, rk817->resume_path); ucontrol->value.integer.value[0] = rk817->resume_path; From 6a0bf600548f30ef4a51dfd5fa3f8edbb28076c1 Mon Sep 17 00:00:00 2001 From: Sergey Khimich Date: Tue, 19 Mar 2024 14:59:31 +0300 Subject: [PATCH 02/17] UPSTREAM: mmc: cqhci: Add cqhci set_tran_desc() callback There are could be specific limitations for some mmc controllers for setting cqhci transfer descriptors. So add callback to allow implement driver specific function. Signed-off-by: Sergey Khimich Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20240319115932.4108904-2-serghox@gmail.com Signed-off-by: Ulf Hansson Change-Id: Iae6c9e69b5a695f8cbdf91aa7586c3e1649bc466 (cherry-picked from 52bf134fca61f0cdb400f4b27766149ca6e1550c) Signed-off-by: Shawn Lin Signed-off-by: Yifeng Zhao --- drivers/mmc/host/cqhci-core.c | 11 ++++++++--- drivers/mmc/host/cqhci.h | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c index 41e94cd14109..c14d7251d0bb 100644 --- a/drivers/mmc/host/cqhci-core.c +++ b/drivers/mmc/host/cqhci-core.c @@ -474,8 +474,8 @@ static int cqhci_dma_map(struct mmc_host *host, struct mmc_request *mrq) return sg_count; } -static void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, - bool dma64) +void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, + bool dma64) { __le32 *attr = (__le32 __force *)desc; @@ -495,6 +495,7 @@ static void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, dataddr[0] = cpu_to_le32(addr); } } +EXPORT_SYMBOL(cqhci_set_tran_desc); static int cqhci_prep_tran_desc(struct mmc_request *mrq, struct cqhci_host *cq_host, int tag) @@ -522,7 +523,11 @@ static int cqhci_prep_tran_desc(struct mmc_request *mrq, if ((i+1) == sg_count) end = true; - cqhci_set_tran_desc(desc, addr, len, end, dma64); + if (cq_host->ops->set_tran_desc) + cq_host->ops->set_tran_desc(cq_host, &desc, addr, len, end, dma64); + else + cqhci_set_tran_desc(desc, addr, len, end, dma64); + desc += cq_host->trans_desc_len; } diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h index ba9387ed90eb..1efd3802d66b 100644 --- a/drivers/mmc/host/cqhci.h +++ b/drivers/mmc/host/cqhci.h @@ -290,6 +290,9 @@ struct cqhci_host_ops { int (*program_key)(struct cqhci_host *cq_host, const union cqhci_crypto_cfg_entry *cfg, int slot); #endif + void (*set_tran_desc)(struct cqhci_host *cq_host, u8 **desc, + dma_addr_t addr, int len, bool end, bool dma64); + }; static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg) @@ -315,6 +318,7 @@ irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, bool dma64); struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev); int cqhci_deactivate(struct mmc_host *mmc); +void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, bool dma64); static inline int cqhci_suspend(struct mmc_host *mmc) { return cqhci_deactivate(mmc); From 0f7ce4fc64881de62904204f60d0459e1198412a Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Tue, 22 Aug 2023 15:59:28 -0400 Subject: [PATCH 03/17] BACKPORT: mmc: sdhci-of-dwcmshc: Add error handling in dwcmshc_resume This commit adds handling in dwcmshc_resume() for different error cases. Signed-off-by: Liming Sun Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20230822195929.168552-1-limings@nvidia.com Signed-off-by: Ulf Hansson (cherry picked from commit a11937b3cff5449871f428e46e202481dc61a9de) Change-Id: Iade159c45df614cf250234c9297fd309b38b0ea6 Signed-off-by: Shawn Lin Signed-off-by: Yifeng Zhao Signed-off-by: Tao Huang --- drivers/mmc/host/sdhci-of-dwcmshc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index df63b9c9ba0c..1b90c9186344 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -782,21 +782,32 @@ static int dwcmshc_resume(struct device *dev) if (!IS_ERR(priv->bus_clk)) { ret = clk_prepare_enable(priv->bus_clk); if (ret) - return ret; + goto disable_clk; } if (rk_priv) { ret = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, rk_priv->rockchip_clks); if (ret) - return ret; + goto disable_bus_clk; } ret = sdhci_resume_host(host); if (ret) - return ret; + goto disable_rockchip_clks; return mmc_hsq_resume(host->mmc); + +disable_rockchip_clks: + if (rk_priv) + clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, + rk_priv->rockchip_clks); +disable_bus_clk: + if (!IS_ERR(priv->bus_clk)) + clk_disable_unprepare(priv->bus_clk); +disable_clk: + clk_disable_unprepare(pltfm_host->clk); + return ret; } static int dwcmshc_runtime_suspend(struct device *dev) From 8d00b0267ebe9ad353d5ca37d251cb547d1170f6 Mon Sep 17 00:00:00 2001 From: Sergey Khimich Date: Tue, 19 Mar 2024 14:59:32 +0300 Subject: [PATCH 04/17] BACKPORT: mmc: sdhci-of-dwcmshc: Implement SDHCI CQE support For enabling CQE support just set 'supports-cqe' in your DevTree file for appropriate mmc node. Signed-off-by: Sergey Khimich Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20240319115932.4108904-3-serghox@gmail.com Signed-off-by: Ulf Hansson Change-Id: Ie91c0f42f7ac76d92778190c5b0eee770ecb7352 (cherry picked from commit 53ab7f7fe412abd294262e86459f17d965cd65b9) Signed-off-by: Shawn Lin Signed-off-by: Yifeng Zhao --- drivers/mmc/host/Kconfig | 1 + drivers/mmc/host/sdhci-of-dwcmshc.c | 198 +++++++++++++++++++++++++++- 2 files changed, 195 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index ba2b7c4fc1d1..d56fa23b87fe 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -225,6 +225,7 @@ config MMC_SDHCI_OF_DWCMSHC depends on OF depends on COMMON_CLK select MMC_HSQ + select MMC_CQHCI help This selects Synopsys DesignWare Cores Mobile Storage Controller support. diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 1b90c9186344..fcd6c85a8447 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -22,6 +22,7 @@ #include "sdhci-pltfm.h" #include "mmc_hsq.h" +#include "cqhci.h" #define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16) @@ -38,6 +39,9 @@ #define DWCMSHC_ENHANCED_STROBE BIT(8) #define DWCMSHC_EMMC_ATCTRL 0x40 +/* DWC IP vendor area 2 pointer */ +#define DWCMSHC_P_VENDOR_AREA2 0xea + /* Rockchip specific Registers */ #define DWCMSHC_EMMC_DLL_CTRL 0x800 #define DWCMSHC_EMMC_DLL_RXCLK 0x804 @@ -83,6 +87,10 @@ #define BOUNDARY_OK(addr, len) \ ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1))) +#define DWCMSHC_SDHCI_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \ + SDHCI_TRNS_BLK_CNT_EN | \ + SDHCI_TRNS_DMA) + enum dwcmshc_rk_type { DWCMSHC_RK3568, DWCMSHC_RK3588, @@ -117,7 +125,9 @@ struct rk35xx_priv { struct dwcmshc_priv { struct clk *bus_clk; - int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA reg */ + int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA1 reg */ + int vendor_specific_area2; /* P_VENDOR_SPECIFIC_AREA2 reg */ + void *priv; /* pointer to SoC private stuff */ }; @@ -230,6 +240,90 @@ static void dwcmshc_hs400_enhanced_strobe(struct mmc_host *mmc, sdhci_writel(host, vendor, reg); } +static int dwcmshc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + int err = sdhci_execute_tuning(mmc, opcode); + struct sdhci_host *host = mmc_priv(mmc); + + if (err) + return err; + + /* + * Tuning can leave the IP in an active state (Buffer Read Enable bit + * set) which prevents the entry to low power states (i.e. S0i3). Data + * reset will clear it. + */ + sdhci_reset(host, SDHCI_RESET_DATA); + + return 0; +} + +static u32 dwcmshc_cqe_irq_handler(struct sdhci_host *host, u32 intmask) +{ + int cmd_error = 0; + int data_error = 0; + + if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) + return intmask; + + cqhci_irq(host->mmc, intmask, cmd_error, data_error); + + return 0; +} + +static void dwcmshc_sdhci_cqe_enable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + u8 ctrl; + + sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); + + sdhci_cqe_enable(mmc); + + /* + * The "DesignWare Cores Mobile Storage Host Controller + * DWC_mshc / DWC_mshc_lite Databook" says: + * when Host Version 4 Enable" is 1 in Host Control 2 register, + * SDHCI_CTRL_ADMA32 bit means ADMA2 is selected. + * Selection of 32-bit/64-bit System Addressing: + * either 32-bit or 64-bit system addressing is selected by + * 64-bit Addressing bit in Host Control 2 register. + * + * On the other hand the "DesignWare Cores Mobile Storage Host + * Controller DWC_mshc / DWC_mshc_lite User Guide" says, that we have to + * set DMA_SEL to ADMA2 _only_ mode in the Host Control 2 register. + */ + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ctrl &= ~SDHCI_CTRL_DMA_MASK; + ctrl |= SDHCI_CTRL_ADMA32; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +} + +static void dwcmshc_set_tran_desc(struct cqhci_host *cq_host, u8 **desc, + dma_addr_t addr, int len, bool end, bool dma64) +{ + int tmplen, offset; + + if (likely(!len || BOUNDARY_OK(addr, len))) { + cqhci_set_tran_desc(*desc, addr, len, end, dma64); + return; + } + + offset = addr & (SZ_128M - 1); + tmplen = SZ_128M - offset; + cqhci_set_tran_desc(*desc, addr, tmplen, false, dma64); + + addr += tmplen; + len -= tmplen; + *desc += cq_host->trans_desc_len; + cqhci_set_tran_desc(*desc, addr, len, end, dma64); +} + +static void dwcmshc_cqhci_dumpregs(struct mmc_host *mmc) +{ + sdhci_dumpregs(mmc_priv(mmc)); +} + static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -400,6 +494,7 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = { .get_max_clock = dwcmshc_get_max_clock, .reset = sdhci_reset, .adma_write_desc = dwcmshc_adma_write_desc, + .irq = dwcmshc_cqe_irq_handler, }; static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = { @@ -409,6 +504,7 @@ static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = { .get_max_clock = sdhci_pltfm_clk_get_max_clock, .reset = rk35xx_sdhci_reset, .adma_write_desc = dwcmshc_adma_write_desc, + .irq = dwcmshc_cqe_irq_handler, .request_done = sdhci_dwcmshc_request_done, }; @@ -434,6 +530,73 @@ static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, }; +static const struct cqhci_host_ops dwcmshc_cqhci_ops = { + .enable = dwcmshc_sdhci_cqe_enable, + .disable = sdhci_cqe_disable, + .dumpregs = dwcmshc_cqhci_dumpregs, + .set_tran_desc = dwcmshc_set_tran_desc, +}; + +static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev) +{ + struct cqhci_host *cq_host; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + bool dma64 = false; + u16 clk; + int err; + + host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; + cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL); + if (!cq_host) { + dev_err(mmc_dev(host->mmc), "Unable to setup CQE: not enough memory\n"); + goto dsbl_cqe_caps; + } + + /* + * For dwcmshc host controller we have to enable internal clock + * before access to some registers from Vendor Specific Area 2. + */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if (!(clk & SDHCI_CLOCK_INT_EN)) { + dev_err(mmc_dev(host->mmc), "Unable to setup CQE: internal clock enable error\n"); + goto free_cq_host; + } + + cq_host->mmio = host->ioaddr + priv->vendor_specific_area2; + cq_host->ops = &dwcmshc_cqhci_ops; + + /* Enable using of 128-bit task descriptors */ + dma64 = host->flags & SDHCI_USE_64_BIT_DMA; + if (dma64) { + dev_dbg(mmc_dev(host->mmc), "128-bit task descriptors\n"); + cq_host->caps |= CQHCI_TASK_DESC_SZ_128; + } + err = cqhci_init(cq_host, host->mmc, dma64); + if (err) { + dev_err(mmc_dev(host->mmc), "Unable to setup CQE: error %d\n", err); + goto int_clock_disable; + } + + dev_dbg(mmc_dev(host->mmc), "CQE init done\n"); + + return; + +int_clock_disable: + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + +free_cq_host: + devm_kfree(&pdev->dev, cq_host); + +dsbl_cqe_caps: + host->mmc->caps2 &= ~(MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD); +} + static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) { int err; @@ -594,7 +757,7 @@ static int dwcmshc_probe(struct platform_device *pdev) const struct dwcmshc_driver_data *drv_data; struct mmc_hsq *hsq; int err; - u32 extra; + u32 extra, caps; drv_data = device_get_match_data(&pdev->dev); if (!drv_data) { @@ -646,6 +809,7 @@ static int dwcmshc_probe(struct platform_device *pdev) host->mmc_host_ops.request = dwcmshc_request; host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe; + host->mmc_host_ops.execute_tuning = dwcmshc_execute_tuning; hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL); if (!hsq) { @@ -679,12 +843,24 @@ static int dwcmshc_probe(struct platform_device *pdev) goto err_clk; } + caps = sdhci_readl(host, SDHCI_CAPABILITIES); + if (caps & SDHCI_CAN_64BIT_V4) + sdhci_enable_v4_mode(host); + host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; err = sdhci_setup_host(host); if (err) goto err_clk; + /* Setup Command Queue Engine if enabled */ + if (device_property_read_bool(&pdev->dev, "supports-cqe")) { + priv->vendor_specific_area2 = + sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2); + + dwcmshc_cqhci_init(host, pdev); + } + if (rk_priv) dwcmshc_rk35xx_postinit(host, priv); @@ -750,7 +926,13 @@ static int dwcmshc_suspend(struct device *dev) struct rk35xx_priv *rk_priv = priv->priv; int ret; - mmc_hsq_suspend(host->mmc); + if (host->mmc->caps2 & MMC_CAP2_CQE) { + ret = cqhci_suspend(host->mmc); + if (ret) + return ret; + } else { + mmc_hsq_suspend(host->mmc); + } ret = sdhci_suspend_host(host); if (ret) @@ -796,7 +978,15 @@ static int dwcmshc_resume(struct device *dev) if (ret) goto disable_rockchip_clks; - return mmc_hsq_resume(host->mmc); + if (host->mmc->caps2 & MMC_CAP2_CQE) { + ret = cqhci_resume(host->mmc); + if (ret) + goto disable_rockchip_clks; + } else { + return mmc_hsq_resume(host->mmc); + } + + return 0; disable_rockchip_clks: if (rk_priv) From 2ef0767967138d333360ec0f399f1d68646741c3 Mon Sep 17 00:00:00 2001 From: Yifeng Zhao Date: Wed, 22 May 2024 15:20:52 +0800 Subject: [PATCH 05/17] mmc: sdhci-of-dwcmshc: add command queue support for rockchip SOCs Signed-off-by: Yifeng Zhao Change-Id: I96c3dfb56d4dd3af3e70cdd59bbd16403abd2fcb --- drivers/mmc/host/sdhci-of-dwcmshc.c | 101 ++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index fcd6c85a8447..24db6a819fa2 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -23,6 +23,7 @@ #include "sdhci-pltfm.h" #include "mmc_hsq.h" #include "cqhci.h" +#include "sdhci-cqhci.h" #define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16) @@ -48,6 +49,8 @@ #define DWCMSHC_EMMC_DLL_TXCLK 0x808 #define DWCMSHC_EMMC_DLL_STRBIN 0x80c #define DECMSHC_EMMC_DLL_CMDOUT 0x810 +#define DECMSHC_EMMC_MISC_CON 0x81C +#define MISC_INTCLK_EN BIT(1) #define DWCMSHC_EMMC_DLL_STATUS0 0x840 #define DWCMSHC_EMMC_DLL_STATUS1 0x844 #define DWCMSHC_EMMC_DLL_START BIT(0) @@ -299,6 +302,61 @@ static void dwcmshc_sdhci_cqe_enable(struct mmc_host *mmc) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } +static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 reg; + + reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG); + reg |= CQHCI_ENABLE; + sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG); + + reg = sdhci_readl(host, SDHCI_PRESENT_STATE); + while (reg & SDHCI_DATA_AVAILABLE) { + sdhci_readl(host, SDHCI_BUFFER); + reg = sdhci_readl(host, SDHCI_PRESENT_STATE); + } + + sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); + + sdhci_cqe_enable(mmc); + + sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); +} + +static void rk35xx_sdhci_cqe_disabled(struct mmc_host *mmc, bool recovery) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + unsigned long flags; + u32 ctrl; + + mmc->cqe_ops->cqe_wait_for_idle(mmc); + spin_lock_irqsave(&host->lock, flags); + + /* + * During CQE command transfers, command complete bit gets latched. + * So s/w should clear command complete interrupt status when CQE is + * either halted or disabled. Otherwise unexpected SDCHI legacy + * interrupt gets triggered when CQE is halted/disabled. + */ + ctrl = sdhci_readl(host, SDHCI_INT_ENABLE); + ctrl |= SDHCI_INT_RESPONSE; + sdhci_writel(host, ctrl, SDHCI_INT_ENABLE); + sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); + + spin_unlock_irqrestore(&host->lock, flags); + + sdhci_cqe_disable(mmc, recovery); + + ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG); + ctrl &= ~CQHCI_ENABLE; + sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG); +} + static void dwcmshc_set_tran_desc(struct cqhci_host *cq_host, u8 **desc, dma_addr_t addr, int len, bool end, bool dma64) { @@ -469,6 +527,7 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); struct rk35xx_priv *priv = dwc_priv->priv; + u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON); if (mask & SDHCI_RESET_ALL && priv->reset) { reset_control_assert(priv->reset); @@ -477,12 +536,17 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) } sdhci_reset(host, mask); + + /* Enable INTERNAL CLOCK */ + sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON); } static void sdhci_dwcmshc_request_done(struct sdhci_host *host, struct mmc_request *mrq) { - if (mmc_hsq_finalize_request(host->mmc, mrq)) - return; + if (!(host->mmc->caps2 & MMC_CAP2_CQE)) { + if (mmc_hsq_finalize_request(host->mmc, mrq)) + return; + } mmc_request_done(host->mmc, mrq); } @@ -537,11 +601,19 @@ static const struct cqhci_host_ops dwcmshc_cqhci_ops = { .set_tran_desc = dwcmshc_set_tran_desc, }; +static const struct cqhci_host_ops rk35xx_cqhci_ops = { + .enable = rk35xx_sdhci_cqe_enable, + .disable = rk35xx_sdhci_cqe_disabled, + .dumpregs = dwcmshc_cqhci_dumpregs, + .set_tran_desc = dwcmshc_set_tran_desc, +}; + static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev) { struct cqhci_host *cq_host; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + struct rk35xx_priv *rk_priv = priv->priv; bool dma64 = false; u16 clk; int err; @@ -567,7 +639,10 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device * } cq_host->mmio = host->ioaddr + priv->vendor_specific_area2; - cq_host->ops = &dwcmshc_cqhci_ops; + if (rk_priv) + cq_host->ops = &rk35xx_cqhci_ops; + else + cq_host->ops = &dwcmshc_cqhci_ops; /* Enable using of 128-bit task descriptors */ dma64 = host->flags & SDHCI_USE_64_BIT_DMA; @@ -811,16 +886,6 @@ static int dwcmshc_probe(struct platform_device *pdev) host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe; host->mmc_host_ops.execute_tuning = dwcmshc_execute_tuning; - hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL); - if (!hsq) { - err = -ENOMEM; - goto err_clk; - } - - err = mmc_hsq_init(hsq, host->mmc); - if (err) - goto err_clk; - if (drv_data->flags & RK_PLATFROM) { rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL); if (!rk_priv) { @@ -859,6 +924,16 @@ static int dwcmshc_probe(struct platform_device *pdev) sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2); dwcmshc_cqhci_init(host, pdev); + } else { + hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL); + if (!hsq) { + err = -ENOMEM; + goto err_setup_host; + } + + err = mmc_hsq_init(hsq, host->mmc); + if (err) + goto err_setup_host; } if (rk_priv) From d5d3f0a6fb97f9fac368b6f356bb6d8ec7717973 Mon Sep 17 00:00:00 2001 From: Yifeng Zhao Date: Wed, 22 May 2024 15:22:19 +0800 Subject: [PATCH 06/17] arm64: dts: rockchip: rk3576: enable emmc command queue Signed-off-by: Yifeng Zhao Change-Id: Ia58b5cefcacac8430bb289315cc0306c6694f1f2 --- arch/arm64/boot/dts/rockchip/rk3576.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi index 357c6e2518a9..c0612b58be26 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi @@ -3993,6 +3993,7 @@ <&cru SRST_T_EMMC>; reset-names = "core", "bus", "axi", "block", "timer"; max-frequency = <200000000>; + supports-cqe; status = "disabled"; }; From d03a5046f13a8a5b4d7c4b02e442aa4508890413 Mon Sep 17 00:00:00 2001 From: Yifeng Zhao Date: Wed, 22 May 2024 15:34:46 +0800 Subject: [PATCH 07/17] arm64: dts: rockchip: rk3588: enable emmc command queue Signed-off-by: Yifeng Zhao Change-Id: I3ca4ccf7920626752115a2a76c0c80c4795412e6 --- arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi index 6d42de7248fa..2cc2481592ec 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi @@ -5711,6 +5711,7 @@ <&cru SRST_T_EMMC>; reset-names = "core", "bus", "axi", "block", "timer"; max-frequency = <200000000>; + supports-cqe; status = "disabled"; }; From 07bed61e89d08b169bfcee1d1e317e8440fc2911 Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Fri, 31 May 2024 09:44:20 +0800 Subject: [PATCH 08/17] drm/rockchip: vop2: udpate linear 10bit yuv format align role At RK356X/RK3588/RK3562/RK3528 linear 10bit yuv format actual_w should align as 4 pixel, from RK3576 linear 10bit yuv format actual_w should align as 2 pixel. Signed-off-by: Sandy Huang Change-Id: I719f59574442628f2ed2410d6ca20194cf6f580d --- drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 5 +++-- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index ac14f540e8c3..f522bbac867c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -28,11 +28,12 @@ #define VOP2_MINOR(version) (((version) >> 16) & 0xff) #define VOP2_BUILD(version) ((version) & 0xffff) +/* The new SOC VOP version is bigger than the old */ +#define VOP_VERSION_RK3568 VOP2_VERSION(0x40, 0x15, 0x8023) +#define VOP_VERSION_RK3588 VOP2_VERSION(0x40, 0x17, 0x6786) #define VOP_VERSION_RK3528 VOP2_VERSION(0x50, 0x17, 0x1263) #define VOP_VERSION_RK3562 VOP2_VERSION(0x50, 0x17, 0x4350) -#define VOP_VERSION_RK3568 VOP2_VERSION(0x40, 0x15, 0x8023) #define VOP_VERSION_RK3576 VOP2_VERSION(0x50, 0x19, 0x9765) -#define VOP_VERSION_RK3588 VOP2_VERSION(0x40, 0x17, 0x6786) /* register one connector */ #define ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE BIT(0) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 5e59371afd04..d3510629be09 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -5914,9 +5914,18 @@ static void vop2_win_atomic_update(struct vop2_win *win, struct drm_rect *src, s } } - if (is_linear_10bit_yuv(fb->format->format) && actual_w & 0x3) { - DRM_WARN("vp%d %s actual_w[%d] should align as 4 pixel when is linear 10 bit yuv format\n", vp->id, win->name, actual_w); - actual_w = ALIGN_DOWN(actual_w, 4); + /* + * At RK356X/RK3588/RK3562/RK3528 linear 10bit yuv format actual_w should align as 4 pixel, + * from RK3576 linear 10bit yuv format actual_w should align as 2 pixel. + */ + if (is_linear_10bit_yuv(fb->format->format)) { + if (vop2->version < VOP_VERSION_RK3576 && actual_w & 0x3) { + DRM_WARN("vp%d %s actual_w[%d] should align as 4 pixel when is linear 10 bit yuv format\n", vp->id, win->name, actual_w); + actual_w = ALIGN_DOWN(actual_w, 4); + } else if (vop2->version >= VOP_VERSION_RK3576 && actual_w & 0x1) { + DRM_WARN("vp%d %s actual_w[%d] should align as 2 pixel when is linear 10 bit yuv format\n", vp->id, win->name, actual_w); + actual_w = ALIGN_DOWN(actual_w, 2); + } } act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); From 4ca8cfff717c1ec532765f2ce43115b371bac430 Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Wed, 29 May 2024 14:23:24 +0800 Subject: [PATCH 09/17] drm/rockchip: vop2: recover vop aclk when enter psr and suspend Usperspace not commit new frame for long time will triggle driver enter psr mode, If userspace directly close display at next time and without any new frame commit, driver will not exit psr, at this case we need to recover aclk here Signed-off-by: Sandy Huang Change-Id: I4bf78c5fa12ca7fa557d8e82d92427b0f849f2eb --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index d3510629be09..9ac0caeddd10 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -4849,6 +4849,17 @@ static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, goto out; } + /* + * Usperspace not commit new frame for long time will triggle driver enter + * psr mode, If userspace directly close display at next time and without + * any new frame commit, driver will not exit psr, at this case we need to + * recover aclk here. + */ + if (vop2->aclk_rate_reset) { + clk_set_rate(vop2->aclk, vop2->aclk_current_freq); + vop2->aclk_rate_reset = false; + } + vop2_lock(vop2); DRM_DEV_INFO(vop2->dev, "Crtc atomic disable vp%d\n", vp->id); VOP_MODULE_SET(vop2, vp, almost_full_or_en, 0); From c235872c426e476d11e91393404679a8114698da Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Tue, 4 Jun 2024 14:51:21 +0800 Subject: [PATCH 10/17] drm/rockchip: vop2: resolution bigger than 2560 need high performance Signed-off-by: Sandy Huang Change-Id: Ie7933cb68a32c421c1f35ce4780aa7011d09602b --- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 3 ++- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 9b6194babdf6..c513ad2c6f6f 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -200,7 +200,8 @@ static int rockchip_drm_aclk_adjust(struct drm_device *dev, funcs = priv->crtc_funcs[drm_crtc_index(crtc)]; if (funcs && funcs->set_aclk) { if (vop_bw_info->plane_num_4k || crtc_num > 1 || - crtc->state->adjusted_mode.crtc_hdisplay > 4096) { + crtc->state->adjusted_mode.crtc_hdisplay > 2560 || + crtc->state->adjusted_mode.crtc_vdisplay > 2560) { funcs->set_aclk(crtc, ROCKCHIP_VOP_ACLK_ADVANCED_MODE); priv->aclk_adjust_frame_num = 2; } else { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 9ac0caeddd10..cfc7d20f0abd 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -7720,7 +7720,7 @@ static size_t vop2_crtc_bandwidth(struct drm_crtc *crtc, act_w = drm_rect_width(&pstate->src) >> 16; act_h = drm_rect_height(&pstate->src) >> 16; - if (pstate->fb->format->is_yuv && (act_w >= 3840 || act_h >= 3840)) + if (pstate->fb->format->is_yuv && (act_w > 2560 || act_h > 2560)) vop_bw_info->plane_num_4k++; bpp = rockchip_drm_get_bpp(pstate->fb->format); From 8be865c4bee3f95732a04ed72eab5da1aa4e5967 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 15 May 2024 10:29:52 +0800 Subject: [PATCH 11/17] arm64: dts: rockchip: rk3576-iotest: enable edp sound for edp2dp board Change-Id: I5dfe5e39bbfdd9e617f394d79aac8f392591b750 Signed-off-by: Damon Ding --- arch/arm64/boot/dts/rockchip/rk3576-iotest-v10-edp2dp.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-iotest-v10-edp2dp.dts b/arch/arm64/boot/dts/rockchip/rk3576-iotest-v10-edp2dp.dts index abc9c3e1e95e..e5c116260bbc 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-iotest-v10-edp2dp.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-iotest-v10-edp2dp.dts @@ -37,6 +37,10 @@ status = "okay"; }; +&edp_sound { + status = "okay"; +}; + &hdmi { status = "disabled"; }; @@ -66,6 +70,10 @@ status = "disabled"; }; +&sai6 { + status = "okay"; +}; + &vp0 { assigned-clocks = <&cru DCLK_VP0_SRC>; assigned-clock-parents = <&cru PLL_VPLL>; From 3220dcb643edf593e728d2f8fcdbfc9ce01f6dec Mon Sep 17 00:00:00 2001 From: Zhibin Huang Date: Mon, 3 Jun 2024 19:36:47 +0800 Subject: [PATCH 12/17] phy: rockchip: mipi-dcphy: optimize signal Type: Fix Redmine ID: #487592 Associated modifications: N/A Test: N/A Signed-off-by: Zhibin Huang Change-Id: I35143b35c06a9460f45016b4eb24e1abbf6a8fd3 --- .../phy/rockchip/phy-rockchip-samsung-dcphy.c | 54 ++++++++++++++++--- .../phy/rockchip/phy-rockchip-samsung-dcphy.h | 27 ++++++++++ 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c index eaaf6955805c..8edfa71ad08c 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c @@ -67,6 +67,11 @@ #define DPHY_MC_GNR_CON1 0x0304 #define T_PHY_READY(x) UPDATE(x, 15, 0) #define DPHY_MC_ANA_CON0 0x0308 +#define EDGE_CON(x) UPDATE(x, 14, 12) +#define EDGE_CON_DIR(x) UPDATE(x, 9, 9) +#define EDGE_CON_EN BIT(8) +#define RES_UP(x) UPDATE(x, 7, 4) +#define RES_DN(x) UPDATE(x, 3, 0) #define DPHY_MC_ANA_CON1 0x030c #define DPHY_MC_ANA_CON2 0x0310 #define HS_VREG_AMP_ICON(x) UPDATE(x, 1, 0) @@ -1594,15 +1599,25 @@ samsung_mipi_dphy_clk_lane_timing_init(struct samsung_mipi_dcphy *samsung) { const struct samsung_mipi_dphy_timing *timing; unsigned int lane_hs_rate = div64_ul(samsung->pll.rate, USEC_PER_SEC); - u32 val = 0; + u32 val, res_up, res_down; timing = samsung_mipi_dphy_get_timing(samsung); regmap_write(samsung->regmap, DPHY_MC_GNR_CON0, 0xf000); - regmap_write(samsung->regmap, DPHY_MC_ANA_CON0, 0x7133); + + /* + * The Drive-Strength / Voltage-Amplitude is adjusted by adjusting the + * Driver-Up Resistor and Driver-Down Resistor. + */ + res_up = samsung->pdata->dphy_hs_drv_res_cfg->clk_hs_drv_up_ohm; + res_down = samsung->pdata->dphy_hs_drv_res_cfg->clk_hs_drv_down_ohm; + val = EDGE_CON(7) | EDGE_CON_DIR(0) | EDGE_CON_EN | + RES_UP(res_up) | RES_DN(res_down); + regmap_write(samsung->regmap, DPHY_MC_ANA_CON0, val); if (lane_hs_rate >= 4500) regmap_write(samsung->regmap, DPHY_MC_ANA_CON1, 0x0001); + val = 0; /* * Divide-by-2 Clock from Serial Clock. Use this when data rate is under * 1500Mbps, otherwise divide-by-16 Clock from Serial Clock @@ -1639,14 +1654,22 @@ samsung_mipi_dphy_data_lane_timing_init(struct samsung_mipi_dcphy *samsung) { const struct samsung_mipi_dphy_timing *timing; unsigned int lane_hs_rate = div64_ul(samsung->pll.rate, USEC_PER_SEC); - u32 val = 0; + u32 val, res_up, res_down; timing = samsung_mipi_dphy_get_timing(samsung); - regmap_write(samsung->regmap, COMBO_MD0_ANA_CON0, 0x7133); - regmap_write(samsung->regmap, COMBO_MD1_ANA_CON0, 0x7133); - regmap_write(samsung->regmap, COMBO_MD2_ANA_CON0, 0x7133); - regmap_write(samsung->regmap, DPHY_MD3_ANA_CON0, 0x7133); + /* + * The Drive-Strength / Voltage-Amplitude is adjusted by adjusting the + * Driver-Up Resistor and Driver-Down Resistor. + */ + res_up = samsung->pdata->dphy_hs_drv_res_cfg->data_hs_drv_up_ohm; + res_down = samsung->pdata->dphy_hs_drv_res_cfg->data_hs_drv_down_ohm; + val = EDGE_CON(7) | EDGE_CON_DIR(0) | EDGE_CON_EN | + RES_UP(res_up) | RES_DN(res_down); + regmap_write(samsung->regmap, COMBO_MD0_ANA_CON0, val); + regmap_write(samsung->regmap, COMBO_MD1_ANA_CON0, val); + regmap_write(samsung->regmap, COMBO_MD2_ANA_CON0, val); + regmap_write(samsung->regmap, DPHY_MD3_ANA_CON0, val); if (lane_hs_rate >= 4500) { regmap_write(samsung->regmap, COMBO_MD0_ANA_CON1, 0x0001); @@ -1655,6 +1678,7 @@ samsung_mipi_dphy_data_lane_timing_init(struct samsung_mipi_dcphy *samsung) regmap_write(samsung->regmap, DPHY_MD3_ANA_CON1, 0x0001); } + val = 0; /* * Divide-by-2 Clock from Serial Clock. Use this when data rate is under * 1500Mbps, otherwise divide-by-16 Clock from Serial Clock @@ -2473,12 +2497,28 @@ static const struct dev_pm_ops samsung_mipi_dcphy_pm_ops = { samsung_mipi_dcphy_runtime_resume, NULL) }; +static const struct hs_drv_res_cfg rk3576_dphy_hs_drv_res_cfg = { + .clk_hs_drv_up_ohm = _52_OHM, + .clk_hs_drv_down_ohm = _52_OHM, + .data_hs_drv_up_ohm = _39_OHM, + .data_hs_drv_down_ohm = _39_OHM, +}; + +static const struct hs_drv_res_cfg rk3588_dphy_hs_drv_res_cfg = { + .clk_hs_drv_up_ohm = _34_OHM, + .clk_hs_drv_down_ohm = _34_OHM, + .data_hs_drv_up_ohm = _43_OHM, + .data_hs_drv_down_ohm = _43_OHM, +}; + static const struct samsung_mipi_dcphy_plat_data rk3576_samsung_mipi_dcphy_plat_data = { + .dphy_hs_drv_res_cfg = &rk3576_dphy_hs_drv_res_cfg, .dphy_tx_max_kbps_per_lane = 2500000L, .cphy_tx_max_ksps_per_lane = 1700000L, }; static const struct samsung_mipi_dcphy_plat_data rk3588_samsung_mipi_dcphy_plat_data = { + .dphy_hs_drv_res_cfg = &rk3588_dphy_hs_drv_res_cfg, .dphy_tx_max_kbps_per_lane = 4500000L, .cphy_tx_max_ksps_per_lane = 2000000L, }; diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.h b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.h index 4155a1a65b0c..535b0f424dbe 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.h +++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.h @@ -10,7 +10,34 @@ #define MAX_NUM_CSI2_DPHY (0x2) +enum hs_drv_res_ohm { + _30_OHM = 0x8, + _31_2_OHM, + _32_5_OHM, + _34_OHM, + _35_5_OHM, + _37_OHM, + _39_OHM, + _41_OHM, + _43_OHM = 0x0, + _46_OHM, + _49_OHM, + _52_OHM, + _56_OHM, + _60_OHM, + _66_OHM, + _73_OHM, +}; + +struct hs_drv_res_cfg { + enum hs_drv_res_ohm clk_hs_drv_up_ohm; + enum hs_drv_res_ohm clk_hs_drv_down_ohm; + enum hs_drv_res_ohm data_hs_drv_up_ohm; + enum hs_drv_res_ohm data_hs_drv_down_ohm; +}; + struct samsung_mipi_dcphy_plat_data { + const struct hs_drv_res_cfg *dphy_hs_drv_res_cfg; u32 dphy_tx_max_kbps_per_lane; u32 cphy_tx_max_ksps_per_lane; }; From 793f858ef26565e5aac924a4b4e87f63a22b49ae Mon Sep 17 00:00:00 2001 From: Zhang Yubing Date: Wed, 17 Apr 2024 14:15:42 +0800 Subject: [PATCH 13/17] drm/rockchip: dw-dp: support fix stream and connector The DP Port may attach a DP MST bridge device as follow: +-------------------------+ +---------------------------------+ | | | | | +---------+ | | +-------------+ | | | | | | | | | stream0 | | | | Output Port1| | | | | | | | | +---------+ | | +-------------+ | | | | | | | | | +---------+ | +-------------+ +-------------+ | | | | | | | | | DP Port | stream1 | +------> Input Port0 | | Output Port2| | | | | | | | | | +---------+ | +-------------+ +-------------+ | | | | | | | | | +---------+ | | +-------------+ | | | | | | | | | stream2 | | | | Output Port3| | | | | | | | | +---------+ | | +-------------+ | | | DP MST Bridge | +-------------------------+ +---------------------------------+ The DP MST bridge device may be a branch device, such as DP MST HUB, the branch unit in DP MST monitor, A DP MST bridge chip and so on. When a sink device connected to the DP MST bridege device's output port, The sink device may receive the stream from steram0/1/2. When the DP MST bridge device is a bridge chip and the output port attach to a fixed device(another bridge device or panel). The output port may want bind itself to a fixed stream. To satisfy this requirement, The prop "rockchip,mst-fixed-ports" is used to do it. For example, define as follow: rockchip,mst-fixed-ports = <1 2 3>; It mean that: DP Port stream0 --> DP MST bridge Output Port1 DP Port stream1 --> DP MST bridge Output Port2 DP Port stream2 --> DP MST bridge Output Port3 Change-Id: I91dfc51a5e8a533ebbabdcae572163907ec2c9fe Signed-off-by: Zhang Yubing --- drivers/gpu/drm/rockchip/dw-dp.c | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/gpu/drm/rockchip/dw-dp.c b/drivers/gpu/drm/rockchip/dw-dp.c index 7ece490e33fa..48ef798f426d 100644 --- a/drivers/gpu/drm/rockchip/dw-dp.c +++ b/drivers/gpu/drm/rockchip/dw-dp.c @@ -424,6 +424,7 @@ struct dw_dp_mst_enc { struct dw_dp_mst_conn *mst_conn; struct dw_dp *dp; int stream_id; + int fix_port_num; bool active; }; @@ -488,6 +489,7 @@ struct dw_dp { bool is_loader_protect; bool support_mst; bool is_mst; + bool is_fix_port; int mst_port_num; int active_mst_links; struct drm_dp_mst_topology_mgr mst_mgr; @@ -1306,6 +1308,7 @@ static int dw_dp_mst_info_dump(struct seq_file *s, void *data) struct dw_dp *dp = node->info_ent->data; struct dw_dp_mst_conn *mst_conn; struct drm_property_blob *path_blob; + int i; if (dp->mst_mgr.cbs) { drm_dp_mst_dump_topology(s, &dp->mst_mgr); @@ -1321,6 +1324,17 @@ static int dw_dp_mst_info_dump(struct seq_file *s, void *data) (char *)path_blob->data); } seq_puts(s, "\n"); + if (dp->is_fix_port) { + seq_puts(s, "\n*** Fix port info ***\n"); + seq_puts(s, "stream id | port num\n"); + + for (i = 0; i < dp->mst_port_num; i++) { + if (!dp->mst_enc[i].dp) + continue; + seq_printf(s, "%-16d %d\n", dp->mst_enc[i].stream_id, + dp->mst_enc[i].fix_port_num); + } + } } return 0; @@ -3924,6 +3938,8 @@ dw_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_p for (i = 0; i < dp->mst_port_num; i++) { if (!of_device_is_available(dp->mst_enc[i].port_node)) continue; + if (dp->is_fix_port && dp->mst_enc[i].fix_port_num != port->port_num) + continue; ret = drm_connector_attach_encoder(&mst_conn->connector, &dp->mst_enc[i].encoder); if (ret) goto err; @@ -3979,6 +3995,31 @@ dw_dp_create_fake_mst_encoders(struct dw_dp *dp) return true; } +static int dw_dp_mst_get_fix_port(struct dw_dp *dp) +{ + char *prop_name = "rockchip,mst-fixed-ports"; + int elem_len, ret, i; + int elem_data[DPTX_MAX_STREAMS]; + + if (!device_property_present(dp->dev, prop_name)) + return 0; + + elem_len = device_property_count_u32(dp->dev, prop_name); + if (dp->mst_port_num != elem_len) + return -EINVAL; + + ret = device_property_read_u32_array(dp->dev, prop_name, elem_data, elem_len); + if (ret) + return -EINVAL; + + dp->is_fix_port = true; + + for (i = 0; i < dp->mst_port_num; i++) + dp->mst_enc[i].fix_port_num = elem_data[i]; + + return 0; +} + static int dw_dp_mst_encoder_init(struct dw_dp *dp, int conn_base_id) { int ret; @@ -3989,6 +4030,10 @@ static int dw_dp_mst_encoder_init(struct dw_dp *dp, int conn_base_id) INIT_LIST_HEAD(&dp->mst_conn_list); dp->mst_mgr.cbs = &mst_cbs; dw_dp_create_fake_mst_encoders(dp); + ret = dw_dp_mst_get_fix_port(dp); + if (ret) + return ret; + ret = drm_dp_mst_topology_mgr_init(&dp->mst_mgr, dp->encoder.dev, &dp->aux, 16, dp->mst_port_num, conn_base_id); if (ret) From c8094ce9fd11d8d9e2fad8998d840afb980f4732 Mon Sep 17 00:00:00 2001 From: Zhang Yubing Date: Wed, 17 Apr 2024 11:19:50 +0800 Subject: [PATCH 14/17] drm/rockchip: dw-dp: support external bridge Change-Id: Ic04fdec4c4559851124fe6a205c8442c120e146d Signed-off-by: Zhang Yubing --- drivers/gpu/drm/rockchip/dw-dp.c | 59 +++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/dw-dp.c b/drivers/gpu/drm/rockchip/dw-dp.c index 48ef798f426d..4b9cf4fdd22f 100644 --- a/drivers/gpu/drm/rockchip/dw-dp.c +++ b/drivers/gpu/drm/rockchip/dw-dp.c @@ -418,6 +418,7 @@ struct dw_dp_mst_enc { struct dw_dp_video video; struct dw_dp_audio *audio; struct device_node *port_node; + struct drm_bridge *next_bridge; DECLARE_BITMAP(sdp_reg_bank, SDP_REG_BANK_SIZE); @@ -3264,14 +3265,39 @@ static const struct drm_connector_funcs dw_dp_mst_connector_funcs = { .early_unregister = dw_dp_mst_connector_early_unregister, }; +static struct drm_bridge *dw_dp_mst_connector_get_bridge(struct dw_dp_mst_conn *mst_conn) +{ + struct dw_dp *dp = mst_conn->dp; + int i; + + if (!dp->is_fix_port) + return NULL; + + for (i = 0; i < dp->mst_port_num; i++) + if (dp->mst_enc[i].fix_port_num == mst_conn->port->port_num) + return dp->mst_enc[i].next_bridge; + + return NULL; +} + static int dw_dp_mst_connector_get_modes(struct drm_connector *connector) { struct dw_dp_mst_conn *mst_conn = container_of(connector, struct dw_dp_mst_conn, connector); struct dw_dp *dp = mst_conn->dp; + struct drm_bridge *bridge; struct edid *edid; int num_modes = 0; + if (dp->is_fix_port) { + bridge = dw_dp_mst_connector_get_bridge(mst_conn); + if (bridge) { + num_modes = drm_bridge_get_modes(bridge, connector); + if (num_modes) + return num_modes; + } + } + edid = drm_dp_mst_get_edid(connector, &dp->mst_mgr, mst_conn->port); if (edid) { drm_connector_update_edid_property(connector, edid); @@ -4020,6 +4046,32 @@ static int dw_dp_mst_get_fix_port(struct dw_dp *dp) return 0; } +static int dw_dp_mst_find_ext_bridges(struct dw_dp *dp) +{ + struct dw_dp_mst_enc *mst_enc; + int i, ret; + + for (i = 0; i < dp->mst_port_num; i++) { + mst_enc = &dp->mst_enc[i]; + if (!of_device_is_available(dp->mst_enc[i].port_node)) + continue; + ret = drm_of_find_panel_or_bridge(mst_enc->port_node, 2, -1, NULL, + &mst_enc->next_bridge); + if (ret < 0 && ret != -ENODEV) + return ret; + + if (mst_enc->next_bridge) { + ret = drm_bridge_attach(&mst_enc->encoder, mst_enc->next_bridge, NULL, 0); + if (ret) { + DRM_DEV_ERROR(dp->dev, "failed to attach next bridge: %d\n", ret); + return ret; + } + } + } + + return 0; +} + static int dw_dp_mst_encoder_init(struct dw_dp *dp, int conn_base_id) { int ret; @@ -4034,6 +4086,9 @@ static int dw_dp_mst_encoder_init(struct dw_dp *dp, int conn_base_id) if (ret) return ret; + ret = dw_dp_mst_find_ext_bridges(dp); + if (ret) + return ret; ret = drm_dp_mst_topology_mgr_init(&dp->mst_mgr, dp->encoder.dev, &dp->aux, 16, dp->mst_port_num, conn_base_id); if (ret) @@ -4070,7 +4125,9 @@ static int dw_dp_connector_init(struct dw_dp *dp) drm_connector_attach_encoder(connector, bridge->encoder); - dw_dp_mst_encoder_init(dp, connector->base.id); + ret = dw_dp_mst_encoder_init(dp, connector->base.id); + if (ret) + return ret; prop = drm_property_create_enum(connector->dev, 0, RK_IF_PROP_COLOR_DEPTH, color_depth_enum_list, ARRAY_SIZE(color_depth_enum_list)); From e50eabf923499b17c9cb6ddfa071d5cb4ef73d56 Mon Sep 17 00:00:00 2001 From: Guochun Huang Date: Mon, 3 Jun 2024 15:41:43 +0800 Subject: [PATCH 15/17] drm/rockchip: dsi2: set phy mode in .loader_protect helper if the mode of the PHY is not set in .loader_protect helper, when entering sleep mode for the first time bring up with display logo, the PHY will not be able to shut down completely and accurately because it does not know in which mode it is operating, resulting in some power consumption. Change-Id: If5606c24fd2a542936cdb2f1c3d4e380d6c3889a Signed-off-by: Guochun Huang --- drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c index 434a6cf03969..4945d7e420f6 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c @@ -1057,6 +1057,10 @@ dw_mipi_dsi2_encoder_atomic_check(struct drm_encoder *encoder, static void dw_mipi_dsi2_loader_protect(struct dw_mipi_dsi2 *dsi2, bool on) { + if (dsi2->dcphy) + if (!dsi2->c_option) + phy_set_mode(dsi2->dcphy, PHY_MODE_MIPI_DPHY); + if (on) { pm_runtime_get_sync(dsi2->dev); phy_init(dsi2->dcphy); From c5be1bded8de042eb7bf4cb66f9f49e01f1313d2 Mon Sep 17 00:00:00 2001 From: Guochun Huang Date: Tue, 21 May 2024 11:00:20 +0800 Subject: [PATCH 16/17] drm/rockchip: dsi2: add support PSR for mipi command mode As described in: https://patchwork.freedesktop.org/patch/msgid/20190228210939.83386-2-sean@poorly.run From the driver's perspective, this works like a regular disable/enable cycle. The driver need only check the 'psr_transition' state in connector_state and keep the panel turned on when in .disable(), while everything else will cycle off as normal. If drivers want more control, they can use the psr_transition state to enter a low-power state to minimize PSR exit time. While this carries the PSR moniker, it is not specific to the DisplayPort technology. This can be used for power savings with other types of self refresh, such as MIPI command mode. Change-Id: I80799c7f1356645e50dae98159591dde6aa5abff Signed-off-by: Guochun Huang --- .../gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c | 170 +++++++++++++++--- 1 file changed, 149 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c index 4945d7e420f6..1a329f90fca1 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c @@ -282,6 +282,9 @@ struct dw_mipi_dsi2 { bool dual_connector_split; bool left_display; u32 split_area; + + bool support_psr; + bool enabled; }; static inline struct dw_mipi_dsi2 *host_to_dsi2(struct mipi_dsi_host *host) @@ -318,6 +321,14 @@ static void grf_field_write(struct dw_mipi_dsi2 *dsi2, enum grf_reg_fields index regmap_write(dsi2->grf, reg, (val << lsb) | (GENMASK(msb, lsb) << 16)); } +static int dw_mipi_dsi2_is_cmd_mode(struct dw_mipi_dsi2 *dsi2) +{ + if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) + return true; + + return false; +} + static int cri_fifos_wait_avail(struct dw_mipi_dsi2 *dsi2) { u32 sts, mask; @@ -432,53 +443,95 @@ static void dw_mipi_dsi2_set_cmd_mode(struct dw_mipi_dsi2 *dsi2) mode, mode & COMMAND_MODE, 1000, MODE_STATUS_TIMEOUT_US); if (ret < 0) - dev_err(dsi2->dev, "failed to enter data stream mode\n"); + dev_err(dsi2->dev, "failed to enter command mode\n"); } static void dw_mipi_dsi2_disable(struct dw_mipi_dsi2 *dsi2) { + if (!dsi2->enabled) + goto out; + regmap_write(dsi2->regmap, DSI2_IPI_PIX_PKT_CFG, 0); dw_mipi_dsi2_set_cmd_mode(dsi2); +out: if (dsi2->slave) dw_mipi_dsi2_disable(dsi2->slave); } static void dw_mipi_dsi2_post_disable(struct dw_mipi_dsi2 *dsi2) { + if (!dsi2->enabled) + goto out; + dw_mipi_dsi2_irq_enable(dsi2, 0); regmap_write(dsi2->regmap, DSI2_PWR_UP, RESET); mipi_dcphy_power_off(dsi2); pm_runtime_put(dsi2->dev); +out: if (dsi2->slave) dw_mipi_dsi2_post_disable(dsi2->slave); } +static struct drm_crtc *dw_mipi_dsi2_get_new_crtc(struct dw_mipi_dsi2 *dsi2, + struct drm_atomic_state *state) +{ + struct drm_encoder *encoder = &dsi2->encoder; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + + connector = drm_atomic_get_new_connector_for_encoder(state, encoder); + if (!connector) + return NULL; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (!conn_state) + return NULL; + + return conn_state->crtc; +} + static void dw_mipi_dsi2_encoder_atomic_disable(struct drm_encoder *encoder, - struct drm_atomic_state *state) + struct drm_atomic_state *old_state) { struct dw_mipi_dsi2 *dsi2 = encoder_to_dsi2(encoder); - struct drm_crtc *crtc = encoder->crtc; - struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); + struct rockchip_crtc_state *s = to_rockchip_crtc_state(encoder->crtc->state); + struct drm_crtc *new_crtc; + struct drm_crtc_state *new_crtc_state = NULL; - if (dsi2->panel) - drm_panel_disable(dsi2->panel); + new_crtc = dw_mipi_dsi2_get_new_crtc(dsi2, old_state); + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(old_state, new_crtc); - if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) + if (new_crtc_state && new_crtc_state->self_refresh_active) + dev_dbg(dsi2->dev, "%s:%d: psr entry\n", __func__, __LINE__); + + if (!new_crtc_state || !new_crtc_state->self_refresh_active) { + if (dsi2->panel) + drm_panel_disable(dsi2->panel); + } + + if (dw_mipi_dsi2_is_cmd_mode(dsi2)) rockchip_drm_crtc_standby(encoder->crtc, 1); dw_mipi_dsi2_disable(dsi2); - if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) + if (dw_mipi_dsi2_is_cmd_mode(dsi2)) rockchip_drm_crtc_standby(encoder->crtc, 0); - if (dsi2->panel) - drm_panel_unprepare(dsi2->panel); + if (!new_crtc_state || !new_crtc_state->self_refresh_active) { + if (dsi2->panel) + drm_panel_unprepare(dsi2->panel); + } dw_mipi_dsi2_post_disable(dsi2); - if (!crtc->state->active_changed) + dsi2->enabled = false; + if (dsi2->slave) + dsi2->slave->enabled = false; + + if (!encoder->crtc->state->active_changed) return; if (dsi2->slave) @@ -749,7 +802,7 @@ static void dw_mipi_dsi2_ipi_set(struct dw_mipi_dsi2 *dsi2) * if the controller is intended to operate in data stream mode, * no more steps are required. */ - if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) + if (dw_mipi_dsi2_is_cmd_mode(dsi2)) return; vact = mode->crtc_vdisplay; @@ -811,6 +864,9 @@ dw_mipi_dsi2_work_mode(struct dw_mipi_dsi2 *dsi2, u32 mode) static void dw_mipi_dsi2_pre_enable(struct dw_mipi_dsi2 *dsi2) { + if (dsi2->enabled) + goto out; + pm_runtime_get_sync(dsi2->dev); dw_mipi_dsi2_host_softrst(dsi2); @@ -827,6 +883,7 @@ static void dw_mipi_dsi2_pre_enable(struct dw_mipi_dsi2 *dsi2) regmap_write(dsi2->regmap, DSI2_PWR_UP, POWER_UP); dw_mipi_dsi2_set_cmd_mode(dsi2); +out: if (dsi2->slave) dw_mipi_dsi2_pre_enable(dsi2->slave); } @@ -853,6 +910,9 @@ static void dw_mipi_dsi2_enable(struct dw_mipi_dsi2 *dsi2) u32 mode; int ret; + if (dsi2->enabled) + goto out; + dw_mipi_dsi2_clk_management(dsi2); dw_mipi_dsi2_ipi_set(dsi2); @@ -870,6 +930,7 @@ static void dw_mipi_dsi2_enable(struct dw_mipi_dsi2 *dsi2) else dw_mipi_dsi2_set_data_stream_mode(dsi2); +out: if (dsi2->slave) dw_mipi_dsi2_enable(dsi2->slave); } @@ -943,8 +1004,19 @@ static void dw_mipi_dsi2_encoder_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct dw_mipi_dsi2 *dsi2 = encoder_to_dsi2(encoder); + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state = NULL; int ret; + crtc = dw_mipi_dsi2_get_new_crtc(dsi2, state); + if (crtc) + old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + + if (old_crtc_state && old_crtc_state->self_refresh_active) { + rockchip_drm_crtc_standby(encoder->crtc, 1); + dev_dbg(dsi2->dev, "%s:%d: psr exit\n", __func__, __LINE__); + } + ret = dw_mipi_dsi2_encoder_mode_set(dsi2, state); if (ret) { dev_err(dsi2->dev, "failed to set dsi2 mode\n"); @@ -961,13 +1033,24 @@ static void dw_mipi_dsi2_encoder_atomic_enable(struct drm_encoder *encoder, dw_mipi_dsi2_pre_enable(dsi2); - if (dsi2->panel) - drm_panel_prepare(dsi2->panel); + if (!(old_crtc_state && old_crtc_state->self_refresh_active)) { + if (dsi2->panel) + drm_panel_prepare(dsi2->panel); + } dw_mipi_dsi2_enable(dsi2); - if (dsi2->panel) - drm_panel_enable(dsi2->panel); + if (old_crtc_state && old_crtc_state->self_refresh_active) + rockchip_drm_crtc_standby(encoder->crtc, 0); + + dsi2->enabled = true; + if (dsi2->slave) + dsi2->slave->enabled = true; + + if (!(old_crtc_state && old_crtc_state->self_refresh_active)) { + if (dsi2->panel) + drm_panel_enable(dsi2->panel); + } DRM_DEV_INFO(dsi2->dev, "final DSI-Link bandwidth: %u x %d %s\n", dsi2->lane_hs_rate, @@ -1014,7 +1097,7 @@ dw_mipi_dsi2_encoder_atomic_check(struct drm_encoder *encoder, s->color_encoding = DRM_COLOR_YCBCR_BT709; s->color_range = DRM_COLOR_YCBCR_FULL_RANGE; - if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) { + if (dw_mipi_dsi2_is_cmd_mode(dsi2)) { s->output_flags |= ROCKCHIP_OUTPUT_MIPI_DS_MODE; s->soft_te = dsi2->te_gpio ? true : false; s->hold_mode = dsi2->disable_hold_mode ? false : true; @@ -1067,12 +1150,16 @@ static void dw_mipi_dsi2_loader_protect(struct dw_mipi_dsi2 *dsi2, bool on) dsi2->phy_enabled = true; if (dsi2->dcphy) dsi2->dcphy->power_count++; + + dsi2->enabled = true; } else { pm_runtime_put(dsi2->dev); phy_exit(dsi2->dcphy); dsi2->phy_enabled = false; if (dsi2->dcphy) dsi2->dcphy->power_count--; + + dsi2->enabled = false; } if (dsi2->slave) @@ -1156,9 +1243,36 @@ dw_mipi_dsi2_connector_mode_valid(struct drm_connector *connector, return MODE_OK; } +static int dw_mipi_dsi2_connector_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct dw_mipi_dsi2 *dsi2 = con_to_dsi2(connector); + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return -ENODEV; + + conn_state->self_refresh_aware = dsi2->support_psr && dw_mipi_dsi2_is_cmd_mode(dsi2); + + if (!conn_state->crtc) + return 0; + + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (!crtc_state) + return 0; + + if (crtc_state->self_refresh_active && !dw_mipi_dsi2_is_cmd_mode(dsi2)) + return -EINVAL; + + return 0; +} + static struct drm_connector_helper_funcs dw_mipi_dsi2_connector_helper_funcs = { .get_modes = dw_mipi_dsi2_connector_get_modes, .mode_valid = dw_mipi_dsi2_connector_mode_valid, + .atomic_check = dw_mipi_dsi2_connector_atomic_check, }; static enum drm_connector_status @@ -1261,6 +1375,7 @@ static int dw_mipi_dsi2_dual_channel_probe(struct dw_mipi_dsi2 *dsi2) dsi2->slave->channel = dsi2->channel; dsi2->slave->format = dsi2->format; dsi2->slave->mode_flags = dsi2->mode_flags; + dsi2->slave->support_psr = dsi2->support_psr; } return 0; @@ -1672,6 +1787,7 @@ static ssize_t dw_mipi_dsi2_transfer(struct dw_mipi_dsi2 *dsi2, u32 val; u32 mode; + pm_runtime_get_sync(dsi2->dev); dw_mipi_dsi2_clk_management(dsi2); regmap_update_bits(dsi2->regmap, DSI2_DSI_VID_TX_CFG, LPDT_DISPLAY_CMD_EN, msg->flags & MIPI_DSI_MSG_USE_LPM ? LPDT_DISPLAY_CMD_EN : 0); @@ -1680,12 +1796,12 @@ static ssize_t dw_mipi_dsi2_transfer(struct dw_mipi_dsi2 *dsi2, ret = mipi_dsi_create_packet(&packet, msg); if (ret) { DRM_DEV_ERROR(dsi2->dev, "failed to create packet: %d\n", ret); - return ret; + goto err; } ret = cri_fifos_wait_avail(dsi2); if (ret) - return ret; + goto err; /* Send payload */ while (DIV_ROUND_UP(packet.payload_length, 4)) { @@ -1712,18 +1828,24 @@ static ssize_t dw_mipi_dsi2_transfer(struct dw_mipi_dsi2 *dsi2, ret = cri_fifos_wait_avail(dsi2); if (ret) - return ret; + goto err; if (msg->rx_len) { ret = dw_mipi_dsi2_read_from_fifo(dsi2, msg); if (ret < 0) - return ret; + goto err; } + pm_runtime_put(dsi2->dev); + if (dsi2->slave) dw_mipi_dsi2_transfer(dsi2->slave, msg); return msg->tx_len; + +err: + pm_runtime_put(dsi2->dev); + return ret; } static ssize_t dw_mipi_dsi2_host_transfer(struct mipi_dsi_host *host, @@ -1778,6 +1900,12 @@ static int dw_mipi_dsi2_probe(struct platform_device *pdev) if (device_property_read_u32(dev, "split-area", &dsi2->split_area)) dsi2->split_area = 0; + dsi2->support_psr = device_property_read_bool(dev, "support-psr"); + if (dsi2->support_psr && dsi2->auto_calc_mode) { + dsi2->auto_calc_mode = false; + dev_info(dev, "disable auto-calculation-mode in PSR mode\n"); + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res); if (IS_ERR(regs)) From 1ebca99c8f1e959c86646bc3cd72aa22dabb71b0 Mon Sep 17 00:00:00 2001 From: Yiqing Zeng Date: Wed, 29 May 2024 11:32:07 +0800 Subject: [PATCH 17/17] media: i2c: maxim4c: support subscribe hot plug detect event Change-Id: Ia646b37a323885f54bedefa1b3bfcc3b7c642b0f Signed-off-by: Yiqing Zeng --- .../i2c/maxim/local/maxim4c/maxim4c_drv.c | 15 ++++++++++++++ .../i2c/maxim/local/maxim4c/maxim4c_drv.h | 6 ++++++ .../i2c/maxim/local/maxim4c/maxim4c_v4l2.c | 20 ++++++++++++++----- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c index 53785ee1e89d..8da9fdd775d5 100644 --- a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c +++ b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c @@ -136,6 +136,20 @@ static int maxim4c_check_local_chipid(maxim4c_t *maxim4c) return -ENODEV; } +static void maxim4c_hot_plug_event_report(maxim4c_t *maxim4c, int data) +{ + struct v4l2_subdev *sd = &maxim4c->subdev; + struct device *dev = &maxim4c->client->dev; + struct v4l2_event evt_hot_plug = { + .type = V4L2_EVENT_HOT_PLUG, + .u.data[0] = data, + }; + + dev_dbg(dev, "%s data %d\n", __func__, data); + + v4l2_event_queue(sd->devnode, &evt_hot_plug); +} + static irqreturn_t maxim4c_hot_plug_detect_irq_handler(int irq, void *dev_id) { maxim4c_t *maxim4c = dev_id; @@ -223,6 +237,7 @@ static void maxim4c_hot_plug_state_check_work(struct work_struct *work) dev_dbg(dev, "lock state: current = 0x%02x, last = 0x%02x\n", curr_lock_state, last_lock_state); + maxim4c_hot_plug_event_report(maxim4c, curr_lock_state); maxim4c->link_lock_state = curr_lock_state; } diff --git a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h index df355dff0755..9ea41c7ba3d5 100644 --- a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h +++ b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h @@ -9,12 +9,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -32,6 +34,10 @@ /* power supply numbers */ #define MAXIM4C_NUM_SUPPLIES 2 +/* Private v4l2 event */ +#define V4L2_EVENT_HOT_PLUG \ + (V4L2_EVENT_PRIVATE_START + 0x10) + enum { MAXIM4C_HOT_PLUG_OUT = 0, MAXIM4C_HOT_PLUG_IN, diff --git a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c index d517f5b71adb..c434290722b6 100644 --- a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c +++ b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c @@ -779,10 +779,7 @@ static int maxim4c_get_fmt(struct v4l2_subdev *sd, fmt->format.height = mode->height; fmt->format.code = mode->bus_fmt; fmt->format.field = V4L2_FIELD_NONE; - if (fmt->pad < PAD_MAX && fmt->pad >= PAD0) - fmt->reserved[0] = mode->vc[fmt->pad]; - else - fmt->reserved[0] = mode->vc[PAD0]; + fmt->reserved[0] = mode->vc[fmt->pad]; } mutex_unlock(&maxim4c->mutex); @@ -925,6 +922,17 @@ static int maxim4c_g_mbus_config(struct v4l2_subdev *sd, } #endif /* LINUX_VERSION_CODE */ +static int maxim4c_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_HOT_PLUG: + return v4l2_event_subscribe(fh, sub, 0, NULL); + default: + return -EINVAL; + } +} + #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_internal_ops maxim4c_internal_ops = { .open = maxim4c_open, @@ -933,6 +941,8 @@ static const struct v4l2_subdev_internal_ops maxim4c_internal_ops = { static const struct v4l2_subdev_core_ops maxim4c_core_ops = { .s_power = maxim4c_s_power, + .subscribe_event = maxim4c_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, .ioctl = maxim4c_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl32 = maxim4c_compat_ioctl32, @@ -1062,7 +1072,7 @@ int maxim4c_v4l2_subdev_init(maxim4c_t *maxim4c) #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &maxim4c_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER)