Skip to content

Commit

Permalink
Support HDMI audio on NVIDIA Tegra20
Browse files Browse the repository at this point in the history
Merge series from Dmitry Osipenko <digetx@gmail.com>:

This series revives Tegra20 S/PDIF driver which was upstreamed long time
ago, but never was used. It also turns Tegra DRM HDMI driver into HDMI
audio CODEC provider. Finally, HDMI audio is enabled in device-trees.
For now the audio is enable only for Acer A500 tablet and Toshiba AC100
netbook because they're already supported by upstream, later on ASUS TF101
tablet will join them.

I based S/PDIF patches on Arnd's Bergmann patch from a separate series [1]
that removes obsolete slave_id. This eases merging of the patches by
removing the merge conflict. This is a note for Mark Brown.

I also based this series on top of power management series [2]. I.e. [2]
should be applied first, otherwise "Add S/PDIF node to Tegra20 device-tree"
patch should have merge conflict. This is a note for Thierry.

[1] https://patchwork.ozlabs.org/project/linux-tegra/list/?series=273312
[2] https://patchwork.ozlabs.org/project/linux-tegra/list/?series=274534

Changelog:

v4: - Added patches that update multi_v7_defconfig with the enabled S/PDIF
      and APB DMA drivers.

v3: - Renamed S/PDIF device-tree clocks as was suggested by Rob Herring.

    - Added r-bs and acks that were given by Rob Herring to v2.

v2: - Corrected I2S yaml problem that was reported by the DT bot for v1
      by removing the non-existent required clock-names property.

    - Removed assigned-clocks property from S/PDIF yaml since this property
      is now inherited from the clocks property.

    - Reordered the "tegra20: spdif: Set FIFO trigger level" patch, making
      it the first sound/soc patch in the series, like it was suggested by
      Mark Brown in the comment to v1. Also reworded commit message of this
      patch to *not* make it looks like it should be backported to stable
      kernels.

Arnd Bergmann (1):
  ASoC: tegra20-spdif: stop setting slave_id

Dmitry Osipenko (21):
  ASoC: dt-bindings: Add binding for Tegra20 S/PDIF
  ASoC: dt-bindings: tegra20-i2s: Convert to schema
  ASoC: dt-bindings: tegra20-i2s: Document new nvidia,fixed-parent-rate
    property
  dt-bindings: host1x: Document optional HDMI sound-dai-cells
  ASoC: tegra20: spdif: Set FIFO trigger level
  ASoC: tegra20: spdif: Support device-tree
  ASoC: tegra20: spdif: Improve driver's code
  ASoC: tegra20: spdif: Use more resource-managed helpers
  ASoC: tegra20: spdif: Reset hardware
  ASoC: tegra20: spdif: Support system suspend
  ASoC: tegra20: spdif: Filter out unsupported rates
  ASoC: tegra20: i2s: Filter out unsupported rates
  drm/tegra: hdmi: Unwind tegra_hdmi_init() errors
  drm/tegra: hdmi: Register audio CODEC on Tegra20
  ARM: tegra_defconfig: Enable S/PDIF driver
  ARM: config: multi v7: Enable NVIDIA Tegra20 S/PDIF driver
  ARM: config: multi v7: Enable NVIDIA Tegra20 APB DMA driver
  ARM: tegra: Add S/PDIF node to Tegra20 device-tree
  ARM: tegra: Add HDMI audio graph to Tegra20 device-tree
  ARM: tegra: acer-a500: Enable S/PDIF and HDMI audio
  ARM: tegra: paz00: Enable S/PDIF and HDMI audio

 .../display/tegra/nvidia,tegra20-host1x.txt   |   1 +
 .../bindings/sound/nvidia,tegra20-i2s.txt     |  30 ---
 .../bindings/sound/nvidia,tegra20-i2s.yaml    |  77 +++++++
 .../bindings/sound/nvidia,tegra20-spdif.yaml  |  85 ++++++++
 .../boot/dts/tegra20-acer-a500-picasso.dts    |   8 +
 arch/arm/boot/dts/tegra20-paz00.dts           |   8 +
 arch/arm/boot/dts/tegra20.dtsi                |  40 +++-
 arch/arm/configs/multi_v7_defconfig           |   2 +
 arch/arm/configs/tegra_defconfig              |   1 +
 drivers/gpu/drm/tegra/Kconfig                 |   3 +
 drivers/gpu/drm/tegra/hdmi.c                  | 168 +++++++++++++--
 sound/soc/tegra/tegra20_i2s.c                 |  49 +++++
 sound/soc/tegra/tegra20_spdif.c               | 197 ++++++++++++------
 sound/soc/tegra/tegra20_spdif.h               |   1 +
 sound/soc/tegra/tegra_pcm.c                   |   6 +
 sound/soc/tegra/tegra_pcm.h                   |   1 +
 16 files changed, 574 insertions(+), 103 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml

--
2.33.1
  • Loading branch information
Mark Brown committed Dec 17, 2021
2 parents a92c1cd + bfa4671 commit be1d03e
Show file tree
Hide file tree
Showing 25 changed files with 475 additions and 150 deletions.
30 changes: 0 additions & 30 deletions Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt

This file was deleted.

77 changes: 77 additions & 0 deletions Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml
Original file line number Diff line number Diff line change
@@ -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";
};
...
85 changes: 85 additions & 0 deletions Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml
Original file line number Diff line number Diff line change
@@ -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>;
};
...
6 changes: 0 additions & 6 deletions drivers/dma/mmp_pdma.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
7 changes: 0 additions & 7 deletions drivers/dma/pxa_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
Expand Down
56 changes: 49 additions & 7 deletions drivers/dma/qcom/qcom_adm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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) +
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 0 additions & 8 deletions drivers/dma/sh/shdma-base.c
Original file line number Diff line number Diff line change
Expand Up @@ -786,14 +786,6 @@ static int shdma_config(struct dma_chan *chan,
if (!config)
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...
Expand Down
3 changes: 0 additions & 3 deletions drivers/dma/sprd-dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/*
Expand Down
6 changes: 0 additions & 6 deletions drivers/dma/tegra20-apb-dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit be1d03e

Please sign in to comment.