More sound updates for 6.2-rc1
A few more updates for 6.2 since the last PR: most of changes are about ASoC device-specific fixes. - Lots of ASoC Intel AVS extensions and refactoring - Quirks for ASoC Intel SOF as well as regression fixes - ASoC Mediatek and Rockchip fixes - Intel HD-audio HDMI workarounds - Usual HD- and USB-audio device-specific quirks -----BEGIN PGP SIGNATURE----- iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAmOlhFcOHHRpd2FpQHN1 c2UuZGUACgkQLtJE4w1nLE/urxAAg4UNnglVwZ5yGNndo2Im6kf++aZbwsPMjTFS VIfPLD2UFs+s7SOnHYv+Gaai1fhv39bMyg4IsSJvH/kyi/5iWUf85fW7W+xK701N rWwkvEpmjpla/ojisryF+Liy0in3hc/YgrNPlxzTL5uExcHTmBA8mw44wjxnWPwB eS31HowboL9aSeUBMmZma6wME7lVqzOAogIG2q8Pj1VucaE2MLGAtSeJLzy5Bnhm QTG66EmybxNbwRZuv3+CNv6t8Lo/WcwRJ3iReOePc/ePDqreGYfrKeVGhDLt+P5J owagDBDLLs+DZ9UuCFg/Td0ncZufTYYkg2KDQgOeSInVj8NeSFBbDbYQp89qwkSq mxfyoFS9Jx6CWiI/F0P9SOhIYbePLZfYyHZHj7yiGCGX2x+ZaOtKrPkvzmxw38cb nbmBbHmcwRpYirNeOtVPiBKOaIt6i9rzmfGiy9LeMScYd6Jjuq2xWOG+z9zWHxun EQHu3vqW/6DJRgfw4k/KcrrGzT3m71UxMd1jSL+Veb6Fv9wTrCeG95YNA3k6B2nO Xr1nNFCPzKqfYizRLGGjTy4SSNzDQXjt1O0vbbtrfAJ4iK+as5tNqObUMO6ItUoY 6JMQyyKHLgyE+1KdrE0ltZYoP6LZ60rTFDTQBMXMb+S5CXz5k9JbmlL7790tL+VL /3cSm64= =2Uko -----END PGP SIGNATURE----- Merge tag 'sound-6.2-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull more sound updates from Takashi Iwai: "A few more updates for 6.2: most of changes are about ASoC device-specific fixes. - Lots of ASoC Intel AVS extensions and refactoring - Quirks for ASoC Intel SOF as well as regression fixes - ASoC Mediatek and Rockchip fixes - Intel HD-audio HDMI workarounds - Usual HD- and USB-audio device-specific quirks" * tag 'sound-6.2-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (54 commits) ALSA: usb-audio: Add new quirk FIXED_RATE for JBL Quantum810 Wireless ALSA: azt3328: Remove the unused function snd_azf3328_codec_outl() ASoC: lochnagar: Fix unused lochnagar_of_match warning ASoC: Intel: Add HP Stream 8 to bytcr_rt5640.c ASoC: SOF: mediatek: initialize panic_info to zero ASoC: rt5670: Remove unbalanced pm_runtime_put() ASoC: Intel: bytcr_rt5640: Add quirk for the Advantech MICA-071 tablet ASoC: Intel: soc-acpi: update codec addr on 0C11/0C4F product ASoC: rockchip: spdif: Add missing clk_disable_unprepare() in rk_spdif_runtime_resume() ASoC: wm8994: Fix potential deadlock ASoC: mediatek: mt8195: add sof be ops to check audio active ASoC: SOF: Revert: "core: unregister clients and machine drivers in .shutdown" ASoC: SOF: Intel: pci-tgl: unblock S5 entry if DMA stop has failed" ALSA: hda/hdmi: fix stream-id config keep-alive for rt suspend ALSA: hda/hdmi: set default audio parameters for KAE silent-stream ALSA: hda/hdmi: fix i915 silent stream programming flow ALSA: hda: Error out if invalid stream is being setup ASoC: dt-bindings: fsl-sai: Reinstate i.MX93 SAI compatible string ASoC: soc-pcm.c: Clear DAIs parameters after stream_active is updated ASoC: codecs: wcd-clsh: Remove the unused function ...
This commit is contained in:
commit
a27405b2ed
@ -38,6 +38,7 @@ properties:
|
||||
- fsl,imx8mq-sai
|
||||
- fsl,imx8qm-sai
|
||||
- fsl,imx8ulp-sai
|
||||
- fsl,imx93-sai
|
||||
- fsl,vf610-sai
|
||||
|
||||
reg:
|
||||
|
@ -258,6 +258,7 @@ struct hda_codec {
|
||||
unsigned int link_down_at_suspend:1; /* link down at runtime suspend */
|
||||
unsigned int relaxed_resume:1; /* don't resume forcibly for jack */
|
||||
unsigned int forced_resume:1; /* forced resume for jack */
|
||||
unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
unsigned long power_on_acct;
|
||||
|
@ -75,6 +75,8 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
|
||||
struct snd_pcm_substream *substream,
|
||||
int type);
|
||||
void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type);
|
||||
struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus,
|
||||
struct snd_compr_stream *cstream);
|
||||
void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
|
||||
struct hdac_ext_stream *hext_stream, bool decouple);
|
||||
void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/hda_register.h>
|
||||
#include <sound/hdaudio_ext.h>
|
||||
#include <sound/compress_driver.h>
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_stream_init - initialize each stream (aka device)
|
||||
@ -367,3 +368,43 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type)
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_cstream_assign - assign a host stream for compress
|
||||
* @bus: HD-audio core bus
|
||||
* @cstream: Compress stream to assign
|
||||
*
|
||||
* Assign an unused host stream for the given compress stream.
|
||||
* If no stream is free, NULL is returned. Stream is decoupled
|
||||
* before assignment.
|
||||
*/
|
||||
struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus,
|
||||
struct snd_compr_stream *cstream)
|
||||
{
|
||||
struct hdac_ext_stream *res = NULL;
|
||||
struct hdac_stream *hstream;
|
||||
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
list_for_each_entry(hstream, &bus->stream_list, list) {
|
||||
struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
|
||||
|
||||
if (hstream->direction != cstream->direction)
|
||||
continue;
|
||||
|
||||
if (!hstream->opened) {
|
||||
res = hext_stream;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res) {
|
||||
snd_hdac_ext_stream_decouple_locked(bus, res, true);
|
||||
res->hstream.opened = 1;
|
||||
res->hstream.running = 0;
|
||||
res->hstream.cstream = cstream;
|
||||
}
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_cstream_assign);
|
||||
|
@ -578,8 +578,8 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
|
||||
sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
|
||||
snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
|
||||
handled |= 1 << azx_dev->index;
|
||||
if (!azx_dev->substream || !azx_dev->running ||
|
||||
!(sd_status & SD_INT_COMPLETE))
|
||||
if ((!azx_dev->substream && !azx_dev->cstream) ||
|
||||
!azx_dev->running || !(sd_status & SD_INT_COMPLETE))
|
||||
continue;
|
||||
if (ack)
|
||||
ack(bus, azx_dev);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <sound/compress_driver.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/hdaudio.h>
|
||||
@ -487,11 +488,23 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
|
||||
{
|
||||
struct hdac_bus *bus = azx_dev->bus;
|
||||
struct snd_pcm_substream *substream = azx_dev->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_compr_stream *cstream = azx_dev->cstream;
|
||||
struct snd_pcm_runtime *runtime = NULL;
|
||||
struct snd_dma_buffer *dmab;
|
||||
__le32 *bdl;
|
||||
int i, ofs, periods, period_bytes;
|
||||
int pos_adj, pos_align;
|
||||
|
||||
if (substream) {
|
||||
runtime = substream->runtime;
|
||||
dmab = snd_pcm_get_dma_buf(substream);
|
||||
} else if (cstream) {
|
||||
dmab = snd_pcm_get_dma_buf(cstream);
|
||||
} else {
|
||||
WARN(1, "No substream or cstream assigned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* reset BDL address */
|
||||
snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
|
||||
snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
|
||||
@ -505,7 +518,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
|
||||
azx_dev->frags = 0;
|
||||
|
||||
pos_adj = bus->bdl_pos_adj;
|
||||
if (!azx_dev->no_period_wakeup && pos_adj > 0) {
|
||||
if (runtime && !azx_dev->no_period_wakeup && pos_adj > 0) {
|
||||
pos_align = pos_adj;
|
||||
pos_adj = DIV_ROUND_UP(pos_adj * runtime->rate, 48000);
|
||||
if (!pos_adj)
|
||||
@ -518,8 +531,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
|
||||
pos_adj);
|
||||
pos_adj = 0;
|
||||
} else {
|
||||
ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
|
||||
azx_dev,
|
||||
ofs = setup_bdle(bus, dmab, azx_dev,
|
||||
&bdl, ofs, pos_adj, true);
|
||||
if (ofs < 0)
|
||||
goto error;
|
||||
@ -529,13 +541,11 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
|
||||
|
||||
for (i = 0; i < periods; i++) {
|
||||
if (i == periods - 1 && pos_adj)
|
||||
ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
|
||||
azx_dev, &bdl, ofs,
|
||||
period_bytes - pos_adj, 0);
|
||||
ofs = setup_bdle(bus, dmab, azx_dev,
|
||||
&bdl, ofs, period_bytes - pos_adj, 0);
|
||||
else
|
||||
ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
|
||||
azx_dev, &bdl, ofs,
|
||||
period_bytes,
|
||||
ofs = setup_bdle(bus, dmab, azx_dev,
|
||||
&bdl, ofs, period_bytes,
|
||||
!azx_dev->no_period_wakeup);
|
||||
if (ofs < 0)
|
||||
goto error;
|
||||
@ -560,26 +570,32 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
|
||||
int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
|
||||
unsigned int format_val)
|
||||
{
|
||||
|
||||
unsigned int bufsize, period_bytes;
|
||||
struct snd_pcm_substream *substream = azx_dev->substream;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_compr_stream *cstream = azx_dev->cstream;
|
||||
unsigned int bufsize, period_bytes;
|
||||
unsigned int no_period_wakeup;
|
||||
int err;
|
||||
|
||||
if (!substream)
|
||||
if (substream) {
|
||||
bufsize = snd_pcm_lib_buffer_bytes(substream);
|
||||
period_bytes = snd_pcm_lib_period_bytes(substream);
|
||||
no_period_wakeup = substream->runtime->no_period_wakeup;
|
||||
} else if (cstream) {
|
||||
bufsize = cstream->runtime->buffer_size;
|
||||
period_bytes = cstream->runtime->fragment_size;
|
||||
no_period_wakeup = 0;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
runtime = substream->runtime;
|
||||
bufsize = snd_pcm_lib_buffer_bytes(substream);
|
||||
period_bytes = snd_pcm_lib_period_bytes(substream);
|
||||
}
|
||||
|
||||
if (bufsize != azx_dev->bufsize ||
|
||||
period_bytes != azx_dev->period_bytes ||
|
||||
format_val != azx_dev->format_val ||
|
||||
runtime->no_period_wakeup != azx_dev->no_period_wakeup) {
|
||||
no_period_wakeup != azx_dev->no_period_wakeup) {
|
||||
azx_dev->bufsize = bufsize;
|
||||
azx_dev->period_bytes = period_bytes;
|
||||
azx_dev->format_val = format_val;
|
||||
azx_dev->no_period_wakeup = runtime->no_period_wakeup;
|
||||
azx_dev->no_period_wakeup = no_period_wakeup;
|
||||
err = snd_hdac_stream_setup_periods(azx_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -363,15 +363,6 @@ snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
|
||||
return inw(codec->io_base + reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
|
||||
unsigned reg,
|
||||
u32 value
|
||||
)
|
||||
{
|
||||
outl(value, codec->io_base + reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
|
||||
unsigned reg, const void *buffer, int count
|
||||
|
@ -2886,7 +2886,8 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
|
||||
snd_hdac_enter_pm(&codec->core);
|
||||
if (codec->patch_ops.suspend)
|
||||
codec->patch_ops.suspend(codec);
|
||||
hda_cleanup_all_streams(codec);
|
||||
if (!codec->no_stream_clean_at_suspend)
|
||||
hda_cleanup_all_streams(codec);
|
||||
state = hda_set_power_state(codec, AC_PWRST_D3);
|
||||
update_power_acct(codec, true);
|
||||
snd_hdac_leave_pm(&codec->core);
|
||||
|
@ -1738,6 +1738,7 @@ static void silent_stream_enable(struct hda_codec *codec,
|
||||
|
||||
switch (spec->silent_stream_type) {
|
||||
case SILENT_STREAM_KAE:
|
||||
silent_stream_enable_i915(codec, per_pin);
|
||||
silent_stream_set_kae(codec, per_pin, true);
|
||||
break;
|
||||
case SILENT_STREAM_I915:
|
||||
@ -1975,6 +1976,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
|
||||
static const struct snd_pci_quirk force_connect_list[] = {
|
||||
SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1),
|
||||
SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1),
|
||||
SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1),
|
||||
SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1),
|
||||
SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1),
|
||||
{}
|
||||
@ -2878,9 +2880,33 @@ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
|
||||
hda_nid_t pin_nid, int dev_id, u32 stream_tag,
|
||||
int format)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id);
|
||||
struct hdmi_spec_per_pin *per_pin;
|
||||
int res;
|
||||
|
||||
if (pin_idx < 0)
|
||||
per_pin = NULL;
|
||||
else
|
||||
per_pin = get_pin(spec, pin_idx);
|
||||
|
||||
haswell_verify_D0(codec, cvt_nid, pin_nid);
|
||||
return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
|
||||
stream_tag, format);
|
||||
|
||||
if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
|
||||
silent_stream_set_kae(codec, per_pin, false);
|
||||
/* wait for pending transfers in codec to clear */
|
||||
usleep_range(100, 200);
|
||||
}
|
||||
|
||||
res = hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
|
||||
stream_tag, format);
|
||||
|
||||
if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
|
||||
usleep_range(100, 200);
|
||||
silent_stream_set_kae(codec, per_pin, true);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* pin_cvt_fixup ops override for HSW+ and VLV+ */
|
||||
@ -2900,6 +2926,88 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int i915_adlp_hdmi_suspend(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
bool silent_streams = false;
|
||||
int pin_idx, res;
|
||||
|
||||
res = generic_hdmi_suspend(codec);
|
||||
|
||||
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
|
||||
if (per_pin->silent_stream) {
|
||||
silent_streams = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (silent_streams && spec->silent_stream_type == SILENT_STREAM_KAE) {
|
||||
/*
|
||||
* stream-id should remain programmed when codec goes
|
||||
* to runtime suspend
|
||||
*/
|
||||
codec->no_stream_clean_at_suspend = 1;
|
||||
|
||||
/*
|
||||
* the system might go to S3, in which case keep-alive
|
||||
* must be reprogrammed upon resume
|
||||
*/
|
||||
codec->forced_resume = 1;
|
||||
|
||||
codec_dbg(codec, "HDMI: KAE active at suspend\n");
|
||||
} else {
|
||||
codec->no_stream_clean_at_suspend = 0;
|
||||
codec->forced_resume = 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int i915_adlp_hdmi_resume(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int pin_idx, res;
|
||||
|
||||
res = generic_hdmi_resume(codec);
|
||||
|
||||
/* KAE not programmed at suspend, nothing to do here */
|
||||
if (!codec->no_stream_clean_at_suspend)
|
||||
return res;
|
||||
|
||||
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
|
||||
/*
|
||||
* If system was in suspend with monitor connected,
|
||||
* the codec setting may have been lost. Re-enable
|
||||
* keep-alive.
|
||||
*/
|
||||
if (per_pin->silent_stream) {
|
||||
unsigned int param;
|
||||
|
||||
param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0,
|
||||
AC_VERB_GET_CONV, 0);
|
||||
if (!param) {
|
||||
codec_dbg(codec, "HDMI: KAE: restore stream id\n");
|
||||
silent_stream_enable_i915(codec, per_pin);
|
||||
}
|
||||
|
||||
param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0,
|
||||
AC_VERB_GET_DIGI_CONVERT_1, 0);
|
||||
if (!(param & (AC_DIG3_KAE << 16))) {
|
||||
codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n");
|
||||
silent_stream_set_kae(codec, per_pin, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* precondition and allocation for Intel codecs */
|
||||
static int alloc_intel_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
@ -3030,8 +3138,14 @@ static int patch_i915_adlp_hdmi(struct hda_codec *codec)
|
||||
if (!res) {
|
||||
spec = codec->spec;
|
||||
|
||||
if (spec->silent_stream_type)
|
||||
if (spec->silent_stream_type) {
|
||||
spec->silent_stream_type = SILENT_STREAM_KAE;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
codec->patch_ops.resume = i915_adlp_hdmi_resume;
|
||||
codec->patch_ops.suspend = i915_adlp_hdmi_suspend;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
@ -10999,6 +10999,17 @@ static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec,
|
||||
}
|
||||
}
|
||||
|
||||
static void alc897_fixup_lenovo_headset_mode(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
|
||||
spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
|
||||
spec->gen.hp_automute_hook = alc897_hp_automute_hook;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct coef_fw alc668_coefs[] = {
|
||||
WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
|
||||
WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
|
||||
@ -11082,6 +11093,8 @@ enum {
|
||||
ALC897_FIXUP_LENOVO_HEADSET_MIC,
|
||||
ALC897_FIXUP_HEADSET_MIC_PIN,
|
||||
ALC897_FIXUP_HP_HSMIC_VERB,
|
||||
ALC897_FIXUP_LENOVO_HEADSET_MODE,
|
||||
ALC897_FIXUP_HEADSET_MIC_PIN2,
|
||||
};
|
||||
|
||||
static const struct hda_fixup alc662_fixups[] = {
|
||||
@ -11508,6 +11521,19 @@ static const struct hda_fixup alc662_fixups[] = {
|
||||
{ }
|
||||
},
|
||||
},
|
||||
[ALC897_FIXUP_LENOVO_HEADSET_MODE] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc897_fixup_lenovo_headset_mode,
|
||||
},
|
||||
[ALC897_FIXUP_HEADSET_MIC_PIN2] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
{ 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */
|
||||
{ }
|
||||
},
|
||||
.chained = true,
|
||||
.chain_id = ALC897_FIXUP_LENOVO_HEADSET_MODE
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
|
||||
@ -11560,6 +11586,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN),
|
||||
SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN),
|
||||
SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2),
|
||||
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
|
||||
SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO),
|
||||
|
@ -253,7 +253,7 @@ MODULE_DEVICE_TABLE(of, lochnagar_of_match);
|
||||
static struct platform_driver lochnagar_sc_codec_driver = {
|
||||
.driver = {
|
||||
.name = "lochnagar-soundcard",
|
||||
.of_match_table = of_match_ptr(lochnagar_of_match),
|
||||
.of_match_table = lochnagar_of_match,
|
||||
},
|
||||
|
||||
.probe = lochnagar_sc_probe,
|
||||
|
@ -3311,8 +3311,6 @@ static int rt5670_i2c_probe(struct i2c_client *i2c)
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
pm_runtime_put(&i2c->dev);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
pm_runtime_disable(&i2c->dev);
|
||||
|
@ -130,12 +130,6 @@ static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl,
|
||||
ctrl->clsh_users = 0;
|
||||
}
|
||||
|
||||
static inline bool wcd_clsh_enable_status(struct snd_soc_component *comp)
|
||||
{
|
||||
return snd_soc_component_read(comp, WCD9XXX_A_CDC_CLSH_CRC) &
|
||||
WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK;
|
||||
}
|
||||
|
||||
static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp,
|
||||
int mode)
|
||||
{
|
||||
|
@ -3853,7 +3853,12 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
|
||||
} else {
|
||||
dev_dbg(component->dev, "Jack not detected\n");
|
||||
|
||||
/* Release wm8994->accdet_lock to avoid deadlock:
|
||||
* cancel_delayed_work_sync() takes wm8994->mic_work internal
|
||||
* lock and wm1811_mic_work takes wm8994->accdet_lock */
|
||||
mutex_unlock(&wm8994->accdet_lock);
|
||||
cancel_delayed_work_sync(&wm8994->mic_work);
|
||||
mutex_lock(&wm8994->accdet_lock);
|
||||
|
||||
snd_soc_component_update_bits(component, WM8958_MICBIAS2,
|
||||
WM8958_MICB2_DISCH, WM8958_MICB2_DISCH);
|
||||
|
@ -75,8 +75,7 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf,
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = scnprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n",
|
||||
pdcr, ptcr);
|
||||
ret = sysfs_emit(buf, "PDCR: %08x\nPTCR: %08x\n", pdcr, ptcr);
|
||||
|
||||
if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR)
|
||||
ret += scnprintf(buf + ret, PAGE_SIZE - ret,
|
||||
|
@ -485,8 +485,10 @@ static int __graph_for_each_link(struct asoc_simple_priv *priv,
|
||||
of_node_put(codec_ep);
|
||||
of_node_put(codec_port);
|
||||
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
of_node_put(cpu_ep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
codec_port_old = codec_port;
|
||||
}
|
||||
|
@ -217,6 +217,7 @@ config SND_SOC_INTEL_AVS
|
||||
select SND_SOC_ACPI if ACPI
|
||||
select SND_SOC_TOPOLOGY
|
||||
select SND_SOC_HDA
|
||||
select SND_SOC_COMPRESS if DEBUG_FS
|
||||
select SND_HDA_EXT_CORE
|
||||
select SND_HDA_DSP_LOADER
|
||||
select SND_INTEL_DSP_CONFIG
|
||||
|
@ -9,6 +9,10 @@ snd-soc-avs-objs += trace.o
|
||||
# tell define_trace.h where to find the trace header
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
||||
ifneq ($(CONFIG_DEBUG_FS),)
|
||||
snd-soc-avs-objs += probes.o debugfs.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
|
||||
|
||||
# Machine support
|
||||
|
@ -13,8 +13,9 @@
|
||||
#include "path.h"
|
||||
#include "topology.h"
|
||||
|
||||
static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
|
||||
u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
|
||||
static int __maybe_unused
|
||||
apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
|
||||
u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
|
||||
{
|
||||
struct apl_log_state_info *info;
|
||||
u32 size, num_cores = adev->hw_cfg.dsp_cores;
|
||||
@ -50,7 +51,6 @@ static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32
|
||||
static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
|
||||
{
|
||||
struct apl_log_buffer_layout layout;
|
||||
unsigned long flags;
|
||||
void __iomem *addr, *buf;
|
||||
|
||||
addr = avs_log_buffer_addr(adev, msg->log.core);
|
||||
@ -59,26 +59,20 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg
|
||||
|
||||
memcpy_fromio(&layout, addr, sizeof(layout));
|
||||
|
||||
spin_lock_irqsave(&adev->dbg.trace_lock, flags);
|
||||
if (!kfifo_initialized(&adev->dbg.trace_fifo))
|
||||
if (!avs_logging_fw(adev))
|
||||
/* consume the logs regardless of consumer presence */
|
||||
goto update_read_ptr;
|
||||
|
||||
buf = apl_log_payload_addr(addr);
|
||||
|
||||
if (layout.read_ptr > layout.write_ptr) {
|
||||
__kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr,
|
||||
apl_log_payload_size(adev) - layout.read_ptr,
|
||||
&adev->dbg.fifo_lock);
|
||||
avs_dump_fw_log(adev, buf + layout.read_ptr,
|
||||
apl_log_payload_size(adev) - layout.read_ptr);
|
||||
layout.read_ptr = 0;
|
||||
}
|
||||
__kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr,
|
||||
layout.write_ptr - layout.read_ptr, &adev->dbg.fifo_lock);
|
||||
|
||||
wake_up(&adev->dbg.trace_waitq);
|
||||
avs_dump_fw_log_wakeup(adev, buf + layout.read_ptr, layout.write_ptr - layout.read_ptr);
|
||||
|
||||
update_read_ptr:
|
||||
spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
|
||||
writel(layout.write_ptr, addr);
|
||||
return 0;
|
||||
}
|
||||
@ -140,7 +134,7 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
|
||||
* gathered before dumping stack
|
||||
*/
|
||||
lbs_msg.log.core = msg->ext.coredump.core_id;
|
||||
avs_dsp_op(adev, log_buffer_status, &lbs_msg);
|
||||
avs_log_buffer_status_locked(adev, &lbs_msg);
|
||||
}
|
||||
|
||||
pos = dump + AVS_FW_REGS_SIZE;
|
||||
@ -243,10 +237,10 @@ const struct avs_dsp_ops apl_dsp_ops = {
|
||||
.load_basefw = avs_hda_load_basefw,
|
||||
.load_lib = avs_hda_load_library,
|
||||
.transfer_mods = avs_hda_transfer_modules,
|
||||
.enable_logs = apl_enable_logs,
|
||||
.log_buffer_offset = skl_log_buffer_offset,
|
||||
.log_buffer_status = apl_log_buffer_status,
|
||||
.coredump = apl_coredump,
|
||||
.d0ix_toggle = apl_d0ix_toggle,
|
||||
.set_d0ix = apl_set_d0ix,
|
||||
AVS_SET_ENABLE_LOGS_OP(apl)
|
||||
};
|
||||
|
@ -9,6 +9,7 @@
|
||||
#ifndef __SOUND_SOC_INTEL_AVS_H
|
||||
#define __SOUND_SOC_INTEL_AVS_H
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/kfifo.h>
|
||||
@ -93,16 +94,6 @@ struct avs_fw_entry {
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct avs_debug {
|
||||
struct kfifo trace_fifo;
|
||||
spinlock_t fifo_lock; /* serialize I/O for trace_fifo */
|
||||
spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */
|
||||
wait_queue_head_t trace_waitq;
|
||||
u32 aging_timer_period;
|
||||
u32 fifo_full_timer_period;
|
||||
u32 logged_resources; /* context dependent: core or library */
|
||||
};
|
||||
|
||||
/*
|
||||
* struct avs_dev - Intel HD-Audio driver data
|
||||
*
|
||||
@ -146,7 +137,18 @@ struct avs_dev {
|
||||
spinlock_t path_list_lock;
|
||||
struct mutex path_mutex;
|
||||
|
||||
struct avs_debug dbg;
|
||||
spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct kfifo trace_fifo;
|
||||
wait_queue_head_t trace_waitq;
|
||||
u32 aging_timer_period;
|
||||
u32 fifo_full_timer_period;
|
||||
u32 logged_resources; /* context dependent: core or library */
|
||||
struct dentry *debugfs_root;
|
||||
/* probes */
|
||||
struct hdac_ext_stream *extractor;
|
||||
unsigned int num_probe_streams;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* from hda_bus to avs_dev */
|
||||
@ -321,6 +323,9 @@ struct avs_soc_component {
|
||||
|
||||
extern const struct snd_soc_dai_ops avs_dai_fe_ops;
|
||||
|
||||
int avs_soc_component_register(struct device *dev, const char *name,
|
||||
const struct snd_soc_component_driver *drv,
|
||||
struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
|
||||
int avs_dmic_platform_register(struct avs_dev *adev, const char *name);
|
||||
int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
|
||||
unsigned long *tdms);
|
||||
@ -331,9 +336,6 @@ void avs_unregister_all_boards(struct avs_dev *adev);
|
||||
|
||||
/* Firmware tracing helpers */
|
||||
|
||||
unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
|
||||
spinlock_t *lock);
|
||||
|
||||
#define avs_log_buffer_size(adev) \
|
||||
((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
|
||||
|
||||
@ -344,6 +346,18 @@ unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src,
|
||||
(avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
|
||||
})
|
||||
|
||||
static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&adev->trace_lock, flags);
|
||||
ret = avs_dsp_op(adev, log_buffer_status, msg);
|
||||
spin_unlock_irqrestore(&adev->trace_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct apl_log_buffer_layout {
|
||||
u32 read_ptr;
|
||||
u32 write_ptr;
|
||||
@ -356,4 +370,42 @@ struct apl_log_buffer_layout {
|
||||
#define apl_log_payload_addr(addr) \
|
||||
(addr + sizeof(struct apl_log_buffer_layout))
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#define AVS_SET_ENABLE_LOGS_OP(name) \
|
||||
.enable_logs = name##_enable_logs
|
||||
|
||||
bool avs_logging_fw(struct avs_dev *adev);
|
||||
void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len);
|
||||
void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len);
|
||||
|
||||
int avs_probe_platform_register(struct avs_dev *adev, const char *name);
|
||||
|
||||
void avs_debugfs_init(struct avs_dev *adev);
|
||||
void avs_debugfs_exit(struct avs_dev *adev);
|
||||
#else
|
||||
#define AVS_SET_ENABLE_LOGS_OP(name)
|
||||
|
||||
static inline bool avs_logging_fw(struct avs_dev *adev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void
|
||||
avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int avs_probe_platform_register(struct avs_dev *adev, const char *name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void avs_debugfs_init(struct avs_dev *adev) { }
|
||||
static inline void avs_debugfs_exit(struct avs_dev *adev) { }
|
||||
#endif
|
||||
|
||||
#endif /* __SOUND_SOC_INTEL_AVS_H */
|
||||
|
@ -291,6 +291,33 @@ static void board_pdev_unregister(void *data)
|
||||
platform_device_unregister(data);
|
||||
}
|
||||
|
||||
static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
|
||||
{
|
||||
struct platform_device *board;
|
||||
struct snd_soc_acpi_mach mach = {{0}};
|
||||
int ret;
|
||||
|
||||
ret = avs_probe_platform_register(adev, "probe-platform");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mach.mach_params.platform = "probe-platform";
|
||||
|
||||
board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE,
|
||||
(const void *)&mach, sizeof(mach));
|
||||
if (IS_ERR(board)) {
|
||||
dev_err(adev->dev, "probe board register failed\n");
|
||||
return PTR_ERR(board);
|
||||
}
|
||||
|
||||
ret = devm_add_action(adev->dev, board_pdev_unregister, board);
|
||||
if (ret < 0) {
|
||||
platform_device_unregister(board);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avs_register_dmic_board(struct avs_dev *adev)
|
||||
{
|
||||
struct platform_device *codec, *board;
|
||||
@ -500,6 +527,12 @@ int avs_register_all_boards(struct avs_dev *adev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
ret = avs_register_probe_board(adev);
|
||||
if (ret < 0)
|
||||
dev_warn(adev->dev, "enumerate PROBE endpoints failed: %d\n", ret);
|
||||
#endif
|
||||
|
||||
ret = avs_register_dmic_board(adev);
|
||||
if (ret < 0)
|
||||
dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
|
||||
|
@ -77,6 +77,14 @@ config SND_SOC_INTEL_AVS_MACH_NAU8825
|
||||
Say Y or m if you have such a device. This is a recommended option.
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_INTEL_AVS_MACH_PROBE
|
||||
tristate "Probing (data) board"
|
||||
depends on DEBUG_FS
|
||||
select SND_HWDEP
|
||||
help
|
||||
This adds support for data probing board which can be used to
|
||||
gather data from runtime stream over compress operations.
|
||||
|
||||
config SND_SOC_INTEL_AVS_MACH_RT274
|
||||
tristate "rt274 in I2S mode"
|
||||
depends on I2C
|
||||
|
@ -8,6 +8,7 @@ snd-soc-avs-max98927-objs := max98927.o
|
||||
snd-soc-avs-max98357a-objs := max98357a.o
|
||||
snd-soc-avs-max98373-objs := max98373.o
|
||||
snd-soc-avs-nau8825-objs := nau8825.o
|
||||
snd-soc-avs-probe-objs := probe.o
|
||||
snd-soc-avs-rt274-objs := rt274.o
|
||||
snd-soc-avs-rt286-objs := rt286.o
|
||||
snd-soc-avs-rt298-objs := rt298.o
|
||||
@ -22,6 +23,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o
|
||||
|
64
sound/soc/intel/avs/boards/probe.c
Normal file
64
sound/soc/intel/avs/boards/probe.c
Normal file
@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
|
||||
//
|
||||
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
|
||||
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
|
||||
//
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-acpi.h>
|
||||
|
||||
SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
|
||||
SND_SOC_DAILINK_DEF(probe_cp, DAILINK_COMP_ARRAY(COMP_CPU("Probe Extraction CPU DAI")));
|
||||
SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("probe-platform")));
|
||||
|
||||
static struct snd_soc_dai_link probe_mb_dai_links[] = {
|
||||
{
|
||||
.name = "Compress Probe Capture",
|
||||
.nonatomic = 1,
|
||||
SND_SOC_DAILINK_REG(probe_cp, dummy, platform),
|
||||
},
|
||||
};
|
||||
|
||||
static int avs_probe_mb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct snd_soc_acpi_mach *mach;
|
||||
struct snd_soc_card *card;
|
||||
int ret;
|
||||
|
||||
mach = dev_get_platdata(dev);
|
||||
|
||||
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
|
||||
if (!card)
|
||||
return -ENOMEM;
|
||||
|
||||
card->name = "avs_probe_mb";
|
||||
card->dev = dev;
|
||||
card->owner = THIS_MODULE;
|
||||
card->dai_link = probe_mb_dai_links;
|
||||
card->num_links = ARRAY_SIZE(probe_mb_dai_links);
|
||||
card->fully_routed = true;
|
||||
|
||||
ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_snd_soc_register_card(dev, card);
|
||||
}
|
||||
|
||||
static struct platform_driver avs_probe_mb_driver = {
|
||||
.probe = avs_probe_mb_probe,
|
||||
.driver = {
|
||||
.name = "avs_probe_mb",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(avs_probe_mb_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:avs_probe_mb");
|
@ -214,6 +214,7 @@ static void avs_hda_probe_work(struct work_struct *work)
|
||||
adev->nhlt = intel_nhlt_init(adev->dev);
|
||||
if (!adev->nhlt)
|
||||
dev_info(bus->dev, "platform has no NHLT\n");
|
||||
avs_debugfs_init(adev);
|
||||
|
||||
avs_register_all_boards(adev);
|
||||
|
||||
@ -491,6 +492,7 @@ static void avs_pci_remove(struct pci_dev *pci)
|
||||
|
||||
avs_unregister_all_boards(adev);
|
||||
|
||||
avs_debugfs_exit(adev);
|
||||
if (adev->nhlt)
|
||||
intel_nhlt_free(adev->nhlt);
|
||||
|
||||
|
436
sound/soc/intel/avs/debugfs.c
Normal file
436
sound/soc/intel/avs/debugfs.c
Normal file
@ -0,0 +1,436 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
|
||||
//
|
||||
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
|
||||
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
|
||||
//
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <sound/soc.h>
|
||||
#include "avs.h"
|
||||
#include "messages.h"
|
||||
|
||||
static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len)
|
||||
{
|
||||
struct __kfifo *__fifo = &fifo->kfifo;
|
||||
unsigned int l, off;
|
||||
|
||||
len = min(len, kfifo_avail(fifo));
|
||||
off = __fifo->in & __fifo->mask;
|
||||
l = min(len, kfifo_size(fifo) - off);
|
||||
|
||||
memcpy_fromio(__fifo->data + off, src, l);
|
||||
memcpy_fromio(__fifo->data, src + l, len - l);
|
||||
/* Make sure data copied from SRAM is visible to all CPUs. */
|
||||
smp_mb();
|
||||
__fifo->in += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
bool avs_logging_fw(struct avs_dev *adev)
|
||||
{
|
||||
return kfifo_initialized(&adev->trace_fifo);
|
||||
}
|
||||
|
||||
void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
|
||||
{
|
||||
__kfifo_fromio(&adev->trace_fifo, src, len);
|
||||
}
|
||||
|
||||
void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len)
|
||||
{
|
||||
avs_dump_fw_log(adev, src, len);
|
||||
wake_up(&adev->trace_waitq);
|
||||
}
|
||||
|
||||
static ssize_t fw_regs_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct avs_dev *adev = file->private_data;
|
||||
char *buf;
|
||||
int ret;
|
||||
|
||||
buf = kzalloc(AVS_FW_REGS_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy_fromio(buf, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
|
||||
|
||||
ret = simple_read_from_buffer(to, count, ppos, buf, AVS_FW_REGS_SIZE);
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations fw_regs_fops = {
|
||||
.open = simple_open,
|
||||
.read = fw_regs_read,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static ssize_t debug_window_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct avs_dev *adev = file->private_data;
|
||||
size_t size;
|
||||
char *buf;
|
||||
int ret;
|
||||
|
||||
size = adev->hw_cfg.dsp_cores * AVS_WINDOW_CHUNK_SIZE;
|
||||
buf = kzalloc(size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy_fromio(buf, avs_sram_addr(adev, AVS_DEBUG_WINDOW), size);
|
||||
|
||||
ret = simple_read_from_buffer(to, count, ppos, buf, size);
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations debug_window_fops = {
|
||||
.open = simple_open,
|
||||
.read = debug_window_read,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static ssize_t probe_points_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct avs_dev *adev = file->private_data;
|
||||
struct avs_probe_point_desc *desc;
|
||||
size_t num_desc, len = 0;
|
||||
char *buf;
|
||||
int i, ret;
|
||||
|
||||
/* Prevent chaining, send and dump IPC value just once. */
|
||||
if (*ppos)
|
||||
return 0;
|
||||
|
||||
buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = avs_ipc_probe_get_points(adev, &desc, &num_desc);
|
||||
if (ret) {
|
||||
ret = AVS_IPC_RET(ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_desc; i++) {
|
||||
ret = snprintf(buf + len, PAGE_SIZE - len,
|
||||
"Id: %#010x Purpose: %d Node id: %#x\n",
|
||||
desc[i].id.value, desc[i].purpose, desc[i].node_id.val);
|
||||
if (ret < 0)
|
||||
goto free_desc;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
ret = simple_read_from_buffer(to, count, ppos, buf, len);
|
||||
free_desc:
|
||||
kfree(desc);
|
||||
exit:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t probe_points_write(struct file *file, const char __user *from, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct avs_dev *adev = file->private_data;
|
||||
struct avs_probe_point_desc *desc;
|
||||
u32 *array, num_elems;
|
||||
size_t bytes;
|
||||
int ret;
|
||||
|
||||
ret = parse_int_array_user(from, count, (int **)&array);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
num_elems = *array;
|
||||
bytes = sizeof(*array) * num_elems;
|
||||
if (bytes % sizeof(*desc)) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
desc = (struct avs_probe_point_desc *)&array[1];
|
||||
ret = avs_ipc_probe_connect_points(adev, desc, bytes / sizeof(*desc));
|
||||
if (ret)
|
||||
ret = AVS_IPC_RET(ret);
|
||||
else
|
||||
ret = count;
|
||||
exit:
|
||||
kfree(array);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations probe_points_fops = {
|
||||
.open = simple_open,
|
||||
.read = probe_points_read,
|
||||
.write = probe_points_write,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static ssize_t probe_points_disconnect_write(struct file *file, const char __user *from,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct avs_dev *adev = file->private_data;
|
||||
union avs_probe_point_id *id;
|
||||
u32 *array, num_elems;
|
||||
size_t bytes;
|
||||
int ret;
|
||||
|
||||
ret = parse_int_array_user(from, count, (int **)&array);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
num_elems = *array;
|
||||
bytes = sizeof(*array) * num_elems;
|
||||
if (bytes % sizeof(*id)) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
id = (union avs_probe_point_id *)&array[1];
|
||||
ret = avs_ipc_probe_disconnect_points(adev, id, bytes / sizeof(*id));
|
||||
if (ret)
|
||||
ret = AVS_IPC_RET(ret);
|
||||
else
|
||||
ret = count;
|
||||
exit:
|
||||
kfree(array);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations probe_points_disconnect_fops = {
|
||||
.open = simple_open,
|
||||
.write = probe_points_disconnect_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t strace_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct avs_dev *adev = file->private_data;
|
||||
struct kfifo *fifo = &adev->trace_fifo;
|
||||
unsigned int copied;
|
||||
|
||||
if (kfifo_is_empty(fifo)) {
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
prepare_to_wait(&adev->trace_waitq, &wait, TASK_INTERRUPTIBLE);
|
||||
if (!signal_pending(current))
|
||||
schedule();
|
||||
finish_wait(&adev->trace_waitq, &wait);
|
||||
}
|
||||
|
||||
if (kfifo_to_user(fifo, to, count, &copied))
|
||||
return -EFAULT;
|
||||
*ppos += copied;
|
||||
return copied;
|
||||
}
|
||||
|
||||
static int strace_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct avs_dev *adev = inode->i_private;
|
||||
int ret;
|
||||
|
||||
if (kfifo_initialized(&adev->trace_fifo))
|
||||
return -EBUSY;
|
||||
|
||||
ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
file->private_data = adev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int strace_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS);
|
||||
struct avs_dev *adev = file->private_data;
|
||||
unsigned long resource_mask;
|
||||
unsigned long flags, i;
|
||||
u32 num_cores;
|
||||
|
||||
resource_mask = adev->logged_resources;
|
||||
num_cores = adev->hw_cfg.dsp_cores;
|
||||
|
||||
spin_lock_irqsave(&adev->trace_lock, flags);
|
||||
|
||||
/* Gather any remaining logs. */
|
||||
for_each_set_bit(i, &resource_mask, num_cores) {
|
||||
msg.log.core = i;
|
||||
avs_dsp_op(adev, log_buffer_status, &msg);
|
||||
}
|
||||
|
||||
kfifo_free(&adev->trace_fifo);
|
||||
|
||||
spin_unlock_irqrestore(&adev->trace_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations strace_fops = {
|
||||
.llseek = default_llseek,
|
||||
.read = strace_read,
|
||||
.open = strace_open,
|
||||
.release = strace_release,
|
||||
};
|
||||
|
||||
#define DISABLE_TIMERS UINT_MAX
|
||||
|
||||
static int enable_logs(struct avs_dev *adev, u32 resource_mask, u32 *priorities)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Logging demands D0i0 state from DSP. */
|
||||
if (!adev->logged_resources) {
|
||||
pm_runtime_get_sync(adev->dev);
|
||||
|
||||
ret = avs_dsp_disable_d0ix(adev);
|
||||
if (ret)
|
||||
goto err_d0ix;
|
||||
}
|
||||
|
||||
ret = avs_ipc_set_system_time(adev);
|
||||
if (ret && ret != AVS_IPC_NOT_SUPPORTED) {
|
||||
ret = AVS_IPC_RET(ret);
|
||||
goto err_ipc;
|
||||
}
|
||||
|
||||
ret = avs_dsp_op(adev, enable_logs, AVS_LOG_ENABLE, adev->aging_timer_period,
|
||||
adev->fifo_full_timer_period, resource_mask, priorities);
|
||||
if (ret)
|
||||
goto err_ipc;
|
||||
|
||||
adev->logged_resources |= resource_mask;
|
||||
return 0;
|
||||
|
||||
err_ipc:
|
||||
if (!adev->logged_resources) {
|
||||
avs_dsp_enable_d0ix(adev);
|
||||
err_d0ix:
|
||||
pm_runtime_mark_last_busy(adev->dev);
|
||||
pm_runtime_put_autosuspend(adev->dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int disable_logs(struct avs_dev *adev, u32 resource_mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Check if there's anything to do. */
|
||||
if (!adev->logged_resources)
|
||||
return 0;
|
||||
|
||||
ret = avs_dsp_op(adev, enable_logs, AVS_LOG_DISABLE, DISABLE_TIMERS, DISABLE_TIMERS,
|
||||
resource_mask, NULL);
|
||||
|
||||
/*
|
||||
* If IPC fails causing recovery, logged_resources is already zero
|
||||
* so unsetting bits is still safe.
|
||||
*/
|
||||
adev->logged_resources &= ~resource_mask;
|
||||
|
||||
/* If that's the last resource, allow for D3. */
|
||||
if (!adev->logged_resources) {
|
||||
avs_dsp_enable_d0ix(adev);
|
||||
pm_runtime_mark_last_busy(adev->dev);
|
||||
pm_runtime_put_autosuspend(adev->dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t trace_control_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct avs_dev *adev = file->private_data;
|
||||
char buf[64];
|
||||
int len;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "0x%08x\n", adev->logged_resources);
|
||||
|
||||
return simple_read_from_buffer(to, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t trace_control_write(struct file *file, const char __user *from, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct avs_dev *adev = file->private_data;
|
||||
u32 *array, num_elems;
|
||||
u32 resource_mask;
|
||||
int ret;
|
||||
|
||||
ret = parse_int_array_user(from, count, (int **)&array);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
num_elems = *array;
|
||||
resource_mask = array[1];
|
||||
|
||||
/*
|
||||
* Disable if just resource mask is provided - no log priority flags.
|
||||
*
|
||||
* Enable input format: mask, prio1, .., prioN
|
||||
* Where 'N' equals number of bits set in the 'mask'.
|
||||
*/
|
||||
if (num_elems == 1) {
|
||||
ret = disable_logs(adev, resource_mask);
|
||||
} else {
|
||||
if (num_elems != (hweight_long(resource_mask) + 1)) {
|
||||
ret = -EINVAL;
|
||||
goto free_array;
|
||||
}
|
||||
|
||||
ret = enable_logs(adev, resource_mask, &array[2]);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
ret = count;
|
||||
free_array:
|
||||
kfree(array);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations trace_control_fops = {
|
||||
.llseek = default_llseek,
|
||||
.read = trace_control_read,
|
||||
.write = trace_control_write,
|
||||
.open = simple_open,
|
||||
};
|
||||
|
||||
void avs_debugfs_init(struct avs_dev *adev)
|
||||
{
|
||||
init_waitqueue_head(&adev->trace_waitq);
|
||||
spin_lock_init(&adev->trace_lock);
|
||||
|
||||
adev->debugfs_root = debugfs_create_dir("avs", snd_soc_debugfs_root);
|
||||
|
||||
/* Initialize timer periods with recommended defaults. */
|
||||
adev->aging_timer_period = 10;
|
||||
adev->fifo_full_timer_period = 10;
|
||||
|
||||
debugfs_create_file("strace", 0444, adev->debugfs_root, adev, &strace_fops);
|
||||
debugfs_create_file("trace_control", 0644, adev->debugfs_root, adev, &trace_control_fops);
|
||||
debugfs_create_file("fw_regs", 0444, adev->debugfs_root, adev, &fw_regs_fops);
|
||||
debugfs_create_file("debug_window", 0444, adev->debugfs_root, adev, &debug_window_fops);
|
||||
|
||||
debugfs_create_u32("trace_aging_period", 0644, adev->debugfs_root,
|
||||
&adev->aging_timer_period);
|
||||
debugfs_create_u32("trace_fifo_full_period", 0644, adev->debugfs_root,
|
||||
&adev->fifo_full_timer_period);
|
||||
|
||||
debugfs_create_file("probe_points", 0644, adev->debugfs_root, adev, &probe_points_fops);
|
||||
debugfs_create_file("probe_points_disconnect", 0200, adev->debugfs_root, adev,
|
||||
&probe_points_disconnect_fops);
|
||||
}
|
||||
|
||||
void avs_debugfs_exit(struct avs_dev *adev)
|
||||
{
|
||||
debugfs_remove_recursive(adev->debugfs_root);
|
||||
}
|
@ -266,7 +266,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
|
||||
break;
|
||||
|
||||
case AVS_NOTIFY_LOG_BUFFER_STATUS:
|
||||
avs_dsp_op(adev, log_buffer_status, &msg);
|
||||
avs_log_buffer_status_locked(adev, &msg);
|
||||
break;
|
||||
|
||||
case AVS_NOTIFY_EXCEPTION_CAUGHT:
|
||||
|
@ -685,6 +685,24 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
|
||||
u8 instance_id, u32 sink_id,
|
||||
const struct avs_audio_format *src_fmt,
|
||||
const struct avs_audio_format *sink_fmt)
|
||||
{
|
||||
struct avs_copier_sink_format cpr_fmt;
|
||||
|
||||
cpr_fmt.sink_id = sink_id;
|
||||
/* Firmware expects driver to resend copier's input format. */
|
||||
cpr_fmt.src_fmt = *src_fmt;
|
||||
cpr_fmt.sink_fmt = *sink_fmt;
|
||||
|
||||
return avs_ipc_set_large_config(adev, module_id, instance_id,
|
||||
AVS_COPIER_SET_SINK_FORMAT,
|
||||
(u8 *)&cpr_fmt, sizeof(cpr_fmt));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size)
|
||||
{
|
||||
return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
|
||||
@ -705,19 +723,81 @@ int avs_ipc_set_system_time(struct avs_dev *adev)
|
||||
AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time));
|
||||
}
|
||||
|
||||
int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
|
||||
u8 instance_id, u32 sink_id,
|
||||
const struct avs_audio_format *src_fmt,
|
||||
const struct avs_audio_format *sink_fmt)
|
||||
int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas)
|
||||
{
|
||||
struct avs_copier_sink_format cpr_fmt;
|
||||
size_t payload_size;
|
||||
u32 module_id;
|
||||
u8 *payload;
|
||||
int ret;
|
||||
|
||||
cpr_fmt.sink_id = sink_id;
|
||||
/* Firmware expects driver to resend copier's input format. */
|
||||
cpr_fmt.src_fmt = *src_fmt;
|
||||
cpr_fmt.sink_fmt = *sink_fmt;
|
||||
module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
|
||||
|
||||
return avs_ipc_set_large_config(adev, module_id, instance_id,
|
||||
AVS_COPIER_SET_SINK_FORMAT,
|
||||
(u8 *)&cpr_fmt, sizeof(cpr_fmt));
|
||||
ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA,
|
||||
NULL, 0, &payload, &payload_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*dmas = (struct avs_probe_dma *)payload;
|
||||
*num_dmas = payload_size / sizeof(**dmas);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas)
|
||||
{
|
||||
u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
|
||||
|
||||
return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA,
|
||||
(u8 *)dmas, array_size(sizeof(*dmas), num_dmas));
|
||||
}
|
||||
|
||||
int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids,
|
||||
size_t num_node_ids)
|
||||
{
|
||||
u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
|
||||
|
||||
return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID,
|
||||
AVS_PROBE_INJECTION_DMA_DETACH, (u8 *)node_ids,
|
||||
array_size(sizeof(*node_ids), num_node_ids));
|
||||
}
|
||||
|
||||
int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs,
|
||||
size_t *num_descs)
|
||||
{
|
||||
size_t payload_size;
|
||||
u32 module_id;
|
||||
u8 *payload;
|
||||
int ret;
|
||||
|
||||
module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
|
||||
|
||||
ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_POINTS, NULL,
|
||||
0, &payload, &payload_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*descs = (struct avs_probe_point_desc *)payload;
|
||||
*num_descs = payload_size / sizeof(**descs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs,
|
||||
size_t num_descs)
|
||||
{
|
||||
u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
|
||||
|
||||
return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_POINTS,
|
||||
(u8 *)descs, array_size(sizeof(*descs), num_descs));
|
||||
}
|
||||
|
||||
int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids,
|
||||
size_t num_ids)
|
||||
{
|
||||
u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
|
||||
|
||||
return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID,
|
||||
AVS_PROBE_POINTS_DISCONNECT, (u8 *)ids,
|
||||
array_size(sizeof(*ids), num_ids));
|
||||
}
|
||||
#endif
|
||||
|
@ -802,4 +802,57 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
|
||||
const struct avs_audio_format *src_fmt,
|
||||
const struct avs_audio_format *sink_fmt);
|
||||
|
||||
#define AVS_PROBE_INST_ID 0
|
||||
|
||||
enum avs_probe_runtime_param {
|
||||
AVS_PROBE_INJECTION_DMA = 1,
|
||||
AVS_PROBE_INJECTION_DMA_DETACH,
|
||||
AVS_PROBE_POINTS,
|
||||
AVS_PROBE_POINTS_DISCONNECT,
|
||||
};
|
||||
|
||||
struct avs_probe_dma {
|
||||
union avs_connector_node_id node_id;
|
||||
u32 dma_buffer_size;
|
||||
} __packed;
|
||||
|
||||
enum avs_probe_type {
|
||||
AVS_PROBE_TYPE_INPUT = 0,
|
||||
AVS_PROBE_TYPE_OUTPUT,
|
||||
AVS_PROBE_TYPE_INTERNAL
|
||||
};
|
||||
|
||||
union avs_probe_point_id {
|
||||
u32 value;
|
||||
struct {
|
||||
u32 module_id:16;
|
||||
u32 instance_id:8;
|
||||
u32 type:2;
|
||||
u32 index:6;
|
||||
} id;
|
||||
} __packed;
|
||||
|
||||
enum avs_connection_purpose {
|
||||
AVS_CONNECTION_PURPOSE_EXTRACT = 0,
|
||||
AVS_CONNECTION_PURPOSE_INJECT,
|
||||
AVS_CONNECTION_PURPOSE_INJECT_REEXTRACT,
|
||||
};
|
||||
|
||||
struct avs_probe_point_desc {
|
||||
union avs_probe_point_id id;
|
||||
u32 purpose;
|
||||
union avs_connector_node_id node_id;
|
||||
} __packed;
|
||||
|
||||
int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas);
|
||||
int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas);
|
||||
int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids,
|
||||
size_t num_node_ids);
|
||||
int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs,
|
||||
size_t *num_descs);
|
||||
int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs,
|
||||
size_t num_descs);
|
||||
int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids,
|
||||
size_t num_ids);
|
||||
|
||||
#endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
|
||||
|
@ -1126,9 +1126,9 @@ static const struct snd_soc_component_driver avs_component_driver = {
|
||||
.topology_name_prefix = "intel/avs",
|
||||
};
|
||||
|
||||
static int avs_soc_component_register(struct device *dev, const char *name,
|
||||
const struct snd_soc_component_driver *drv,
|
||||
struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
|
||||
int avs_soc_component_register(struct device *dev, const char *name,
|
||||
const struct snd_soc_component_driver *drv,
|
||||
struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
|
||||
{
|
||||
struct avs_soc_component *acomp;
|
||||
int ret;
|
||||
|
313
sound/soc/intel/avs/probes.c
Normal file
313
sound/soc/intel/avs/probes.c
Normal file
@ -0,0 +1,313 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
|
||||
//
|
||||
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
|
||||
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
|
||||
//
|
||||
|
||||
#include <sound/compress_driver.h>
|
||||
#include <sound/hdaudio_ext.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/soc.h>
|
||||
#include "avs.h"
|
||||
#include "messages.h"
|
||||
|
||||
static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id node_id,
|
||||
size_t buffer_size)
|
||||
{
|
||||
struct avs_probe_cfg cfg = {{0}};
|
||||
struct avs_module_entry mentry;
|
||||
u16 dummy;
|
||||
|
||||
avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry);
|
||||
|
||||
/*
|
||||
* Probe module uses no cycles, audio data format and input and output
|
||||
* frame sizes are unused. It is also not owned by any pipeline.
|
||||
*/
|
||||
cfg.base.ibs = 1;
|
||||
/* BSS module descriptor is always segment of index=2. */
|
||||
cfg.base.is_pages = mentry.segments[2].flags.length;
|
||||
cfg.gtw_cfg.node_id = node_id;
|
||||
cfg.gtw_cfg.dma_buffer_size = buffer_size;
|
||||
|
||||
return avs_dsp_init_module(adev, mentry.module_id, INVALID_PIPELINE_ID, 0, 0, &cfg,
|
||||
sizeof(cfg), &dummy);
|
||||
}
|
||||
|
||||
static void avs_dsp_delete_probe(struct avs_dev *adev)
|
||||
{
|
||||
struct avs_module_entry mentry;
|
||||
|
||||
avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry);
|
||||
|
||||
/* There is only ever one probe module instance. */
|
||||
avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0);
|
||||
}
|
||||
|
||||
static inline struct hdac_ext_stream *avs_compr_get_host_stream(struct snd_compr_stream *cstream)
|
||||
{
|
||||
return cstream->runtime->private_data;
|
||||
}
|
||||
|
||||
static int avs_probe_compr_open(struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct avs_dev *adev = to_avs_dev(dai->dev);
|
||||
struct hdac_bus *bus = &adev->base.core;
|
||||
struct hdac_ext_stream *host_stream;
|
||||
|
||||
if (adev->extractor) {
|
||||
dev_err(dai->dev, "Cannot open more than one extractor stream\n");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
host_stream = snd_hdac_ext_cstream_assign(bus, cstream);
|
||||
if (!host_stream) {
|
||||
dev_err(dai->dev, "Failed to assign HDAudio stream for extraction\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
adev->extractor = host_stream;
|
||||
hdac_stream(host_stream)->curr_pos = 0;
|
||||
cstream->runtime->private_data = host_stream;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avs_probe_compr_free(struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
|
||||
struct avs_dev *adev = to_avs_dev(dai->dev);
|
||||
struct avs_probe_point_desc *desc;
|
||||
/* Extractor node identifier. */
|
||||
unsigned int vindex = INVALID_NODE_ID.vindex;
|
||||
size_t num_desc;
|
||||
int i, ret;
|
||||
|
||||
/* Disconnect all probe points. */
|
||||
ret = avs_ipc_probe_get_points(adev, &desc, &num_desc);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "get probe points failed: %d\n", ret);
|
||||
ret = AVS_IPC_RET(ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_desc; i++)
|
||||
if (desc[i].node_id.vindex == vindex)
|
||||
avs_ipc_probe_disconnect_points(adev, &desc[i].id, 1);
|
||||
kfree(desc);
|
||||
|
||||
exit:
|
||||
if (adev->num_probe_streams) {
|
||||
adev->num_probe_streams--;
|
||||
if (!adev->num_probe_streams) {
|
||||
avs_dsp_delete_probe(adev);
|
||||
avs_dsp_enable_d0ix(adev);
|
||||
}
|
||||
}
|
||||
|
||||
snd_hdac_stream_cleanup(hdac_stream(host_stream));
|
||||
hdac_stream(host_stream)->prepared = 0;
|
||||
snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST);
|
||||
|
||||
snd_compr_free_pages(cstream);
|
||||
adev->extractor = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int avs_probe_compr_set_params(struct snd_compr_stream *cstream,
|
||||
struct snd_compr_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
|
||||
struct snd_compr_runtime *rtd = cstream->runtime;
|
||||
struct avs_dev *adev = to_avs_dev(dai->dev);
|
||||
/* compr params do not store bit depth, default to S32_LE. */
|
||||
snd_pcm_format_t format = SNDRV_PCM_FORMAT_S32_LE;
|
||||
unsigned int format_val;
|
||||
int bps, ret;
|
||||
|
||||
hdac_stream(host_stream)->bufsize = 0;
|
||||
hdac_stream(host_stream)->period_bytes = 0;
|
||||
hdac_stream(host_stream)->format_val = 0;
|
||||
cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
|
||||
cstream->dma_buffer.dev.dev = adev->dev;
|
||||
|
||||
ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
bps = snd_pcm_format_physical_width(format);
|
||||
if (bps < 0)
|
||||
return bps;
|
||||
format_val = snd_hdac_calc_stream_format(params->codec.sample_rate, params->codec.ch_out,
|
||||
format, bps, 0);
|
||||
ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = snd_hdac_stream_setup(hdac_stream(host_stream));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
hdac_stream(host_stream)->prepared = 1;
|
||||
|
||||
if (!adev->num_probe_streams) {
|
||||
union avs_connector_node_id node_id;
|
||||
|
||||
/* D0ix not allowed during probing. */
|
||||
ret = avs_dsp_disable_d0ix(adev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
node_id.vindex = hdac_stream(host_stream)->stream_tag - 1;
|
||||
node_id.dma_type = AVS_DMA_HDA_HOST_INPUT;
|
||||
|
||||
ret = avs_dsp_init_probe(adev, node_id, rtd->dma_bytes);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "probe init failed: %d\n", ret);
|
||||
avs_dsp_enable_d0ix(adev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
adev->num_probe_streams++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
|
||||
struct avs_dev *adev = to_avs_dev(dai->dev);
|
||||
struct hdac_bus *bus = &adev->base.core;
|
||||
unsigned long cookie;
|
||||
|
||||
if (!hdac_stream(host_stream)->prepared)
|
||||
return -EPIPE;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
spin_lock_irqsave(&bus->reg_lock, cookie);
|
||||
snd_hdac_stream_start(hdac_stream(host_stream), true);
|
||||
spin_unlock_irqrestore(&bus->reg_lock, cookie);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
spin_lock_irqsave(&bus->reg_lock, cookie);
|
||||
snd_hdac_stream_stop(hdac_stream(host_stream));
|
||||
spin_unlock_irqrestore(&bus->reg_lock, cookie);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avs_probe_compr_pointer(struct snd_compr_stream *cstream,
|
||||
struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
|
||||
struct snd_soc_pcm_stream *pstream;
|
||||
|
||||
pstream = &dai->driver->capture;
|
||||
tstamp->copied_total = hdac_stream(host_stream)->curr_pos;
|
||||
tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr_stream *cstream,
|
||||
char __user *buf, size_t count)
|
||||
{
|
||||
struct snd_compr_runtime *rtd = cstream->runtime;
|
||||
unsigned int offset, n;
|
||||
void *ptr;
|
||||
int ret;
|
||||
|
||||
if (count > rtd->buffer_size)
|
||||
count = rtd->buffer_size;
|
||||
|
||||
div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
|
||||
ptr = rtd->dma_area + offset;
|
||||
n = rtd->buffer_size - offset;
|
||||
|
||||
if (count < n) {
|
||||
ret = copy_to_user(buf, ptr, count);
|
||||
} else {
|
||||
ret = copy_to_user(buf, ptr, n);
|
||||
ret += copy_to_user(buf + n, rtd->dma_area, count - n);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return count - ret;
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct snd_soc_cdai_ops avs_probe_dai_ops = {
|
||||
.startup = avs_probe_compr_open,
|
||||
.shutdown = avs_probe_compr_free,
|
||||
.set_params = avs_probe_compr_set_params,
|
||||
.trigger = avs_probe_compr_trigger,
|
||||
.pointer = avs_probe_compr_pointer,
|
||||
};
|
||||
|
||||
static const struct snd_compress_ops avs_probe_compress_ops = {
|
||||
.copy = avs_probe_compr_copy,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver probe_cpu_dais[] = {
|
||||
{
|
||||
.name = "Probe Extraction CPU DAI",
|
||||
.compress_new = snd_soc_new_compress,
|
||||
.cops = &avs_probe_dai_ops,
|
||||
.capture = {
|
||||
.stream_name = "Probe Extraction",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int avs_probe_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct avs_soc_component *acomp = to_avs_soc_component(component);
|
||||
struct avs_dev *adev = to_avs_dev(component->dev);
|
||||
|
||||
mutex_lock(&adev->comp_list_mutex);
|
||||
list_add_tail(&acomp->node, &adev->comp_list);
|
||||
mutex_unlock(&adev->comp_list_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void avs_probe_component_remove(struct snd_soc_component *component)
|
||||
{
|
||||
struct avs_soc_component *acomp = to_avs_soc_component(component);
|
||||
struct avs_dev *adev = to_avs_dev(component->dev);
|
||||
|
||||
mutex_lock(&adev->comp_list_mutex);
|
||||
list_del(&acomp->node);
|
||||
mutex_unlock(&adev->comp_list_mutex);
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver avs_probe_component_driver = {
|
||||
.name = "avs-probe-compr",
|
||||
.probe = avs_probe_component_probe,
|
||||
.remove = avs_probe_component_remove,
|
||||
.compress_ops = &avs_probe_compress_ops,
|
||||
.module_get_upon_open = 1, /* increment refcount when a stream is opened */
|
||||
};
|
||||
|
||||
int avs_probe_platform_register(struct avs_dev *adev, const char *name)
|
||||
{
|
||||
return avs_soc_component_register(adev->dev, name, &avs_probe_component_driver,
|
||||
probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
|
||||
}
|
@ -59,7 +59,8 @@
|
||||
#define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0)
|
||||
#define AVS_FW_REG_ERROR_CODE(adev) (AVS_FW_REG_BASE(adev) + 0x4)
|
||||
|
||||
#define AVS_FW_REGS_SIZE PAGE_SIZE
|
||||
#define AVS_WINDOW_CHUNK_SIZE PAGE_SIZE
|
||||
#define AVS_FW_REGS_SIZE AVS_WINDOW_CHUNK_SIZE
|
||||
#define AVS_FW_REGS_WINDOW 0
|
||||
/* DSP -> HOST communication window */
|
||||
#define AVS_UPLINK_WINDOW AVS_FW_REGS_WINDOW
|
||||
|
@ -12,8 +12,9 @@
|
||||
#include "avs.h"
|
||||
#include "messages.h"
|
||||
|
||||
static int skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
|
||||
u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
|
||||
static int __maybe_unused
|
||||
skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
|
||||
u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
|
||||
{
|
||||
struct skl_log_state_info *info;
|
||||
u32 size, num_cores = adev->hw_cfg.dsp_cores;
|
||||
@ -55,15 +56,11 @@ int skl_log_buffer_offset(struct avs_dev *adev, u32 core)
|
||||
static int
|
||||
skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
|
||||
{
|
||||
unsigned long flags;
|
||||
void __iomem *buf;
|
||||
u16 size, write, offset;
|
||||
|
||||
spin_lock_irqsave(&adev->dbg.trace_lock, flags);
|
||||
if (!kfifo_initialized(&adev->dbg.trace_fifo)) {
|
||||
spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
|
||||
if (!avs_logging_fw(adev))
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = avs_log_buffer_size(adev) / 2;
|
||||
write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core));
|
||||
@ -72,9 +69,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
|
||||
|
||||
/* Address is guaranteed to exist in SRAM2. */
|
||||
buf = avs_log_buffer_addr(adev, msg->log.core) + offset;
|
||||
__kfifo_fromio_locked(&adev->dbg.trace_fifo, buf, size, &adev->dbg.fifo_lock);
|
||||
wake_up(&adev->dbg.trace_waitq);
|
||||
spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
|
||||
avs_dump_fw_log_wakeup(adev, buf, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -116,10 +111,10 @@ const struct avs_dsp_ops skl_dsp_ops = {
|
||||
.load_basefw = avs_cldma_load_basefw,
|
||||
.load_lib = avs_cldma_load_library,
|
||||
.transfer_mods = avs_cldma_transfer_modules,
|
||||
.enable_logs = skl_enable_logs,
|
||||
.log_buffer_offset = skl_log_buffer_offset,
|
||||
.log_buffer_status = skl_log_buffer_status,
|
||||
.coredump = skl_coredump,
|
||||
.d0ix_toggle = skl_d0ix_toggle,
|
||||
.set_d0ix = skl_set_d0ix,
|
||||
AVS_SET_ENABLE_LOGS_OP(skl)
|
||||
};
|
||||
|
@ -300,25 +300,3 @@ void avs_release_firmwares(struct avs_dev *adev)
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct __kfifo *__fifo = &fifo->kfifo;
|
||||
unsigned long flags;
|
||||
unsigned int l, off;
|
||||
|
||||
spin_lock_irqsave(lock, flags);
|
||||
len = min(len, kfifo_avail(fifo));
|
||||
off = __fifo->in & __fifo->mask;
|
||||
l = min(len, kfifo_size(fifo) - off);
|
||||
|
||||
memcpy_fromio(__fifo->data + off, src, l);
|
||||
memcpy_fromio(__fifo->data, src + l, len - l);
|
||||
/* Make sure data copied from SRAM is visible to all CPUs. */
|
||||
smp_mb();
|
||||
__fifo->in += len;
|
||||
spin_unlock_irqrestore(lock, flags);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
@ -37,8 +37,7 @@ snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o
|
||||
snd-soc-ehl-rt5660-objs := ehl_rt5660.o
|
||||
snd-soc-sof-ssp-amp-objs := sof_ssp_amp.o
|
||||
snd-soc-sof-sdw-objs += sof_sdw.o \
|
||||
sof_sdw_max98373.o sof_sdw_rt1308.o \
|
||||
sof_sdw_rt1316.o sof_sdw_rt1318.o \
|
||||
sof_sdw_max98373.o sof_sdw_rt_amp.o \
|
||||
sof_sdw_rt5682.o sof_sdw_rt700.o \
|
||||
sof_sdw_rt711.o sof_sdw_rt711_sdca.o \
|
||||
sof_sdw_rt715.o sof_sdw_rt715_sdca.o \
|
||||
|
@ -570,6 +570,21 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
|
||||
BYT_RT5640_SSP0_AIF1 |
|
||||
BYT_RT5640_MCLK_EN),
|
||||
},
|
||||
{
|
||||
/* Advantech MICA-071 */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"),
|
||||
},
|
||||
/* OVCD Th = 1500uA to reliable detect head-phones vs -set */
|
||||
.driver_data = (void *)(BYT_RT5640_IN3_MAP |
|
||||
BYT_RT5640_JD_SRC_JD2_IN4N |
|
||||
BYT_RT5640_OVCD_TH_1500UA |
|
||||
BYT_RT5640_OVCD_SF_0P75 |
|
||||
BYT_RT5640_MONO_SPEAKER |
|
||||
BYT_RT5640_DIFF_MIC |
|
||||
BYT_RT5640_MCLK_EN),
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"),
|
||||
@ -796,6 +811,16 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
|
||||
BYT_RT5640_SSP0_AIF1 |
|
||||
BYT_RT5640_MCLK_EN),
|
||||
},
|
||||
{ /* HP Stream 8 */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Stream 8 Tablet"),
|
||||
},
|
||||
.driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
|
||||
BYT_RT5640_JD_NOT_INV |
|
||||
BYT_RT5640_SSP0_AIF1 |
|
||||
BYT_RT5640_MCLK_EN),
|
||||
},
|
||||
{ /* I.T.Works TW891 */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
|
||||
|
@ -783,7 +783,7 @@ static int sof_es8336_remove(struct platform_device *pdev)
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
|
||||
|
||||
cancel_delayed_work(&priv->pcm_pop_work);
|
||||
cancel_delayed_work_sync(&priv->pcm_pop_work);
|
||||
gpiod_put(priv->gpio_speakers);
|
||||
device_remove_software_node(priv->codec_dev);
|
||||
put_device(priv->codec_dev);
|
||||
|
@ -267,7 +267,8 @@ static int rt1015_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai_link *dai_link = rtd->dai_link;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int i, clk_freq, ret;
|
||||
int i, clk_freq;
|
||||
int ret = 0;
|
||||
|
||||
clk_freq = sof_dai_get_bclk(rtd);
|
||||
|
||||
|
@ -1104,6 +1104,12 @@ static const struct platform_device_id board_ids[] = {
|
||||
SOF_RT5682_SSP_AMP(1) |
|
||||
SOF_RT5682_NUM_HDMIDEV(4)),
|
||||
},
|
||||
{
|
||||
.name = "jsl_rt5682",
|
||||
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
|
||||
SOF_RT5682_MCLK_24MHZ |
|
||||
SOF_RT5682_SSP_CODEC(0)),
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, board_ids);
|
||||
|
@ -550,23 +550,23 @@ static struct sof_sdw_codec_info codec_info_list[] = {
|
||||
.direction = {true, false},
|
||||
.dai_name = "rt1308-aif",
|
||||
.ops = &sof_sdw_rt1308_i2s_ops,
|
||||
.init = sof_sdw_rt1308_init,
|
||||
.exit = sof_sdw_rt1308_exit,
|
||||
.init = sof_sdw_rt_amp_init,
|
||||
.exit = sof_sdw_rt_amp_exit,
|
||||
.codec_type = SOF_SDW_CODEC_TYPE_AMP,
|
||||
},
|
||||
{
|
||||
.part_id = 0x1316,
|
||||
.direction = {true, true},
|
||||
.dai_name = "rt1316-aif",
|
||||
.init = sof_sdw_rt1316_init,
|
||||
.exit = sof_sdw_rt1316_exit,
|
||||
.init = sof_sdw_rt_amp_init,
|
||||
.exit = sof_sdw_rt_amp_exit,
|
||||
.codec_type = SOF_SDW_CODEC_TYPE_AMP,
|
||||
},
|
||||
{
|
||||
.part_id = 0x1318,
|
||||
.direction = {true, true},
|
||||
.dai_name = "rt1318-aif",
|
||||
.init = sof_sdw_rt1318_init,
|
||||
.init = sof_sdw_rt_amp_init,
|
||||
.codec_type = SOF_SDW_CODEC_TYPE_AMP,
|
||||
},
|
||||
{
|
||||
|
@ -125,30 +125,18 @@ int sof_sdw_rt700_init(struct snd_soc_card *card,
|
||||
struct sof_sdw_codec_info *info,
|
||||
bool playback);
|
||||
|
||||
/* RT1308 support */
|
||||
/* RT1308 I2S support */
|
||||
extern struct snd_soc_ops sof_sdw_rt1308_i2s_ops;
|
||||
|
||||
int sof_sdw_rt1308_init(struct snd_soc_card *card,
|
||||
/* generic amp support */
|
||||
int sof_sdw_rt_amp_init(struct snd_soc_card *card,
|
||||
const struct snd_soc_acpi_link_adr *link,
|
||||
struct snd_soc_dai_link *dai_links,
|
||||
struct sof_sdw_codec_info *info,
|
||||
bool playback);
|
||||
int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
|
||||
int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
|
||||
|
||||
/* RT1316 support */
|
||||
int sof_sdw_rt1316_init(struct snd_soc_card *card,
|
||||
const struct snd_soc_acpi_link_adr *link,
|
||||
struct snd_soc_dai_link *dai_links,
|
||||
struct sof_sdw_codec_info *info,
|
||||
bool playback);
|
||||
int sof_sdw_rt1316_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
|
||||
|
||||
/* RT1318 support */
|
||||
int sof_sdw_rt1318_init(struct snd_soc_card *card,
|
||||
const struct snd_soc_acpi_link_adr *link,
|
||||
struct snd_soc_dai_link *dai_links,
|
||||
struct sof_sdw_codec_info *info,
|
||||
bool playback);
|
||||
|
||||
/* RT715 support */
|
||||
int sof_sdw_rt715_init(struct snd_soc_card *card,
|
||||
|
@ -1,239 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// Copyright (c) 2020 Intel Corporation
|
||||
|
||||
/*
|
||||
* sof_sdw_rt1316 - Helpers to handle RT1316 from generic machine driver
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-acpi.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_type.h>
|
||||
#include <linux/dmi.h>
|
||||
#include "sof_sdw_common.h"
|
||||
#include "sof_sdw_amp_coeff_tables.h"
|
||||
|
||||
struct rt1316_platform_data {
|
||||
const unsigned char *bq_params;
|
||||
const unsigned int bq_params_cnt;
|
||||
};
|
||||
|
||||
static const struct rt1316_platform_data dell_0b00_platform_data = {
|
||||
.bq_params = dell_0b00_bq_params,
|
||||
.bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params),
|
||||
};
|
||||
|
||||
static const struct dmi_system_id dmi_platform_data[] = {
|
||||
/* AlderLake devices */
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
|
||||
},
|
||||
.driver_data = (void *)&dell_0b00_platform_data,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
|
||||
},
|
||||
.driver_data = (void *)&dell_0b00_platform_data,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")
|
||||
},
|
||||
.driver_data = (void *)&dell_0b00_platform_data,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")
|
||||
},
|
||||
.driver_data = (void *)&dell_0b00_platform_data,
|
||||
},
|
||||
};
|
||||
|
||||
static int rt1316_add_device_props(struct device *sdw_dev)
|
||||
{
|
||||
struct property_entry props[3] = {};
|
||||
struct fwnode_handle *fwnode;
|
||||
const struct dmi_system_id *dmi_data;
|
||||
const struct rt1316_platform_data *pdata;
|
||||
unsigned char params[RT1316_MAX_BQ_REG];
|
||||
int ret;
|
||||
|
||||
dmi_data = dmi_first_match(dmi_platform_data);
|
||||
if (!dmi_data)
|
||||
return 0;
|
||||
|
||||
pdata = dmi_data->driver_data;
|
||||
memcpy(¶ms, pdata->bq_params, sizeof(unsigned char) * pdata->bq_params_cnt);
|
||||
|
||||
props[0] = PROPERTY_ENTRY_U8_ARRAY("realtek,bq-params", params);
|
||||
props[1] = PROPERTY_ENTRY_U32("realtek,bq-params-cnt", pdata->bq_params_cnt);
|
||||
|
||||
fwnode = fwnode_create_software_node(props, NULL);
|
||||
if (IS_ERR(fwnode))
|
||||
return PTR_ERR(fwnode);
|
||||
|
||||
ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
|
||||
|
||||
fwnode_handle_put(fwnode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget rt1316_widgets[] = {
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
};
|
||||
|
||||
/*
|
||||
* dapm routes for rt1316 will be registered dynamically according
|
||||
* to the number of rt1316 used. The first two entries will be registered
|
||||
* for one codec case, and the last two entries are also registered
|
||||
* if two 1316s are used.
|
||||
*/
|
||||
static const struct snd_soc_dapm_route rt1316_map[] = {
|
||||
{ "Speaker", NULL, "rt1316-1 SPOL" },
|
||||
{ "Speaker", NULL, "rt1316-1 SPOR" },
|
||||
{ "Speaker", NULL, "rt1316-2 SPOL" },
|
||||
{ "Speaker", NULL, "rt1316-2 SPOR" },
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rt1316_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Speaker"),
|
||||
};
|
||||
|
||||
static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
int ret;
|
||||
|
||||
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
|
||||
"%s spk:rt1316",
|
||||
card->components);
|
||||
if (!card->components)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = snd_soc_add_card_controls(card, rt1316_controls,
|
||||
ARRAY_SIZE(rt1316_controls));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "rt1316 controls addition failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_new_controls(&card->dapm, rt1316_widgets,
|
||||
ARRAY_SIZE(rt1316_widgets));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "rt1316 widgets addition failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, rt1316_map, 2);
|
||||
if (ret)
|
||||
dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int second_spk_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, rt1316_map + 2, 2);
|
||||
if (ret)
|
||||
dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int all_spk_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = first_spk_init(rtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return second_spk_init(rtd);
|
||||
}
|
||||
|
||||
int sof_sdw_rt1316_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
|
||||
{
|
||||
struct mc_private *ctx = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (ctx->amp_dev1) {
|
||||
device_remove_software_node(ctx->amp_dev1);
|
||||
put_device(ctx->amp_dev1);
|
||||
}
|
||||
|
||||
if (ctx->amp_dev2) {
|
||||
device_remove_software_node(ctx->amp_dev2);
|
||||
put_device(ctx->amp_dev2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sof_sdw_rt1316_init(struct snd_soc_card *card,
|
||||
const struct snd_soc_acpi_link_adr *link,
|
||||
struct snd_soc_dai_link *dai_links,
|
||||
struct sof_sdw_codec_info *info,
|
||||
bool playback)
|
||||
{
|
||||
struct mc_private *ctx = snd_soc_card_get_drvdata(card);
|
||||
struct device *sdw_dev1, *sdw_dev2;
|
||||
int ret;
|
||||
|
||||
/* Count amp number and do init on playback link only. */
|
||||
if (!playback)
|
||||
return 0;
|
||||
|
||||
info->amp_num++;
|
||||
if (info->amp_num == 1)
|
||||
dai_links->init = first_spk_init;
|
||||
|
||||
if (info->amp_num == 2) {
|
||||
sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
|
||||
if (!sdw_dev1)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = rt1316_add_device_props(sdw_dev1);
|
||||
if (ret < 0) {
|
||||
put_device(sdw_dev1);
|
||||
return ret;
|
||||
}
|
||||
ctx->amp_dev1 = sdw_dev1;
|
||||
|
||||
sdw_dev2 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[1].name);
|
||||
if (!sdw_dev2)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = rt1316_add_device_props(sdw_dev2);
|
||||
if (ret < 0) {
|
||||
put_device(sdw_dev2);
|
||||
return ret;
|
||||
}
|
||||
ctx->amp_dev2 = sdw_dev2;
|
||||
|
||||
/*
|
||||
* if two 1316s are in one dai link, the init function
|
||||
* in this dai link will be first set for the first speaker,
|
||||
* and it should be reset to initialize all speakers when
|
||||
* the second speaker is found.
|
||||
*/
|
||||
if (dai_links->init)
|
||||
dai_links->init = all_spk_init;
|
||||
else
|
||||
dai_links->init = second_spk_init;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// Copyright (c) 2022 Intel Corporation
|
||||
|
||||
/*
|
||||
* sof_sdw_rt1318 - Helpers to handle RT1318 from generic machine driver
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-acpi.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include "sof_sdw_common.h"
|
||||
|
||||
static const struct snd_soc_dapm_widget rt1318_widgets[] = {
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
};
|
||||
|
||||
/*
|
||||
* dapm routes for rt1318 will be registered dynamically according
|
||||
* to the number of rt1318 used. The first two entries will be registered
|
||||
* for one codec case, and the last two entries are also registered
|
||||
* if two 1318s are used.
|
||||
*/
|
||||
static const struct snd_soc_dapm_route rt1318_map[] = {
|
||||
{ "Speaker", NULL, "rt1318-1 SPOL" },
|
||||
{ "Speaker", NULL, "rt1318-1 SPOR" },
|
||||
{ "Speaker", NULL, "rt1318-2 SPOL" },
|
||||
{ "Speaker", NULL, "rt1318-2 SPOR" },
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rt1318_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Speaker"),
|
||||
};
|
||||
|
||||
static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
int ret;
|
||||
|
||||
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
|
||||
"%s spk:rt1318",
|
||||
card->components);
|
||||
if (!card->components)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = snd_soc_add_card_controls(card, rt1318_controls,
|
||||
ARRAY_SIZE(rt1318_controls));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "rt1318 controls addition failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_new_controls(&card->dapm, rt1318_widgets,
|
||||
ARRAY_SIZE(rt1318_widgets));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "rt1318 widgets addition failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, rt1318_map, 2);
|
||||
if (ret)
|
||||
dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int second_spk_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, rt1318_map + 2, 2);
|
||||
if (ret)
|
||||
dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int all_spk_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = first_spk_init(rtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return second_spk_init(rtd);
|
||||
}
|
||||
|
||||
int sof_sdw_rt1318_init(struct snd_soc_card *card,
|
||||
const struct snd_soc_acpi_link_adr *link,
|
||||
struct snd_soc_dai_link *dai_links,
|
||||
struct sof_sdw_codec_info *info,
|
||||
bool playback)
|
||||
{
|
||||
/* Count amp number and do init on playback link only. */
|
||||
if (!playback)
|
||||
return 0;
|
||||
|
||||
info->amp_num++;
|
||||
if (info->amp_num == 1)
|
||||
dai_links->init = first_spk_init;
|
||||
|
||||
if (info->amp_num == 2) {
|
||||
/*
|
||||
* if two 1318s are in one dai link, the init function
|
||||
* in this dai link will be first set for the first speaker,
|
||||
* and it should be reset to initialize all speakers when
|
||||
* the second speaker is found.
|
||||
*/
|
||||
if (dai_links->init)
|
||||
dai_links->init = all_spk_init;
|
||||
else
|
||||
dai_links->init = second_spk_init;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// Copyright (c) 2020 Intel Corporation
|
||||
// Copyright (c) 2022 Intel Corporation
|
||||
|
||||
/*
|
||||
* sof_sdw_rt1308 - Helpers to handle RT1308 from generic machine driver
|
||||
* sof_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
@ -18,16 +18,26 @@
|
||||
#include "sof_sdw_amp_coeff_tables.h"
|
||||
#include "../../codecs/rt1308.h"
|
||||
|
||||
struct rt1308_platform_data {
|
||||
#define CODEC_NAME_SIZE 7
|
||||
|
||||
/* choose a larger value to resolve compatibility issues */
|
||||
#define RT_AMP_MAX_BQ_REG RT1316_MAX_BQ_REG
|
||||
|
||||
struct rt_amp_platform_data {
|
||||
const unsigned char *bq_params;
|
||||
const unsigned int bq_params_cnt;
|
||||
};
|
||||
|
||||
static const struct rt1308_platform_data dell_0a5d_platform_data = {
|
||||
static const struct rt_amp_platform_data dell_0a5d_platform_data = {
|
||||
.bq_params = dell_0a5d_bq_params,
|
||||
.bq_params_cnt = ARRAY_SIZE(dell_0a5d_bq_params),
|
||||
};
|
||||
|
||||
static const struct rt_amp_platform_data dell_0b00_platform_data = {
|
||||
.bq_params = dell_0b00_bq_params,
|
||||
.bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params),
|
||||
};
|
||||
|
||||
static const struct dmi_system_id dmi_platform_data[] = {
|
||||
/* CometLake devices */
|
||||
{
|
||||
@ -59,15 +69,45 @@ static const struct dmi_system_id dmi_platform_data[] = {
|
||||
},
|
||||
.driver_data = (void *)&dell_0a5d_platform_data,
|
||||
},
|
||||
/* AlderLake devices */
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
|
||||
},
|
||||
.driver_data = (void *)&dell_0b00_platform_data,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
|
||||
},
|
||||
.driver_data = (void *)&dell_0b00_platform_data,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")
|
||||
},
|
||||
.driver_data = (void *)&dell_0b00_platform_data,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")
|
||||
},
|
||||
.driver_data = (void *)&dell_0b00_platform_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int rt1308_add_device_props(struct device *sdw_dev)
|
||||
static int rt_amp_add_device_props(struct device *sdw_dev)
|
||||
{
|
||||
struct property_entry props[3] = {};
|
||||
struct fwnode_handle *fwnode;
|
||||
const struct dmi_system_id *dmi_data;
|
||||
const struct rt1308_platform_data *pdata;
|
||||
unsigned char params[RT1308_MAX_BQ_REG];
|
||||
const struct rt_amp_platform_data *pdata;
|
||||
unsigned char params[RT_AMP_MAX_BQ_REG];
|
||||
int ret;
|
||||
|
||||
dmi_data = dmi_first_match(dmi_platform_data);
|
||||
@ -91,15 +131,19 @@ static int rt1308_add_device_props(struct device *sdw_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget rt1308_widgets[] = {
|
||||
static const struct snd_kcontrol_new rt_amp_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Speaker"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget rt_amp_widgets[] = {
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
};
|
||||
|
||||
/*
|
||||
* dapm routes for rt1308 will be registered dynamically according
|
||||
* to the number of rt1308 used. The first two entries will be registered
|
||||
* for one codec case, and the last two entries are also registered
|
||||
* if two 1308s are used.
|
||||
* dapm routes for rt1308/rt1316/rt1318 will be registered dynamically
|
||||
* according to the number of rt1308/rt1316/rt1318 used. The first two
|
||||
* entries will be registered for one codec case, and the last two entries
|
||||
* are also registered if two 1308s/1316s/1318s are used.
|
||||
*/
|
||||
static const struct snd_soc_dapm_route rt1308_map[] = {
|
||||
{ "Speaker", NULL, "rt1308-1 SPOL" },
|
||||
@ -108,36 +152,69 @@ static const struct snd_soc_dapm_route rt1308_map[] = {
|
||||
{ "Speaker", NULL, "rt1308-2 SPOR" },
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rt1308_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Speaker"),
|
||||
static const struct snd_soc_dapm_route rt1316_map[] = {
|
||||
{ "Speaker", NULL, "rt1316-1 SPOL" },
|
||||
{ "Speaker", NULL, "rt1316-1 SPOR" },
|
||||
{ "Speaker", NULL, "rt1316-2 SPOL" },
|
||||
{ "Speaker", NULL, "rt1316-2 SPOR" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt1318_map[] = {
|
||||
{ "Speaker", NULL, "rt1318-1 SPOL" },
|
||||
{ "Speaker", NULL, "rt1318-1 SPOR" },
|
||||
{ "Speaker", NULL, "rt1318-2 SPOL" },
|
||||
{ "Speaker", NULL, "rt1318-2 SPOR" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_pcm_runtime *rtd,
|
||||
char *codec_name)
|
||||
{
|
||||
const char *dai_name;
|
||||
|
||||
dai_name = rtd->dai_link->codecs->dai_name;
|
||||
|
||||
/* get the codec name */
|
||||
snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name);
|
||||
|
||||
/* choose the right codec's map */
|
||||
if (strcmp(codec_name, "rt1308") == 0)
|
||||
return rt1308_map;
|
||||
else if (strcmp(codec_name, "rt1316") == 0)
|
||||
return rt1316_map;
|
||||
else
|
||||
return rt1318_map;
|
||||
}
|
||||
|
||||
static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
const struct snd_soc_dapm_route *rt_amp_map;
|
||||
char codec_name[CODEC_NAME_SIZE];
|
||||
int ret;
|
||||
|
||||
rt_amp_map = get_codec_name_and_route(rtd, codec_name);
|
||||
|
||||
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
|
||||
"%s spk:rt1308",
|
||||
card->components);
|
||||
"%s spk:%s",
|
||||
card->components, codec_name);
|
||||
if (!card->components)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = snd_soc_add_card_controls(card, rt1308_controls,
|
||||
ARRAY_SIZE(rt1308_controls));
|
||||
ret = snd_soc_add_card_controls(card, rt_amp_controls,
|
||||
ARRAY_SIZE(rt_amp_controls));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "rt1308 controls addition failed: %d\n", ret);
|
||||
dev_err(card->dev, "%s controls addition failed: %d\n", codec_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_new_controls(&card->dapm, rt1308_widgets,
|
||||
ARRAY_SIZE(rt1308_widgets));
|
||||
ret = snd_soc_dapm_new_controls(&card->dapm, rt_amp_widgets,
|
||||
ARRAY_SIZE(rt_amp_widgets));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "rt1308 widgets addition failed: %d\n", ret);
|
||||
dev_err(card->dev, "%s widgets addition failed: %d\n", codec_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map, 2);
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2);
|
||||
if (ret)
|
||||
dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
|
||||
|
||||
@ -147,9 +224,13 @@ static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
|
||||
static int second_spk_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
const struct snd_soc_dapm_route *rt_amp_map;
|
||||
char codec_name[CODEC_NAME_SIZE];
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map + 2, 2);
|
||||
rt_amp_map = get_codec_name_and_route(rtd, codec_name);
|
||||
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2);
|
||||
if (ret)
|
||||
dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret);
|
||||
|
||||
@ -204,7 +285,7 @@ struct snd_soc_ops sof_sdw_rt1308_i2s_ops = {
|
||||
.hw_params = rt1308_i2s_hw_params,
|
||||
};
|
||||
|
||||
int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
|
||||
int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
|
||||
{
|
||||
struct mc_private *ctx = snd_soc_card_get_drvdata(card);
|
||||
|
||||
@ -221,7 +302,7 @@ int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sof_sdw_rt1308_init(struct snd_soc_card *card,
|
||||
int sof_sdw_rt_amp_init(struct snd_soc_card *card,
|
||||
const struct snd_soc_acpi_link_adr *link,
|
||||
struct snd_soc_dai_link *dai_links,
|
||||
struct sof_sdw_codec_info *info,
|
||||
@ -244,7 +325,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card,
|
||||
if (!sdw_dev1)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = rt1308_add_device_props(sdw_dev1);
|
||||
ret = rt_amp_add_device_props(sdw_dev1);
|
||||
if (ret < 0) {
|
||||
put_device(sdw_dev1);
|
||||
return ret;
|
||||
@ -255,7 +336,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card,
|
||||
if (!sdw_dev2)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = rt1308_add_device_props(sdw_dev2);
|
||||
ret = rt_amp_add_device_props(sdw_dev2);
|
||||
if (ret < 0) {
|
||||
put_device(sdw_dev2);
|
||||
return ret;
|
||||
@ -263,7 +344,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card,
|
||||
ctx->amp_dev2 = sdw_dev2;
|
||||
|
||||
/*
|
||||
* if two 1308s are in one dai link, the init function
|
||||
* if two amps are in one dai link, the init function
|
||||
* in this dai link will be first set for the first speaker,
|
||||
* and it should be reset to initialize all speakers when
|
||||
* the second speaker is found.
|
@ -78,6 +78,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
|
||||
.quirk_data = &mx98360a_spk,
|
||||
.sof_tplg_filename = "sof-jsl-rt5682-mx98360a.tplg",
|
||||
},
|
||||
{
|
||||
.comp_ids = &rt5682_rt5682s_hp,
|
||||
.drv_name = "jsl_rt5682",
|
||||
.sof_tplg_filename = "sof-jsl-rt5682.tplg",
|
||||
},
|
||||
{
|
||||
.id = "10134242",
|
||||
.drv_name = "jsl_cs4242_mx98360a",
|
||||
|
@ -112,7 +112,7 @@ static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = {
|
||||
|
||||
static const struct snd_soc_acpi_adr_device rt1318_1_group1_adr[] = {
|
||||
{
|
||||
.adr = 0x000131025D131801ull,
|
||||
.adr = 0x000132025D131801ull,
|
||||
.num_endpoints = 1,
|
||||
.endpoints = &spk_l_endpoint,
|
||||
.name_prefix = "rt1318-1"
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/hda_register.h>
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
|
||||
@ -79,21 +80,25 @@ static void skl_cldma_setup_bdle(struct sst_dsp *ctx,
|
||||
__le32 **bdlp, int size, int with_ioc)
|
||||
{
|
||||
__le32 *bdl = *bdlp;
|
||||
int remaining = ctx->cl_dev.bufsize;
|
||||
int offset = 0;
|
||||
|
||||
ctx->cl_dev.frags = 0;
|
||||
while (size > 0) {
|
||||
phys_addr_t addr = virt_to_phys(dmab_data->area +
|
||||
(ctx->cl_dev.frags * ctx->cl_dev.bufsize));
|
||||
while (remaining > 0) {
|
||||
phys_addr_t addr;
|
||||
int chunk;
|
||||
|
||||
addr = snd_sgbuf_get_addr(dmab_data, offset);
|
||||
bdl[0] = cpu_to_le32(lower_32_bits(addr));
|
||||
bdl[1] = cpu_to_le32(upper_32_bits(addr));
|
||||
chunk = snd_sgbuf_get_chunk_size(dmab_data, offset, size);
|
||||
bdl[2] = cpu_to_le32(chunk);
|
||||
|
||||
bdl[2] = cpu_to_le32(ctx->cl_dev.bufsize);
|
||||
|
||||
size -= ctx->cl_dev.bufsize;
|
||||
bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
|
||||
remaining -= chunk;
|
||||
bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01);
|
||||
|
||||
bdl += 4;
|
||||
offset += chunk;
|
||||
ctx->cl_dev.frags++;
|
||||
}
|
||||
}
|
||||
@ -338,15 +343,15 @@ int skl_cldma_prepare(struct sst_dsp *ctx)
|
||||
ctx->cl_dev.ops.cl_stop_dma = skl_cldma_stop;
|
||||
|
||||
/* Allocate buffer*/
|
||||
ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
|
||||
&ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize);
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, ctx->dev, ctx->cl_dev.bufsize,
|
||||
&ctx->cl_dev.dmab_data);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Alloc buffer for base fw failed: %x\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup Code loader BDL */
|
||||
ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
|
||||
&ctx->cl_dev.dmab_bdl, PAGE_SIZE);
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, ctx->dev, BDL_SIZE, &ctx->cl_dev.dmab_bdl);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Alloc buffer for blde failed: %x\n", ret);
|
||||
ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data);
|
||||
|
@ -582,36 +582,10 @@ static int skl_tplg_unload_pipe_modules(struct skl_dev *skl,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool skl_tplg_is_multi_fmt(struct skl_dev *skl, struct skl_pipe *pipe)
|
||||
static void skl_tplg_set_pipe_config_idx(struct skl_pipe *pipe, int idx)
|
||||
{
|
||||
struct skl_pipe_fmt *cur_fmt;
|
||||
struct skl_pipe_fmt *next_fmt;
|
||||
int i;
|
||||
|
||||
if (pipe->nr_cfgs <= 1)
|
||||
return false;
|
||||
|
||||
if (pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < pipe->nr_cfgs - 1; i++) {
|
||||
if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
cur_fmt = &pipe->configs[i].out_fmt;
|
||||
next_fmt = &pipe->configs[i + 1].out_fmt;
|
||||
} else {
|
||||
cur_fmt = &pipe->configs[i].in_fmt;
|
||||
next_fmt = &pipe->configs[i + 1].in_fmt;
|
||||
}
|
||||
|
||||
if (!CHECK_HW_PARAMS(cur_fmt->channels, cur_fmt->freq,
|
||||
cur_fmt->bps,
|
||||
next_fmt->channels,
|
||||
next_fmt->freq,
|
||||
next_fmt->bps))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
pipe->cur_config_idx = idx;
|
||||
pipe->memory_pages = pipe->configs[idx].mem_pages;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -632,24 +606,14 @@ skl_tplg_get_pipe_config(struct skl_dev *skl, struct skl_module_cfg *mconfig)
|
||||
int i;
|
||||
|
||||
if (pipe->nr_cfgs == 0) {
|
||||
pipe->cur_config_idx = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (skl_tplg_is_multi_fmt(skl, pipe)) {
|
||||
pipe->cur_config_idx = pipe->pipe_config_idx;
|
||||
pipe->memory_pages = pconfig->mem_pages;
|
||||
dev_dbg(skl->dev, "found pipe config idx:%d\n",
|
||||
pipe->cur_config_idx);
|
||||
skl_tplg_set_pipe_config_idx(pipe, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pipe->conn_type == SKL_PIPE_CONN_TYPE_NONE || pipe->nr_cfgs == 1) {
|
||||
dev_dbg(skl->dev, "No conn_type or just 1 pathcfg, taking 0th for %d\n",
|
||||
pipe->ppl_id);
|
||||
pipe->cur_config_idx = 0;
|
||||
pipe->memory_pages = pconfig->mem_pages;
|
||||
|
||||
skl_tplg_set_pipe_config_idx(pipe, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -668,10 +632,8 @@ skl_tplg_get_pipe_config(struct skl_dev *skl, struct skl_module_cfg *mconfig)
|
||||
|
||||
if (CHECK_HW_PARAMS(params->ch, params->s_freq, params->s_fmt,
|
||||
fmt->channels, fmt->freq, fmt->bps)) {
|
||||
pipe->cur_config_idx = i;
|
||||
pipe->memory_pages = pconfig->mem_pages;
|
||||
skl_tplg_set_pipe_config_idx(pipe, i);
|
||||
dev_dbg(skl->dev, "Using pipe config: %d\n", i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1391,9 +1353,9 @@ static int skl_tplg_multi_config_set_get(struct snd_kcontrol *kcontrol,
|
||||
return -EIO;
|
||||
|
||||
if (is_set)
|
||||
pipe->pipe_config_idx = ucontrol->value.enumerated.item[0];
|
||||
skl_tplg_set_pipe_config_idx(pipe, ucontrol->value.enumerated.item[0]);
|
||||
else
|
||||
ucontrol->value.enumerated.item[0] = pipe->pipe_config_idx;
|
||||
ucontrol->value.enumerated.item[0] = pipe->cur_config_idx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1837,20 +1799,28 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
|
||||
{
|
||||
struct nhlt_specific_cfg *cfg;
|
||||
struct skl_pipe *pipe = mconfig->pipe;
|
||||
struct skl_pipe_params save = *pipe->p_params;
|
||||
struct skl_pipe_fmt *pipe_fmt;
|
||||
struct skl_dev *skl = get_skl_ctx(dai->dev);
|
||||
int link_type = skl_tplg_be_link_type(mconfig->dev_type);
|
||||
u8 dev_type = skl_tplg_be_dev_type(mconfig->dev_type);
|
||||
int ret;
|
||||
|
||||
skl_tplg_fill_dma_id(mconfig, params);
|
||||
|
||||
if (link_type == NHLT_LINK_HDA)
|
||||
return 0;
|
||||
|
||||
*pipe->p_params = *params;
|
||||
ret = skl_tplg_get_pipe_config(skl, mconfig);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dev_dbg(skl->dev, "%s using pipe config: %d\n", __func__, pipe->cur_config_idx);
|
||||
if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
pipe_fmt = &pipe->configs[pipe->pipe_config_idx].out_fmt;
|
||||
pipe_fmt = &pipe->configs[pipe->cur_config_idx].out_fmt;
|
||||
else
|
||||
pipe_fmt = &pipe->configs[pipe->pipe_config_idx].in_fmt;
|
||||
pipe_fmt = &pipe->configs[pipe->cur_config_idx].in_fmt;
|
||||
|
||||
/* update the blob based on virtual bus_id*/
|
||||
cfg = intel_nhlt_get_endpoint_blob(dai->dev, skl->nhlt,
|
||||
@ -1865,10 +1835,15 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
|
||||
dev_err(dai->dev, "Blob NULL for id:%d type:%d dirn:%d ch:%d, freq:%d, fmt:%d\n",
|
||||
mconfig->vbus_id, link_type, params->stream,
|
||||
params->ch, params->s_freq, params->s_fmt);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
*pipe->p_params = save;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
|
||||
|
@ -324,7 +324,6 @@ struct skl_pipe {
|
||||
struct skl_path_config configs[SKL_MAX_PATH_CONFIGS];
|
||||
struct list_head w_list;
|
||||
bool passthru;
|
||||
u32 pipe_config_idx;
|
||||
};
|
||||
|
||||
enum skl_module_state {
|
||||
|
@ -1107,7 +1107,10 @@ static void skl_shutdown(struct pci_dev *pci)
|
||||
if (!skl->init_done)
|
||||
return;
|
||||
|
||||
snd_hdac_stop_streams_and_chip(bus);
|
||||
snd_hdac_stop_streams(bus);
|
||||
snd_hdac_ext_bus_link_power_down_all(bus);
|
||||
skl_dsp_sleep(skl->dsp);
|
||||
|
||||
list_for_each_entry(s, &bus->stream_list, list) {
|
||||
stream = stream_to_hdac_ext_stream(s);
|
||||
snd_hdac_ext_stream_decouple(bus, stream, false);
|
||||
|
@ -200,14 +200,16 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev)
|
||||
if (!mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'audio-codec' missing or invalid\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node =
|
||||
of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
|
||||
if (!mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'audio-codec' missing or invalid\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mt8173_rt5650_rt5514_codec_conf[0].dlc.of_node =
|
||||
mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node;
|
||||
@ -216,6 +218,7 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, card);
|
||||
|
||||
out:
|
||||
of_node_put(platform_node);
|
||||
return ret;
|
||||
}
|
||||
|
@ -677,8 +677,10 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
|
||||
if (!card)
|
||||
if (!card) {
|
||||
of_node_put(platform_node);
|
||||
return -EINVAL;
|
||||
}
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0);
|
||||
@ -767,8 +769,10 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
snd_soc_card_set_drvdata(card, priv);
|
||||
|
||||
@ -776,7 +780,8 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->pinctrl)) {
|
||||
dev_err(&pdev->dev, "%s devm_pinctrl_get failed\n",
|
||||
__func__);
|
||||
return PTR_ERR(priv->pinctrl);
|
||||
ret = PTR_ERR(priv->pinctrl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < PIN_STATE_MAX; i++) {
|
||||
@ -809,6 +814,7 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, card);
|
||||
|
||||
out:
|
||||
of_node_put(platform_node);
|
||||
of_node_put(ec_codec);
|
||||
of_node_put(hdmi_codec);
|
||||
|
@ -633,6 +633,32 @@ static const struct snd_soc_ops mt8195_rt1011_etdm_ops = {
|
||||
.hw_params = mt8195_rt1011_etdm_hw_params,
|
||||
};
|
||||
|
||||
static int mt8195_sof_be_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_component *cmpnt_afe = NULL;
|
||||
struct snd_soc_pcm_runtime *runtime;
|
||||
|
||||
/* find afe component */
|
||||
for_each_card_rtds(rtd->card, runtime) {
|
||||
cmpnt_afe = snd_soc_rtdcom_lookup(runtime, AFE_PCM_NAME);
|
||||
if (cmpnt_afe)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmpnt_afe && !pm_runtime_active(cmpnt_afe->dev)) {
|
||||
dev_err(rtd->dev, "afe pm runtime is not active!!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops mt8195_sof_be_ops = {
|
||||
.hw_params = mt8195_sof_be_hw_params,
|
||||
};
|
||||
|
||||
static int mt8195_rt1011_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
@ -1272,24 +1298,28 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = {
|
||||
.name = "AFE_SOF_DL2",
|
||||
.no_pcm = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &mt8195_sof_be_ops,
|
||||
SND_SOC_DAILINK_REG(AFE_SOF_DL2),
|
||||
},
|
||||
[DAI_LINK_SOF_DL3_BE] = {
|
||||
.name = "AFE_SOF_DL3",
|
||||
.no_pcm = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &mt8195_sof_be_ops,
|
||||
SND_SOC_DAILINK_REG(AFE_SOF_DL3),
|
||||
},
|
||||
[DAI_LINK_SOF_UL4_BE] = {
|
||||
.name = "AFE_SOF_UL4",
|
||||
.no_pcm = 1,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &mt8195_sof_be_ops,
|
||||
SND_SOC_DAILINK_REG(AFE_SOF_UL4),
|
||||
},
|
||||
[DAI_LINK_SOF_UL5_BE] = {
|
||||
.name = "AFE_SOF_UL5",
|
||||
.no_pcm = 1,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &mt8195_sof_be_ops,
|
||||
SND_SOC_DAILINK_REG(AFE_SOF_UL5),
|
||||
},
|
||||
};
|
||||
|
@ -431,6 +431,7 @@ static int rockchip_pdm_runtime_resume(struct device *dev)
|
||||
|
||||
ret = clk_prepare_enable(pdm->hclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(pdm->clk);
|
||||
dev_err(pdm->dev, "hclock enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ static int __maybe_unused rk_spdif_runtime_resume(struct device *dev)
|
||||
|
||||
ret = clk_prepare_enable(spdif->hclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(spdif->mclk);
|
||||
dev_err(spdif->dev, "hclk clock enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -709,8 +709,17 @@ static int soc_pcm_clean(struct snd_soc_pcm_runtime *rtd,
|
||||
|
||||
snd_soc_dpcm_mutex_assert_held(rtd);
|
||||
|
||||
if (!rollback)
|
||||
if (!rollback) {
|
||||
snd_soc_runtime_deactivate(rtd, substream->stream);
|
||||
/* clear the corresponding DAIs parameters when going to be inactive */
|
||||
for_each_rtd_dais(rtd, i, dai) {
|
||||
if (snd_soc_dai_active(dai) == 0)
|
||||
soc_pcm_set_dai_params(dai, NULL);
|
||||
|
||||
if (snd_soc_dai_stream_active(dai, substream->stream) == 0)
|
||||
snd_soc_dai_digital_mute(dai, 1, substream->stream);
|
||||
}
|
||||
}
|
||||
|
||||
for_each_rtd_dais(rtd, i, dai)
|
||||
snd_soc_dai_shutdown(dai, substream, rollback);
|
||||
@ -940,15 +949,6 @@ static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd,
|
||||
|
||||
snd_soc_dpcm_mutex_assert_held(rtd);
|
||||
|
||||
/* clear the corresponding DAIs parameters when going to be inactive */
|
||||
for_each_rtd_dais(rtd, i, dai) {
|
||||
if (snd_soc_dai_active(dai) == 1)
|
||||
soc_pcm_set_dai_params(dai, NULL);
|
||||
|
||||
if (snd_soc_dai_stream_active(dai, substream->stream) == 1)
|
||||
snd_soc_dai_digital_mute(dai, 1, substream->stream);
|
||||
}
|
||||
|
||||
/* run the stream event */
|
||||
snd_soc_dapm_stream_stop(rtd, substream->stream);
|
||||
|
||||
|
@ -475,19 +475,10 @@ EXPORT_SYMBOL(snd_sof_device_remove);
|
||||
int snd_sof_device_shutdown(struct device *dev)
|
||||
{
|
||||
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
|
||||
struct snd_sof_pdata *pdata = sdev->pdata;
|
||||
|
||||
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
|
||||
cancel_work_sync(&sdev->probe_work);
|
||||
|
||||
/*
|
||||
* make sure clients and machine driver(s) are unregistered to force
|
||||
* all userspace devices to be closed prior to the DSP shutdown sequence
|
||||
*/
|
||||
sof_unregister_clients(sdev);
|
||||
|
||||
snd_sof_machine_unregister(sdev, pdata);
|
||||
|
||||
if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
|
||||
return snd_sof_shutdown(sdev);
|
||||
|
||||
|
@ -878,6 +878,78 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
|
||||
return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
|
||||
}
|
||||
|
||||
static unsigned int hda_dsp_check_for_dma_streams(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct hdac_bus *bus = sof_to_bus(sdev);
|
||||
struct hdac_stream *s;
|
||||
unsigned int active_streams = 0;
|
||||
int sd_offset;
|
||||
u32 val;
|
||||
|
||||
list_for_each_entry(s, &bus->stream_list, list) {
|
||||
sd_offset = SOF_STREAM_SD_OFFSET(s);
|
||||
val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
|
||||
sd_offset);
|
||||
if (val & SOF_HDA_SD_CTL_DMA_START)
|
||||
active_streams |= BIT(s->index);
|
||||
}
|
||||
|
||||
return active_streams;
|
||||
}
|
||||
|
||||
static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Do not assume a certain timing between the prior
|
||||
* suspend flow, and running of this quirk function.
|
||||
* This is needed if the controller was just put
|
||||
* to reset before calling this function.
|
||||
*/
|
||||
usleep_range(500, 1000);
|
||||
|
||||
/*
|
||||
* Take controller out of reset to flush DMA
|
||||
* transactions.
|
||||
*/
|
||||
ret = hda_dsp_ctrl_link_reset(sdev, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(500, 1000);
|
||||
|
||||
/* Restore state for shutdown, back to reset */
|
||||
ret = hda_dsp_ctrl_link_reset(sdev, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev)
|
||||
{
|
||||
unsigned int active_streams;
|
||||
int ret, ret2;
|
||||
|
||||
/* check if DMA cleanup has been successful */
|
||||
active_streams = hda_dsp_check_for_dma_streams(sdev);
|
||||
|
||||
sdev->system_suspend_target = SOF_SUSPEND_S3;
|
||||
ret = snd_sof_suspend(sdev->dev);
|
||||
|
||||
if (active_streams) {
|
||||
dev_warn(sdev->dev,
|
||||
"There were active DSP streams (%#x) at shutdown, trying to recover\n",
|
||||
active_streams);
|
||||
ret2 = hda_dsp_s5_quirk(sdev);
|
||||
if (ret2 < 0)
|
||||
dev_err(sdev->dev, "shutdown recovery failed (%d)\n", ret2);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hda_dsp_shutdown(struct snd_sof_dev *sdev)
|
||||
{
|
||||
sdev->system_suspend_target = SOF_SUSPEND_S3;
|
||||
|
@ -592,6 +592,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev);
|
||||
int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev);
|
||||
int hda_dsp_runtime_resume(struct snd_sof_dev *sdev);
|
||||
int hda_dsp_runtime_idle(struct snd_sof_dev *sdev);
|
||||
int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev);
|
||||
int hda_dsp_shutdown(struct snd_sof_dev *sdev);
|
||||
int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev);
|
||||
void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags);
|
||||
|
@ -60,7 +60,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
|
||||
memcpy(&sof_tgl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
|
||||
|
||||
/* probe/remove/shutdown */
|
||||
sof_tgl_ops.shutdown = hda_dsp_shutdown;
|
||||
sof_tgl_ops.shutdown = hda_dsp_shutdown_dma_flush;
|
||||
|
||||
if (sdev->pdata->ipc_type == SOF_IPC) {
|
||||
/* doorbell */
|
||||
|
@ -60,7 +60,7 @@ void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags)
|
||||
{
|
||||
char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
|
||||
struct sof_ipc_dsp_oops_xtensa xoops;
|
||||
struct sof_ipc_panic_info panic_info;
|
||||
struct sof_ipc_panic_info panic_info = {};
|
||||
u32 stack[MTK_ADSP_STACK_DUMP_SIZE];
|
||||
u32 status;
|
||||
|
||||
|
@ -131,6 +131,7 @@ struct snd_usb_endpoint {
|
||||
bool lowlatency_playback; /* low-latency playback mode */
|
||||
bool need_setup; /* (re-)need for hw_params? */
|
||||
bool need_prepare; /* (re-)need for prepare? */
|
||||
bool fixed_rate; /* skip rate setup */
|
||||
|
||||
/* for hw constraints */
|
||||
const struct audioformat *cur_audiofmt;
|
||||
|
@ -769,7 +769,8 @@ struct snd_usb_endpoint *
|
||||
snd_usb_endpoint_open(struct snd_usb_audio *chip,
|
||||
const struct audioformat *fp,
|
||||
const struct snd_pcm_hw_params *params,
|
||||
bool is_sync_ep)
|
||||
bool is_sync_ep,
|
||||
bool fixed_rate)
|
||||
{
|
||||
struct snd_usb_endpoint *ep;
|
||||
int ep_num = is_sync_ep ? fp->sync_ep : fp->endpoint;
|
||||
@ -825,6 +826,7 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
|
||||
ep->implicit_fb_sync = fp->implicit_fb;
|
||||
ep->need_setup = true;
|
||||
ep->need_prepare = true;
|
||||
ep->fixed_rate = fixed_rate;
|
||||
|
||||
usb_audio_dbg(chip, " channels=%d, rate=%d, format=%s, period_bytes=%d, periods=%d, implicit_fb=%d\n",
|
||||
ep->cur_channels, ep->cur_rate,
|
||||
@ -1413,11 +1415,13 @@ static int init_sample_rate(struct snd_usb_audio *chip,
|
||||
if (clock && !clock->need_setup)
|
||||
return 0;
|
||||
|
||||
err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate);
|
||||
if (err < 0) {
|
||||
if (clock)
|
||||
clock->rate = 0; /* reset rate */
|
||||
return err;
|
||||
if (!ep->fixed_rate) {
|
||||
err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate);
|
||||
if (err < 0) {
|
||||
if (clock)
|
||||
clock->rate = 0; /* reset rate */
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (clock)
|
||||
|
@ -14,7 +14,8 @@ struct snd_usb_endpoint *
|
||||
snd_usb_endpoint_open(struct snd_usb_audio *chip,
|
||||
const struct audioformat *fp,
|
||||
const struct snd_pcm_hw_params *params,
|
||||
bool is_sync_ep);
|
||||
bool is_sync_ep,
|
||||
bool fixed_rate);
|
||||
void snd_usb_endpoint_close(struct snd_usb_audio *chip,
|
||||
struct snd_usb_endpoint *ep);
|
||||
int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "usbaudio.h"
|
||||
#include "card.h"
|
||||
#include "helper.h"
|
||||
#include "pcm.h"
|
||||
#include "implicit.h"
|
||||
|
||||
enum {
|
||||
@ -455,7 +456,8 @@ const struct audioformat *
|
||||
snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
|
||||
const struct audioformat *target,
|
||||
const struct snd_pcm_hw_params *params,
|
||||
int stream)
|
||||
int stream,
|
||||
bool *fixed_rate)
|
||||
{
|
||||
struct snd_usb_substream *subs;
|
||||
const struct audioformat *fp, *sync_fmt = NULL;
|
||||
@ -483,6 +485,8 @@ snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
|
||||
}
|
||||
}
|
||||
|
||||
if (fixed_rate)
|
||||
*fixed_rate = snd_usb_pcm_has_fixed_rate(subs);
|
||||
return sync_fmt;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,6 @@ const struct audioformat *
|
||||
snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
|
||||
const struct audioformat *target,
|
||||
const struct snd_pcm_hw_params *params,
|
||||
int stream);
|
||||
int stream, bool *fixed_rate);
|
||||
|
||||
#endif /* __USBAUDIO_IMPLICIT_H */
|
||||
|
@ -157,6 +157,31 @@ find_substream_format(struct snd_usb_substream *subs,
|
||||
true, subs);
|
||||
}
|
||||
|
||||
bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *subs)
|
||||
{
|
||||
const struct audioformat *fp;
|
||||
struct snd_usb_audio *chip = subs->stream->chip;
|
||||
int rate = -1;
|
||||
|
||||
if (!(chip->quirk_flags & QUIRK_FLAG_FIXED_RATE))
|
||||
return false;
|
||||
list_for_each_entry(fp, &subs->fmt_list, list) {
|
||||
if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
|
||||
return false;
|
||||
if (fp->nr_rates < 1)
|
||||
continue;
|
||||
if (fp->nr_rates > 1)
|
||||
return false;
|
||||
if (rate < 0) {
|
||||
rate = fp->rate_table[0];
|
||||
continue;
|
||||
}
|
||||
if (rate != fp->rate_table[0])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int init_pitch_v1(struct snd_usb_audio *chip, int ep)
|
||||
{
|
||||
struct usb_device *dev = chip->dev;
|
||||
@ -450,12 +475,14 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_usb_audio *chip = subs->stream->chip;
|
||||
const struct audioformat *fmt;
|
||||
const struct audioformat *sync_fmt;
|
||||
bool fixed_rate, sync_fixed_rate;
|
||||
int ret;
|
||||
|
||||
ret = snd_media_start_pipeline(subs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fixed_rate = snd_usb_pcm_has_fixed_rate(subs);
|
||||
fmt = find_substream_format(subs, hw_params);
|
||||
if (!fmt) {
|
||||
usb_audio_dbg(chip,
|
||||
@ -469,7 +496,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
|
||||
if (fmt->implicit_fb) {
|
||||
sync_fmt = snd_usb_find_implicit_fb_sync_format(chip, fmt,
|
||||
hw_params,
|
||||
!substream->stream);
|
||||
!substream->stream,
|
||||
&sync_fixed_rate);
|
||||
if (!sync_fmt) {
|
||||
usb_audio_dbg(chip,
|
||||
"cannot find sync format: ep=0x%x, iface=%d:%d, format=%s, rate=%d, channels=%d\n",
|
||||
@ -482,6 +510,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
|
||||
}
|
||||
} else {
|
||||
sync_fmt = fmt;
|
||||
sync_fixed_rate = fixed_rate;
|
||||
}
|
||||
|
||||
ret = snd_usb_lock_shutdown(chip);
|
||||
@ -499,7 +528,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
|
||||
close_endpoints(chip, subs);
|
||||
}
|
||||
|
||||
subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false);
|
||||
subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false, fixed_rate);
|
||||
if (!subs->data_endpoint) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
@ -508,7 +537,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
|
||||
if (fmt->sync_ep) {
|
||||
subs->sync_endpoint = snd_usb_endpoint_open(chip, sync_fmt,
|
||||
hw_params,
|
||||
fmt == sync_fmt);
|
||||
fmt == sync_fmt,
|
||||
sync_fixed_rate);
|
||||
if (!subs->sync_endpoint) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
|
@ -6,6 +6,8 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
|
||||
int snd_usb_pcm_suspend(struct snd_usb_stream *as);
|
||||
int snd_usb_pcm_resume(struct snd_usb_stream *as);
|
||||
|
||||
bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *as);
|
||||
|
||||
int snd_usb_init_pitch(struct snd_usb_audio *chip,
|
||||
const struct audioformat *fmt);
|
||||
void snd_usb_preallocate_buffer(struct snd_usb_substream *subs);
|
||||
|
@ -76,6 +76,8 @@
|
||||
{ USB_DEVICE_VENDOR_SPEC(0x041e, 0x3f0a) },
|
||||
/* E-Mu 0204 USB */
|
||||
{ USB_DEVICE_VENDOR_SPEC(0x041e, 0x3f19) },
|
||||
/* Ktmicro Usb_audio device */
|
||||
{ USB_DEVICE_VENDOR_SPEC(0x31b2, 0x0011) },
|
||||
|
||||
/*
|
||||
* Creative Technology, Ltd Live! Cam Sync HD [VF0770]
|
||||
|
@ -2152,6 +2152,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
||||
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
|
||||
QUIRK_FLAG_IFACE_SKIP_CLOSE),
|
||||
DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
|
||||
QUIRK_FLAG_FIXED_RATE),
|
||||
|
||||
/* Vendor matches */
|
||||
VENDOR_FLG(0x045e, /* MS Lifecam */
|
||||
|
@ -175,6 +175,9 @@ extern bool snd_usb_skip_validation;
|
||||
* QUIRK_FLAG_FORCE_IFACE_RESET
|
||||
* Force an interface reset whenever stopping & restarting a stream
|
||||
* (e.g. after xrun)
|
||||
* QUIRK_FLAG_FIXED_RATE
|
||||
* Do not set PCM rate (frequency) when only one rate is available
|
||||
* for the given endpoint.
|
||||
*/
|
||||
|
||||
#define QUIRK_FLAG_GET_SAMPLE_RATE (1U << 0)
|
||||
@ -198,5 +201,6 @@ extern bool snd_usb_skip_validation;
|
||||
#define QUIRK_FLAG_SKIP_IMPLICIT_FB (1U << 18)
|
||||
#define QUIRK_FLAG_IFACE_SKIP_CLOSE (1U << 19)
|
||||
#define QUIRK_FLAG_FORCE_IFACE_RESET (1U << 20)
|
||||
#define QUIRK_FLAG_FIXED_RATE (1U << 21)
|
||||
|
||||
#endif /* __USBAUDIO_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user