[PATCH v1 0/6] Add Tegra234 HDA support

This series add the support for TEGRA234 HDA driver support
Mohan Kumar (6): ALSA: hda/tegra: Add Tegra234 hda driver support ALSA: hda/tegra: Hardcode GCAP ISS value on T234 ALSA: hda/tegra: Update scratch reg. communication dt-bindings: Add HDA support for Tegra234 dt-bindings: Document Tegra234 HDA support arm64: tegra: Add hda dts node for Tegra234
.../bindings/sound/nvidia,tegra30-hda.yaml | 3 + .../nvidia/tegra234-p3737-0000+p3701-0000.dts | 6 + arch/arm64/boot/dts/nvidia/tegra234.dtsi | 18 +++ include/dt-bindings/clock/tegra234-clock.h | 4 + include/dt-bindings/memory/tegra234-mc.h | 6 + .../dt-bindings/power/tegra234-powergate.h | 9 ++ include/dt-bindings/reset/tegra234-reset.h | 2 + include/sound/hda_codec.h | 4 + sound/pci/hda/hda_tegra.c | 33 ++++- sound/pci/hda/patch_hdmi.c | 115 ++++++++++++++---- 10 files changed, 176 insertions(+), 24 deletions(-) create mode 100644 include/dt-bindings/power/tegra234-powergate.h

Add hda driver support for the Tegra234 chip. The hdacodec on this chip now supports DP MST feature, HDA block contains azalia controller and one hda-codec instance by supporting 4 independent output streams over DP MST mode. There is no input stream support.
Signed-off-by: Mohan Kumar mkumard@nvidia.com --- sound/pci/hda/hda_tegra.c | 21 +++++++++++++-- sound/pci/hda/patch_hdmi.c | 54 +++++++++++++++++++++++++++++++++----- 2 files changed, 67 insertions(+), 8 deletions(-)
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 773f4903550a..95df52b0505b 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -70,6 +70,7 @@
struct hda_tegra_soc { bool has_hda2codec_2x_reset; + bool has_hda2hdmi; };
struct hda_tegra { @@ -435,15 +436,23 @@ static int hda_tegra_create(struct snd_card *card,
static const struct hda_tegra_soc tegra30_data = { .has_hda2codec_2x_reset = true, + .has_hda2hdmi = true, };
static const struct hda_tegra_soc tegra194_data = { .has_hda2codec_2x_reset = false, + .has_hda2hdmi = true, +}; + +static const struct hda_tegra_soc tegra234_data = { + .has_hda2codec_2x_reset = true, + .has_hda2hdmi = false, };
static const struct of_device_id hda_tegra_match[] = { { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data }, { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data }, + { .compatible = "nvidia,tegra234-hda", .data = &tegra234_data }, {}, }; MODULE_DEVICE_TABLE(of, hda_tegra_match); @@ -473,7 +482,14 @@ static int hda_tegra_probe(struct platform_device *pdev) }
hda->resets[hda->nresets++].id = "hda"; - hda->resets[hda->nresets++].id = "hda2hdmi"; + + /* + * "hda2hdmi" is not applicable for Tegra234. This is because the + * codec is separate IP and not under display SOR partition now. + */ + if (hda->soc->has_hda2hdmi) + hda->resets[hda->nresets++].id = "hda2hdmi"; + /* * "hda2codec_2x" reset is not present on Tegra194. Though DT would * be updated to reflect this, but to have backward compatibility @@ -488,7 +504,8 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free;
hda->clocks[hda->nclocks++].id = "hda"; - hda->clocks[hda->nclocks++].id = "hda2hdmi"; + if (hda->soc->has_hda2hdmi) + hda->clocks[hda->nclocks++].id = "hda2hdmi"; hda->clocks[hda->nclocks++].id = "hda2codec_2x";
err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 92df4f243ec6..879f886d2406 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3851,17 +3851,29 @@ static int tegra_hdmi_build_pcms(struct hda_codec *codec) return 0; }
-static int patch_tegra_hdmi(struct hda_codec *codec) +static int tegra_hdmi_init(struct hda_codec *codec) { - struct hdmi_spec *spec; - int err; + struct hdmi_spec *spec = codec->spec; + int i, err;
- err = patch_generic_hdmi(codec); - if (err) + err = hdmi_parse_codec(codec); + if (err < 0) { + generic_spec_free(codec); return err; + } + + for (i = 0; i < spec->num_cvts; i++) + snd_hda_codec_write(codec, spec->cvt_nids[i], 0, + AC_VERB_SET_DIGI_CONVERT_1, + AC_DIG1_ENABLE); + + generic_hdmi_init_per_pins(codec);
codec->patch_ops.build_pcms = tegra_hdmi_build_pcms; - spec = codec->spec; + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + spec->chmap.ops.chmap_cea_alloc_validate_get_type = nvhdmi_chmap_cea_alloc_validate_get_type; spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; @@ -3869,6 +3881,35 @@ static int patch_tegra_hdmi(struct hda_codec *codec) return 0; }
+static int patch_tegra_hdmi(struct hda_codec *codec) +{ + int err; + + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + + return tegra_hdmi_init(codec); +} + +static int patch_tegra234_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + + codec->dp_mst = true; + codec->mst_no_extra_pcms = true; + spec = codec->spec; + spec->dyn_pin_out = true; + spec->dyn_pcm_assign = true; + + return tegra_hdmi_init(codec); +} + /* * ATI/AMD-specific implementations */ @@ -4322,6 +4363,7 @@ HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0031, "Tegra234 HDMI/DP", patch_tegra234_hdmi), HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi),

The GCAP register on Tegra234 implies no Input Streams(ISS) supported, but the HW output stream descriptor programming should start with offset 0x20*4 from base stream descriptor address. This will be a problem while calculating the offset for output stream descriptor which will be considering input stream also. So here output stream starts with offset 0 which is wrong as HW register for output stream offset starts with 4. So hardcode the input stream numbers to 4 to avoid the issue in offset calculation.
Signed-off-by: Mohan Kumar mkumard@nvidia.com --- sound/pci/hda/hda_tegra.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 95df52b0505b..2347d0304f93 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -315,6 +315,18 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) * hardcoded value */ chip->capture_streams = (gcap >> 8) & 0x0f; + + /* The GCAP register on Tegra234 implies no Input Streams(ISS) support, + * but the HW output stream descriptor programming should start with + * offset 0x20*4 from base stream descriptor address. This will be a + * problem while calculating the offset for output stream descriptor + * which will be considering input stream also. So here output stream + * starts with offset 0 which is wrong as HW register for output stream + * offset starts with 4. + */ + if (of_device_is_compatible(np, "nvidia,tegra234-hda")) + chip->capture_streams = 4; + chip->playback_streams = (gcap >> 12) & 0x0f; if (!chip->playback_streams && !chip->capture_streams) { /* gcap didn't give any info, switching to old method */

Tegra234 chip scratch register communication between audio and hdmi driver differs slightly in the way it triggers the interrupt compared to legacy chips. Interrupt is triggered by writing non-zero values to verb 0xF80 instead of 31st bit of scratch register.
DP MST support changed the NID to be used for scratch register read/write from audio function group NID to Converter widget NID.
Signed-off-by: Mohan Kumar mkumard@nvidia.com --- include/sound/hda_codec.h | 4 +++ sound/pci/hda/patch_hdmi.c | 61 ++++++++++++++++++++++++++++---------- 2 files changed, 49 insertions(+), 16 deletions(-)
diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 82d9daa17851..c1c19dd4c423 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -240,6 +240,10 @@ struct hda_codec { unsigned int single_adc_amp:1; /* adc in-amp takes no index * (e.g. CX20549 codec) */ + unsigned int hdmi_intr_trig_ctrl:1; /* hdmi interrupt trigger + * control flag + * (e.g. Nvidia codecs) + */ unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */ unsigned int pins_shutup:1; /* pins are shut up */ unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 879f886d2406..f0e87e39c53e 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3721,8 +3721,11 @@ static int patch_nvhdmi_legacy(struct hda_codec *codec) * +-----------------------------------| * * Note that for the trigger bit to take effect it needs to change value - * (i.e. it needs to be toggled). + * (i.e. it needs to be toggled). The trigger bit is not applicable from + * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt + * trigger to hdmi. */ +#define NVIDIA_SET_HOST_INTR 0xf80 #define NVIDIA_GET_SCRATCH0 0xfa6 #define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7 #define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8 @@ -3741,25 +3744,37 @@ static int patch_nvhdmi_legacy(struct hda_codec *codec) * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0, * the format is invalidated so that the HDMI codec can be disabled. */ -static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format) +static void tegra_hdmi_set_format(struct hda_codec *codec, + hda_nid_t cvt_nid, + unsigned int format) { unsigned int value; + unsigned int nid = NVIDIA_AFG_NID; + + /* + * Tegra HDA codec design from TEGRA234 chip onwards support DP MST. + * This resulted in moving scratch registers from audio function + * group to converter widget context. So CVT NID should be used for + * scratch register read/write for DP MST supported Tegra HDA codec. + */ + if (codec->dp_mst) + nid = cvt_nid;
/* bits [31:30] contain the trigger and valid bits */ - value = snd_hda_codec_read(codec, NVIDIA_AFG_NID, 0, + value = snd_hda_codec_read(codec, nid, 0, NVIDIA_GET_SCRATCH0, 0); value = (value >> 24) & 0xff;
/* bits [15:0] are used to store the HDA format */ - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + snd_hda_codec_write(codec, nid, 0, NVIDIA_SET_SCRATCH0_BYTE0, (format >> 0) & 0xff); - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + snd_hda_codec_write(codec, nid, 0, NVIDIA_SET_SCRATCH0_BYTE1, (format >> 8) & 0xff);
/* bits [16:24] are unused */ - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + snd_hda_codec_write(codec, nid, 0, NVIDIA_SET_SCRATCH0_BYTE2, 0);
/* @@ -3771,15 +3786,28 @@ static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format) else value |= NVIDIA_SCRATCH_VALID;
- /* - * Whenever the trigger bit is toggled, an interrupt is raised in the - * HDMI codec. The HDMI driver will use that as trigger to update its - * configuration. - */ - value ^= NVIDIA_SCRATCH_TRIGGER; + if (codec->hdmi_intr_trig_ctrl) { + /* + * For Tegra HDA Codec design from TEGRA234 onwards, the + * Interrupt to hdmi driver is triggered by writing + * non-zero values to verb 0xF80 instead of 31st bit of + * scratch register. + */ + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_HOST_INTR, 0x1); + } else { + /* + * Whenever the 31st trigger bit is toggled, an interrupt is raised + * in the HDMI codec. The HDMI driver will use that as trigger + * to update its configuration. + */ + value ^= NVIDIA_SCRATCH_TRIGGER;
- snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, - NVIDIA_SET_SCRATCH0_BYTE3, value); + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); + } }
static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -3796,7 +3824,7 @@ static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, return err;
/* notify the HDMI codec of the format change */ - tegra_hdmi_set_format(codec, format); + tegra_hdmi_set_format(codec, hinfo->nid, format);
return 0; } @@ -3806,7 +3834,7 @@ static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { /* invalidate the format in the HDMI codec */ - tegra_hdmi_set_format(codec, 0); + tegra_hdmi_set_format(codec, hinfo->nid, 0);
return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream); } @@ -3903,6 +3931,7 @@ static int patch_tegra234_hdmi(struct hda_codec *codec)
codec->dp_mst = true; codec->mst_no_extra_pcms = true; + codec->hdmi_intr_trig_ctrl = true; spec = codec->spec; spec->dyn_pin_out = true; spec->dyn_pcm_assign = true;

On Wed, 09 Feb 2022 05:23:23 +0100, Mohan Kumar wrote:
--- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -240,6 +240,10 @@ struct hda_codec { unsigned int single_adc_amp:1; /* adc in-amp takes no index * (e.g. CX20549 codec) */
- unsigned int hdmi_intr_trig_ctrl:1; /* hdmi interrupt trigger
* control flag
* (e.g. Nvidia codecs)
*/
This flag is specific to (Nvidia) HDMI codec and referred only from patch_hdmi.c, right? If so, the flag would be better put locally to struct hdmi_spec instead of a global hda_codec.
thanks,
Takashi

Add hda clocks, memory ,power and reset binding entries for Tegra234.
Signed-off-by: Mohan Kumar mkumard@nvidia.com --- include/dt-bindings/clock/tegra234-clock.h | 4 ++++ include/dt-bindings/memory/tegra234-mc.h | 6 ++++++ include/dt-bindings/power/tegra234-powergate.h | 9 +++++++++ include/dt-bindings/reset/tegra234-reset.h | 2 ++ 4 files changed, 21 insertions(+) create mode 100644 include/dt-bindings/power/tegra234-powergate.h
diff --git a/include/dt-bindings/clock/tegra234-clock.h b/include/dt-bindings/clock/tegra234-clock.h index 8d7e66e1b6ef..c014269b7245 100644 --- a/include/dt-bindings/clock/tegra234-clock.h +++ b/include/dt-bindings/clock/tegra234-clock.h @@ -30,5 +30,9 @@ #define TEGRA234_CLK_PLLC4 237U /** @brief 32K input clock provided by PMIC */ #define TEGRA234_CLK_CLK_32K 289U +/** @brief CLK_RST_CONTROLLER_AZA2XBITCLK_OUT_SWITCH_DIVIDER switch divider output (aza_2xbitclk) */ +#define TEGRA234_CLK_AZA_2XBIT 457U +/** @brief aza_2xbitclk / 2 (aza_bitclk) */ +#define TEGRA234_CLK_AZA_BIT 458U
#endif diff --git a/include/dt-bindings/memory/tegra234-mc.h b/include/dt-bindings/memory/tegra234-mc.h index 2662f70c15c6..f538fc442cee 100644 --- a/include/dt-bindings/memory/tegra234-mc.h +++ b/include/dt-bindings/memory/tegra234-mc.h @@ -7,6 +7,8 @@ #define TEGRA234_SID_INVALID 0x00 #define TEGRA234_SID_PASSTHROUGH 0x7f
+/* NISO0 SMMU STREAM IDs */ +#define TEGRA234_SID_NISO0_HDA 0x03
/* NISO1 stream IDs */ #define TEGRA234_SID_SDMMC4 0x02 @@ -16,6 +18,10 @@ * memory client IDs */
+/* High-definition audio (HDA) read clients */ +#define TEGRA234_MEMORY_CLIENT_HDAR 0x15 +/* High-definition audio (HDA) write clients */ +#define TEGRA234_MEMORY_CLIENT_HDAW 0x35 /* sdmmcd memory read client */ #define TEGRA234_MEMORY_CLIENT_SDMMCRAB 0x63 /* sdmmcd memory write client */ diff --git a/include/dt-bindings/power/tegra234-powergate.h b/include/dt-bindings/power/tegra234-powergate.h new file mode 100644 index 000000000000..3c5575a51296 --- /dev/null +++ b/include/dt-bindings/power/tegra234-powergate.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. */ + +#ifndef __ABI_MACH_T234_POWERGATE_T234_H_ +#define __ABI_MACH_T234_POWERGATE_T234_H_ + +#define TEGRA234_POWER_DOMAIN_DISP 3U + +#endif diff --git a/include/dt-bindings/reset/tegra234-reset.h b/include/dt-bindings/reset/tegra234-reset.h index 50e13bced642..2ab61c69a3d9 100644 --- a/include/dt-bindings/reset/tegra234-reset.h +++ b/include/dt-bindings/reset/tegra234-reset.h @@ -10,6 +10,8 @@ * @brief Identifiers for Resets controllable by firmware * @{ */ +#define TEGRA234_RESET_HDA 20U +#define TEGRA234_RESET_HDACODEC 21U #define TEGRA234_RESET_SDMMC4 85U #define TEGRA234_RESET_UARTA 100U

Update binding document for HDA support on Tegra234 chip.
Tegra234 has max of 2 clocks and 2 resets which requires to add minItems and maxItems for clocks and resets as Tegra chips can now have minimum of 2 and maximum of 3 clocks and reset support.
Signed-off-by: Mohan Kumar mkumard@nvidia.com --- .../devicetree/bindings/sound/nvidia,tegra30-hda.yaml | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml index 2c913aa44fee..12c31b4b99e1 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml @@ -23,6 +23,7 @@ properties: - const: nvidia,tegra30-hda - items: - enum: + - nvidia,tegra234-hda - nvidia,tegra194-hda - nvidia,tegra186-hda - nvidia,tegra210-hda @@ -41,9 +42,11 @@ properties: maxItems: 1
clocks: + minItems: 2 maxItems: 3
clock-names: + minItems: 2 items: - const: hda - const: hda2hdmi

Add HDA dts node for Tegra234 chip and for AGX orin platform.
Signed-off-by: Mohan Kumar mkumard@nvidia.com --- .../nvidia/tegra234-p3737-0000+p3701-0000.dts | 6 ++++++ arch/arm64/boot/dts/nvidia/tegra234.dtsi | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra234-p3737-0000+p3701-0000.dts b/arch/arm64/boot/dts/nvidia/tegra234-p3737-0000+p3701-0000.dts index efbbb878ba5a..792e4a8b272b 100644 --- a/arch/arm64/boot/dts/nvidia/tegra234-p3737-0000+p3701-0000.dts +++ b/arch/arm64/boot/dts/nvidia/tegra234-p3737-0000+p3701-0000.dts @@ -21,4 +21,10 @@ serial { status = "okay"; }; + + bus@0 { + hda@3510000 { + nvidia,model = "NVIDIA Jetson AGX Orin HDA"; + }; + }; }; diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi index 6b6f15804a1a..d39d41968ffb 100644 --- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi @@ -4,6 +4,7 @@ #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/mailbox/tegra186-hsp.h> #include <dt-bindings/memory/tegra234-mc.h> +#include <dt-bindings/power/tegra234-powergate.h> #include <dt-bindings/reset/tegra234-reset.h>
/ { @@ -261,6 +262,23 @@ #interrupt-cells = <3>; interrupt-controller; }; + + hda@3510000 { + compatible = "nvidia,tegra234-hda", "nvidia,tegra30-hda"; + reg = <0x3510000 0x10000>; + interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&bpmp TEGRA234_CLK_AZA_BIT>, + <&bpmp TEGRA234_CLK_AZA_2XBIT>; + clock-names = "hda", "hda2codec_2x"; + resets = <&bpmp TEGRA234_RESET_HDA>, + <&bpmp TEGRA234_RESET_HDACODEC>; + reset-names = "hda", "hda2codec_2x"; + power-domains = <&bpmp TEGRA234_POWER_DOMAIN_DISP>; + interconnects = <&mc TEGRA234_MEMORY_CLIENT_HDAR &emc>, + <&mc TEGRA234_MEMORY_CLIENT_HDAW &emc>; + interconnect-names = "dma-mem", "write"; + status = "disabled"; + }; };
sram@40000000 {
participants (2)
-
Mohan Kumar
-
Takashi Iwai