diff options
Diffstat (limited to 'sound/hda')
-rw-r--r-- | sound/hda/Kconfig | 20 | ||||
-rw-r--r-- | sound/hda/Makefile | 4 | ||||
-rw-r--r-- | sound/hda/ext/hdac_ext_bus.c | 71 | ||||
-rw-r--r-- | sound/hda/ext/hdac_ext_controller.c | 19 | ||||
-rw-r--r-- | sound/hda/ext/hdac_ext_stream.c | 1 | ||||
-rw-r--r-- | sound/hda/hdac_bus.c | 49 | ||||
-rw-r--r-- | sound/hda/hdac_component.c | 1 | ||||
-rw-r--r-- | sound/hda/hdac_controller.c | 49 | ||||
-rw-r--r-- | sound/hda/hdac_device.c | 23 | ||||
-rw-r--r-- | sound/hda/hdac_regmap.c | 145 | ||||
-rw-r--r-- | sound/hda/hdac_stream.c | 64 | ||||
-rw-r--r-- | sound/hda/hdmi_chmap.c | 2 | ||||
-rw-r--r-- | sound/hda/intel-dsp-config.c | 357 | ||||
-rw-r--r-- | sound/hda/intel-nhlt.c | 104 | ||||
-rw-r--r-- | sound/hda/local.h | 7 |
15 files changed, 747 insertions, 169 deletions
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index f6feced15f17..4ca6b09056f3 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 @@ -18,14 +21,27 @@ config SND_HDA_EXT_CORE select SND_HDA_CORE config SND_HDA_PREALLOC_SIZE - int "Pre-allocated buffer size for HD-audio driver" + int "Pre-allocated buffer size for HD-audio driver" if !SND_DMA_SGBUF range 0 32768 - default 64 + default 0 if SND_DMA_SGBUF + default 64 if !SND_DMA_SGBUF help Specifies the default pre-allocated buffer-size in kB for the HD-audio driver. A larger buffer (e.g. 2048) is preferred for systems using PulseAudio. The default 64 is chosen just for compatibility reasons. + On x86 systems, the default is zero as we need no preallocation. 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 + bool + # this config should be selected only for Intel ACPI platforms. + # A fallback is provided so that the code compiles in all cases. + +config SND_INTEL_DSP_CONFIG + tristate + select SND_INTEL_NHLT if ACPI + # this config should be selected only for Intel DSP platforms. + # A fallback is provided so that the code compiles in all cases. diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 2160202e2dc1..601e617918b8 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -13,3 +13,7 @@ obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o #extended hda obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/ + +snd-intel-dspcfg-objs := intel-dsp-config.o +snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o +obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 4f9f1d2a2ec5..73bfa71845f6 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 + * @bus: the pointer to HDAC 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 + * @ext_ops: operators used for ASoC HDA codec drivers * * 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; @@ -109,7 +51,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); /** * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus - * @ebus: the pointer to extended bus object + * @bus: the pointer to HDAC bus object */ void snd_hdac_ext_bus_exit(struct hdac_bus *bus) { @@ -125,8 +67,9 @@ static void default_release(struct device *dev) /** * snd_hdac_ext_bus_device_init - initialize the HDA extended codec base device - * @ebus: hdac extended bus to attach to + * @bus: hdac bus to attach to * @addr: codec address + * @hdev: hdac device to init * * Returns zero for success or a negative error code. */ @@ -172,7 +115,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); /** * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices * - * @ebus: HD-audio extended bus + * @bus: the pointer to HDAC bus object */ void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus) { diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index 211ca85acd8c..a684f0520b4b 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -28,7 +28,7 @@ /** * snd_hdac_ext_bus_ppcap_enable - enable/disable processing pipe capability - * @ebus: HD-audio extended core bus + * @bus: the pointer to HDAC bus object * @enable: flag to turn on/off the capability */ void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable) @@ -50,7 +50,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable); /** * snd_hdac_ext_bus_ppcap_int_enable - ppcap interrupt enable/disable - * @ebus: HD-audio extended core bus + * @bus: the pointer to HDAC bus object * @enable: flag to enable/disable interrupt */ void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable) @@ -77,7 +77,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable); /** * snd_hdac_ext_bus_get_ml_capabilities - get multilink capability - * @ebus: HD-audio extended core bus + * @bus: the pointer to HDAC bus object * * This will parse all links and read the mlink capabilities and add them * in hlink_list of extended hdac bus @@ -117,7 +117,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities); /** * snd_hdac_link_free_all- free hdac extended link objects * - * @ebus: HD-audio ext core bus + * @bus: the pointer to HDAC bus object */ void snd_hdac_link_free_all(struct hdac_bus *bus) @@ -134,7 +134,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_link_free_all); /** * snd_hdac_ext_bus_get_link_index - get link based on codec name - * @ebus: HD-audio extended core bus + * @bus: the pointer to HDAC bus object * @codec_name: codec name */ struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, @@ -211,7 +211,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down); /** * snd_hdac_ext_bus_link_power_up_all -power up all hda link - * @ebus: HD-audio extended bus + * @bus: the pointer to HDAC bus object */ int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus) { @@ -232,7 +232,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all); /** * snd_hdac_ext_bus_link_power_down_all -power down all hda link - * @ebus: HD-audio extended bus + * @bus: the pointer to HDAC bus object */ int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus) { @@ -271,6 +271,11 @@ int snd_hdac_ext_bus_link_get(struct hdac_bus *bus, ret = snd_hdac_ext_bus_link_power_up(link); /* + * clear the register to invalidate all the output streams + */ + snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, + ML_LOSIDV_STREAM_MASK, 0); + /* * wait for 521usec for codec to report status * HDA spec section 4.3 - Codec Discovery */ diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 6b1b4b834bae..c4d54a838773 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -530,7 +530,6 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); /** * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream - * @bus: HD-audio core bus * @stream: hdac_ext_stream * @value: lpib value to set */ diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 14e57ffd5bc1..3fe62be1cbcc 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, @@ -18,14 +22,13 @@ static const struct hdac_bus_ops default_ops = { /** * snd_hdac_bus_init - initialize a HD-audio bas bus * @bus: the pointer to bus object + * @dev: device pointer * @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 +36,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); @@ -41,6 +44,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, mutex_init(&bus->cmd_mutex); mutex_init(&bus->lock); INIT_LIST_HEAD(&bus->hlink_list); + init_waitqueue_head(&bus->rirb_wq); bus->irq = -1; return 0; } @@ -61,6 +65,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_exit); /** * snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus * @bus: bus object + * @addr: the HDAC device address * @cmd: HD-audio encoded verb * @res: pointer to store the response, NULL if performing asynchronously * @@ -81,6 +86,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb); /** * snd_hdac_bus_exec_verb_unlocked - unlocked version * @bus: bus object + * @addr: the HDAC device address * @cmd: HD-audio encoded verb * @res: pointer to store the response, NULL if performing asynchronously * @@ -149,7 +155,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 +178,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 +202,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 +220,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_component.c b/sound/hda/hdac_component.c index dfe7e755f594..89126c6fd216 100644 --- a/sound/hda/hdac_component.c +++ b/sound/hda/hdac_component.c @@ -262,6 +262,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_acomp_register_notifier); /** * snd_hdac_acomp_init - Initialize audio component * @bus: HDA core bus + * @aops: audio component ops * @match_master: match function for finding components * @extra_size: Extra bytes to allocate * diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 3b0110545070..bc4a8b606020 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -181,6 +181,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd); * @bus: HD-audio core bus * * Usually called from interrupt handler. + * The caller needs bus->reg_lock spinlock before calling this. */ void snd_hdac_bus_update_rirb(struct hdac_bus *bus) { @@ -216,6 +217,9 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus) else if (bus->rirb.cmds[addr]) { bus->rirb.res[addr] = res; bus->rirb.cmds[addr]--; + if (!bus->rirb.cmds[addr] && + waitqueue_active(&bus->rirb_wq)) + wake_up(&bus->rirb_wq); } else { dev_err_ratelimited(bus->dev, "spurious response %#x:%#x, last cmd=%#08x\n", @@ -238,30 +242,51 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, { unsigned long timeout; unsigned long loopcounter; + wait_queue_entry_t wait; + bool warned = false; + init_wait_entry(&wait, 0); timeout = jiffies + msecs_to_jiffies(1000); for (loopcounter = 0;; loopcounter++) { spin_lock_irq(&bus->reg_lock); + if (!bus->polling_mode) + prepare_to_wait(&bus->rirb_wq, &wait, + TASK_UNINTERRUPTIBLE); if (bus->polling_mode) snd_hdac_bus_update_rirb(bus); if (!bus->rirb.cmds[addr]) { if (res) *res = bus->rirb.res[addr]; /* the last value */ + if (!bus->polling_mode) + finish_wait(&bus->rirb_wq, &wait); spin_unlock_irq(&bus->reg_lock); return 0; } spin_unlock_irq(&bus->reg_lock); if (time_after(jiffies, timeout)) break; - if (loopcounter > 3000) +#define LOOP_COUNT_MAX 3000 + if (!bus->polling_mode) { + schedule_timeout(msecs_to_jiffies(2)); + } else if (bus->needs_damn_long_delay || + loopcounter > LOOP_COUNT_MAX) { + if (loopcounter > LOOP_COUNT_MAX && !warned) { + dev_dbg_ratelimited(bus->dev, + "too slow response, last cmd=%#08x\n", + bus->last_cmd[addr]); + warned = true; + } msleep(2); /* temporary workaround */ - else { + } else { udelay(10); cond_resched(); } } + if (!bus->polling_mode) + finish_wait(&bus->rirb_wq, &wait); + return -EIO; } EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response); @@ -536,7 +561,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip); * snd_hdac_bus_handle_stream_irq - interrupt handler for streams * @bus: HD-audio core bus * @status: INTSTS register value - * @ask: callback to be called for woken streams + * @ack: callback to be called for woken streams * * Returns the bits of handled streams, or zero if no stream is handled. */ @@ -575,12 +600,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 +615,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 +637,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..9a526aeef8da 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -57,6 +57,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus, codec->addr = addr; codec->type = HDA_DEV_CORE; mutex_init(&codec->widget_lock); + mutex_init(&codec->regmap_lock); pm_runtime_set_active(&codec->dev); pm_runtime_get_noresume(&codec->dev); atomic_set(&codec->in_pm, 0); @@ -134,7 +135,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_device_exit); /** * snd_hdac_device_register - register the hd-audio codec base device - * codec: the device to register + * @codec: the device to register */ int snd_hdac_device_register(struct hdac_device *codec) { @@ -157,7 +158,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_device_register); /** * snd_hdac_device_unregister - unregister the hd-audio codec base device - * codec: the device to unregister + * @codec: the device to unregister */ void snd_hdac_device_unregister(struct hdac_device *codec) { @@ -218,8 +219,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 +238,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 +258,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); /** @@ -282,6 +281,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_read); /** * _snd_hdac_read_parm - read a parmeter + * @codec: the codec object + * @nid: NID to read a parameter + * @parm: parameter to read + * @res: pointer to store the read value * * This function returns zero or an error unlike snd_hdac_read_parm(). */ @@ -639,7 +642,7 @@ struct hda_vendor_id { const char *name; }; -static struct hda_vendor_id hda_vendor_ids[] = { +static const struct hda_vendor_id hda_vendor_ids[] = { { 0x1002, "ATI" }, { 0x1013, "Cirrus Logic" }, { 0x1057, "Motorola" }, @@ -694,7 +697,7 @@ struct hda_rate_tbl { (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ (((div) - 1) << AC_FMT_DIV_SHIFT)) -static struct hda_rate_tbl rate_bits[] = { +static const struct hda_rate_tbl rate_bits[] = { /* rate in Hz, ALSA rate bitmask, HDA format value */ /* autodetected value used in snd_hda_query_supported_pcm */ @@ -1063,9 +1066,9 @@ EXPORT_SYMBOL_GPL(snd_hdac_check_power_state); * snd_hdac_sync_power_state - wait until actual power state matches * with the target state * - * @hdac: the HDAC device + * @codec: the HDAC device * @nid: NID to send the command - * @target_state: target state to check for + * @power_state: target power state to wait for * * Return power state or PS_ERROR if codec rejects GET verb. */ diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index f399a1552e73..d75f31eb9d78 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) { @@ -362,6 +363,7 @@ static const struct regmap_config hda_regmap_cfg = { .reg_write = hda_reg_write, .use_single_read = true, .use_single_write = true, + .disable_locking = true, }; /** @@ -424,12 +426,29 @@ EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb); static int reg_raw_write(struct hdac_device *codec, unsigned int reg, unsigned int val) { + int err; + + mutex_lock(&codec->regmap_lock); if (!codec->regmap) - return hda_reg_write(codec, reg, val); + err = hda_reg_write(codec, reg, val); else - return regmap_write(codec->regmap, reg, val); + err = regmap_write(codec->regmap, reg, val); + mutex_unlock(&codec->regmap_lock); + return err; } +/* a helper macro to call @func_call; retry with power-up if failed */ +#define CALL_RAW_FUNC(codec, func_call) \ + ({ \ + int _err = func_call; \ + if (_err == -EAGAIN) { \ + _err = snd_hdac_power_up_pm(codec); \ + if (_err >= 0) \ + _err = func_call; \ + snd_hdac_power_down_pm(codec); \ + } \ + _err;}) + /** * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt * @codec: the codec object @@ -441,42 +460,29 @@ static int reg_raw_write(struct hdac_device *codec, unsigned int reg, int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, unsigned int val) { - int err; - - err = reg_raw_write(codec, reg, val); - if (err == -EAGAIN) { - err = snd_hdac_power_up_pm(codec); - if (err >= 0) - err = reg_raw_write(codec, reg, val); - snd_hdac_power_down_pm(codec); - } - return err; + return CALL_RAW_FUNC(codec, reg_raw_write(codec, reg, val)); } EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw); static int reg_raw_read(struct hdac_device *codec, unsigned int reg, unsigned int *val, bool uncached) { + int err; + + mutex_lock(&codec->regmap_lock); if (uncached || !codec->regmap) - return hda_reg_read(codec, reg, val); + err = hda_reg_read(codec, reg, val); else - return regmap_read(codec->regmap, reg, val); + err = regmap_read(codec->regmap, reg, val); + mutex_unlock(&codec->regmap_lock); + return err; } static int __snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, unsigned int *val, bool uncached) { - int err; - - err = reg_raw_read(codec, reg, val, uncached); - if (err == -EAGAIN) { - err = snd_hdac_power_up_pm(codec); - if (err >= 0) - err = reg_raw_read(codec, reg, val, uncached); - snd_hdac_power_down_pm(codec); - } - return err; + return CALL_RAW_FUNC(codec, reg_raw_read(codec, reg, val, uncached)); } /** @@ -503,11 +509,40 @@ int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec, return __snd_hdac_regmap_read_raw(codec, reg, val, true); } +static int reg_raw_update(struct hdac_device *codec, unsigned int reg, + unsigned int mask, unsigned int val) +{ + unsigned int orig; + bool change; + int err; + + mutex_lock(&codec->regmap_lock); + if (codec->regmap) { + err = regmap_update_bits_check(codec->regmap, reg, mask, val, + &change); + if (!err) + err = change ? 1 : 0; + } else { + err = hda_reg_read(codec, reg, &orig); + if (!err) { + val &= mask; + val |= orig & ~mask; + if (val != orig) { + err = hda_reg_write(codec, reg, val); + if (!err) + err = 1; + } + } + } + mutex_unlock(&codec->regmap_lock); + return err; +} + /** * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt * @codec: the codec object * @reg: pseudo register - * @mask: bit mask to udpate + * @mask: bit mask to update * @val: value to update * * Returns zero if successful or a negative error code. @@ -515,19 +550,57 @@ int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec, int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg, unsigned int mask, unsigned int val) { + return CALL_RAW_FUNC(codec, reg_raw_update(codec, reg, mask, val)); +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw); + +static int reg_raw_update_once(struct hdac_device *codec, unsigned int reg, + unsigned int mask, unsigned int val) +{ unsigned int orig; int err; - val &= mask; - err = snd_hdac_regmap_read_raw(codec, reg, &orig); - if (err < 0) - return err; - val |= orig & ~mask; - if (val == orig) - return 0; - err = snd_hdac_regmap_write_raw(codec, reg, val); + if (!codec->regmap) + return reg_raw_update(codec, reg, mask, val); + + mutex_lock(&codec->regmap_lock); + regcache_cache_only(codec->regmap, true); + err = regmap_read(codec->regmap, reg, &orig); + regcache_cache_only(codec->regmap, false); if (err < 0) - return err; - return 1; + err = regmap_update_bits(codec->regmap, reg, mask, val); + mutex_unlock(&codec->regmap_lock); + return err; } -EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw); + +/** + * snd_hdac_regmap_update_raw_once - initialize the register value only once + * @codec: the codec object + * @reg: pseudo register + * @mask: bit mask to update + * @val: value to update + * + * Performs the update of the register bits only once when the register + * hasn't been initialized yet. Used in HD-audio legacy driver. + * Returns zero if successful or a negative error code + */ +int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg, + unsigned int mask, unsigned int val) +{ + return CALL_RAW_FUNC(codec, reg_raw_update_once(codec, reg, mask, val)); +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once); + +/** + * snd_hdac_regmap_sync - sync out the cached values for PM resume + * @codec: the codec object + */ +void snd_hdac_regmap_sync(struct hdac_device *codec) +{ + if (codec->regmap) { + mutex_lock(&codec->regmap_lock); + regcache_sync(codec->regmap); + mutex_unlock(&codec->regmap_lock); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 55d53b89ac21..a314b03b4a4c 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -96,12 +96,14 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) 1 << azx_dev->index, 1 << azx_dev->index); /* set stripe control */ - if (azx_dev->substream) - stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream); - else - stripe_ctl = 0; - snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, - stripe_ctl); + if (azx_dev->stripe) { + if (azx_dev->substream) + stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream); + else + stripe_ctl = 0; + snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, + stripe_ctl); + } /* set DMA start and interrupt mask */ snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_DMA_START | SD_INT_MASK); @@ -118,7 +120,8 @@ void snd_hdac_stream_clear(struct hdac_stream *azx_dev) snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_DMA_START | SD_INT_MASK, 0); snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ - snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); + if (azx_dev->stripe) + snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); azx_dev->running = false; } EXPORT_SYMBOL_GPL(snd_hdac_stream_clear); @@ -229,11 +232,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 @@ -591,7 +590,9 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_timecounter_init); /** * snd_hdac_stream_sync_trigger - turn on/off stream sync register * @azx_dev: HD-audio core stream (master stream) + * @set: true = set, false = clear * @streams: bit flags of streams to sync + * @reg: the stream sync register address */ void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set, unsigned int streams, unsigned int reg) @@ -630,20 +631,27 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, nwait = 0; i = 0; list_for_each_entry(s, &bus->stream_list, list) { - if (streams & (1 << i)) { - if (start) { - /* check FIFO gets ready */ - if (!(snd_hdac_stream_readb(s, SD_STS) & - SD_STS_FIFO_READY)) - nwait++; - } else { - /* check RUN bit is cleared */ - if (snd_hdac_stream_readb(s, SD_CTL) & - SD_CTL_DMA_START) - nwait++; + if (!(streams & (1 << i++))) + continue; + + if (start) { + /* check FIFO gets ready */ + if (!(snd_hdac_stream_readb(s, SD_STS) & + SD_STS_FIFO_READY)) + nwait++; + } else { + /* check RUN bit is cleared */ + if (snd_hdac_stream_readb(s, SD_CTL) & + SD_CTL_DMA_START) { + nwait++; + /* + * Perform stream reset if DMA RUN + * bit not cleared within given timeout + */ + if (timeout == 1) + snd_hdac_stream_reset(s); } } - i++; } if (!nwait) break; @@ -680,8 +688,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 +715,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 +762,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/hdmi_chmap.c b/sound/hda/hdmi_chmap.c index 886cb7811bd6..5fd6d575e123 100644 --- a/sound/hda/hdmi_chmap.c +++ b/sound/hda/hdmi_chmap.c @@ -59,7 +59,7 @@ static const char * const cea_speaker_allocation_names[] = { /* * ELD SA bits in the CEA Speaker Allocation data block */ -static int eld_speaker_allocation_bits[] = { +static const int eld_speaker_allocation_bits[] = { [0] = FL | FR, [1] = LFE, [2] = FC, diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c new file mode 100644 index 000000000000..be1df80ed013 --- /dev/null +++ b/sound/hda/intel-dsp-config.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz> + +#include <linux/bits.h> +#include <linux/dmi.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <sound/core.h> +#include <sound/intel-dsp-config.h> +#include <sound/intel-nhlt.h> + +static int dsp_driver; + +module_param(dsp_driver, int, 0444); +MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF)"); + +#define FLAG_SST BIT(0) +#define FLAG_SOF BIT(1) +#define FLAG_SOF_ONLY_IF_DMIC BIT(16) + +struct config_entry { + u32 flags; + u16 device; + const struct dmi_system_id *dmi_table; +}; + +/* + * configuration table + * - the order of similar PCI ID entries is important! + * - the first successful match will win + */ +static const struct config_entry config_table[] = { +/* Merrifield */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD) + { + .flags = FLAG_SOF, + .device = 0x119a, + }, +#endif +/* Broxton-T */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) + { + .flags = FLAG_SOF, + .device = 0x1a98, + }, +#endif +/* + * Apollolake (Broxton-P) + * the legacy HDaudio driver is used except on Up Squared (SOF) and + * Chromebooks (SST) + */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) + { + .flags = FLAG_SOF, + .device = 0x5a98, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Up Squared", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), + DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"), + } + }, + {} + } + }, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL) + { + .flags = FLAG_SST, + .device = 0x5a98, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, +#endif +/* + * Skylake and Kabylake use legacy HDaudio driver except for Google + * Chromebooks (SST) + */ + +/* Sunrise Point-LP */ +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL) + { + .flags = FLAG_SST, + .device = 0x9d70, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, +#endif +/* Kabylake-LP */ +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) + { + .flags = FLAG_SST, + .device = 0x9d71, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, +#endif + +/* + * Geminilake uses legacy HDaudio driver except for Google + * Chromebooks + */ +/* Geminilake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) + { + .flags = FLAG_SOF, + .device = 0x3198, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, +#endif + +/* + * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy + * HDaudio driver except for Google Chromebooks and when DMICs are + * present. Two cases are required since Coreboot does not expose NHLT + * tables. + * + * When the Chromebook quirk is not present, it's based on information + * that no such device exists. When the quirk is present, it could be + * either based on product information or a placeholder. + */ + +/* Cannonlake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) + { + .flags = FLAG_SOF, + .device = 0x9dc8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x9dc8, + }, +#endif + +/* Coffelake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE) + { + .flags = FLAG_SOF, + .device = 0xa348, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0xa348, + }, +#endif + +/* Cometlake-LP */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_LP) + { + .flags = FLAG_SOF, + .device = 0x02c8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x02c8, + }, +#endif +/* Cometlake-H */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H) + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x06c8, + }, +#endif + +/* Icelake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) + { + .flags = FLAG_SOF, + .device = 0x34c8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x34c8, + }, +#endif + +/* Tigerlake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE) + { + .flags = FLAG_SOF, + .device = 0xa0c8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0xa0c8, + }, +#endif + +/* Elkhart Lake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE) + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x4b55, + }, +#endif + +}; + +static const struct config_entry *snd_intel_dsp_find_config + (struct pci_dev *pci, const struct config_entry *table, u32 len) +{ + u16 device; + + device = pci->device; + for (; len > 0; len--, table++) { + if (table->device != device) + continue; + if (table->dmi_table && !dmi_check_system(table->dmi_table)) + continue; + return table; + } + return NULL; +} + +static int snd_intel_dsp_check_dmic(struct pci_dev *pci) +{ + struct nhlt_acpi_table *nhlt; + int ret = 0; + + nhlt = intel_nhlt_init(&pci->dev); + if (nhlt) { + if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) + ret = 1; + intel_nhlt_free(nhlt); + } + return ret; +} + +int snd_intel_dsp_driver_probe(struct pci_dev *pci) +{ + const struct config_entry *cfg; + + /* Intel vendor only */ + if (pci->vendor != 0x8086) + return SND_INTEL_DSP_DRIVER_ANY; + + if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST) + return dsp_driver; + + /* + * detect DSP by checking class/subclass/prog-id information + * class=04 subclass 03 prog-if 00: no DSP, use legacy driver + * class=04 subclass 01 prog-if 00: DSP is present + * (and may be required e.g. for DMIC or SSP support) + * class=04 subclass 03 prog-if 80: use DSP or legacy mode + */ + if (pci->class == 0x040300) + return SND_INTEL_DSP_DRIVER_LEGACY; + if (pci->class != 0x040100 && pci->class != 0x040380) { + dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDA legacy driver\n", pci->class); + return SND_INTEL_DSP_DRIVER_LEGACY; + } + + dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); + + /* find the configuration for the specific device */ + cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table)); + if (!cfg) + return SND_INTEL_DSP_DRIVER_ANY; + + if (cfg->flags & FLAG_SOF) { + if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC) { + if (snd_intel_dsp_check_dmic(pci)) { + dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n"); + return SND_INTEL_DSP_DRIVER_SOF; + } + } else { + return SND_INTEL_DSP_DRIVER_SOF; + } + } + + if (cfg->flags & FLAG_SST) + return SND_INTEL_DSP_DRIVER_SST; + + return SND_INTEL_DSP_DRIVER_LEGACY; +} +EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel DSP config driver"); diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c new file mode 100644 index 000000000000..99a23fe7fab9 --- /dev/null +++ b/sound/hda/intel-nhlt.c @@ -0,0 +1,104 @@ +// 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 const 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); 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 */ |