[PATCH 0/4] Add Loongson HD Audio support
* The Loongson HDA controller is compatible with High Definition Audio Specification Revision 1.0a. See <https://loongson.github.io/LoongArch-Documentation/Loongson- 7A1000-usermanual-EN.html#hda-controller>
Yanteng Si (4): ALSA: hda: Add Loongson LS7A HD-Audio support ALSA: hda: Using polling mode for loongson controller by default ALSA: hda: Workaround for SDnCTL register on loongson controller ALSA: hda/intel: Workaround for WALLCLK register for loongson controller
include/linux/pci_ids.h | 3 +++ sound/hda/hdac_controller.c | 8 +++++++- sound/hda/hdac_device.c | 1 + sound/hda/hdac_stream.c | 8 +++++++- sound/pci/hda/hda_controller.h | 1 + sound/pci/hda/hda_intel.c | 10 ++++++++++ sound/pci/hda/patch_hdmi.c | 1 + 7 files changed, 30 insertions(+), 2 deletions(-)
On loongson controller, RIRBSTS.RINTFL cannot be cleared, azx_interrupt() is called all the time. We disable RIRB interrupt, and use polling mode by default.
Signed-off-by: Yanteng Si siyanteng@loongson.cn Signed-off-by: Yingkun Meng mengyingkun@loongson.cn --- sound/hda/hdac_controller.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 3c7af6558249..dbaa19cb8162 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -10,6 +10,7 @@ #include <sound/hdaudio.h> #include <sound/hda_register.h> #include "local.h" +#include "../pci/hda/hda_controller.h"
/* clear CORB read pointer properly */ static void azx_clear_corbrp(struct hdac_bus *bus) @@ -42,6 +43,8 @@ static void azx_clear_corbrp(struct hdac_bus *bus) */ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) { + struct azx *chip = bus_to_azx(bus); + WARN_ON_ONCE(!bus->rb.area);
spin_lock_irq(&bus->reg_lock); @@ -79,7 +82,10 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) /* set N=1, get RIRB response interrupt for new entry */ snd_hdac_chip_writew(bus, RINTCNT, 1); /* enable rirb dma and response irq */ - snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); + if (chip->driver_caps & AZX_DCAPS_LOONGSON_WORKAROUND) + snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN); + else + snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); /* Accept unsolicited responses */ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); spin_unlock_irq(&bus->reg_lock);
On Wed, 31 May 2023 05:21:32 +0200, Yanteng Si wrote:
On loongson controller, RIRBSTS.RINTFL cannot be cleared, azx_interrupt() is called all the time. We disable RIRB interrupt, and use polling mode by default.
Signed-off-by: Yanteng Si siyanteng@loongson.cn Signed-off-by: Yingkun Meng mengyingkun@loongson.cn
sound/hda/hdac_controller.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 3c7af6558249..dbaa19cb8162 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -10,6 +10,7 @@ #include <sound/hdaudio.h> #include <sound/hda_register.h> #include "local.h" +#include "../pci/hda/hda_controller.h"
/* clear CORB read pointer properly */ static void azx_clear_corbrp(struct hdac_bus *bus) @@ -42,6 +43,8 @@ static void azx_clear_corbrp(struct hdac_bus *bus) */ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) {
- struct azx *chip = bus_to_azx(bus);
You can't convert in this way in the generic HD-audio bus code in sound/hda/*. The struct azx is specific to sound/pci/hda/*.
WARN_ON_ONCE(!bus->rb.area);
spin_lock_irq(&bus->reg_lock); @@ -79,7 +82,10 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) /* set N=1, get RIRB response interrupt for new entry */ snd_hdac_chip_writew(bus, RINTCNT, 1); /* enable rirb dma and response irq */
- snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
- if (chip->driver_caps & AZX_DCAPS_LOONGSON_WORKAROUND)
snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN);
- else
snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
That is, for some device-specific workaround like this, you'd need to introduce a new flag in struct hdac_bus, set up in the sound/pci/hda/hda_intel.c instead.
thanks,
Takashi
On 2023/6/5 15:35, Takashi Iwai wrote:
On Wed, 31 May 2023 05:21:32 +0200, Yanteng Si wrote:
On loongson controller, RIRBSTS.RINTFL cannot be cleared, azx_interrupt() is called all the time. We disable RIRB interrupt, and use polling mode by default.
Signed-off-by: Yanteng Si siyanteng@loongson.cn Signed-off-by: Yingkun Meng mengyingkun@loongson.cn
sound/hda/hdac_controller.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 3c7af6558249..dbaa19cb8162 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -10,6 +10,7 @@ #include <sound/hdaudio.h> #include <sound/hda_register.h> #include "local.h" +#include "../pci/hda/hda_controller.h"
/* clear CORB read pointer properly */ static void azx_clear_corbrp(struct hdac_bus *bus) @@ -42,6 +43,8 @@ static void azx_clear_corbrp(struct hdac_bus *bus) */ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) {
- struct azx *chip = bus_to_azx(bus);
You can't convert in this way in the generic HD-audio bus code in sound/hda/*. The struct azx is specific to sound/pci/hda/*.
I see!
WARN_ON_ONCE(!bus->rb.area);
spin_lock_irq(&bus->reg_lock); @@ -79,7 +82,10 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) /* set N=1, get RIRB response interrupt for new entry */ snd_hdac_chip_writew(bus, RINTCNT, 1); /* enable rirb dma and response irq */
- snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
- if (chip->driver_caps & AZX_DCAPS_LOONGSON_WORKAROUND)
snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN);
- else
snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
That is, for some device-specific workaround like this, you'd need to introduce a new flag in struct hdac_bus, set up in the sound/pci/hda/hda_intel.c instead.
OK! I will try to put your ideas into practice.
Thanks,
Yanteng
thanks,
Takashi
On loongson controller, the value of WALLCLK register is always 0, which is meaningless, so we return directly.
Signed-off-by: Yanteng Si siyanteng@loongson.cn Signed-off-by: Yingkun Meng mengyingkun@loongson.cn --- sound/pci/hda/hda_intel.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index f28806b3e308..6df9ec999e88 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -655,6 +655,9 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) unsigned int pos; snd_pcm_uframes_t hwptr, target;
+ if (chip->driver_caps & AZX_DCAPS_LOONGSON_WORKAROUND) + return 1; + wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk; if (wallclk < (azx_dev->core.period_wallclk * 2) / 3) return -1; /* bogus (too early) interrupt */
On loongson controller, after calling snd_hdac_stream_updateb() to enable DMA engine, the SDnCTL.STRM will become to zero. We need to access SDnCTL in dword to keep SDnCTL.STRM is not changed.
Signed-off-by: Yanteng Si siyanteng@loongson.cn Signed-off-by: Yingkun Meng mengyingkun@loongson.cn --- sound/hda/hdac_stream.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 1f56fd33b9af..90fbf9fea2b3 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -13,6 +13,7 @@ #include <sound/hdaudio.h> #include <sound/hda_register.h> #include "trace.h" +#include "../pci/hda/hda_controller.h"
/* * the hdac_stream library is intended to be used with the following @@ -130,6 +131,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init); void snd_hdac_stream_start(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus; + struct azx *chip = bus_to_azx(bus); int stripe_ctl;
trace_snd_hdac_stream_start(bus, azx_dev); @@ -150,7 +152,11 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev) stripe_ctl); } /* set DMA start and interrupt mask */ - snd_hdac_stream_updateb(azx_dev, SD_CTL, + if (chip->driver_caps & AZX_DCAPS_LOONGSON_WORKAROUND) + snd_hdac_stream_updatel(azx_dev, SD_CTL, + 0, SD_CTL_DMA_START | SD_INT_MASK); + else + snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_DMA_START | SD_INT_MASK); azx_dev->running = true; }
On Wed, 31 May 2023 05:21:33 +0200, Yanteng Si wrote:
On loongson controller, after calling snd_hdac_stream_updateb() to enable DMA engine, the SDnCTL.STRM will become to zero. We need to access SDnCTL in dword to keep SDnCTL.STRM is not changed.
Signed-off-by: Yanteng Si siyanteng@loongson.cn Signed-off-by: Yingkun Meng mengyingkun@loongson.cn
sound/hda/hdac_stream.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 1f56fd33b9af..90fbf9fea2b3 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -13,6 +13,7 @@ #include <sound/hdaudio.h> #include <sound/hda_register.h> #include "trace.h" +#include "../pci/hda/hda_controller.h"
/*
- the hdac_stream library is intended to be used with the following
@@ -130,6 +131,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init); void snd_hdac_stream_start(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus;
struct azx *chip = bus_to_azx(bus); int stripe_ctl;
trace_snd_hdac_stream_start(bus, azx_dev);
@@ -150,7 +152,11 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev) stripe_ctl); } /* set DMA start and interrupt mask */
- snd_hdac_stream_updateb(azx_dev, SD_CTL,
- if (chip->driver_caps & AZX_DCAPS_LOONGSON_WORKAROUND)
snd_hdac_stream_updatel(azx_dev, SD_CTL,
0, SD_CTL_DMA_START | SD_INT_MASK);
- else
snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_DMA_START | SD_INT_MASK);
Similarly like the patch 2, this has to be handled with a new flag added to struct hdac_bus.
Takashi
On 2023/6/5 15:36, Takashi Iwai wrote:
On Wed, 31 May 2023 05:21:33 +0200, Yanteng Si wrote:
On loongson controller, after calling snd_hdac_stream_updateb() to enable DMA engine, the SDnCTL.STRM will become to zero. We need to access SDnCTL in dword to keep SDnCTL.STRM is not changed.
Signed-off-by: Yanteng Sisiyanteng@loongson.cn Signed-off-by: Yingkun Mengmengyingkun@loongson.cn
sound/hda/hdac_stream.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 1f56fd33b9af..90fbf9fea2b3 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -13,6 +13,7 @@ #include <sound/hdaudio.h> #include <sound/hda_register.h> #include "trace.h" +#include "../pci/hda/hda_controller.h"
/*
- the hdac_stream library is intended to be used with the following
@@ -130,6 +131,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init); void snd_hdac_stream_start(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus;
struct azx *chip = bus_to_azx(bus); int stripe_ctl;
trace_snd_hdac_stream_start(bus, azx_dev);
@@ -150,7 +152,11 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev) stripe_ctl); } /* set DMA start and interrupt mask */
- snd_hdac_stream_updateb(azx_dev, SD_CTL,
- if (chip->driver_caps & AZX_DCAPS_LOONGSON_WORKAROUND)
snd_hdac_stream_updatel(azx_dev, SD_CTL,
0, SD_CTL_DMA_START | SD_INT_MASK);
- else
snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_DMA_START | SD_INT_MASK);
Similarly like the patch 2, this has to be handled with a new flag added to struct hdac_bus.
Okay, I'll do it right now.
Thanks,
Yanteng
Takashi
Add the new PCI ID 0x0014 0x7a07 and the new PCI ID 0x0014 0x7a37 Loongson HDA controller.
Signed-off-by: Yanteng Si siyanteng@loongson.cn --- include/linux/pci_ids.h | 3 +++ sound/hda/hdac_device.c | 1 + sound/pci/hda/hda_controller.h | 1 + sound/pci/hda/hda_intel.c | 7 +++++++ sound/pci/hda/patch_hdmi.c | 1 + 5 files changed, 13 insertions(+)
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index e43ab203054a..f2aecb78f628 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -158,6 +158,9 @@
#define PCI_VENDOR_ID_LOONGSON 0x0014
+#define PCI_DEVICE_ID_LOONGSON_HDA 0x7a07 +#define PCI_DEVICE_ID_LOONGSON_HDMI 0x7a37 + #define PCI_VENDOR_ID_SOLIDIGM 0x025e
#define PCI_VENDOR_ID_TTTECH 0x0357 diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 6c043fbd606f..bbf7bcdb449a 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -645,6 +645,7 @@ struct hda_vendor_id { };
static const struct hda_vendor_id hda_vendor_ids[] = { + { 0x0014, "Loongson" }, { 0x1002, "ATI" }, { 0x1013, "Cirrus Logic" }, { 0x1057, "Motorola" }, diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index 8556031bcd68..1fd60246412f 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -45,6 +45,7 @@ #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ #define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ #define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ +#define AZX_DCAPS_LOONGSON_WORKAROUND (1 << 31) /* Loongson workaround */
enum { AZX_SNOOP_TYPE_NONE, diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 3226691ac923..f28806b3e308 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -237,6 +237,7 @@ enum { AZX_DRIVER_CTHDA, AZX_DRIVER_CMEDIA, AZX_DRIVER_ZHAOXIN, + AZX_DRIVER_LOONGSON, AZX_DRIVER_GENERIC, AZX_NUM_DRIVERS, /* keep this as last entry */ }; @@ -360,6 +361,7 @@ static const char * const driver_short_names[] = { [AZX_DRIVER_CTHDA] = "HDA Creative", [AZX_DRIVER_CMEDIA] = "HDA C-Media", [AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin", + [AZX_DRIVER_LOONGSON] = "HDA Loongson", [AZX_DRIVER_GENERIC] = "HD-Audio Generic", };
@@ -2809,6 +2811,11 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI }, /* Zhaoxin */ { PCI_DEVICE(0x1d17, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN }, + /* Loongson HDAudio*/ + {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA), + .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_LOONGSON_WORKAROUND }, + {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI), + .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_LOONGSON_WORKAROUND }, { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 5c0b1a09fd57..260d3e64f658 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -4505,6 +4505,7 @@ static int patch_gf_hdmi(struct hda_codec *codec) * patch entries */ static const struct hda_device_id snd_hda_id_hdmi[] = { +HDA_CODEC_ENTRY(0x00147a47, "Loongson HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi), HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi), HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi),
participants (2)
-
Takashi Iwai
-
Yanteng Si