sound fixes for 6.18-rc1
A few more small fixes for 6.18-rc1. Most of changes are about ASoC Intel and SOF drivers, while a few other device-specific fixes are found for HD-audio, USB-audio, ASoC RT722VB and Meson. -----BEGIN PGP SIGNATURE----- iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAmjpDRYOHHRpd2FpQHN1 c2UuZGUACgkQLtJE4w1nLE8E2w//ZZ+SGtxenwlrFGdBiPeU82/Zjfq5Iqjyx1ka 3lptovcX6wAJUkczdyMFiMZgx+B+tnCF8zUMb5VnsKLe5GkS13NcKgWfyjfC5aSW M6nmF+fI96CVOr+jwqWtAKU5aGMY4pKUhPYfLg4jFyx2kZJcKA+HJ4DP89qw+y1S sOBchQdcwuBXBRPXbI5Fc1EEwpNwn4BNM7E98m4GEx8nsZhT7VLPEdqbwugz19ZY csP5JX4UM4dHYjSrK8QdBFjOTwFZwrLsjYJrw4bpORRmIw4WSCnvuBIzgp/xbrq4 eGQFRFImwj71ZbaiQKfUez3MYxWY1ffjDVV9BRmioEE0os5i8aZnRyEy40OZcUvI 5s8UQmnI2VE2jXVKl3Kzd7qj2Rhsby5B6wz82IJ7M9sXZiov3MkijKprS95UBCMt IpmzyhPB0QxmiibqOoWyYdSSXpwTPr0u/z+m1EWYHmi5y0vgII1oFnyJqXY9QV9n UqeZ6P1U96HW4nKfGH1s/Ck3/oFqAUQvcvEJ0IrLb9vUQei5eJn6EoE4+pFfSTz6 WzK383BtrF6sJd/Gf866tFs2KC7qCwspYuQjQjW+hSdLGvQXvcdi/s87iYqR4iXs t/92Xk/MueWVg9Ix19ffZI2ntscr+2kRPy4B5mVdgkCHv0ICIPG0Qn8K3tBpnKey AJwRPT4= =sfXN -----END PGP SIGNATURE----- Merge tag 'sound-fix-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound fixes from Takashi Iwai: "A few more small fixes for 6.18-rc1. Most of changes are about ASoC Intel and SOF drivers, while a few other device-specific fixes are found for HD-audio, USB-audio, ASoC RT722VB and Meson" * tag 'sound-fix-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: ASoC: rt722: add settings for rt722VB ASoC: meson: aiu-encoder-i2s: fix bit clock polarity ALSA: usb: fpc: replace kmalloc_array followed by copy_from_user with memdup_array_user ALSA: hda/tas2781: Enable init_profile_id for device initialization ALSA: emu10k1: Fix typo in docs ALSA: hda/realtek: Add quirk for ASUS ROG Zephyrus Duo ASoC: SOF: Intel: Read the LLP via the associated Link DMA channel ASoC: SOF: ipc4-pcm: do not report invalid delay values ASoC: SOF: sof-audio: add dev_dbg_ratelimited wrapper ASoC: SOF: Intel: hda-pcm: Place the constraint on period time instead of buffer time ASoC: SOF: ipc4-topology: Account for different ChainDMA host buffer size ASoC: SOF: ipc4-topology: Correct the minimum host DMA buffer size ASoC: SOF: ipc4-pcm: fix start offset calculation for chain DMA ASoC: SOF: ipc4-pcm: fix delay calculation when DSP resamples ASoC: SOF: ipc3-topology: Fix multi-core and static pipelines tear down ALSA: hda/hdmi: Add pin fix for HP ProDesk model
This commit is contained in:
commit
fdfa38e95e
|
@ -66,7 +66,7 @@ FX-bus
|
|||
|
||||
name='Clock Source',index=0
|
||||
---------------------------
|
||||
This control allows switching the word clock between interally generated
|
||||
This control allows switching the word clock between internally generated
|
||||
44.1 or 48 kHz, or a number of external sources.
|
||||
|
||||
Note: the sources for the 1616 CardBus card are unclear. Please report your
|
||||
|
|
|
@ -1549,6 +1549,7 @@ static const struct snd_pci_quirk force_connect_list[] = {
|
|||
SND_PCI_QUIRK(0x103c, 0x83e2, "HP EliteDesk 800 G4", 1),
|
||||
SND_PCI_QUIRK(0x103c, 0x83ef, "HP MP9 G4 Retail System AMS", 1),
|
||||
SND_PCI_QUIRK(0x103c, 0x845a, "HP EliteDesk 800 G4 DM 65W", 1),
|
||||
SND_PCI_QUIRK(0x103c, 0x83f3, "HP ProDesk 400", 1),
|
||||
SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1),
|
||||
SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1),
|
||||
SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1),
|
||||
|
|
|
@ -3735,6 +3735,7 @@ enum {
|
|||
ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC,
|
||||
ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1,
|
||||
ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC,
|
||||
ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK,
|
||||
};
|
||||
|
||||
/* A special fixup for Lenovo C940 and Yoga Duet 7;
|
||||
|
@ -6164,6 +6165,14 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.chained = true,
|
||||
.chain_id = ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE,
|
||||
},
|
||||
[ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
{ 0x17, 0x90170151 }, /* Internal Speaker LFE */
|
||||
{ 0x1e, 0x90170150 }, /* Internal Speaker */
|
||||
{ }
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static const struct hda_quirk alc269_fixup_tbl[] = {
|
||||
|
@ -6718,6 +6727,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
|
||||
SND_PCI_QUIRK(0x1043, 0x1533, "ASUS GV302XA/XJ/XQ/XU/XV/XI", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x1573, "ASUS GZ301VV/VQ/VU/VJ/VA/VC/VE/VVC/VQC/VUC/VJC/VEC/VCC", ALC285_FIXUP_ASUS_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1043, 0x1652, "ASUS ROG Zephyrus Do 15 SE", ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK),
|
||||
SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK),
|
||||
SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
|
|
|
@ -474,6 +474,12 @@ static void tasdevice_dspfw_init(void *context)
|
|||
if (tas_priv->fmw->nr_configurations > 0)
|
||||
tas_priv->cur_conf = 0;
|
||||
|
||||
/* Init common setting for different audio profiles */
|
||||
if (tas_priv->rcabin.init_profile_id >= 0)
|
||||
tasdevice_select_cfg_blk(tas_priv,
|
||||
tas_priv->rcabin.init_profile_id,
|
||||
TASDEVICE_BIN_BLK_PRE_POWER_UP);
|
||||
|
||||
/* If calibrated data occurs error, dsp will still works with default
|
||||
* calibrated data inside algo.
|
||||
*/
|
||||
|
@ -770,6 +776,12 @@ static int tas2781_system_resume(struct device *dev)
|
|||
tasdevice_reset(tas_hda->priv);
|
||||
tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog);
|
||||
|
||||
/* Init common setting for different audio profiles */
|
||||
if (tas_hda->priv->rcabin.init_profile_id >= 0)
|
||||
tasdevice_select_cfg_blk(tas_hda->priv,
|
||||
tas_hda->priv->rcabin.init_profile_id,
|
||||
TASDEVICE_BIN_BLK_PRE_POWER_UP);
|
||||
|
||||
if (tas_hda->priv->playback_started)
|
||||
tasdevice_tuning_switch(tas_hda->priv, 0);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
|
|||
switch (reg) {
|
||||
case 0x2f01 ... 0x2f0a:
|
||||
case 0x2f35 ... 0x2f36:
|
||||
case 0x2f50:
|
||||
case 0x2f50 ... 0x2f52:
|
||||
case 0x2f54:
|
||||
case 0x2f58 ... 0x2f5d:
|
||||
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
|
||||
|
|
|
@ -1378,6 +1378,9 @@ static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722)
|
|||
/* PHYtiming TDZ/TZD control */
|
||||
regmap_write(rt722->regmap, 0x2f03, 0x06);
|
||||
|
||||
if (rt722->hw_vid == RT722_VB)
|
||||
regmap_write(rt722->regmap, 0x2f52, 0x00);
|
||||
|
||||
/* clear flag */
|
||||
regmap_write(rt722->regmap,
|
||||
SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
|
||||
|
@ -1415,6 +1418,9 @@ static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722)
|
|||
SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23,
|
||||
RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04);
|
||||
|
||||
if (rt722->hw_vid == RT722_VB)
|
||||
regmap_write(rt722->regmap, 0x2f54, 0x00);
|
||||
|
||||
/* clear flag */
|
||||
regmap_write(rt722->regmap,
|
||||
SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
|
||||
|
@ -1506,6 +1512,9 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722)
|
|||
rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4,
|
||||
0x0010);
|
||||
|
||||
if (rt722->hw_vid == RT722_VB)
|
||||
regmap_write(rt722->regmap, 0x2f51, 0x00);
|
||||
|
||||
/* clear flag */
|
||||
regmap_write(rt722->regmap,
|
||||
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
|
||||
|
@ -1516,6 +1525,7 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722)
|
|||
int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
|
||||
{
|
||||
struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
|
||||
rt722->disable_irq = false;
|
||||
|
||||
|
@ -1545,6 +1555,10 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
|
|||
|
||||
pm_runtime_get_noresume(&slave->dev);
|
||||
|
||||
rt722_sdca_index_read(rt722, RT722_VENDOR_REG, RT722_JD_PRODUCT_NUM, &val);
|
||||
rt722->hw_vid = (val & 0x0f00) >> 8;
|
||||
dev_dbg(&slave->dev, "%s hw_vid=0x%x\n", __func__, rt722->hw_vid);
|
||||
|
||||
rt722_sdca_dmic_preset(rt722);
|
||||
rt722_sdca_amp_preset(rt722);
|
||||
rt722_sdca_jack_preset(rt722);
|
||||
|
|
|
@ -39,6 +39,7 @@ struct rt722_sdca_priv {
|
|||
/* For DMIC */
|
||||
bool fu1e_dapm_mute;
|
||||
bool fu1e_mixer_mute[4];
|
||||
int hw_vid;
|
||||
};
|
||||
|
||||
struct rt722_sdca_dmic_kctrl_priv {
|
||||
|
@ -233,6 +234,11 @@ enum rt722_sdca_jd_src {
|
|||
RT722_JD1,
|
||||
};
|
||||
|
||||
enum rt722_sdca_version {
|
||||
RT722_VA,
|
||||
RT722_VB,
|
||||
};
|
||||
|
||||
int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave);
|
||||
int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave);
|
||||
int rt722_sdca_index_write(struct rt722_sdca_priv *rt722,
|
||||
|
|
|
@ -236,8 +236,12 @@ static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|||
inv == SND_SOC_DAIFMT_IB_IF)
|
||||
val |= AIU_CLK_CTRL_LRCLK_INVERT;
|
||||
|
||||
if (inv == SND_SOC_DAIFMT_IB_NF ||
|
||||
inv == SND_SOC_DAIFMT_IB_IF)
|
||||
/*
|
||||
* The SoC changes data on the rising edge of the bitclock
|
||||
* so an inversion of the bitclock is required in normal mode
|
||||
*/
|
||||
if (inv == SND_SOC_DAIFMT_NB_NF ||
|
||||
inv == SND_SOC_DAIFMT_NB_IF)
|
||||
val |= AIU_CLK_CTRL_AOCLK_INVERT;
|
||||
|
||||
/* Signal skew */
|
||||
|
@ -328,4 +332,3 @@ const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = {
|
|||
.startup = aiu_encoder_i2s_startup,
|
||||
.shutdown = aiu_encoder_i2s_shutdown,
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#define SDnFMT_BITS(x) ((x) << 4)
|
||||
#define SDnFMT_CHAN(x) ((x) << 0)
|
||||
|
||||
#define HDA_MAX_PERIOD_TIME_HEADROOM 10
|
||||
|
||||
static bool hda_always_enable_dmi_l1;
|
||||
module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444);
|
||||
MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1");
|
||||
|
@ -291,19 +293,30 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
|
|||
* On playback start the DMA will transfer dsp_max_burst_size_in_ms
|
||||
* amount of data in one initial burst to fill up the host DMA buffer.
|
||||
* Consequent DMA burst sizes are shorter and their length can vary.
|
||||
* To make sure that userspace allocate large enough ALSA buffer we need
|
||||
* to place a constraint on the buffer time.
|
||||
* To avoid immediate xrun by the initial burst we need to place
|
||||
* constraint on the period size (via PERIOD_TIME) to cover the size of
|
||||
* the host buffer.
|
||||
* We need to add headroom of max 10ms as the firmware needs time to
|
||||
* settle to the 1ms pacing and initially it can run faster for few
|
||||
* internal periods.
|
||||
*
|
||||
* On capture the DMA will transfer 1ms chunks.
|
||||
*
|
||||
* Exact dsp_max_burst_size_in_ms constraint is racy, so set the
|
||||
* constraint to a minimum of 2x dsp_max_burst_size_in_ms.
|
||||
*/
|
||||
if (spcm->stream[direction].dsp_max_burst_size_in_ms)
|
||||
if (spcm->stream[direction].dsp_max_burst_size_in_ms) {
|
||||
unsigned int period_time = spcm->stream[direction].dsp_max_burst_size_in_ms;
|
||||
|
||||
/*
|
||||
* add headroom over the maximum burst size to cover the time
|
||||
* needed for the DMA pace to settle.
|
||||
* Limit the headroom time to HDA_MAX_PERIOD_TIME_HEADROOM
|
||||
*/
|
||||
period_time += min(period_time, HDA_MAX_PERIOD_TIME_HEADROOM);
|
||||
|
||||
snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_TIME,
|
||||
spcm->stream[direction].dsp_max_burst_size_in_ms * USEC_PER_MSEC * 2,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
|
||||
period_time * USEC_PER_MSEC,
|
||||
UINT_MAX);
|
||||
}
|
||||
|
||||
/* binding pcm substream to hda stream */
|
||||
substream->runtime->private_data = &dsp_stream->hstream;
|
||||
|
|
|
@ -1129,10 +1129,35 @@ u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev,
|
|||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct hdac_stream *hstream = substream->runtime->private_data;
|
||||
struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
|
||||
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
|
||||
struct snd_soc_pcm_runtime *be_rtd = NULL;
|
||||
struct hdac_ext_stream *hext_stream;
|
||||
struct snd_soc_dai *cpu_dai;
|
||||
struct snd_soc_dpcm *dpcm;
|
||||
u32 llp_l, llp_u;
|
||||
|
||||
/*
|
||||
* The LLP needs to be read from the Link DMA used for this FE as it is
|
||||
* allowed to use any combination of Link and Host channels
|
||||
*/
|
||||
for_each_dpcm_be(rtd, substream->stream, dpcm) {
|
||||
if (dpcm->fe != rtd)
|
||||
continue;
|
||||
|
||||
be_rtd = dpcm->be;
|
||||
}
|
||||
|
||||
if (!be_rtd)
|
||||
return 0;
|
||||
|
||||
cpu_dai = snd_soc_rtd_to_cpu(be_rtd, 0);
|
||||
if (!cpu_dai)
|
||||
return 0;
|
||||
|
||||
hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
|
||||
if (!hext_stream)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The pplc_addr have been calculated during probe in
|
||||
* hda_dsp_stream_init():
|
||||
|
|
|
@ -2473,11 +2473,6 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* free all the scheduler widgets now */
|
||||
ret = sof_ipc3_free_widgets_in_list(sdev, true, &dyn_widgets, verify);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Tear down all pipelines associated with PCMs that did not get suspended
|
||||
* and unset the prepare flag so that they can be set up again during resume.
|
||||
|
@ -2493,6 +2488,11 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
|
|||
}
|
||||
}
|
||||
|
||||
/* free all the scheduler widgets now. This will also power down the secondary cores */
|
||||
ret = sof_ipc3_free_widgets_in_list(sdev, true, &dyn_widgets, verify);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(sroute, &sdev->route_list, list)
|
||||
sroute->setup = false;
|
||||
|
||||
|
|
|
@ -19,12 +19,14 @@
|
|||
* struct sof_ipc4_timestamp_info - IPC4 timestamp info
|
||||
* @host_copier: the host copier of the pcm stream
|
||||
* @dai_copier: the dai copier of the pcm stream
|
||||
* @stream_start_offset: reported by fw in memory window (converted to frames)
|
||||
* @stream_end_offset: reported by fw in memory window (converted to frames)
|
||||
* @stream_start_offset: reported by fw in memory window (converted to
|
||||
* frames at host_copier sampling rate)
|
||||
* @stream_end_offset: reported by fw in memory window (converted to
|
||||
* frames at host_copier sampling rate)
|
||||
* @llp_offset: llp offset in memory window
|
||||
* @boundary: wrap boundary should be used for the LLP frame counter
|
||||
* @delay: Calculated and stored in pointer callback. The stored value is
|
||||
* returned in the delay callback.
|
||||
* returned in the delay callback. Expressed in frames at host copier
|
||||
* sampling rate.
|
||||
*/
|
||||
struct sof_ipc4_timestamp_info {
|
||||
struct sof_ipc4_copier *host_copier;
|
||||
|
@ -33,7 +35,6 @@ struct sof_ipc4_timestamp_info {
|
|||
u64 stream_end_offset;
|
||||
u32 llp_offset;
|
||||
|
||||
u64 boundary;
|
||||
snd_pcm_sframes_t delay;
|
||||
};
|
||||
|
||||
|
@ -48,6 +49,18 @@ struct sof_ipc4_pcm_stream_priv {
|
|||
bool chain_dma_allocated;
|
||||
};
|
||||
|
||||
/*
|
||||
* Modulus to use to compare host and link position counters. The sampling
|
||||
* rates may be different, so the raw hardware counters will wrap
|
||||
* around at different times. To calculate differences, use
|
||||
* DELAY_BOUNDARY as a common modulus. This value must be smaller than
|
||||
* the wrap-around point of any hardware counter, and larger than any
|
||||
* valid delay measurement.
|
||||
*/
|
||||
#define DELAY_BOUNDARY U32_MAX
|
||||
|
||||
#define DELAY_MAX (DELAY_BOUNDARY >> 1)
|
||||
|
||||
static inline struct sof_ipc4_timestamp_info *
|
||||
sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps)
|
||||
{
|
||||
|
@ -1049,6 +1062,35 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value)
|
||||
{
|
||||
u64 dai_rate, host_rate;
|
||||
|
||||
if (!time_info->dai_copier || !time_info->host_copier)
|
||||
return value;
|
||||
|
||||
/*
|
||||
* copiers do not change sampling rate, so we can use the
|
||||
* out_format independently of stream direction
|
||||
*/
|
||||
dai_rate = time_info->dai_copier->data.out_format.sampling_frequency;
|
||||
host_rate = time_info->host_copier->data.out_format.sampling_frequency;
|
||||
|
||||
if (!dai_rate || !host_rate || dai_rate == host_rate)
|
||||
return value;
|
||||
|
||||
/* take care not to overflow u64, rates can be up to 768000 */
|
||||
if (value > U32_MAX) {
|
||||
value = div64_u64(value, dai_rate);
|
||||
value *= host_rate;
|
||||
} else {
|
||||
value *= host_rate;
|
||||
value = div64_u64(value, dai_rate);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_sof_pcm_stream *sps,
|
||||
|
@ -1068,7 +1110,7 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
|
|||
return -EINVAL;
|
||||
} else if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_CHAIN_DMA_NODE_ID) {
|
||||
/*
|
||||
* While the firmware does not supports time_info reporting for
|
||||
* While the firmware does not support time_info reporting for
|
||||
* streams using ChainDMA, it is granted that ChainDMA can only
|
||||
* be used on Host+Link pairs where the link position is
|
||||
* accessible from the host side.
|
||||
|
@ -1076,10 +1118,16 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
|
|||
* Enable delay calculation in case of ChainDMA via host
|
||||
* accessible registers.
|
||||
*
|
||||
* The ChainDMA uses 2x 1ms ping-pong buffer, dai side starts
|
||||
* when 1ms data is available
|
||||
* The ChainDMA prefills the link DMA with a preamble
|
||||
* of zero samples. Set the stream start offset based
|
||||
* on size of the preamble (driver provided fifo size
|
||||
* multiplied by 2.5). We add 1ms of margin as the FW
|
||||
* will align the buffer size to DMA hardware
|
||||
* alignment that is not known to host.
|
||||
*/
|
||||
time_info->stream_start_offset = substream->runtime->rate / MSEC_PER_SEC;
|
||||
int pre_ms = SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS * 5 / 2 + 1;
|
||||
|
||||
time_info->stream_start_offset = pre_ms * substream->runtime->rate / MSEC_PER_SEC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1099,14 +1147,13 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
|
|||
time_info->stream_end_offset = ppl_reg.stream_end_offset;
|
||||
do_div(time_info->stream_end_offset, dai_sample_size);
|
||||
|
||||
/* convert to host frame time */
|
||||
time_info->stream_start_offset =
|
||||
sof_ipc4_frames_dai_to_host(time_info, time_info->stream_start_offset);
|
||||
time_info->stream_end_offset =
|
||||
sof_ipc4_frames_dai_to_host(time_info, time_info->stream_end_offset);
|
||||
|
||||
out:
|
||||
/*
|
||||
* Calculate the wrap boundary need to be used for delay calculation
|
||||
* The host counter is in bytes, it will wrap earlier than the frames
|
||||
* based link counter.
|
||||
*/
|
||||
time_info->boundary = div64_u64(~((u64)0),
|
||||
frames_to_bytes(substream->runtime, 1));
|
||||
/* Initialize the delay value to 0 (no delay) */
|
||||
time_info->delay = 0;
|
||||
|
||||
|
@ -1149,6 +1196,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
|
|||
|
||||
/* For delay calculation we need the host counter */
|
||||
host_cnt = snd_sof_pcm_get_host_byte_counter(sdev, component, substream);
|
||||
|
||||
/* Store the original value to host_ptr */
|
||||
host_ptr = host_cnt;
|
||||
|
||||
/* convert the host_cnt to frames */
|
||||
|
@ -1167,6 +1216,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
|
|||
sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp));
|
||||
dai_cnt = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l;
|
||||
}
|
||||
|
||||
dai_cnt = sof_ipc4_frames_dai_to_host(time_info, dai_cnt);
|
||||
dai_cnt += time_info->stream_end_offset;
|
||||
|
||||
/* In two cases dai dma counter is not accurate
|
||||
|
@ -1200,8 +1251,9 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
|
|||
dai_cnt -= time_info->stream_start_offset;
|
||||
}
|
||||
|
||||
/* Wrap the dai counter at the boundary where the host counter wraps */
|
||||
div64_u64_rem(dai_cnt, time_info->boundary, &dai_cnt);
|
||||
/* Convert to a common base before comparisons */
|
||||
dai_cnt &= DELAY_BOUNDARY;
|
||||
host_cnt &= DELAY_BOUNDARY;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
head_cnt = host_cnt;
|
||||
|
@ -1211,14 +1263,18 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
|
|||
tail_cnt = host_cnt;
|
||||
}
|
||||
|
||||
if (head_cnt < tail_cnt) {
|
||||
time_info->delay = time_info->boundary - tail_cnt + head_cnt;
|
||||
goto out;
|
||||
if (unlikely(head_cnt < tail_cnt))
|
||||
time_info->delay = DELAY_BOUNDARY - tail_cnt + head_cnt;
|
||||
else
|
||||
time_info->delay = head_cnt - tail_cnt;
|
||||
|
||||
if (time_info->delay > DELAY_MAX) {
|
||||
spcm_dbg_ratelimited(spcm, substream->stream,
|
||||
"inaccurate delay, host %llu dai_cnt %llu",
|
||||
host_cnt, dai_cnt);
|
||||
time_info->delay = 0;
|
||||
}
|
||||
|
||||
time_info->delay = head_cnt - tail_cnt;
|
||||
|
||||
out:
|
||||
/*
|
||||
* Convert the host byte counter to PCM pointer which wraps in buffer
|
||||
* and it is in frames
|
||||
|
|
|
@ -33,7 +33,6 @@ MODULE_PARM_DESC(ipc4_ignore_cpc,
|
|||
|
||||
#define SOF_IPC4_GAIN_PARAM_ID 0
|
||||
#define SOF_IPC4_TPLG_ABI_SIZE 6
|
||||
#define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2
|
||||
|
||||
static DEFINE_IDA(alh_group_ida);
|
||||
static DEFINE_IDA(pipeline_ida);
|
||||
|
@ -666,8 +665,13 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
|
|||
swidget->tuples,
|
||||
swidget->num_tuples, sizeof(u32), 1);
|
||||
/* Set default DMA buffer size if it is not specified in topology */
|
||||
if (!sps->dsp_max_burst_size_in_ms)
|
||||
sps->dsp_max_burst_size_in_ms = SOF_IPC4_MIN_DMA_BUFFER_SIZE;
|
||||
if (!sps->dsp_max_burst_size_in_ms) {
|
||||
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
|
||||
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
|
||||
|
||||
sps->dsp_max_burst_size_in_ms = pipeline->use_chain_dma ?
|
||||
SOF_IPC4_CHAIN_DMA_BUFFER_SIZE : SOF_IPC4_MIN_DMA_BUFFER_SIZE;
|
||||
}
|
||||
} else {
|
||||
/* Capture data is copied from DSP to host in 1ms bursts */
|
||||
spcm->stream[dir].dsp_max_burst_size_in_ms = 1;
|
||||
|
|
|
@ -70,8 +70,11 @@
|
|||
#define SOF_IPC4_CHAIN_DMA_NODE_ID 0x7fffffff
|
||||
#define SOF_IPC4_INVALID_NODE_ID 0xffffffff
|
||||
|
||||
/* FW requires minimum 2ms DMA buffer size */
|
||||
#define SOF_IPC4_MIN_DMA_BUFFER_SIZE 2
|
||||
/* FW requires minimum 4ms DMA buffer size */
|
||||
#define SOF_IPC4_MIN_DMA_BUFFER_SIZE 4
|
||||
|
||||
/* ChainDMA in fw uses 5ms DMA buffer */
|
||||
#define SOF_IPC4_CHAIN_DMA_BUFFER_SIZE 5
|
||||
|
||||
/*
|
||||
* The base of multi-gateways. Multi-gateways addressing starts from
|
||||
|
@ -263,6 +266,8 @@ struct sof_ipc4_dma_stream_ch_map {
|
|||
#define SOF_IPC4_DMA_METHOD_HDA 1
|
||||
#define SOF_IPC4_DMA_METHOD_GPDMA 2 /* defined for consistency but not used */
|
||||
|
||||
#define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2
|
||||
|
||||
/**
|
||||
* struct sof_ipc4_dma_config: DMA configuration
|
||||
* @dma_method: HDAudio or GPDMA
|
||||
|
|
|
@ -629,6 +629,11 @@ void snd_sof_pcm_init_elapsed_work(struct work_struct *work);
|
|||
(__spcm)->pcm.pcm_id, (__spcm)->pcm.pcm_name, __dir, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define spcm_dbg_ratelimited(__spcm, __dir, __fmt, ...) \
|
||||
dev_dbg_ratelimited((__spcm)->scomp->dev, "pcm%u (%s), dir %d: " __fmt, \
|
||||
(__spcm)->pcm.pcm_id, (__spcm)->pcm.pcm_name, __dir, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define spcm_err(__spcm, __dir, __fmt, ...) \
|
||||
dev_err((__spcm)->scomp->dev, "%s: pcm%u (%s), dir %d: " __fmt, \
|
||||
__func__, (__spcm)->pcm.pcm_id, (__spcm)->pcm.pcm_name, __dir, \
|
||||
|
|
|
@ -641,12 +641,9 @@ static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer,
|
|||
return -EINVAL;
|
||||
|
||||
/* Allocate and copy the map data */
|
||||
tmp_map = kmalloc_array(map.map_size, sizeof(s16), GFP_KERNEL);
|
||||
if (!tmp_map)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(tmp_map, arg->map, map.map_size * sizeof(s16)))
|
||||
return -EFAULT;
|
||||
tmp_map = memdup_array_user(arg->map, map.map_size, sizeof(s16));
|
||||
if (IS_ERR(tmp_map))
|
||||
return PTR_ERR(tmp_map);
|
||||
|
||||
err = validate_meter_map(tmp_map, map.map_size, map.meter_slots);
|
||||
if (err < 0)
|
||||
|
|
Loading…
Reference in New Issue