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/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"; }; 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"; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index f1890698db96..28c9e7e27732 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: @@ -648,6 +650,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: @@ -728,7 +739,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 diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 3d4df7cce596..d125935c4f3d 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); 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 }, 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/rga3_reg_info.c b/drivers/video/rockchip/rga3/rga3_reg_info.c index 83c3cc2e0d8e..78abe724edb0 100644 --- a/drivers/video/rockchip/rga3/rga3_reg_info.c +++ b/drivers/video/rockchip/rga3/rga3_reg_info.c @@ -2151,23 +2151,41 @@ 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 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; + } - 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", @@ -2183,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; } @@ -2224,7 +2244,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, }; 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 0fb81a1c0280..2a1a16e5a640 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) @@ -1253,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; diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 53960ed19d29..2b6944361dcc 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; @@ -162,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; @@ -183,6 +186,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) { @@ -799,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; @@ -906,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; @@ -1084,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); } @@ -1100,7 +1187,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 +1214,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 +1232,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 +3092,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 +3137,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); 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)