diff options
42 files changed, 555 insertions, 244 deletions
diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml index 43e7f86e3b23..476dcb49ece6 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml @@ -42,10 +42,15 @@ patternProperties: $ref: /schemas/types.yaml#/definitions/flag frame-master: description: Indicates dai-link frame master. - $ref: /schemas/types.yaml#/definitions/phandle + oneOf: + - $ref: /schemas/types.yaml#/definitions/flag + - $ref: /schemas/types.yaml#/definitions/phandle bitclock-master: description: Indicates dai-link bit clock master - $ref: /schemas/types.yaml#/definitions/phandle + oneOf: + - $ref: /schemas/types.yaml#/definitions/flag + - $ref: /schemas/types.yaml#/definitions/phandle + dai-format: description: audio format. items: diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt deleted file mode 100644 index dc30c6bfbe95..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt +++ /dev/null @@ -1,30 +0,0 @@ -NVIDIA Tegra 20 I2S controller - -Required properties: -- compatible : "nvidia,tegra20-i2s" -- reg : Should contain I2S registers location and length -- interrupts : Should contain I2S interrupt -- resets : Must contain an entry for each entry in reset-names. - See ../reset/reset.txt for details. -- reset-names : Must include the following entries: - - i2s -- dmas : Must contain an entry for each entry in clock-names. - See ../dma/dma.txt for details. -- dma-names : Must include the following entries: - - rx - - tx -- clocks : Must contain one entry, for the module clock. - See ../clocks/clock-bindings.txt for details. - -Example: - -i2s@70002800 { - compatible = "nvidia,tegra20-i2s"; - reg = <0x70002800 0x200>; - interrupts = < 45 >; - clocks = <&tegra_car 11>; - resets = <&tegra_car 11>; - reset-names = "i2s"; - dmas = <&apbdma 21>, <&apbdma 21>; - dma-names = "rx", "tx"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml new file mode 100644 index 000000000000..68ae124eaf80 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra20-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra20 I2S Controller + +description: | + The I2S Controller streams synchronous serial audio data between system + memory and an external audio device. The controller supports the I2S Left + Justified Mode, Right Justified Mode, and DSP mode formats. + +maintainers: + - Thierry Reding <treding@nvidia.com> + - Jon Hunter <jonathanh@nvidia.com> + +properties: + compatible: + const: nvidia,tegra20-i2s + + reg: + maxItems: 1 + + resets: + maxItems: 1 + + reset-names: + const: i2s + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + + dmas: + minItems: 2 + + dma-names: + items: + - const: rx + - const: tx + + nvidia,fixed-parent-rate: + description: | + Specifies whether board prefers parent clock to stay at a fixed rate. + This allows multiple Tegra20 audio components work simultaneously by + limiting number of supportable audio rates. + type: boolean + +required: + - compatible + - reg + - resets + - reset-names + - interrupts + - clocks + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + i2s@70002800 { + compatible = "nvidia,tegra20-i2s"; + reg = <0x70002800 0x200>; + interrupts = <45>; + clocks = <&tegra_car 11>; + resets = <&tegra_car 11>; + reset-names = "i2s"; + dmas = <&apbdma 21>, <&apbdma 21>; + dma-names = "rx", "tx"; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml new file mode 100644 index 000000000000..60a368a132b8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra20-spdif.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra20 S/PDIF Controller + +description: | + The S/PDIF controller supports both input and output in serial audio + digital interface format. The input controller can digitally recover + a clock from the received stream. The S/PDIF controller is also used + to generate the embedded audio for HDMI output channel. + +maintainers: + - Thierry Reding <treding@nvidia.com> + - Jon Hunter <jonathanh@nvidia.com> + +properties: + compatible: + const: nvidia,tegra20-spdif + + reg: + maxItems: 1 + + resets: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 2 + + clock-names: + items: + - const: out + - const: in + + dmas: + minItems: 2 + + dma-names: + items: + - const: rx + - const: tx + + "#sound-dai-cells": + const: 0 + + nvidia,fixed-parent-rate: + description: | + Specifies whether board prefers parent clock to stay at a fixed rate. + This allows multiple Tegra20 audio components work simultaneously by + limiting number of supportable audio rates. + type: boolean + +required: + - compatible + - reg + - resets + - interrupts + - clocks + - clock-names + - dmas + - dma-names + - "#sound-dai-cells" + +additionalProperties: false + +examples: + - | + spdif@70002400 { + compatible = "nvidia,tegra20-spdif"; + reg = <0x70002400 0x200>; + interrupts = <77>; + clocks = <&clk 99>, <&clk 98>; + clock-names = "out", "in"; + resets = <&rst 10>; + dmas = <&apbdma 3>, <&apbdma 3>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + }; + +... diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index a23563cd118b..5a53d7fcef01 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -727,12 +727,6 @@ static int mmp_pdma_config_write(struct dma_chan *dchan, chan->dir = direction; chan->dev_addr = addr; - /* FIXME: drivers should be ported over to use the filter - * function. Once that's done, the following two lines can - * be removed. - */ - if (cfg->slave_id) - chan->drcmr = cfg->slave_id; return 0; } diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c index 52d04641e361..6078cc81892e 100644 --- a/drivers/dma/pxa_dma.c +++ b/drivers/dma/pxa_dma.c @@ -909,13 +909,6 @@ static void pxad_get_config(struct pxad_chan *chan, *dcmd |= PXA_DCMD_BURST16; else if (maxburst == 32) *dcmd |= PXA_DCMD_BURST32; - - /* FIXME: drivers should be ported over to use the filter - * function. Once that's done, the following two lines can - * be removed. - */ - if (chan->cfg.slave_id) - chan->drcmr = chan->cfg.slave_id; } static struct dma_async_tx_descriptor * diff --git a/drivers/dma/qcom/qcom_adm.c b/drivers/dma/qcom/qcom_adm.c index ee78bed8d60d..facdacf8aede 100644 --- a/drivers/dma/qcom/qcom_adm.c +++ b/drivers/dma/qcom/qcom_adm.c @@ -8,6 +8,7 @@ #include <linux/device.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> +#include <linux/dma/qcom_adm.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -140,6 +141,8 @@ struct adm_chan { struct adm_async_desc *curr_txd; struct dma_slave_config slave; + u32 crci; + u32 mux; struct list_head node; int error; @@ -379,8 +382,8 @@ static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, return ERR_PTR(-EINVAL); } - crci = achan->slave.slave_id & 0xf; - if (!crci || achan->slave.slave_id > 0x1f) { + crci = achan->crci & 0xf; + if (!crci || achan->crci > 0x1f) { dev_err(adev->dev, "invalid crci value\n"); return ERR_PTR(-EINVAL); } @@ -403,9 +406,7 @@ static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, if (!async_desc) return ERR_PTR(-ENOMEM); - if (crci) - async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ? - ADM_CRCI_CTL_MUX_SEL : 0; + async_desc->mux = achan->mux ? ADM_CRCI_CTL_MUX_SEL : 0; async_desc->crci = crci; async_desc->blk_size = blk_size; async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) + @@ -488,10 +489,13 @@ static int adm_terminate_all(struct dma_chan *chan) static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) { struct adm_chan *achan = to_adm_chan(chan); + struct qcom_adm_peripheral_config *config = cfg->peripheral_config; unsigned long flag; spin_lock_irqsave(&achan->vc.lock, flag); memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config)); + if (cfg->peripheral_size == sizeof(config)) + achan->crci = config->crci; spin_unlock_irqrestore(&achan->vc.lock, flag); return 0; @@ -694,6 +698,45 @@ static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, achan->vc.desc_free = adm_dma_free_desc; } +/** + * adm_dma_xlate + * @dma_spec: pointer to DMA specifier as found in the device tree + * @ofdma: pointer to DMA controller data + * + * This can use either 1-cell or 2-cell formats, the first cell + * identifies the slave device, while the optional second cell + * contains the crci value. + * + * Returns pointer to appropriate dma channel on success or NULL on error. + */ +static struct dma_chan *adm_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct dma_device *dev = ofdma->of_dma_data; + struct dma_chan *chan, *candidate = NULL; + struct adm_chan *achan; + + if (!dev || dma_spec->args_count > 2) + return NULL; + + list_for_each_entry(chan, &dev->channels, device_node) + if (chan->chan_id == dma_spec->args[0]) { + candidate = chan; + break; + } + + if (!candidate) + return NULL; + + achan = to_adm_chan(candidate); + if (dma_spec->args_count == 2) + achan->crci = dma_spec->args[1]; + else + achan->crci = 0; + + return dma_get_slave_channel(candidate); +} + static int adm_dma_probe(struct platform_device *pdev) { struct adm_device *adev; @@ -838,8 +881,7 @@ static int adm_dma_probe(struct platform_device *pdev) goto err_disable_clks; } - ret = of_dma_controller_register(pdev->dev.of_node, - of_dma_xlate_by_chan_id, + ret = of_dma_controller_register(pdev->dev.of_node, adm_dma_xlate, &adev->common); if (ret) goto err_unregister_dma; diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 7f72b3f4cd1a..41c6bc650fa3 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -787,14 +787,6 @@ static int shdma_config(struct dma_chan *chan, return -EINVAL; /* - * overriding the slave_id through dma_slave_config is deprecated, - * but possibly some out-of-tree drivers still do it. - */ - if (WARN_ON_ONCE(config->slave_id && - config->slave_id != schan->real_slave_id)) - schan->real_slave_id = config->slave_id; - - /* * We could lock this, but you shouldn't be configuring the * channel, while using it... */ diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 4357d2395e6b..7f158ef5672d 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -795,9 +795,6 @@ static int sprd_dma_fill_desc(struct dma_chan *chan, return dst_datawidth; } - if (slave_cfg->slave_id) - schan->dev_id = slave_cfg->slave_id; - hw->cfg = SPRD_DMA_DONOT_WAIT_BDONE << SPRD_DMA_WAIT_BDONE_OFFSET; /* diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index b7260749e8ee..eaafcbe4ca94 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -343,12 +343,6 @@ static int tegra_dma_slave_config(struct dma_chan *dc, } memcpy(&tdc->dma_sconfig, sconfig, sizeof(*sconfig)); - if (tdc->slave_id == TEGRA_APBDMA_SLAVE_ID_INVALID && - sconfig->device_fc) { - if (sconfig->slave_id > TEGRA_APBDMA_CSR_REQ_SEL_MASK) - return -EINVAL; - tdc->slave_id = sconfig->slave_id; - } tdc->config_init = true; return 0; diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index ce5c66e6897d..b0f4948b00a5 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -12,6 +12,7 @@ #include <linux/clk.h> #include <linux/debugfs.h> #include <linux/delay.h> +#include <linux/dma/xilinx_dpdma.h> #include <linux/dmaengine.h> #include <linux/dmapool.h> #include <linux/interrupt.h> @@ -1273,6 +1274,7 @@ static int xilinx_dpdma_config(struct dma_chan *dchan, struct dma_slave_config *config) { struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan); + struct xilinx_dpdma_peripheral_config *pconfig; unsigned long flags; /* @@ -1282,15 +1284,18 @@ static int xilinx_dpdma_config(struct dma_chan *dchan, * fixed both on the DPDMA side and on the DP controller side. */ - spin_lock_irqsave(&chan->lock, flags); - /* - * Abuse the slave_id to indicate that the channel is part of a video - * group. + * Use the peripheral_config to indicate that the channel is part + * of a video group. This requires matching use of the custom + * structure in each driver. */ - if (chan->id <= ZYNQMP_DPDMA_VIDEO2) - chan->video_group = config->slave_id != 0; + pconfig = config->peripheral_config; + if (WARN_ON(pconfig && config->peripheral_size != sizeof(*pconfig))) + return -EINVAL; + spin_lock_irqsave(&chan->lock, flags); + if (chan->id <= ZYNQMP_DPDMA_VIDEO2 && pconfig) + chan->video_group = pconfig->video_group; spin_unlock_irqrestore(&chan->lock, flags); return 0; diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index ff2b308d8651..11c409cbc88e 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -24,6 +24,7 @@ #include <linux/clk.h> #include <linux/delay.h> +#include <linux/dma/xilinx_dpdma.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/module.h> @@ -1058,14 +1059,18 @@ static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt); /* - * Set slave_id for each DMA channel to indicate they're part of a + * Set pconfig for each DMA channel to indicate they're part of a * video group. */ for (i = 0; i < info->num_planes; i++) { struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; + struct xilinx_dpdma_peripheral_config pconfig = { + .video_group = true, + }; struct dma_slave_config config = { .direction = DMA_MEM_TO_DEV, - .slave_id = 1, + .peripheral_config = &pconfig, + .peripheral_size = sizeof(pconfig), }; dmaengine_slave_config(dma->chan, &config); diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index 8c2361e66277..463b707d9e99 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1293,14 +1293,12 @@ static int bcm2835_add_host(struct bcm2835_host *host) host->dma_cfg_tx.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; host->dma_cfg_tx.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_cfg_tx.slave_id = 13; /* DREQ channel */ host->dma_cfg_tx.direction = DMA_MEM_TO_DEV; host->dma_cfg_tx.src_addr = 0; host->dma_cfg_tx.dst_addr = host->phys_addr + SDDATA; host->dma_cfg_rx.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; host->dma_cfg_rx.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_cfg_rx.slave_id = 13; /* DREQ channel */ host->dma_cfg_rx.direction = DMA_DEV_TO_MEM; host->dma_cfg_rx.src_addr = host->phys_addr + SDDATA; host->dma_cfg_rx.dst_addr = 0; diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 04e6f7b26706..7c6efa3b6255 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -6,6 +6,7 @@ #include <linux/clk.h> #include <linux/slab.h> #include <linux/bitops.h> +#include <linux/dma/qcom_adm.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/module.h> @@ -952,6 +953,7 @@ static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, struct dma_async_tx_descriptor *dma_desc; struct scatterlist *sgl; struct dma_slave_config slave_conf; + struct qcom_adm_peripheral_config periph_conf = {}; enum dma_transfer_direction dir_eng; int ret; @@ -983,11 +985,19 @@ static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, if (read) { slave_conf.src_maxburst = 16; slave_conf.src_addr = nandc->base_dma + reg_off; - slave_conf.slave_id = nandc->data_crci; + if (nandc->data_crci) { + periph_conf.crci = nandc->data_crci; + slave_conf.peripheral_config = &periph_conf; + slave_conf.peripheral_size = sizeof(periph_conf); + } } else { slave_conf.dst_maxburst = 16; slave_conf.dst_addr = nandc->base_dma + reg_off; - slave_conf.slave_id = nandc->cmd_crci; + if (nandc->cmd_crci) { + periph_conf.crci = nandc->cmd_crci; + slave_conf.peripheral_config = &periph_conf; + slave_conf.peripheral_size = sizeof(periph_conf); + } } ret = dmaengine_slave_config(nandc->chan, &slave_conf); diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index 5eb7b61bbb4d..f86433b29260 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -370,7 +370,6 @@ static int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width) cfg.src_addr_width = dma_width; cfg.dst_addr_width = dma_width; /* tx channel */ - cfg.slave_id = pic32s->tx_irq; cfg.direction = DMA_MEM_TO_DEV; ret = dmaengine_slave_config(master->dma_tx, &cfg); if (ret) { @@ -378,7 +377,6 @@ static int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width) return ret; } /* rx channel */ - cfg.slave_id = pic32s->rx_irq; cfg.direction = DMA_DEV_TO_MEM; ret = dmaengine_slave_config(master->dma_rx, &cfg); if (ret) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index fcef7a961430..c6be09f44dc1 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -9,6 +9,7 @@ #include <linux/kernel.h> #include <linux/atomic.h> +#include <linux/dma/qcom_adm.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/module.h> @@ -290,6 +291,7 @@ static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base) { struct device *dev = msm_port->uart.dev; struct dma_slave_config conf; + struct qcom_adm_peripheral_config periph_conf = {}; struct msm_dma *dma; u32 crci = 0; int ret; @@ -308,7 +310,11 @@ static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base) conf.device_fc = true; conf.dst_addr = base + UARTDM_TF; conf.dst_maxburst = UARTDM_BURST_SIZE; - conf.slave_id = crci; + if (crci) { + conf.peripheral_config = &periph_conf; + conf.peripheral_size = sizeof(periph_conf); + periph_conf.crci = crci; + } ret = dmaengine_slave_config(dma->chan, &conf); if (ret) @@ -333,6 +339,7 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) { struct device *dev = msm_port->uart.dev; struct dma_slave_config conf; + struct qcom_adm_peripheral_config periph_conf = {}; struct msm_dma *dma; u32 crci = 0; int ret; @@ -355,7 +362,11 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) conf.device_fc = true; conf.src_addr = base + UARTDM_RF; conf.src_maxburst = UARTDM_BURST_SIZE; - conf.slave_id = crci; + if (crci) { + conf.peripheral_config = &periph_conf; + conf.peripheral_size = sizeof(periph_conf); + periph_conf.crci = crci; + } ret = dmaengine_slave_config(dma->chan, &conf); if (ret) diff --git a/include/linux/dma/qcom_adm.h b/include/linux/dma/qcom_adm.h new file mode 100644 index 000000000000..af20df674f0c --- /dev/null +++ b/include/linux/dma/qcom_adm.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only +#ifndef __LINUX_DMA_QCOM_ADM_H +#define __LINUX_DMA_QCOM_ADM_H + +#include <linux/types.h> + +struct qcom_adm_peripheral_config { + u32 crci; + u32 mux; +}; + +#endif /* __LINUX_DMA_QCOM_ADM_H */ diff --git a/include/linux/dma/xilinx_dpdma.h b/include/linux/dma/xilinx_dpdma.h new file mode 100644 index 000000000000..83a1377f03f8 --- /dev/null +++ b/include/linux/dma/xilinx_dpdma.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef __LINUX_DMA_XILINX_DPDMA_H +#define __LINUX_DMA_XILINX_DPDMA_H + +#include <linux/types.h> + +struct xilinx_dpdma_peripheral_config { + bool video_group; +}; + +#endif /* __LINUX_DMA_XILINX_DPDMA_H */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 9000f3ffce8b..0349b35235e6 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -418,9 +418,6 @@ enum dma_slave_buswidth { * @device_fc: Flow Controller Settings. Only valid for slave channels. Fill * with 'true' if peripheral should be flow controller. Direction will be * selected at Runtime. - * @slave_id: Slave requester id. Only valid for slave channels. The dma - * slave peripheral will have unique id as dma requester which need to be - * pass as slave config. * @peripheral_config: peripheral configuration for programming peripheral * for dmaengine transfer * @peripheral_size: peripheral configuration buffer size @@ -448,7 +445,6 @@ struct dma_slave_config { u32 src_port_window_size; u32 dst_port_window_size; bool device_fc; - unsigned int slave_id; void *peripheral_config; size_t peripheral_size; }; diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 96666efddb39..38ea046e653c 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -60,7 +60,6 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) * @maxburst: Maximum number of words(note: words, as in units of the * src_addr_width member, not bytes) that can be send to or received from the * DAI in one burst. - * @slave_id: Slave requester id for the DMA channel. * @filter_data: Custom DMA channel filter data, this will usually be used when * requesting the DMA channel. * @chan_name: Custom channel name to use when requesting DMA channel. @@ -74,7 +73,6 @@ struct snd_dmaengine_dai_dma_data { dma_addr_t addr; enum dma_slave_buswidth addr_width; u32 maxburst; - unsigned int slave_id; void *filter_data; const char *chan_name; unsigned int fifo_size; diff --git a/include/sound/sof/debug.h b/include/sound/sof/debug.h index 3ecb5793789d..38693e3fb514 100644 --- a/include/sound/sof/debug.h +++ b/include/sound/sof/debug.h @@ -19,6 +19,8 @@ enum sof_ipc_dbg_mem_zone { SOF_IPC_MEM_ZONE_SYS_RUNTIME = 1, /**< System-runtime zone */ SOF_IPC_MEM_ZONE_RUNTIME = 2, /**< Runtime zone */ SOF_IPC_MEM_ZONE_BUFFER = 3, /**< Buffer zone */ + SOF_IPC_MEM_ZONE_RUNTIME_SHARED = 4, /**< System runtime zone */ + SOF_IPC_MEM_ZONE_SYS_SHARED = 5, /**< System shared zone */ }; /** ABI3.18 */ diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 1fc2fa077574..af6f717e1e7e 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -91,8 +91,8 @@ EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); * @dma_data: DAI DMA data * @slave_config: DMA slave configuration * - * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and - * slave_id fields of the DMA slave config from the same fields of the DAI DMA + * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width + * fields of the DMA slave config from the same fields of the DAI DMA * data struct. The src and dst fields will be initialized depending on the * direction of the substream. If the substream is a playback stream the dst * fields will be initialized, if it is a capture stream the src fields will be @@ -124,7 +124,6 @@ void snd_dmaengine_pcm_set_config_from_dai_data( slave_config->src_addr_width = dma_data->addr_width; } - slave_config->slave_id = dma_data->slave_id; slave_config->peripheral_config = dma_data->peripheral_config; slave_config->peripheral_size = dma_data->peripheral_size; } diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index fa0cc08f70ec..e8d98b362f9d 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -1020,7 +1020,12 @@ static int rz_ssi_probe(struct platform_device *pdev) reset_control_deassert(ssi->rstc); pm_runtime_enable(&pdev->dev); - pm_runtime_resume_and_get(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) { + pm_runtime_disable(ssi->dev); + reset_control_assert(ssi->rstc); + return dev_err_probe(ssi->dev, ret, "pm_runtime_resume_and_get failed\n"); + } spin_lock_init(&ssi->lock); dev_set_drvdata(&pdev->dev, ssi); diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index ac34c330cf0c..1a7d6cefd3b7 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -63,7 +63,7 @@ config SND_SOC_SOF_DEBUG_PROBES config SND_SOC_SOF_DEVELOPER_SUPPORT bool "SOF developer options support" - depends on EXPERT + depends on EXPERT && SND_SOC_SOF help This option unlocks SOF developer options for debug/performance/ code hardening. diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index 43037109e130..c3ecb9e9d5ba 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -104,7 +104,7 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = { }, }; -static void amd_sof_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -113,12 +113,13 @@ static void amd_sof_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { dev_warn(sdev->dev, "No matching ASoC machine driver found\n"); - return; + return NULL; } sof_pdata->tplg_filename = mach->sof_tplg_filename; sof_pdata->fw_filename = mach->fw_filename; - sof_pdata->machine = mach; + + return mach; } /* AMD Renoir DSP ops */ diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index cdc96a7df493..5aa064b28fca 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -293,7 +293,7 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev, return tplg_filename; } -void atom_machine_select(struct snd_sof_dev *sdev) +struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -304,7 +304,7 @@ void atom_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); - return; + return NULL; } pdev = to_platform_device(sdev->dev); @@ -322,12 +322,13 @@ void atom_machine_select(struct snd_sof_dev *sdev) if (!tplg_filename) { dev_dbg(sdev->dev, "error: no topology filename\n"); - return; + return NULL; } sof_pdata->tplg_filename = tplg_filename; mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; - sof_pdata->machine = mach; + + return mach; } EXPORT_SYMBOL_NS(atom_machine_select, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); @@ -402,14 +403,14 @@ struct snd_soc_dai_driver atom_dai[] = { }; EXPORT_SYMBOL_NS(atom_dai, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); -void atom_set_mach_params(const struct snd_soc_acpi_mach *mach, +void atom_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; diff --git a/sound/soc/sof/intel/atom.h b/sound/soc/sof/intel/atom.h index 96a462c7a2e5..b965e5e080a6 100644 --- a/sound/soc/sof/intel/atom.h +++ b/sound/soc/sof/intel/atom.h @@ -65,8 +65,8 @@ int atom_run(struct snd_sof_dev *sdev); int atom_reset(struct snd_sof_dev *sdev); void atom_dump(struct snd_sof_dev *sdev, u32 flags); -void atom_machine_select(struct snd_sof_dev *sdev); -void atom_set_mach_params(const struct snd_soc_acpi_mach *mach, +struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev); +void atom_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); extern struct snd_soc_dai_driver atom_dai[]; diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 1a8a39a878fd..1121711e9029 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -509,7 +509,7 @@ static int bdw_probe(struct snd_sof_dev *sdev) return ret; } -static void bdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *bdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -518,22 +518,23 @@ static void bdw_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); - return; + return NULL; } sof_pdata->tplg_filename = mach->sof_tplg_filename; mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; - sof_pdata->machine = mach; + + return mach; } -static void bdw_set_mach_params(const struct snd_soc_acpi_mach *mach, +static void bdw_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 35ffb71116c6..6381f2b227f0 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -342,16 +342,6 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, w = snd_soc_dai_get_widget(dai, substream->stream); switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: - /* set up hw_params */ - ret = hda_link_pcm_prepare(substream, dai); - if (ret < 0) { - dev_err(dai->dev, - "error: setting up hw_params during resume\n"); - return ret; - } - - fallthrough; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_hdac_ext_link_stream_start(link_dev); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index e910f68706d9..ba60807fbd8f 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -329,7 +329,6 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, /* cmd must be for audio stream */ switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_START: snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index dabbd5d908f6..21100d2e6644 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1105,7 +1105,8 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) } #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) -static int hda_generic_machine_select(struct snd_sof_dev *sdev) +static void hda_generic_machine_select(struct snd_sof_dev *sdev, + struct snd_soc_acpi_mach **mach) { struct hdac_bus *bus = sof_to_bus(sdev); struct snd_soc_acpi_mach_params *mach_params; @@ -1137,7 +1138,7 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) * - one HDMI codec, and/or * - one external HDAudio codec */ - if (!pdata->machine && codec_num <= 2) { + if (!*mach && codec_num <= 2) { hda_mach = snd_soc_acpi_intel_hda_machines; dev_info(bus->dev, "using HDA machine driver %s now\n", @@ -1152,10 +1153,9 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) tplg_filename = hda_mach->sof_tplg_filename; ret = dmic_topology_fixup(sdev, &tplg_filename, idisp_str, &dmic_num); if (ret < 0) - return ret; + return; hda_mach->mach_params.dmic_num = dmic_num; - pdata->machine = hda_mach; pdata->tplg_filename = tplg_filename; if (codec_num == 2) { @@ -1165,23 +1165,22 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) */ hda_mach->mach_params.link_mask = 0; } + + *mach = hda_mach; } } /* used by hda machine driver to create dai links */ - if (pdata->machine) { - mach_params = (struct snd_soc_acpi_mach_params *) - &pdata->machine->mach_params; + if (*mach) { + mach_params = &(*mach)->mach_params; mach_params->codec_mask = bus->codec_mask; mach_params->common_hdmi_codec_drv = hda_codec_use_common_hdmi; } - - return 0; } #else -static int hda_generic_machine_select(struct snd_sof_dev *sdev) +static void hda_generic_machine_select(struct snd_sof_dev *sdev, + struct snd_soc_acpi_mach **mach) { - return 0; } #endif @@ -1264,7 +1263,7 @@ static bool link_slaves_found(struct snd_sof_dev *sdev, return true; } -static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct snd_soc_acpi_link_adr *link; @@ -1282,7 +1281,7 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) * machines, for mixed cases with I2C/I2S the detection relies * on the HID list. */ - if (link_mask && !pdata->machine) { + if (link_mask) { for (mach = pdata->desc->alt_machines; mach && mach->link_mask; mach++) { /* @@ -1317,7 +1316,6 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) if (mach && mach->link_mask) { int dmic_num = 0; - pdata->machine = mach; mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; mach->mach_params.platform = dev_name(sdev->dev); @@ -1339,9 +1337,8 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) int ret; ret = dmic_topology_fixup(sdev, &tplg_filename, "", &dmic_num); - if (ret < 0) - return ret; + return NULL; pdata->tplg_filename = tplg_filename; } @@ -1351,35 +1348,36 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) "SoundWire machine driver %s topology %s\n", mach->drv_name, pdata->tplg_filename); - } else { - dev_info(sdev->dev, - "No SoundWire machine driver found\n"); + + return mach; } + + dev_info(sdev->dev, "No SoundWire machine driver found\n"); } - return 0; + return NULL; } #else -static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { - return 0; + return NULL; } #endif -void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, +void hda_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; } -void hda_machine_select(struct snd_sof_dev *sdev) +struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -1394,8 +1392,6 @@ void hda_machine_select(struct snd_sof_dev *sdev) if (!sof_pdata->tplg_filename) sof_pdata->tplg_filename = mach->sof_tplg_filename; - sof_pdata->machine = mach; - if (mach->link_mask) { mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; @@ -1405,16 +1401,18 @@ void hda_machine_select(struct snd_sof_dev *sdev) /* * If I2S fails, try SoundWire */ - hda_sdw_machine_select(sdev); + if (!mach) + mach = hda_sdw_machine_select(sdev); /* * Choose HDA generic machine driver if mach is NULL. * Otherwise, set certain mach params. */ - hda_generic_machine_select(sdev); - - if (!sof_pdata->machine) + hda_generic_machine_select(sdev, &mach); + if (!mach) dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); + + return mach; } int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index cb71d9d5cf6c..5b4d59647a1d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -728,8 +728,8 @@ extern const struct sof_intel_dsp_desc jsl_chip_info; extern const struct sof_intel_dsp_desc adls_chip_info; /* machine driver select */ -void hda_machine_select(struct snd_sof_dev *sdev); -void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, +struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev); +void hda_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); /* PCI driver selection and probe */ diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 8a1eacc7ec5f..12860da1d373 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -623,7 +623,8 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) if (spcm->pcm.compress) snd_sof_compr_fragment_elapsed(stream->cstream); - else if (!stream->substream->runtime->no_period_wakeup) + else if (stream->substream->runtime && + !stream->substream->runtime->no_period_wakeup) /* only inform ALSA for period_wakeup mode */ snd_sof_pcm_period_elapsed(stream->substream); } diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 0226a53148c9..b0ffb2a93bcc 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -557,15 +557,17 @@ snd_sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) sof_ops(sdev)->machine_unregister(sdev, pdata); } -static inline void +static inline struct snd_soc_acpi_mach * snd_sof_machine_select(struct snd_sof_dev *sdev) { if (sof_ops(sdev) && sof_ops(sdev)->machine_select) - sof_ops(sdev)->machine_select(sdev); + return sof_ops(sdev)->machine_select(sdev); + + return NULL; } static inline void -snd_sof_set_mach_params(const struct snd_soc_acpi_mach *mach, +snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { if (sof_ops(sdev) && sof_ops(sdev)->set_mach_params) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index e4446defe51e..37fb8e6cd493 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -395,26 +395,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; break; - case SNDRV_PCM_TRIGGER_RESUME: - if (spcm->stream[substream->stream].suspend_ignored) { - /* - * this case will be triggered when INFO_RESUME is - * supported, no need to resume streams that remained - * enabled in D0ix. - */ - spcm->stream[substream->stream].suspend_ignored = false; - return 0; - } - - /* set up hw_params */ - ret = sof_pcm_prepare(component, substream); - if (ret < 0) { - dev_err(component->dev, - "error: failed to set up hw_params upon resume\n"); - return ret; - } - - fallthrough; case SNDRV_PCM_TRIGGER_START: if (spcm->stream[substream->stream].suspend_ignored) { /* diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 91e3fa5a7350..9e76b796502f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -1027,9 +1027,10 @@ int sof_machine_check(struct snd_sof_dev *sdev) if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) { /* find machine */ - snd_sof_machine_select(sdev); - if (sof_pdata->machine) { - snd_sof_set_mach_params(sof_pdata->machine, sdev); + mach = snd_sof_machine_select(sdev); + if (mach) { + sof_pdata->machine = mach; + snd_sof_set_mach_params(mach, sdev); return 0; } @@ -1051,7 +1052,7 @@ int sof_machine_check(struct snd_sof_dev *sdev) sof_pdata->tplg_filename = desc->nocodec_tplg_filename; sof_pdata->machine = mach; - snd_sof_set_mach_params(sof_pdata->machine, sdev); + snd_sof_set_mach_params(mach, sdev); return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 16caf5c74035..114882e4370f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -293,8 +293,8 @@ struct snd_sof_dsp_ops { void *pdata); /* optional */ void (*machine_unregister)(struct snd_sof_dev *sdev, void *pdata); /* optional */ - void (*machine_select)(struct snd_sof_dev *sdev); /* optional */ - void (*set_mach_params)(const struct snd_soc_acpi_mach *mach, + struct snd_soc_acpi_mach * (*machine_select)(struct snd_sof_dev *sdev); /* optional */ + void (*set_mach_params)(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); /* optional */ /* DAI ops */ diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index 266d2cab9f49..27365a877e47 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -262,10 +262,59 @@ static int tegra20_i2s_probe(struct snd_soc_dai *dai) return 0; } +static const unsigned int tegra20_i2s_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000 +}; + +static int tegra20_i2s_filter_rates(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, rule->var); + struct snd_soc_dai *dai = rule->private; + struct tegra20_i2s *i2s = dev_get_drvdata(dai->dev); + struct clk *parent = clk_get_parent(i2s->clk_i2s); + long i, parent_rate, valid_rates = 0; + + parent_rate = clk_get_rate(parent); + if (parent_rate <= 0) { + dev_err(dai->dev, "Can't get parent clock rate: %ld\n", + parent_rate); + return parent_rate ?: -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(tegra20_i2s_rates); i++) { + if (parent_rate % (tegra20_i2s_rates[i] * 128) == 0) + valid_rates |= BIT(i); + } + + /* + * At least one rate must be valid, otherwise the parent clock isn't + * audio PLL. Nothing should be filtered in this case. + */ + if (!valid_rates) + valid_rates = BIT(ARRAY_SIZE(tegra20_i2s_rates)) - 1; + + return snd_interval_list(r, ARRAY_SIZE(tegra20_i2s_rates), + tegra20_i2s_rates, valid_rates); +} + +static int tegra20_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate")) + return 0; + + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + tegra20_i2s_filter_rates, dai, + SNDRV_PCM_HW_PARAM_RATE, -1); +} + static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { .set_fmt = tegra20_i2s_set_fmt, .hw_params = tegra20_i2s_hw_params, .trigger = tegra20_i2s_trigger, + .startup = tegra20_i2s_startup, }; static const struct snd_soc_dai_driver tegra20_i2s_dai_template = { diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 7751575cd6d6..d09cd7ee6879 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -7,12 +7,15 @@ */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/reset.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -22,12 +25,12 @@ #include "tegra20_spdif.h" -#define DRV_NAME "tegra20-spdif" - static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev) { struct tegra20_spdif *spdif = dev_get_drvdata(dev); + regcache_cache_only(spdif->regmap, true); + clk_disable_unprepare(spdif->clk_spdif_out); return 0; @@ -38,23 +41,45 @@ static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev) struct tegra20_spdif *spdif = dev_get_drvdata(dev); int ret; + ret = reset_control_assert(spdif->reset); + if (ret) + return ret; + ret = clk_prepare_enable(spdif->clk_spdif_out); if (ret) { dev_err(dev, "clk_enable failed: %d\n", ret); return ret; } + usleep_range(10, 100); + + ret = reset_control_deassert(spdif->reset); + if (ret) + goto disable_clocks; + + regcache_cache_only(spdif->regmap, false); + regcache_mark_dirty(spdif->regmap); + + ret = regcache_sync(spdif->regmap); + if (ret) + goto disable_clocks; + return 0; + +disable_clocks: + clk_disable_unprepare(spdif->clk_spdif_out); + + return ret; } static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { - struct device *dev = dai->dev; - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); unsigned int mask = 0, val = 0; int ret, spdifclock; + long rate; mask |= TEGRA20_SPDIF_CTRL_PACK | TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; @@ -69,6 +94,14 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val); + /* + * FIFO trigger level must be bigger than DMA burst or equal to it, + * otherwise data is discarded on overflow. + */ + regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_DATA_FIFO_CSR, + TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK, + TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL); + switch (params_rate(params)) { case 32000: spdifclock = 4096000; @@ -97,10 +130,16 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); if (ret) { - dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret); + dev_err(dai->dev, "Can't set SPDIF clock rate: %d\n", ret); return ret; } + rate = clk_get_rate(spdif->clk_spdif_out); + if (rate != spdifclock) + dev_warn_once(dai->dev, + "SPDIF clock rate %d doesn't match requested rate %lu\n", + spdifclock, rate); + return 0; } @@ -118,9 +157,9 @@ static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif) } static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -140,9 +179,62 @@ static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, rule->var); + struct snd_soc_dai *dai = rule->private; + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); + struct clk *parent = clk_get_parent(spdif->clk_spdif_out); + const unsigned int rates[] = { 32000, 44100, 48000 }; + long i, parent_rate, valid_rates = 0; + + parent_rate = clk_get_rate(parent); + if (parent_rate <= 0) { + dev_err(dai->dev, "Can't get parent clock rate: %ld\n", + parent_rate); + return parent_rate ?: -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(rates); i++) { + if (parent_rate % (rates[i] * 128) == 0) + valid_rates |= BIT(i); + } + + /* + * At least one rate must be valid, otherwise the parent clock isn't + * audio PLL. Nothing should be filtered in this case. + */ + if (!valid_rates) + valid_rates = BIT(ARRAY_SIZE(rates)) - 1; + + return snd_interval_list(r, ARRAY_SIZE(rates), rates, valid_rates); +} + +static int tegra20_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate")) + return 0; + + /* + * SPDIF and I2S share audio PLL. HDMI takes audio packets from SPDIF + * and audio may not work on some TVs if clock rate isn't precise. + * + * PLL rate is controlled by I2S side. Filter out audio rates that + * don't match PLL rate at the start of stream to allow both SPDIF + * and I2S work simultaneously, assuming that PLL rate won't be + * changed later on. + */ + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + tegra20_spdif_filter_rates, dai, + SNDRV_PCM_HW_PARAM_RATE, -1); +} + static int tegra20_spdif_probe(struct snd_soc_dai *dai) { - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); dai->capture_dma_data = NULL; dai->playback_dma_data = &spdif->playback_dma_data; @@ -151,26 +243,27 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { - .hw_params = tegra20_spdif_hw_params, - .trigger = tegra20_spdif_trigger, + .hw_params = tegra20_spdif_hw_params, + .trigger = tegra20_spdif_trigger, + .startup = tegra20_spdif_startup, }; static struct snd_soc_dai_driver tegra20_spdif_dai = { - .name = DRV_NAME, + .name = "tegra20-spdif", .probe = tegra20_spdif_probe, .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, + SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &tegra20_spdif_dai_ops, }; static const struct snd_soc_component_driver tegra20_spdif_component = { - .name = DRV_NAME, + .name = "tegra20-spdif", }; static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg) @@ -251,7 +344,7 @@ static const struct regmap_config tegra20_spdif_regmap_config = { static int tegra20_spdif_platform_probe(struct platform_device *pdev) { struct tegra20_spdif *spdif; - struct resource *mem, *dmareq; + struct resource *mem; void __iomem *regs; int ret; @@ -262,89 +355,77 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, spdif); - spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out"); + spdif->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(spdif->reset)) { + dev_err(&pdev->dev, "Can't retrieve spdif reset\n"); + return PTR_ERR(spdif->reset); + } + + spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out"); if (IS_ERR(spdif->clk_spdif_out)) { - pr_err("Can't retrieve spdif clock\n"); - ret = PTR_ERR(spdif->clk_spdif_out); - return ret; + dev_err(&pdev->dev, "Could not retrieve spdif clock\n"); + return PTR_ERR(spdif->clk_spdif_out); } regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(regs)) return PTR_ERR(regs); - dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmareq) { - dev_err(&pdev->dev, "No DMA resource\n"); - return -ENODEV; - } - spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - &tegra20_spdif_regmap_config); + &tegra20_spdif_regmap_config); if (IS_ERR(spdif->regmap)) { dev_err(&pdev->dev, "regmap init failed\n"); - ret = PTR_ERR(spdif->regmap); - return ret; + return PTR_ERR(spdif->regmap); } spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; spdif->playback_dma_data.maxburst = 4; - spdif->playback_dma_data.slave_id = dmareq->start; - pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; - ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component, - &tegra20_spdif_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, + &tegra20_spdif_component, + &tegra20_spdif_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); - ret = -ENOMEM; - goto err_pm_disable; + return ret; } - ret = tegra_pcm_platform_register(&pdev->dev); + ret = devm_tegra_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); - goto err_unregister_component; + return ret; } return 0; - -err_unregister_component: - snd_soc_unregister_component(&pdev->dev); -err_pm_disable: - pm_runtime_disable(&pdev->dev); - - return ret; -} - -static int tegra20_spdif_platform_remove(struct platform_device *pdev) -{ - tegra_pcm_platform_unregister(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); - - pm_runtime_disable(&pdev->dev); - - return 0; } static const struct dev_pm_ops tegra20_spdif_pm_ops = { SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend, tegra20_spdif_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; +static const struct of_device_id tegra20_spdif_of_match[] = { + { .compatible = "nvidia,tegra20-spdif", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra20_spdif_of_match); + static struct platform_driver tegra20_spdif_driver = { .driver = { - .name = DRV_NAME, + .name = "tegra20-spdif", .pm = &tegra20_spdif_pm_ops, + .of_match_table = tegra20_spdif_of_match, }, .probe = tegra20_spdif_platform_probe, - .remove = tegra20_spdif_platform_remove, }; - module_platform_driver(tegra20_spdif_driver); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h index 1973ffc2d5c7..ff4b79e2052f 100644 --- a/sound/soc/tegra/tegra20_spdif.h +++ b/sound/soc/tegra/tegra20_spdif.h @@ -451,6 +451,7 @@ struct tegra20_spdif { struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; struct regmap *regmap; + struct reset_control *reset; }; #endif diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index ef1e74d95236..468c8e77de21 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -48,6 +48,12 @@ int tegra_pcm_platform_register(struct device *dev) } EXPORT_SYMBOL_GPL(tegra_pcm_platform_register); +int devm_tegra_pcm_platform_register(struct device *dev) +{ + return devm_snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0); +} +EXPORT_SYMBOL_GPL(devm_tegra_pcm_platform_register); + int tegra_pcm_platform_register_with_chan_names(struct device *dev, struct snd_dmaengine_pcm_config *config, char *txdmachan, char *rxdmachan) diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h index d602126c65b7..2a36eea1740d 100644 --- a/sound/soc/tegra/tegra_pcm.h +++ b/sound/soc/tegra/tegra_pcm.h @@ -32,6 +32,7 @@ int tegra_pcm_hw_params(struct snd_soc_component *component, snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream); int tegra_pcm_platform_register(struct device *dev); +int devm_tegra_pcm_platform_register(struct device *dev); int tegra_pcm_platform_register_with_chan_names(struct device *dev, struct snd_dmaengine_pcm_config *config, char *txdmachan, char *rxdmachan); |