aboutsummaryrefslogtreecommitdiffstats
path: root/sound/hda
diff options
context:
space:
mode:
Diffstat (limited to 'sound/hda')
-rw-r--r--sound/hda/Kconfig8
-rw-r--r--sound/hda/Makefile3
-rw-r--r--sound/hda/ext/hdac_ext_bus.c60
-rw-r--r--sound/hda/hdac_bus.c45
-rw-r--r--sound/hda/hdac_controller.c20
-rw-r--r--sound/hda/hdac_device.c6
-rw-r--r--sound/hda/hdac_regmap.c1
-rw-r--r--sound/hda/hdac_stream.c14
-rw-r--r--sound/hda/intel-nhlt.c107
-rw-r--r--sound/hda/local.h7
10 files changed, 182 insertions, 89 deletions
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
index f6feced15f17..3d33fc1757ba 100644
--- a/sound/hda/Kconfig
+++ b/sound/hda/Kconfig
@@ -6,6 +6,9 @@ config SND_HDA_CORE
config SND_HDA_DSP_LOADER
bool
+config SND_HDA_ALIGNED_MMIO
+ bool
+
config SND_HDA_COMPONENT
bool
@@ -29,3 +32,8 @@ config SND_HDA_PREALLOC_SIZE
Note that the pre-allocation size can be changed dynamically
via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
+
+config SND_INTEL_NHLT
+ tristate
+ # this config should be selected only for Intel ACPI platforms.
+ # A fallback is provided so that the code compiles in all cases. \ No newline at end of file
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
index 2160202e2dc1..8560f6ef1b19 100644
--- a/sound/hda/Makefile
+++ b/sound/hda/Makefile
@@ -13,3 +13,6 @@ obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
#extended hda
obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
+
+snd-intel-nhlt-objs := intel-nhlt.o
+obj-$(CONFIG_SND_INTEL_NHLT) += snd-intel-nhlt.o
diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c
index 4f9f1d2a2ec5..242306d820ec 100644
--- a/sound/hda/ext/hdac_ext_bus.c
+++ b/sound/hda/ext/hdac_ext_bus.c
@@ -17,80 +17,22 @@
MODULE_DESCRIPTION("HDA extended core");
MODULE_LICENSE("GPL v2");
-static void hdac_ext_writel(u32 value, u32 __iomem *addr)
-{
- writel(value, addr);
-}
-
-static u32 hdac_ext_readl(u32 __iomem *addr)
-{
- return readl(addr);
-}
-
-static void hdac_ext_writew(u16 value, u16 __iomem *addr)
-{
- writew(value, addr);
-}
-
-static u16 hdac_ext_readw(u16 __iomem *addr)
-{
- return readw(addr);
-}
-
-static void hdac_ext_writeb(u8 value, u8 __iomem *addr)
-{
- writeb(value, addr);
-}
-
-static u8 hdac_ext_readb(u8 __iomem *addr)
-{
- return readb(addr);
-}
-
-static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type,
- size_t size, struct snd_dma_buffer *buf)
-{
- return snd_dma_alloc_pages(type, bus->dev, size, buf);
-}
-
-static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
-{
- snd_dma_free_pages(buf);
-}
-
-static const struct hdac_io_ops hdac_ext_default_io = {
- .reg_writel = hdac_ext_writel,
- .reg_readl = hdac_ext_readl,
- .reg_writew = hdac_ext_writew,
- .reg_readw = hdac_ext_readw,
- .reg_writeb = hdac_ext_writeb,
- .reg_readb = hdac_ext_readb,
- .dma_alloc_pages = hdac_ext_dma_alloc_pages,
- .dma_free_pages = hdac_ext_dma_free_pages,
-};
-
/**
* snd_hdac_ext_bus_init - initialize a HD-audio extended bus
* @ebus: the pointer to extended bus object
* @dev: device pointer
* @ops: bus verb operators
- * @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use
* default ops
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops,
- const struct hdac_io_ops *io_ops,
const struct hdac_ext_bus_ops *ext_ops)
{
int ret;
- /* check if io ops are provided, if not load the defaults */
- if (io_ops == NULL)
- io_ops = &hdac_ext_default_io;
-
- ret = snd_hdac_bus_init(bus, dev, ops, io_ops);
+ ret = snd_hdac_bus_init(bus, dev, ops);
if (ret < 0)
return ret;
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c
index 14e57ffd5bc1..8f19876244eb 100644
--- a/sound/hda/hdac_bus.c
+++ b/sound/hda/hdac_bus.c
@@ -4,12 +4,16 @@
*/
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/export.h>
#include <sound/hdaudio.h>
+#include "local.h"
#include "trace.h"
+static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
+
static const struct hdac_bus_ops default_ops = {
.command = snd_hdac_bus_send_cmd,
.get_response = snd_hdac_bus_get_response,
@@ -19,13 +23,11 @@ static const struct hdac_bus_ops default_ops = {
* snd_hdac_bus_init - initialize a HD-audio bas bus
* @bus: the pointer to bus object
* @ops: bus verb operators
- * @io_ops: lowlevel I/O operators
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
- const struct hdac_bus_ops *ops,
- const struct hdac_io_ops *io_ops)
+ const struct hdac_bus_ops *ops)
{
memset(bus, 0, sizeof(*bus));
bus->dev = dev;
@@ -33,7 +35,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
bus->ops = ops;
else
bus->ops = &default_ops;
- bus->io_ops = io_ops;
+ bus->dma_type = SNDRV_DMA_TYPE_DEV;
INIT_LIST_HEAD(&bus->stream_list);
INIT_LIST_HEAD(&bus->codec_list);
INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
@@ -149,7 +151,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);
/*
* process queued unsolicited events
*/
-void snd_hdac_bus_process_unsol_events(struct work_struct *work)
+static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
{
struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
struct hdac_device *codec;
@@ -172,7 +174,6 @@ void snd_hdac_bus_process_unsol_events(struct work_struct *work)
drv->unsol_event(codec, res);
}
}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_process_unsol_events);
/**
* snd_hdac_bus_add_device - Add a codec to bus
@@ -197,7 +198,6 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
bus->num_codecs++;
return 0;
}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
/**
* snd_hdac_bus_remove_device - Remove a codec from bus
@@ -216,4 +216,33 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus,
bus->num_codecs--;
flush_work(&bus->unsol_work);
}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);
+
+#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
+/* Helpers for aligned read/write of mmio space, for Tegra */
+unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask)
+{
+ void __iomem *aligned_addr =
+ (void __iomem *)((unsigned long)(addr) & ~0x3);
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ unsigned int v;
+
+ v = readl(aligned_addr);
+ return (v >> shift) & mask;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_aligned_read);
+
+void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
+ unsigned int mask)
+{
+ void __iomem *aligned_addr =
+ (void __iomem *)((unsigned long)(addr) & ~0x3);
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ unsigned int v;
+
+ v = readl(aligned_addr);
+ v &= ~(mask << shift);
+ v |= val << shift;
+ writel(v, aligned_addr);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_aligned_write);
+#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 3b0110545070..d3999e7b0705 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -447,6 +447,8 @@ static void azx_int_disable(struct hdac_bus *bus)
list_for_each_entry(azx_dev, &bus->stream_list, list)
snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
+ synchronize_irq(bus->irq);
+
/* disable SIE for all streams */
snd_hdac_chip_writeb(bus, INTCTL, 0);
@@ -575,12 +577,13 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
{
struct hdac_stream *s;
int num_streams = 0;
+ int dma_type = bus->dma_type ? bus->dma_type : SNDRV_DMA_TYPE_DEV;
int err;
list_for_each_entry(s, &bus->stream_list, list) {
/* allocate memory for the BDL for each stream */
- err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
- BDL_SIZE, &s->bdl);
+ err = snd_dma_alloc_pages(dma_type, bus->dev,
+ BDL_SIZE, &s->bdl);
num_streams++;
if (err < 0)
return -ENOMEM;
@@ -589,16 +592,15 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
if (WARN_ON(!num_streams))
return -EINVAL;
/* allocate memory for the position buffer */
- err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
- num_streams * 8, &bus->posbuf);
+ err = snd_dma_alloc_pages(dma_type, bus->dev,
+ num_streams * 8, &bus->posbuf);
if (err < 0)
return -ENOMEM;
list_for_each_entry(s, &bus->stream_list, list)
s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
/* single page (at least 4096 bytes) must suffice for both ringbuffes */
- return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
- PAGE_SIZE, &bus->rb);
+ return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb);
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages);
@@ -612,12 +614,12 @@ void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus)
list_for_each_entry(s, &bus->stream_list, list) {
if (s->bdl.area)
- bus->io_ops->dma_free_pages(bus, &s->bdl);
+ snd_dma_free_pages(&s->bdl);
}
if (bus->rb.area)
- bus->io_ops->dma_free_pages(bus, &bus->rb);
+ snd_dma_free_pages(&bus->rb);
if (bus->posbuf.area)
- bus->io_ops->dma_free_pages(bus, &bus->posbuf);
+ snd_dma_free_pages(&bus->posbuf);
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages);
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index b26cc93e7e10..9f3e37511408 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -218,8 +218,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
*
* Return an encoded command verb or -1 for error.
*/
-unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
- unsigned int verb, unsigned int parm)
+static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int parm)
{
u32 val, addr;
@@ -237,7 +237,6 @@ unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
val |= parm;
return val;
}
-EXPORT_SYMBOL_GPL(snd_hdac_make_cmd);
/**
* snd_hdac_exec_verb - execute an encoded verb
@@ -258,7 +257,6 @@ int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
return codec->exec_verb(codec, cmd, flags, res);
return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
}
-EXPORT_SYMBOL_GPL(snd_hdac_exec_verb);
/**
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
index f399a1552e73..286361ecd640 100644
--- a/sound/hda/hdac_regmap.c
+++ b/sound/hda/hdac_regmap.c
@@ -21,6 +21,7 @@
#include <sound/core.h>
#include <sound/hdaudio.h>
#include <sound/hda_regmap.h>
+#include "local.h"
static int codec_pm_lock(struct hdac_device *codec)
{
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index 55d53b89ac21..d8fe7ff0cd58 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -229,11 +229,7 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
/* set the interrupt enable bits in the descriptor control register */
snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
- if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK)
- azx_dev->fifo_size =
- snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
- else
- azx_dev->fifo_size = 0;
+ azx_dev->fifo_size = snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
/* when LPIB delay correction gives a small negative value,
* we ignore it; currently set the threshold statically to
@@ -680,8 +676,8 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
azx_dev->locked = true;
spin_unlock_irq(&bus->reg_lock);
- err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV_SG,
- byte_size, bufp);
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev,
+ byte_size, bufp);
if (err < 0)
goto err_alloc;
@@ -707,7 +703,7 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
return azx_dev->stream_tag;
error:
- bus->io_ops->dma_free_pages(bus, bufp);
+ snd_dma_free_pages(bufp);
err_alloc:
spin_lock_irq(&bus->reg_lock);
azx_dev->locked = false;
@@ -754,7 +750,7 @@ void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
azx_dev->period_bytes = 0;
azx_dev->format_val = 0;
- bus->io_ops->dma_free_pages(bus, dmab);
+ snd_dma_free_pages(dmab);
dmab->area = NULL;
spin_lock_irq(&bus->reg_lock);
diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c
new file mode 100644
index 000000000000..daede96f28ee
--- /dev/null
+++ b/sound/hda/intel-nhlt.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2015-2019 Intel Corporation
+
+#include <linux/acpi.h>
+#include <sound/intel-nhlt.h>
+
+#define NHLT_ACPI_HEADER_SIG "NHLT"
+
+/* Unique identification for getting NHLT blobs */
+static guid_t osc_guid =
+ GUID_INIT(0xA69F886E, 0x6CEB, 0x4594,
+ 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53);
+
+struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
+{
+ acpi_handle handle;
+ union acpi_object *obj;
+ struct nhlt_resource_desc *nhlt_ptr;
+ struct nhlt_acpi_table *nhlt_table = NULL;
+
+ handle = ACPI_HANDLE(dev);
+ if (!handle) {
+ dev_err(dev, "Didn't find ACPI_HANDLE\n");
+ return NULL;
+ }
+
+ obj = acpi_evaluate_dsm(handle, &osc_guid, 1, 1, NULL);
+
+ if (!obj)
+ return NULL;
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ dev_dbg(dev, "No NHLT table found\n");
+ ACPI_FREE(obj);
+ return NULL;
+ }
+
+ nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
+ if (nhlt_ptr->length)
+ nhlt_table = (struct nhlt_acpi_table *)
+ memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
+ MEMREMAP_WB);
+ ACPI_FREE(obj);
+ if (nhlt_table &&
+ (strncmp(nhlt_table->header.signature,
+ NHLT_ACPI_HEADER_SIG,
+ strlen(NHLT_ACPI_HEADER_SIG)) != 0)) {
+ memunmap(nhlt_table);
+ dev_err(dev, "NHLT ACPI header signature incorrect\n");
+ return NULL;
+ }
+ return nhlt_table;
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_init);
+
+void intel_nhlt_free(struct nhlt_acpi_table *nhlt)
+{
+ memunmap((void *)nhlt);
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_free);
+
+int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
+{
+ struct nhlt_endpoint *epnt;
+ struct nhlt_dmic_array_config *cfg;
+ struct nhlt_vendor_dmic_array_config *cfg_vendor;
+ unsigned int dmic_geo = 0;
+ u8 j;
+
+ if (!nhlt)
+ return 0;
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+
+ for (j = 0; j < nhlt->endpoint_count; j++) {
+ if (epnt->linktype == NHLT_LINK_DMIC) {
+ cfg = (struct nhlt_dmic_array_config *)
+ (epnt->config.caps);
+ switch (cfg->array_type) {
+ case NHLT_MIC_ARRAY_2CH_SMALL:
+ case NHLT_MIC_ARRAY_2CH_BIG:
+ dmic_geo = MIC_ARRAY_2CH;
+ break;
+
+ case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
+ case NHLT_MIC_ARRAY_4CH_L_SHAPED:
+ case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
+ dmic_geo = MIC_ARRAY_4CH;
+ break;
+ case NHLT_MIC_ARRAY_VENDOR_DEFINED:
+ cfg_vendor = (struct nhlt_vendor_dmic_array_config *)cfg;
+ dmic_geo = cfg_vendor->nb_mics;
+ break;
+ default:
+ dev_warn(dev, "undefined DMIC array_type 0x%0x\n",
+ cfg->array_type);
+ }
+ }
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return dmic_geo;
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel NHLT driver");
diff --git a/sound/hda/local.h b/sound/hda/local.h
index 877631e39373..5b935219352f 100644
--- a/sound/hda/local.h
+++ b/sound/hda/local.h
@@ -33,4 +33,11 @@ int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid,
int num_nodes);
void hda_widget_sysfs_exit(struct hdac_device *codec);
+int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
+void snd_hdac_bus_remove_device(struct hdac_bus *bus,
+ struct hdac_device *codec);
+
+int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
+ unsigned int flags, unsigned int *res);
+
#endif /* __HDAC_LOCAL_H */