From 48e24d8aca846ac3d236e82454e3d493fa80a225 Mon Sep 17 00:00:00 2001 From: Elaine Zhang Date: Tue, 26 Nov 2024 15:48:14 +0800 Subject: [PATCH 01/11] ARM: dts: rockchip: rk3506: assign default clock rate for can Signed-off-by: Elaine Zhang Change-Id: I6cdcc3f46d85e6293b050ca9875bd5ae78d60177 --- arch/arm/boot/dts/rk3506.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/rk3506.dtsi b/arch/arm/boot/dts/rk3506.dtsi index 1d172643c8b8..3f7562e65160 100644 --- a/arch/arm/boot/dts/rk3506.dtsi +++ b/arch/arm/boot/dts/rk3506.dtsi @@ -47,6 +47,8 @@ clock-names = "baudclk", "apb_pclk"; resets = <&cru SRST_CAN0>, <&cru SRST_H_CAN0>; reset-names = "can", "can-apb"; + assigned-clocks = <&cru CLK_CAN0>; + assigned-clock-rates = <300000000>; status = "disabled"; }; @@ -58,6 +60,8 @@ clock-names = "baudclk", "apb_pclk"; resets = <&cru SRST_CAN1>, <&cru SRST_H_CAN1>; reset-names = "can", "can-apb"; + assigned-clocks = <&cru CLK_CAN1>; + assigned-clock-rates = <300000000>; status = "disabled"; }; From 941eb09f51237fbad720a0f77c1aacc72226f5ca Mon Sep 17 00:00:00 2001 From: Elaine Zhang Date: Mon, 25 Nov 2024 18:56:53 +0800 Subject: [PATCH 02/11] ARM: dts: rockchip: rk3506 boards: Modify can init freq to 300M. 200M is not an integer frequency. 300M is more suitable. Signed-off-by: Elaine Zhang Change-Id: I7a6ce23a47691fbdc6180f9cb63b832950bc2f59 --- arch/arm/boot/dts/rk3506-evb1-v10.dtsi | 2 +- arch/arm/boot/dts/rk3506g-demo-display-control.dts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/rk3506-evb1-v10.dtsi b/arch/arm/boot/dts/rk3506-evb1-v10.dtsi index b1b182de74eb..8be20a6bb665 100644 --- a/arch/arm/boot/dts/rk3506-evb1-v10.dtsi +++ b/arch/arm/boot/dts/rk3506-evb1-v10.dtsi @@ -60,7 +60,7 @@ &can0 { assigned-clocks = <&cru CLK_CAN0>; - assigned-clock-rates = <200000000>; + assigned-clock-rates = <300000000>; pinctrl-0 = <&rm_io30_can0_tx &rm_io31_can0_rx>; pinctrl-names = "default"; status = "disabled"; diff --git a/arch/arm/boot/dts/rk3506g-demo-display-control.dts b/arch/arm/boot/dts/rk3506g-demo-display-control.dts index 47d4a46a1e60..2d50527ae23a 100644 --- a/arch/arm/boot/dts/rk3506g-demo-display-control.dts +++ b/arch/arm/boot/dts/rk3506g-demo-display-control.dts @@ -225,7 +225,7 @@ &can0 { assigned-clocks = <&cru CLK_CAN0>; - assigned-clock-rates = <200000000>; + assigned-clock-rates = <300000000>; pinctrl-0 = <&rm_io27_can0_tx &rm_io28_can0_rx>; pinctrl-names = "default"; status = "okay"; From 3dca0a0420baf8eb52a526f6d6b519fcb08bde0f Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Thu, 21 Nov 2024 10:37:41 +0800 Subject: [PATCH 03/11] PCI: dw: rockchip: Record the rasdes capability offset Change-Id: Ic85b017fe7de6f679c87dba64cd4221ff2ff9875 Signed-off-by: Jon Lin --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 38 ++++++------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index ba43751b3056..f4cce281bf39 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -141,7 +141,6 @@ struct rk_pcie { bool hp_no_link; bool is_lpbk; bool is_comp; - bool have_rasdes; bool finish_probe; struct regulator *vpcie3v3; struct irq_domain *irq_domain; @@ -159,6 +158,7 @@ struct rk_pcie { u32 slot_power_limit; u8 slot_power_limit_value; u8 slot_power_limit_scale; + u32 rasdes_off; }; struct rk_pcie_of_data { @@ -1044,11 +1044,7 @@ static int rockchip_pcie_rasdes_show(struct seq_file *s, void *unused) seq_printf(s, "Common event signal status: 0x%s\n", pm); - cap_base = dw_pcie_find_ext_capability(pcie->pci, PCI_EXT_CAP_ID_VNDR); - if (!cap_base) { - dev_err(pcie->pci->dev, "Not able to find RASDES CAP!\n"); - return 0; - } + cap_base = pcie->rasdes_off; RAS_DES_EVENT("EBUF Overflow: ", 0); RAS_DES_EVENT("EBUF Under-run: ", 0x0010000); @@ -1099,11 +1095,7 @@ static ssize_t rockchip_pcie_rasdes_write(struct file *file, if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; - cap_base = dw_pcie_find_ext_capability(pcie->pci, PCI_EXT_CAP_ID_VNDR); - if (!cap_base) { - dev_err(pcie->pci->dev, "Not able to find RASDES CAP!\n"); - return 0; - } + cap_base = pcie->rasdes_off; if (!strncmp(buf, "enable", 6)) { dev_info(pcie->pci->dev, "RAS DES Event: Enable ALL!\n"); @@ -1137,11 +1129,7 @@ static int rockchip_pcie_fault_inject_show(struct seq_file *s, void *unused) struct rk_pcie *pcie = s->private; u32 cap_base; - cap_base = dw_pcie_find_ext_capability(pcie->pci, PCI_EXT_CAP_ID_VNDR); - if (!cap_base) { - dev_err(pcie->pci->dev, "Not able to find RASDES CAP!\n"); - return -EINVAL; - } + cap_base = pcie->rasdes_off; INJECTION_EVENT("ERROR_INJECTION0_ENABLE: ", 0x30, 1, 0); INJECTION_EVENT("ERROR_INJECTION1_ENABLE: ", 0x30, 1, 1); @@ -1222,11 +1210,7 @@ static ssize_t rockchip_pcie_fault_inject_write(struct file *file, if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, cnt))) return -EFAULT; - cap_base = dw_pcie_find_ext_capability(pcie->pci, PCI_EXT_CAP_ID_VNDR); - if (!cap_base) { - dev_err(dev, "Not able to find RASDES CAP!\n"); - return -EINVAL; - } + cap_base = pcie->rasdes_off; if (sscanf(buf, "%d %d %d %d", &einj, &enable, &type, &count) < 4) { dev_err(dev, @@ -1301,7 +1285,7 @@ static int rockchip_pcie_debugfs_init(struct rk_pcie *pcie) { struct dentry *file; - if (!IS_ENABLED(CONFIG_DEBUG_FS) || !pcie->have_rasdes) + if (!IS_ENABLED(CONFIG_DEBUG_FS) || !pcie->rasdes_off) return 0; pcie->debugfs = debugfs_create_dir(dev_name(pcie->pci->dev), NULL); @@ -1542,13 +1526,13 @@ static int rk_pcie_host_config(struct rk_pcie *rk_pcie) } /* Enable RASDES Error event by default */ - val = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_VNDR); - if (!val) { + if (!rk_pcie->in_suspend) + rk_pcie->rasdes_off = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_VNDR); + if (!rk_pcie->rasdes_off) { dev_err(pci->dev, "Unable to find RASDES CAP!\n"); } else { - dw_pcie_writel_dbi(pci, val + 8, 0x1c); - dw_pcie_writel_dbi(pci, val + 8, 0x3); - rk_pcie->have_rasdes = true; + dw_pcie_writel_dbi(pci, rk_pcie->rasdes_off + 8, 0x1c); + dw_pcie_writel_dbi(pci, rk_pcie->rasdes_off + 8, 0x3); } dw_pcie_dbi_ro_wr_en(pci); From 6b4f98d9380169fa5e6295407412c2ef357da8ae Mon Sep 17 00:00:00 2001 From: Chaoyi Chen Date: Mon, 25 Nov 2024 16:38:31 +0800 Subject: [PATCH 04/11] drm/rockchip: gem: Fix alloc size setting for IOMMU device For IOMMU devices, the mapped size may be larger than the requested alloc size, which may result in inconsistent sizes being used for unmap. [ 12.286016] iova: 0x0000000000000000 already mapped to 0x000000000c201000 cannot remap to phys: 0x000000001369f000 prot: 0x3 [ 12.287163] [drm:rockchip_gem_iommu_map] *ERROR* failed to map buffer: size=-98 request_size=4096 [ 12.299439] iova: 0x0000000000000000 already mapped to 0x000000000c201000 cannot remap to phys: 0x000000000c2b8000 prot: 0x3 [ 12.300541] [drm:rockchip_gem_iommu_map] *ERROR* failed to map buffer: size=-98 request_size=2785280 Fixes: 2ba5e1d68173 ("drm/rockchip: gem: Set size of the allocated object when create gem") Change-Id: I3496544e5a4eb65dde657f8ed3890f4db933258f Signed-off-by: Chaoyi Chen --- drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 862c50d94c97..e8574fe0c1f4 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -67,6 +67,8 @@ static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj) iommu_flush_iotlb_all(private->domain); + rk_obj->size = ret; + return 0; err_remove_node: @@ -649,6 +651,15 @@ rockchip_gem_create_object(struct drm_device *drm, unsigned int size, if (ret) goto err_free_rk_obj; + /** + * For iommu device, this size will be set in rockchip_gem_iommu_map(). + * The actual mapped size may be larger than the request size. + * + * For non-iommu device, set to the requested fb size. + */ + if (rk_obj->size == 0) + rk_obj->size = size; + return rk_obj; err_free_rk_obj: @@ -729,7 +740,6 @@ rockchip_gem_create_with_handle(struct drm_file *file_priv, return ERR_CAST(rk_obj); obj = &rk_obj->base; - rk_obj->size = size; /* * allocate a id of idr table where the obj is registered From 95a3c34298b5a13c72c0e4bbabaadac6b70698c6 Mon Sep 17 00:00:00 2001 From: William Wu Date: Mon, 25 Nov 2024 17:25:59 +0800 Subject: [PATCH 05/11] phy: rockchip: inno-usb2: Support usb wakeup system for rk3576 1. Select the usb2 phy interrupt logic clock from OSC clock to support interrupt detection if VD_LOGIC is powerdown. 2. Set the linestate filter time control register depends on the OSC 24MHz clock during suspend. Signed-off-by: William Wu Change-Id: Ib265747013b0a08dc0599df25e40dce306ccb289 --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 1b0c7c6d6406..d9c3cf376008 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -215,11 +215,13 @@ struct rockchip_usb2phy_port_cfg { * @clkout_ctl_phy: keep on/turn off output clk of phy via phy inner * debug register. * @ls_filter_con: set linestate filter time. - * @port_cfgs: usb-phy port configurations. - * @ls_filter_con: set linestate filter time. * @refclk_fsel: reference clock frequency select, * true - select 24 MHz * false - select 26 MHz + * @detclk_sel: usb phy grf reference clock select, + * true - select OSC clock + * false - select pclk + * @port_cfgs: usb-phy port configurations. * @chg_det: charger detection registers. */ struct rockchip_usb2phy_cfg { @@ -233,6 +235,7 @@ struct rockchip_usb2phy_cfg { struct usb2phy_reg clkout_ctl_phy; struct usb2phy_reg ls_filter_con; struct usb2phy_reg refclk_fsel; + struct usb2phy_reg detclk_sel; const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; const struct rockchip_chg_det_reg chg_det; }; @@ -3327,6 +3330,13 @@ static int rockchip_usb2phy_pm_suspend(struct device *dev) if (wakeup_enable && rphy->irq > 0) enable_irq_wake(rphy->irq); + /* + * Select the usb2 phy interrupt logic clock from OSC clock + * to support interrupt detection if VD_LOGIC is powerdown. + */ + if (phy_cfg->detclk_sel.enable) + property_enable(rphy->grf, &phy_cfg->detclk_sel, true); + return ret; } @@ -3361,6 +3371,9 @@ static int rockchip_usb2phy_pm_resume(struct device *dev) dev_err(rphy->dev, "failed to set ls filter %d\n", ret); } + if (phy_cfg->detclk_sel.enable) + property_enable(rphy->grf, &phy_cfg->detclk_sel, false); + for (index = 0; index < phy_cfg->num_ports; index++) { rport = &rphy->ports[index]; if (!rport->phy) @@ -4275,8 +4288,9 @@ static const struct rockchip_usb2phy_cfg rk3576_phy_cfgs[] = { .num_ports = 1, .phy_tuning = rk3576_usb2phy_tuning, .clkout_ctl = { 0x0008, 0, 0, 1, 0 }, - .ls_filter_con = { 0x0020, 19, 0, 0x30100, 0x00020 }, + .ls_filter_con = { 0x0020, 19, 0, 0x30100, 0x06020 }, .refclk_fsel = { 0x0004, 2, 0, 0x6, 0x2 }, + .detclk_sel = { 0x00d0, 0, 0, 0, 1 }, .port_cfgs = { [USB2PHY_PORT_OTG] = { .phy_sus = { 0x0000, 8, 0, 0, 0x1d1 }, @@ -4330,8 +4344,9 @@ static const struct rockchip_usb2phy_cfg rk3576_phy_cfgs[] = { .num_ports = 1, .phy_tuning = rk3576_usb2phy_tuning, .clkout_ctl = { 0x2008, 0, 0, 1, 0 }, - .ls_filter_con = { 0x2020, 19, 0, 0x30100, 0x00020 }, + .ls_filter_con = { 0x2020, 19, 0, 0x30100, 0x06020 }, .refclk_fsel = { 0x2004, 2, 0, 0x6, 0x2 }, + .detclk_sel = { 0x20d0, 0, 0, 0, 1 }, .port_cfgs = { [USB2PHY_PORT_OTG] = { .phy_sus = { 0x2000, 8, 0, 0, 0x1d1 }, From 843bc5523b82df2b237d6ea92fd3a0279e1c03f7 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Sat, 23 Nov 2024 15:17:10 +0800 Subject: [PATCH 06/11] ASoC: rockchip: i2s-tdm: Fix TRCM panic on Multi Dais Signed-off-by: Sugar Zhang Change-Id: Ie0188ea02424f02dab1ff68134f93224da3819b8 --- sound/soc/rockchip/rockchip_i2s_tdm.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 53960ed19d29..54264adf0aa4 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -138,6 +138,7 @@ struct rk_i2s_tdm_dev { bool tdm_mode; bool tdm_fsync_half_frame; bool is_dma_active[SNDRV_PCM_STREAM_LAST + 1]; + bool no_pcm; unsigned int mclk_rx_freq; unsigned int mclk_tx_freq; unsigned int mclk_root0_freq; @@ -183,6 +184,20 @@ static struct i2s_of_quirks { }, }; +static int rockchip_trcm_dma_guard_ctrl(struct rk_i2s_tdm_dev *i2s_tdm, + int stream, bool en) +{ + if (i2s_tdm->no_pcm) + return 0; + + if (!i2s_tdm->pcm_comp) { + dev_err(i2s_tdm->dev, "Uninitialized component for TRCM\n"); + return -EINVAL; + } + + return dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, stream, en); +} + static bool rockchip_i2s_tdm_stream_valid(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -1100,7 +1115,7 @@ static void rockchip_i2s_tdm_xfer_trcm_start(struct rk_i2s_tdm_dev *i2s_tdm, regmap_read(i2s_tdm->regmap, I2S_DMACR, &val); en = I2S_DMACR_RDE(1) | I2S_DMACR_TDE(1); if ((val & en) != en) { - dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, bstream, 1); + rockchip_trcm_dma_guard_ctrl(i2s_tdm, bstream, 1); rockchip_i2s_tdm_dma_ctrl(i2s_tdm, bstream, 1); } rockchip_i2s_tdm_xfer_start(i2s_tdm, 0); @@ -1127,7 +1142,7 @@ static void rockchip_i2s_tdm_trcm_pause(struct snd_pcm_substream *substream, int bstream = SNDRV_PCM_STREAM_LAST - stream; if (i2s_tdm->pcm_comp) - dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, stream, 0); + rockchip_trcm_dma_guard_ctrl(i2s_tdm, stream, 0); /* store the current state, prepare for resume if necessary */ i2s_tdm->is_dma_active[bstream] = is_dma_active(i2s_tdm, bstream); @@ -1145,7 +1160,7 @@ static void rockchip_i2s_tdm_trcm_resume(struct snd_pcm_substream *substream, int bstream = SNDRV_PCM_STREAM_LAST - substream->stream; if (i2s_tdm->pcm_comp) { - dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, stream, 1); + rockchip_trcm_dma_guard_ctrl(i2s_tdm, stream, 1); rockchip_i2s_tdm_dma_ctrl(i2s_tdm, stream, 1); } @@ -3005,6 +3020,7 @@ static int rockchip_i2s_tdm_register_platform(struct device *dev) int ret = 0; if (device_property_read_bool(dev, "rockchip,no-dmaengine")) { + i2s_tdm->no_pcm = true; dev_info(dev, "Used for Multi-DAI\n"); return 0; } @@ -3049,8 +3065,8 @@ static int __maybe_unused i2s_tdm_runtime_suspend(struct device *dev) if (i2s_tdm->pcm_comp && i2s_tdm->clk_trcm) { rockchip_i2s_tdm_dma_ctrl(i2s_tdm, 0, 0); rockchip_i2s_tdm_dma_ctrl(i2s_tdm, 1, 0); - dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, 0, 0); - dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, 1, 0); + rockchip_trcm_dma_guard_ctrl(i2s_tdm, 0, 0); + rockchip_trcm_dma_guard_ctrl(i2s_tdm, 1, 0); } regcache_cache_only(i2s_tdm->regmap, true); From 3b9ca4cf5119d62a0e7ec9d173c3f282273cc86d Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Sat, 23 Nov 2024 15:17:11 +0800 Subject: [PATCH 07/11] ASoC: rockchip: multi-dais: Add support for TRCM Signed-off-by: Sugar Zhang Change-Id: I9636480c9995c034f8de4005fc106be5bc9190e3 --- sound/soc/rockchip/rockchip_multi_dais.c | 10 +- sound/soc/rockchip/rockchip_multi_dais.h | 1 + sound/soc/rockchip/rockchip_multi_dais_pcm.c | 160 +++++++++++++++++++ 3 files changed, 170 insertions(+), 1 deletion(-) diff --git a/sound/soc/rockchip/rockchip_multi_dais.c b/sound/soc/rockchip/rockchip_multi_dais.c index 532c2528efb4..d3dffb45b98b 100644 --- a/sound/soc/rockchip/rockchip_multi_dais.c +++ b/sound/soc/rockchip/rockchip_multi_dais.c @@ -25,6 +25,9 @@ #define SOUND_NAME_PREFIX "sound-name-prefix" +#define I2S_CKR 0x8 +#define IS_I2S_TRCM(v) ((v) & GENMASK(29, 28)) + static inline struct rk_mdais_dev *to_info(struct snd_soc_dai *dai) { return snd_soc_dai_get_drvdata(dai); @@ -515,7 +518,7 @@ static int rockchip_mdais_probe(struct platform_device *pdev) struct device_node *node; struct snd_soc_dai_driver *soc_dai; struct rk_dai *dais; - unsigned int *map; + unsigned int *map, val; int count, mp_count; int ret = 0, i = 0; @@ -575,6 +578,11 @@ static int rockchip_mdais_probe(struct platform_device *pdev) dais[i].dai = rockchip_mdais_find_dai(node); if (!dais[i].dai) return -EPROBE_DEFER; + + if (strstr(dev_driver_string(dais[i].dai->dev), "i2s")) { + val = snd_soc_component_read(dais[i].dai->component, I2S_CKR); + dais[i].trcm = IS_I2S_TRCM(val); + } } mdais_parse_daifmt(np, dais, count); diff --git a/sound/soc/rockchip/rockchip_multi_dais.h b/sound/soc/rockchip/rockchip_multi_dais.h index 5d9a076afb78..c0a8ef3e4845 100644 --- a/sound/soc/rockchip/rockchip_multi_dais.h +++ b/sound/soc/rockchip/rockchip_multi_dais.h @@ -17,6 +17,7 @@ struct rk_dai { struct snd_soc_dai *dai; unsigned int fmt; unsigned int fmt_msk; + bool trcm; }; struct rk_mdais_dev { diff --git a/sound/soc/rockchip/rockchip_multi_dais_pcm.c b/sound/soc/rockchip/rockchip_multi_dais_pcm.c index b83b3a2bed95..794e20e75b2b 100644 --- a/sound/soc/rockchip/rockchip_multi_dais_pcm.c +++ b/sound/soc/rockchip/rockchip_multi_dais_pcm.c @@ -18,6 +18,8 @@ #include "rockchip_multi_dais.h" #include "rockchip_dlp.h" +#define DMA_GUARD_BUFFER_SIZE 64 + #define I2S_TXFIFOLR 0xc #define I2S_RXFIFOLR 0x2c #define SAI_TXFIFOLR 0x1c @@ -43,11 +45,19 @@ static unsigned int prealloc_buffer_size_kbytes = 512; module_param(prealloc_buffer_size_kbytes, uint, 0444); MODULE_PARM_DESC(prealloc_buffer_size_kbytes, "Preallocate DMA buffer size (KB)."); +struct trcm_dma_guard { + dma_addr_t dma_addr; + unsigned char *dma_area; +}; + struct dmaengine_mpcm { struct dlp dlp; struct rk_mdais_dev *mdais; struct dma_chan *tx_chans[MAX_DAIS]; struct dma_chan *rx_chans[MAX_DAIS]; + struct trcm_dma_guard tx_guards[MAX_DAIS]; + struct trcm_dma_guard rx_guards[MAX_DAIS]; + bool guard; }; struct dmaengine_mpcm_runtime_data { @@ -484,6 +494,73 @@ static void dmaengine_mpcm_dlp_stop(struct snd_soc_component *component, dlp_stop(component, substream, dmaengine_mpcm_raw_pointer); } +static int dmaengine_mpcm_trcm_dma_guard_ctrl(struct snd_soc_component *component, + int stream, bool en) +{ + struct dmaengine_mpcm *pcm = soc_component_to_mpcm(component); + struct dma_chan **chans; + struct trcm_dma_guard *guards; + struct rk_dai *dais; + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction direction; + unsigned int *maps, buf_sz; + int i, ret, num; + + if (!pcm->guard) + return 0; + + dais = pcm->mdais->dais; + num = pcm->mdais->num_dais; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + chans = pcm->tx_chans; + guards = pcm->tx_guards; + direction = DMA_MEM_TO_DEV; + maps = pcm->mdais->playback_channel_maps; + } else { + chans = pcm->rx_chans; + guards = pcm->rx_guards; + direction = DMA_DEV_TO_MEM; + maps = pcm->mdais->capture_channel_maps; + } + + if (!en) { + for (i = 0; i < num; i++) { + if (!chans[i] || !dais[i].trcm || !maps[i]) + continue; + + ret = dmaengine_terminate_all(chans[i]); + if (ret) + return ret; + } + + return 0; + } + + for (i = 0; i < num; i++) { + if (!chans[i] || !dais[i].trcm || !maps[i]) + continue; + + buf_sz = DMA_GUARD_BUFFER_SIZE / (maps[i] * 4); + buf_sz = buf_sz * maps[i] * 4; + if (!buf_sz) + return -EINVAL; + + desc = dmaengine_prep_dma_cyclic(chans[i], guards[i].dma_addr, + buf_sz, buf_sz, direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -ENOMEM; + + desc->callback = NULL; + desc->callback_param = NULL; + dmaengine_submit(desc); + dma_async_issue_pending(chans[i]); + } + + return 0; +} + static int dmaengine_mpcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { @@ -504,6 +581,7 @@ static int dmaengine_mpcm_trigger(struct snd_soc_component *component, mpcm_dma_async_issue_pending(prtd); } #endif + mpcm_dmaengine_terminate_all(prtd); ret = dmaengine_mpcm_prepare_and_submit(substream); if (ret) return ret; @@ -530,6 +608,7 @@ static int dmaengine_mpcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_STOP: dmaengine_mpcm_dlp_stop(component, substream); mpcm_dmaengine_terminate_all(prtd); + dmaengine_mpcm_trcm_dma_guard_ctrl(component, substream->stream, 1); prtd->start_flag = false; break; default: @@ -725,6 +804,82 @@ static int dmaengine_mpcm_open(struct snd_soc_component *component, return 0; } +static int dmaengine_mpcm_trcm_dma_guard_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct dmaengine_mpcm *pcm = soc_component_to_mpcm(component); + struct dma_chan **chans; + struct trcm_dma_guard *guards; + struct rk_dai *dais; + struct snd_dmaengine_dai_dma_data *dma_data; + struct snd_pcm_substream *substream; + struct dma_slave_config slave_config; + struct device *dev; + dma_addr_t dma_addr; + unsigned char *dma_area; + int i, j, ret, num; + + dais = pcm->mdais->dais; + num = pcm->mdais->num_dais; + + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { + substream = rtd->pcm->streams[i].substream; + if (!substream) + continue; + + dev = dmaengine_dma_dev(pcm, substream); + + chans = substream->stream ? pcm->rx_chans : pcm->tx_chans; + guards = substream->stream ? pcm->rx_guards : pcm->tx_guards; + + for (j = 0; j < num; j++) { + if (!chans[j] || !dais[j].trcm) + continue; + + pcm->guard = true; + + dma_area = dma_alloc_coherent(dev, DMA_GUARD_BUFFER_SIZE, + &dma_addr, GFP_KERNEL); + if (!dma_area) + return -ENOMEM; + + memset(dma_area, 0x0, DMA_GUARD_BUFFER_SIZE); + + guards[j].dma_addr = dma_addr; + guards[j].dma_area = dma_area; + + memset(&slave_config, 0, sizeof(slave_config)); + + dma_data = snd_soc_dai_get_dma_data(dais[j].dai, substream); + if (!dma_data) + continue; + + snd_dmaengine_mpcm_set_config_from_dai_data(substream, + dma_data, + &slave_config); + + /* + * Use the max-16w to cover all 2^n cases, maybe better + * per channels and fmt, at the moment, we use the simple + * way. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config.direction = DMA_MEM_TO_DEV; + slave_config.dst_maxburst = 16; + } else { + slave_config.direction = DMA_DEV_TO_MEM; + slave_config.src_maxburst = 16; + } + + ret = dmaengine_slave_config(chans[j], &slave_config); + if (ret) + return ret; + } + } + + return 0; +} + static int dmaengine_mpcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { struct dmaengine_mpcm *pcm = soc_component_to_mpcm(component); @@ -733,10 +888,15 @@ static int dmaengine_mpcm_new(struct snd_soc_component *component, struct snd_so size_t prealloc_buffer_size; size_t max_buffer_size; unsigned int i; + int ret; prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024; max_buffer_size = SIZE_MAX; + ret = dmaengine_mpcm_trcm_dma_guard_new(component, rtd); + if (ret) + return ret; + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { substream = rtd->pcm->streams[i].substream; if (!substream) From e44e890e55921e3719b3ce769f9c5107851b3871 Mon Sep 17 00:00:00 2001 From: Yu Qiaowei Date: Tue, 26 Nov 2024 11:23:54 +0800 Subject: [PATCH 08/11] video: rockchip: rga3: rga3 support read_status ops Signed-off-by: Yu Qiaowei Change-Id: I870147562888b3b7cc9de7d4f719ed23bca9dae3 --- drivers/video/rockchip/rga3/rga3_reg_info.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/video/rockchip/rga3/rga3_reg_info.c b/drivers/video/rockchip/rga3/rga3_reg_info.c index 83c3cc2e0d8e..a32c842c1b8f 100644 --- a/drivers/video/rockchip/rga3/rga3_reg_info.c +++ b/drivers/video/rockchip/rga3/rga3_reg_info.c @@ -2151,6 +2151,16 @@ static int rga3_get_version(struct rga_scheduler_t *scheduler) return 0; } +static int rga3_read_status(struct rga_job *job, struct rga_scheduler_t *scheduler) +{ + job->intr_status = rga_read(RGA3_INT_RAW, scheduler); + job->hw_status = rga_read(RGA3_STATUS0, scheduler); + job->cmd_status = rga_read(RGA3_CMD_STATE, scheduler); + job->work_cycle = 0; + + return 0; +} + static int rga3_irq(struct rga_scheduler_t *scheduler) { struct rga_job *job = scheduler->running_job; @@ -2165,9 +2175,7 @@ static int rga3_irq(struct rga_scheduler_t *scheduler) if (test_bit(RGA_JOB_STATE_INTR_ERR, &job->state)) return IRQ_WAKE_THREAD; - job->intr_status = rga_read(RGA3_INT_RAW, scheduler); - job->hw_status = rga_read(RGA3_STATUS0, scheduler); - job->cmd_status = rga_read(RGA3_CMD_STATE, scheduler); + scheduler->ops->read_status(job, scheduler); if (DEBUGGER_EN(INT_FLAG)) rga_job_log(job, "irq handler, INTR[0x%x], HW_STATUS[0x%x], CMD_STATUS[0x%x]\n", @@ -2224,7 +2232,7 @@ const struct rga_backend_ops rga3_ops = { .init_reg = rga3_init_reg, .soft_reset = rga3_soft_reset, .read_back_reg = NULL, - .read_status = NULL, + .read_status = rga3_read_status, .irq = rga3_irq, .isr_thread = rga3_isr_thread, }; From e3a10aac758ef1fc0fec4a9acd7f377fa4aba0a2 Mon Sep 17 00:00:00 2001 From: Yu Qiaowei Date: Tue, 26 Nov 2024 11:25:06 +0800 Subject: [PATCH 09/11] video: rockchip: rga3: fix RGA3 state has been cleared in IRQ Signed-off-by: Yu Qiaowei Change-Id: Iea781894feca6099949232aa2f53a35e3a41eb49 --- drivers/video/rockchip/rga3/rga3_reg_info.c | 22 ++++++++++++++++----- drivers/video/rockchip/rga3/rga_job.c | 7 ++++--- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/video/rockchip/rga3/rga3_reg_info.c b/drivers/video/rockchip/rga3/rga3_reg_info.c index a32c842c1b8f..78abe724edb0 100644 --- a/drivers/video/rockchip/rga3/rga3_reg_info.c +++ b/drivers/video/rockchip/rga3/rga3_reg_info.c @@ -2161,19 +2161,29 @@ static int rga3_read_status(struct rga_job *job, struct rga_scheduler_t *schedul return 0; } +static void rga3_clear_intr(struct rga_scheduler_t *scheduler) +{ + rga_write(m_RGA3_INT_FRM_DONE | m_RGA3_INT_CMD_LINE_FINISH | m_RGA3_INT_ERROR_MASK, + RGA3_INT_CLR, scheduler); +} + static int rga3_irq(struct rga_scheduler_t *scheduler) { struct rga_job *job = scheduler->running_job; - /*clear INTR */ - rga_write(m_RGA3_INT_FRM_DONE | m_RGA3_INT_CMD_LINE_FINISH | m_RGA3_INT_ERROR_MASK, - RGA3_INT_CLR, scheduler); + if (job == NULL) { + rga3_clear_intr(scheduler); + rga_err("core[%d], invalid job, INTR[0x%x], HW_STATUS[0x%x], CMD_STATUS[0x%x]\n", + scheduler->core, rga_read(RGA3_INT_RAW, scheduler), + rga_read(RGA3_STATUS0, scheduler), rga_read(RGA3_CMD_STATE, scheduler)); - if (job == NULL) return IRQ_HANDLED; + } - if (test_bit(RGA_JOB_STATE_INTR_ERR, &job->state)) + if (test_bit(RGA_JOB_STATE_INTR_ERR, &job->state)) { + rga3_clear_intr(scheduler); return IRQ_WAKE_THREAD; + } scheduler->ops->read_status(job, scheduler); @@ -2191,6 +2201,8 @@ static int rga3_irq(struct rga_scheduler_t *scheduler) scheduler->ops->soft_reset(scheduler); } + rga3_clear_intr(scheduler); + return IRQ_WAKE_THREAD; } diff --git a/drivers/video/rockchip/rga3/rga_job.c b/drivers/video/rockchip/rga3/rga_job.c index 0fb81a1c0280..b7481a07bf9d 100644 --- a/drivers/video/rockchip/rga3/rga_job.c +++ b/drivers/video/rockchip/rga3/rga_job.c @@ -270,6 +270,7 @@ struct rga_job *rga_job_done(struct rga_scheduler_t *scheduler) static int rga_job_timeout_query_state(struct rga_job *job, int orig_ret) { + int ret = orig_ret; struct rga_scheduler_t *scheduler = job->scheduler; if (test_bit(RGA_JOB_STATE_DONE, &job->state) && @@ -279,7 +280,7 @@ static int rga_job_timeout_query_state(struct rga_job *job, int orig_ret) test_bit(RGA_JOB_STATE_FINISH, &job->state)) { rga_job_err(job, "job hardware has finished, but the software has timeout!\n"); - return -EBUSY; + ret = -EBUSY; } else if (!test_bit(RGA_JOB_STATE_DONE, &job->state) && !test_bit(RGA_JOB_STATE_FINISH, &job->state)) { rga_job_err(job, "job hardware has timeout.\n"); @@ -287,7 +288,7 @@ static int rga_job_timeout_query_state(struct rga_job *job, int orig_ret) if (scheduler->ops->read_status) scheduler->ops->read_status(job, scheduler); - return -EBUSY; + ret = -EBUSY; } rga_job_err(job, "timeout core[%d]: INTR[0x%x], HW_STATUS[0x%x], CMD_STATUS[0x%x], WORK_CYCLE[0x%x(%d)]\n", @@ -295,7 +296,7 @@ static int rga_job_timeout_query_state(struct rga_job *job, int orig_ret) job->intr_status, job->hw_status, job->cmd_status, job->work_cycle, job->work_cycle); - return orig_ret; + return ret; } static void rga_job_scheduler_timeout_clean(struct rga_scheduler_t *scheduler) From b8cbadd8c59197d4be6664fc4c2df1dff6c0b119 Mon Sep 17 00:00:00 2001 From: Yu Qiaowei Date: Wed, 13 Nov 2024 17:33:03 +0800 Subject: [PATCH 10/11] video: rockchip: rga3: using async mode to print log when async is disabled Signed-off-by: Yu Qiaowei Change-Id: If97d123618d0ab406e77002d79bd0780162c9c2b --- drivers/video/rockchip/rga3/include/rga_fence.h | 8 ++++---- drivers/video/rockchip/rga3/rga_drv.c | 7 +++++++ drivers/video/rockchip/rga3/rga_job.c | 4 ++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/video/rockchip/rga3/include/rga_fence.h b/drivers/video/rockchip/rga3/include/rga_fence.h index 7e3bbeebbc3a..75c7c22926a2 100644 --- a/drivers/video/rockchip/rga3/include/rga_fence.h +++ b/drivers/video/rockchip/rga3/include/rga_fence.h @@ -58,12 +58,12 @@ static inline int rga_dma_fence_get_status(struct dma_fence *fence) #else static inline struct dma_fence *rga_dma_fence_alloc(void) { - return NULL; + return ERR_PTR(-EINVAL); } static inline int rga_dma_fence_get_fd(struct dma_fence *fence) { - return 0; + return -1; } static inline struct dma_fence *rga_get_dma_fence_from_fd(int fence_fd) @@ -80,7 +80,7 @@ static inline int rga_dma_fence_add_callback(struct dma_fence *fence, dma_fence_func_t func, void *private) { - return 0; + return -EINVAL; } static inline void rga_dma_fence_put(struct dma_fence *fence) @@ -93,7 +93,7 @@ static inline void rga_dma_fence_signal(struct dma_fence *fence, int error) static inline int rga_dma_fence_get_status(struct dma_fence *fence) { - return 0; + return -EINVAL; } #endif /* #ifdef CONFIG_SYNC_FILE */ diff --git a/drivers/video/rockchip/rga3/rga_drv.c b/drivers/video/rockchip/rga3/rga_drv.c index 37d27b7d6dc9..fc96dc198ac5 100644 --- a/drivers/video/rockchip/rga3/rga_drv.c +++ b/drivers/video/rockchip/rga3/rga_drv.c @@ -936,6 +936,13 @@ static long rga_ioctl(struct file *file, uint32_t cmd, unsigned long arg) return -EBUSY; } + if (cmd == RGA_BLIT_ASYNC && !IS_ENABLED(CONFIG_ROCKCHIP_RGA_ASYNC)) { + rga_log("The current driver does not support asynchronous mode, please enable CONFIG_ROCKCHIP_RGA_ASYNC.\n"); + up_read(&rga_drvdata->rwsem); + + return -EINVAL; + } + switch (cmd) { case RGA_BLIT_SYNC: case RGA_BLIT_ASYNC: diff --git a/drivers/video/rockchip/rga3/rga_job.c b/drivers/video/rockchip/rga3/rga_job.c index b7481a07bf9d..2a1a16e5a640 100644 --- a/drivers/video/rockchip/rga3/rga_job.c +++ b/drivers/video/rockchip/rga3/rga_job.c @@ -1254,9 +1254,9 @@ int rga_request_submit(struct rga_request *request) if (request->sync_mode == RGA_BLIT_ASYNC) { release_fence = rga_dma_fence_alloc(); - if (IS_ERR(release_fence)) { + if (IS_ERR_OR_NULL(release_fence)) { rga_req_err(request, "Can not alloc release fence!\n"); - ret = IS_ERR(release_fence); + ret = IS_ERR(release_fence) ? PTR_ERR(release_fence) : -EINVAL; goto err_reset_request; } request->release_fence = release_fence; From 82ac6d206e0f1e202322e5fe99aee0b241fe33c1 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Wed, 14 Aug 2024 11:05:14 +0800 Subject: [PATCH 11/11] ASoC: rockchip: i2s-tdm: Add support for FSXN Control I2STDM acts as SLAVE mode, It requires two GPIO pins to control DSP-FSXN (rk2118) to output LRCK_TX/RX. e.g. &i2s1 { fsxn-rx-gpio = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>; fsxn-tx-gpio = <&gpio4 RK_PA0 GPIO_ACTIVE_HIGH>; pinctrl-names = "default"; pinctrl-0 = <&i2s1m0_lrck &i2s1m0_sclk &i2s1m0_sdi0 &i2s1m0_sdi1 &i2s1m0_sdo0 &i2s1m0_sdo1 &i2s1m0_sdo2 &gpio_fsxn_pins>; }; &pinctrl { fsxn { /omit-if-no-ref/ gpio_fsxn_pins: gpio-fsxn-pins { rockchip,pins = <2 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>, <4 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>; }; }; }; Signed-off-by: Sugar Zhang Change-Id: I1b4d16b51dac4f9ab728fa0889003fc46cd608d3 --- sound/soc/rockchip/rockchip_i2s_tdm.c | 72 +++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 54264adf0aa4..2b6944361dcc 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -163,6 +163,8 @@ struct rk_i2s_tdm_dev { #ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES struct snd_soc_dai *clk_src_dai; struct gpio_desc *tdm_fsync_gpio; + struct gpio_desc *fsxn_tx_gpio; + struct gpio_desc *fsxn_rx_gpio; unsigned int tx_lanes; unsigned int rx_lanes; void __iomem *clk_src_base; @@ -814,11 +816,58 @@ static int rockchip_i2s_tdm_multi_lanes_set_clk(struct snd_pcm_substream *substr return 0; } +static int rockchip_i2s_tdm_fsxn_start(struct rk_i2s_tdm_dev *i2s_tdm, int stream) +{ + struct gpio_desc *fsn; + unsigned int msk, val; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + fsn = i2s_tdm->fsxn_tx_gpio; + msk = I2S_XFER_TXS_MASK; + val = I2S_XFER_TXS_START; + } else { + fsn = i2s_tdm->fsxn_rx_gpio; + msk = I2S_XFER_RXS_MASK; + val = I2S_XFER_RXS_START; + } + + if (!fsn) + return -ENODEV; + + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, msk, val); + udelay(10); + gpiod_set_value(fsn, 1); + + return 0; +} + +static int rockchip_i2s_tdm_fsxn_stop(struct rk_i2s_tdm_dev *i2s_tdm, int stream) +{ + struct gpio_desc *fsn; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + fsn = i2s_tdm->fsxn_tx_gpio; + else + fsn = i2s_tdm->fsxn_rx_gpio; + + if (!fsn) + return -ENODEV; + + gpiod_set_value(fsn, 0); + + return 0; +} + static int rockchip_i2s_tdm_multi_lanes_start(struct rk_i2s_tdm_dev *i2s_tdm, int stream) { unsigned int tdm_h = 0, tdm_l = 0, i2s_h = 0, i2s_l = 0; unsigned int msk, val, reg, fmt; unsigned long flags; + int ret; + + ret = rockchip_i2s_tdm_fsxn_start(i2s_tdm, stream); + if (ret == 0) + return 0; if (!i2s_tdm->tdm_fsync_gpio || !i2s_tdm->i2s_lrck_gpio) return -ENOSYS; @@ -921,6 +970,25 @@ static int rockchip_i2s_tdm_multi_lanes_parse(struct rk_i2s_tdm_dev *i2s_tdm) i2s_tdm->rx_lanes = val; } + i2s_tdm->fsxn_rx_gpio = devm_gpiod_get_optional(i2s_tdm->dev, "fsxn-rx", + GPIOD_OUT_LOW); + if (IS_ERR(i2s_tdm->fsxn_rx_gpio)) { + ret = PTR_ERR(i2s_tdm->fsxn_rx_gpio); + dev_err(i2s_tdm->dev, "Failed to get fsxn_rx_gpio %d\n", ret); + return ret; + } + + i2s_tdm->fsxn_tx_gpio = devm_gpiod_get_optional(i2s_tdm->dev, "fsxn-tx", + GPIOD_OUT_LOW); + if (IS_ERR(i2s_tdm->fsxn_tx_gpio)) { + ret = PTR_ERR(i2s_tdm->fsxn_tx_gpio); + dev_err(i2s_tdm->dev, "Failed to get fsxn_tx_gpio %d\n", ret); + return ret; + } + + if (i2s_tdm->fsxn_rx_gpio || i2s_tdm->fsxn_tx_gpio) + dev_info(i2s_tdm->dev, "FSXN Mode\n"); + /* It's optional, required when use soc clk src, such as: i2s2_2ch */ clk_src_node = of_parse_phandle(i2s_tdm->dev->of_node, "rockchip,clk-src", 0); gpiod_flags = clk_src_node ? GPIOD_ASIS : GPIOD_IN; @@ -1099,6 +1167,10 @@ static void rockchip_i2s_tdm_xfer_stop(struct rk_i2s_tdm_dev *i2s_tdm, rockchip_i2s_tdm_clear(i2s_tdm, clr); +#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES + rockchip_i2s_tdm_fsxn_stop(i2s_tdm, stream); +#endif + dev_dbg(i2s_tdm->dev, "%s: stream: %d force: %d\n", __func__, stream, force); }