[alsa-devel] [PATCH 1/6] ALSA: hda/tegra - Set CORBRP self-clear flag

From: Thierry Reding treding@nvidia.com
This is set for the MCP variants of the NVIDIA HDA controller, which the Tegra variant was derived from. This fixes the following warning at boot time:
[ 2.486610] tegra-hda 70030000.hda: CORB reset timeout#1, CORBRP = 0
Signed-off-by: Thierry Reding treding@nvidia.com --- sound/pci/hda/hda_tegra.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index db0bb50fa5b9..7003677f7473 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -460,11 +460,12 @@ MODULE_DEVICE_TABLE(of, hda_tegra_match);
static int hda_tegra_probe(struct platform_device *pdev) { + const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY | + AZX_DCAPS_CORBRP_SELF_CLEAR; struct snd_card *card; struct azx *chip; struct hda_tegra *hda; int err; - const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY;
hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); if (!hda)

From: Thierry Reding treding@nvidia.com
The HDMI codec on NVIDIA Tegra SoCs has a feature that doesn't exist on the MCP or GPU variants. The highest bit in the vendor-defined scratch registers can be used to trigger an interrupt in the HDMI codec, which is signalled to the HDMI driver. This can be used to pass information, such as the HDA format, to the HDMI driver so that it can reconfigure itself accordingly.
While at it, change the name of the codec to Tegra124 since there are no other SoCs in the Tegra12x family. There isn't really a Tegra12x family.
Signed-off-by: Thierry Reding treding@nvidia.com --- sound/pci/hda/patch_hdmi.c | 167 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index e8d847819d71..17c04544c9a0 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2932,6 +2932,171 @@ static int patch_nvhdmi(struct hda_codec *codec) }
/* + * The HDA codec on NVIDIA Tegra contains two scratch registers that are + * accessed using vendor-defined verbs. These registers can be used for + * interoperability between the HDA and HDMI drivers. + */ + +/* Audio Function Group node */ +#define NVIDIA_AFG_NID 0x01 + +/* + * The SCRATCH0 register is used to notify the HDMI codec of changes in audio + * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to + * be raised in the HDMI codec. The remainder of the bits is arbitrary. This + * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an + * additional bit (at position 30) to signal the validity of the format. + * + * | 31 | 30 | 29 16 | 15 0 | + * +---------+-------+--------+--------+ + * | TRIGGER | VALID | UNUSED | FORMAT | + * +-----------------------------------| + * + * Note that for the trigger bit to take effect it needs to change value + * (i.e. it needs to be toggled). + */ +#define NVIDIA_GET_SCRATCH0 0xfa6 +#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7 +#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8 +#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9 +#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa +#define NVIDIA_SCRATCH_TRIGGER (1 << 7) +#define NVIDIA_SCRATCH_VALID (1 << 6) + +#define NVIDIA_GET_SCRATCH1 0xfab +#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac +#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad +#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae +#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf + +/* + * 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) +{ + unsigned int value; + + /* bits [31:30] contain the trigger and valid bits */ + value = snd_hda_codec_read(codec, NVIDIA_AFG_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, + NVIDIA_SET_SCRATCH0_BYTE0, + (format >> 0) & 0xff); + snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + NVIDIA_SET_SCRATCH0_BYTE1, + (format >> 8) & 0xff); + + /* bits [16:24] are unused */ + snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + NVIDIA_SET_SCRATCH0_BYTE2, 0); + + /* + * Bit 30 signals that the data is valid and hence that HDMI audio can + * be enabled. + */ + if (format == 0) + value &= ~NVIDIA_SCRATCH_VALID; + 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; + + snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); +} + +static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + int err; + + err = generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag, + format, substream); + if (err < 0) + return err; + + /* notify the HDMI codec of the format change */ + tegra_hdmi_set_format(codec, format); + + return 0; +} + +static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + /* invalidate the format in the HDMI codec */ + tegra_hdmi_set_format(codec, 0); + + return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream); +} + +static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type) +{ + struct hdmi_spec *spec = codec->spec; + unsigned int i; + + for (i = 0; i < spec->num_pins; i++) { + struct hda_pcm *pcm = get_pcm_rec(spec, i); + + if (pcm->pcm_type == type) + return pcm; + } + + return NULL; +} + +static int tegra_hdmi_build_pcms(struct hda_codec *codec) +{ + struct hda_pcm_stream *stream; + struct hda_pcm *pcm; + int err; + + err = generic_hdmi_build_pcms(codec); + if (err < 0) + return err; + + pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI); + if (!pcm) + return -ENODEV; + + /* + * Override ->prepare() and ->cleanup() operations to notify the HDMI + * codec about format changes. + */ + stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; + stream->ops.prepare = tegra_hdmi_pcm_prepare; + stream->ops.cleanup = tegra_hdmi_pcm_cleanup; + + return 0; +} + +static int patch_tegra_hdmi(struct hda_codec *codec) +{ + int err; + + err = patch_generic_hdmi(codec); + if (err) + return err; + + codec->patch_ops.build_pcms = tegra_hdmi_build_pcms; + + return 0; +} + +/* * ATI/AMD-specific implementations */
@@ -3330,7 +3495,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0028, .name = "Tegra12x HDMI", .patch = patch_nvhdmi }, +{ .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi }, { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },

From: Thierry Reding treding@nvidia.com
When probing, provide accurate error messages to help with debugging failures.
Signed-off-by: Thierry Reding treding@nvidia.com --- sound/pci/hda/hda_tegra.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 7003677f7473..477742cb70a2 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -316,14 +316,20 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) int err;
hda->hda_clk = devm_clk_get(dev, "hda"); - if (IS_ERR(hda->hda_clk)) + if (IS_ERR(hda->hda_clk)) { + dev_err(dev, "failed to get hda clock\n"); return PTR_ERR(hda->hda_clk); + } hda->hda2codec_2x_clk = devm_clk_get(dev, "hda2codec_2x"); - if (IS_ERR(hda->hda2codec_2x_clk)) + if (IS_ERR(hda->hda2codec_2x_clk)) { + dev_err(dev, "failed to get hda2codec_2x clock\n"); return PTR_ERR(hda->hda2codec_2x_clk); + } hda->hda2hdmi_clk = devm_clk_get(dev, "hda2hdmi"); - if (IS_ERR(hda->hda2hdmi_clk)) + if (IS_ERR(hda->hda2hdmi_clk)) { + dev_err(dev, "failed to get hda2hdmi clock\n"); return PTR_ERR(hda->hda2hdmi_clk); + }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hda->regs = devm_ioremap_resource(dev, res); @@ -334,8 +340,10 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) bus->addr = res->start + HDA_BAR0;
err = hda_tegra_enable_clocks(hda); - if (err) + if (err) { + dev_err(dev, "failed to get enable clocks\n"); return err; + }
hda_tegra_init(hda);
@@ -385,12 +393,17 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
/* initialize streams */ err = azx_init_streams(chip); - if (err < 0) + if (err < 0) { + dev_err(card->dev, "failed to initialize streams: %d\n", err); return err; + }
err = azx_alloc_stream_pages(chip); - if (err < 0) + if (err < 0) { + dev_err(card->dev, "failed to allocate stream pages: %d\n", + err); return err; + }
/* initialize chip */ azx_init_chip(chip, 1);

From: Thierry Reding treding@nvidia.com
Tegra30 contains the same codec as Tegra124 and can be supported using the same patch function.
Signed-off-by: Thierry Reding treding@nvidia.com --- sound/pci/hda/patch_hdmi.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 17c04544c9a0..8d6ce0a5dbe4 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3495,6 +3495,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0020, .name = "Tegra30 HDMI", .patch = patch_tegra_hdmi }, { .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi }, { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },

From: Thierry Reding treding@nvidia.com
Tegra114 contains the same codec as Tegra124 and can be supported using the same patch function.
Signed-off-by: Thierry Reding treding@nvidia.com --- sound/pci/hda/patch_hdmi.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 8d6ce0a5dbe4..74a18ca3cb2f 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3496,6 +3496,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0020, .name = "Tegra30 HDMI", .patch = patch_tegra_hdmi }, +{ .id = 0x10de0022, .name = "Tegra114 HDMI", .patch = patch_tegra_hdmi }, { .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi }, { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },

From: Thierry Reding treding@nvidia.com
Tegra210 contains a similar codec as Tegra124 and can be supported using the same patch function.
Signed-off-by: Thierry Reding treding@nvidia.com --- sound/pci/hda/patch_hdmi.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 74a18ca3cb2f..00cb0522b0bd 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3498,6 +3498,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x10de0020, .name = "Tegra30 HDMI", .patch = patch_tegra_hdmi }, { .id = 0x10de0022, .name = "Tegra114 HDMI", .patch = patch_tegra_hdmi }, { .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi }, +{ .id = 0x10de0029, .name = "Tegra210 HDMI/DP", .patch = patch_tegra_hdmi }, { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },

At Tue, 5 May 2015 14:56:19 +0200, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
This is set for the MCP variants of the NVIDIA HDA controller, which the Tegra variant was derived from. This fixes the following warning at boot time:
[ 2.486610] tegra-hda 70030000.hda: CORB reset timeout#1, CORBRP = 0
Signed-off-by: Thierry Reding treding@nvidia.com
Since I've been traveling in this and next weeks, I'll check and apply this series after my vacation although they look good at a quick glance.
thanks,
Takashi
sound/pci/hda/hda_tegra.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index db0bb50fa5b9..7003677f7473 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -460,11 +460,12 @@ MODULE_DEVICE_TABLE(of, hda_tegra_match);
static int hda_tegra_probe(struct platform_device *pdev) {
- const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY |
struct snd_card *card; struct azx *chip; struct hda_tegra *hda; int err;AZX_DCAPS_CORBRP_SELF_CLEAR;
const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY;
hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); if (!hda)
-- 2.3.5

At Tue, 5 May 2015 14:56:19 +0200, Thierry Reding wrote:
From: Thierry Reding treding@nvidia.com
This is set for the MCP variants of the NVIDIA HDA controller, which the Tegra variant was derived from. This fixes the following warning at boot time:
[ 2.486610] tegra-hda 70030000.hda: CORB reset timeout#1, CORBRP = 0
Signed-off-by: Thierry Reding treding@nvidia.com
Applied all six patches now. Thanks.
Takashi
sound/pci/hda/hda_tegra.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index db0bb50fa5b9..7003677f7473 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -460,11 +460,12 @@ MODULE_DEVICE_TABLE(of, hda_tegra_match);
static int hda_tegra_probe(struct platform_device *pdev) {
- const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY |
struct snd_card *card; struct azx *chip; struct hda_tegra *hda; int err;AZX_DCAPS_CORBRP_SELF_CLEAR;
const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY;
hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); if (!hda)
-- 2.3.5
participants (2)
-
Takashi Iwai
-
Thierry Reding