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:
Linus Torvalds 2022-12-23 11:15:48 -08:00
commit a27405b2ed
71 changed files with 1731 additions and 640 deletions

View File

@ -38,6 +38,7 @@ properties:
- fsl,imx8mq-sai
- fsl,imx8qm-sai
- fsl,imx8ulp-sai
- fsl,imx93-sai
- fsl,vf610-sai
reg:

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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),

View File

@ -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,

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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,

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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)
};

View File

@ -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 */

View File

@ -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",

View File

@ -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

View File

@ -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

View 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");

View File

@ -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);

View 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);
}

View File

@ -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:

View File

@ -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

View File

@ -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 */

View File

@ -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;

View 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));
}

View File

@ -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

View File

@ -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)
};

View File

@ -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;
}

View File

@ -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 \

View File

@ -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."),

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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,
},
{

View File

@ -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,

View File

@ -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(&params, 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;
}

View File

@ -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;
}

View File

@ -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.

View File

@ -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",

View File

@ -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"

View File

@ -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);

View File

@ -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,

View File

@ -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 {

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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),
},
};

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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,

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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]

View File

@ -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 */

View File

@ -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 */