Commit 9b48104b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'sound-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound fixes from Takashi Iwai:
 "The majority of changes here are small device-specific fixes for ASoC
  SOF / Intel and usual HD-audio quirks.

  The only significant high LOC is found in the Cirrus firmware driver,
  but all those are for hardening against malicious firmware blobs, and
  they look fine for taking as a last minute fix, too"

* tag 'sound-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound:
  ALSA: hda/realtek: Enable Mute LED on HP 250 G7
  firmware: cs_dsp: Use strnlen() on name fields in V1 wmfw files
  ALSA: hda/realtek: Limit mic boost on VAIO PRO PX
  ALSA: hda: cs35l41: Fix swapped l/r audio channels for Lenovo ThinBook 13x Gen4
  ASoC: SOF: Intel: hda-pcm: Limit the maximum number of periods by MAX_BDL_ENTRIES
  ASoC: rt711-sdw: add missing readable registers
  ASoC: SOF: Intel: hda: fix null deref on system suspend entry
  ALSA: hda/realtek: add quirk for Clevo V5[46]0TU
  firmware: cs_dsp: Prevent buffer overrun when processing V2 alg headers
  firmware: cs_dsp: Validate payload length before processing block
  firmware: cs_dsp: Return error if block header overflows file
  firmware: cs_dsp: Fix overflow checking of wmfw header
parents 5d4c8513 f19e1027
......@@ -1107,9 +1107,16 @@ struct cs_dsp_coeff_parsed_coeff {
int len;
};
static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, unsigned int avail,
const u8 **str)
{
int length;
int length, total_field_len;
/* String fields are at least one __le32 */
if (sizeof(__le32) > avail) {
*pos = NULL;
return 0;
}
switch (bytes) {
case 1:
......@@ -1122,10 +1129,16 @@ static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
return 0;
}
total_field_len = ((length + bytes) + 3) & ~0x03;
if ((unsigned int)total_field_len > avail) {
*pos = NULL;
return 0;
}
if (str)
*str = *pos + bytes;
*pos += ((length + bytes) + 3) & ~0x03;
*pos += total_field_len;
return length;
}
......@@ -1150,71 +1163,134 @@ static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos)
return val;
}
static inline void cs_dsp_coeff_parse_alg(struct cs_dsp *dsp, const u8 **data,
struct cs_dsp_coeff_parsed_alg *blk)
static int cs_dsp_coeff_parse_alg(struct cs_dsp *dsp,
const struct wmfw_region *region,
struct cs_dsp_coeff_parsed_alg *blk)
{
const struct wmfw_adsp_alg_data *raw;
unsigned int data_len = le32_to_cpu(region->len);
unsigned int pos;
const u8 *tmp;
raw = (const struct wmfw_adsp_alg_data *)region->data;
switch (dsp->fw_ver) {
case 0:
case 1:
raw = (const struct wmfw_adsp_alg_data *)*data;
*data = raw->data;
if (sizeof(*raw) > data_len)
return -EOVERFLOW;
blk->id = le32_to_cpu(raw->id);
blk->name = raw->name;
blk->name_len = strlen(raw->name);
blk->name_len = strnlen(raw->name, ARRAY_SIZE(raw->name));
blk->ncoeff = le32_to_cpu(raw->ncoeff);
pos = sizeof(*raw);
break;
default:
blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), data);
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), data,
if (sizeof(raw->id) > data_len)
return -EOVERFLOW;
tmp = region->data;
blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), &tmp);
pos = tmp - region->data;
tmp = &region->data[pos];
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos,
&blk->name);
cs_dsp_coeff_parse_string(sizeof(u16), data, NULL);
blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), data);
if (!tmp)
return -EOVERFLOW;
pos = tmp - region->data;
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL);
if (!tmp)
return -EOVERFLOW;
pos = tmp - region->data;
if (sizeof(raw->ncoeff) > (data_len - pos))
return -EOVERFLOW;
blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), &tmp);
pos += sizeof(raw->ncoeff);
break;
}
if ((int)blk->ncoeff < 0)
return -EOVERFLOW;
cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
return pos;
}
static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data,
struct cs_dsp_coeff_parsed_coeff *blk)
static int cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp,
const struct wmfw_region *region,
unsigned int pos,
struct cs_dsp_coeff_parsed_coeff *blk)
{
const struct wmfw_adsp_coeff_data *raw;
unsigned int data_len = le32_to_cpu(region->len);
unsigned int blk_len, blk_end_pos;
const u8 *tmp;
int length;
raw = (const struct wmfw_adsp_coeff_data *)&region->data[pos];
if (sizeof(raw->hdr) > (data_len - pos))
return -EOVERFLOW;
blk_len = le32_to_cpu(raw->hdr.size);
if (blk_len > S32_MAX)
return -EOVERFLOW;
if (blk_len > (data_len - pos - sizeof(raw->hdr)))
return -EOVERFLOW;
blk_end_pos = pos + sizeof(raw->hdr) + blk_len;
blk->offset = le16_to_cpu(raw->hdr.offset);
blk->mem_type = le16_to_cpu(raw->hdr.type);
switch (dsp->fw_ver) {
case 0:
case 1:
raw = (const struct wmfw_adsp_coeff_data *)*data;
*data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
if (sizeof(*raw) > (data_len - pos))
return -EOVERFLOW;
blk->offset = le16_to_cpu(raw->hdr.offset);
blk->mem_type = le16_to_cpu(raw->hdr.type);
blk->name = raw->name;
blk->name_len = strlen(raw->name);
blk->name_len = strnlen(raw->name, ARRAY_SIZE(raw->name));
blk->ctl_type = le16_to_cpu(raw->ctl_type);
blk->flags = le16_to_cpu(raw->flags);
blk->len = le32_to_cpu(raw->len);
break;
default:
tmp = *data;
blk->offset = cs_dsp_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
blk->mem_type = cs_dsp_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
length = cs_dsp_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp,
pos += sizeof(raw->hdr);
tmp = &region->data[pos];
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos,
&blk->name);
cs_dsp_coeff_parse_string(sizeof(u8), &tmp, NULL);
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, NULL);
if (!tmp)
return -EOVERFLOW;
pos = tmp - region->data;
cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos, NULL);
if (!tmp)
return -EOVERFLOW;
pos = tmp - region->data;
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL);
if (!tmp)
return -EOVERFLOW;
pos = tmp - region->data;
if (sizeof(raw->ctl_type) + sizeof(raw->flags) + sizeof(raw->len) >
(data_len - pos))
return -EOVERFLOW;
blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
pos += sizeof(raw->ctl_type);
blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp);
pos += sizeof(raw->flags);
blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp);
*data = *data + sizeof(raw->hdr) + length;
break;
}
......@@ -1224,6 +1300,8 @@ static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data,
cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
return blk_end_pos;
}
static int cs_dsp_check_coeff_flags(struct cs_dsp *dsp,
......@@ -1247,12 +1325,16 @@ static int cs_dsp_parse_coeff(struct cs_dsp *dsp,
struct cs_dsp_alg_region alg_region = {};
struct cs_dsp_coeff_parsed_alg alg_blk;
struct cs_dsp_coeff_parsed_coeff coeff_blk;
const u8 *data = region->data;
int i, ret;
int i, pos, ret;
pos = cs_dsp_coeff_parse_alg(dsp, region, &alg_blk);
if (pos < 0)
return pos;
cs_dsp_coeff_parse_alg(dsp, &data, &alg_blk);
for (i = 0; i < alg_blk.ncoeff; i++) {
cs_dsp_coeff_parse_coeff(dsp, &data, &coeff_blk);
pos = cs_dsp_coeff_parse_coeff(dsp, region, pos, &coeff_blk);
if (pos < 0)
return pos;
switch (coeff_blk.ctl_type) {
case WMFW_CTL_TYPE_BYTES:
......@@ -1321,6 +1403,10 @@ static unsigned int cs_dsp_adsp1_parse_sizes(struct cs_dsp *dsp,
const struct wmfw_adsp1_sizes *adsp1_sizes;
adsp1_sizes = (void *)&firmware->data[pos];
if (sizeof(*adsp1_sizes) > firmware->size - pos) {
cs_dsp_err(dsp, "%s: file truncated\n", file);
return 0;
}
cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file,
le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm),
......@@ -1337,6 +1423,10 @@ static unsigned int cs_dsp_adsp2_parse_sizes(struct cs_dsp *dsp,
const struct wmfw_adsp2_sizes *adsp2_sizes;
adsp2_sizes = (void *)&firmware->data[pos];
if (sizeof(*adsp2_sizes) > firmware->size - pos) {
cs_dsp_err(dsp, "%s: file truncated\n", file);
return 0;
}
cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file,
le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym),
......@@ -1376,7 +1466,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
struct regmap *regmap = dsp->regmap;
unsigned int pos = 0;
const struct wmfw_header *header;
const struct wmfw_adsp1_sizes *adsp1_sizes;
const struct wmfw_footer *footer;
const struct wmfw_region *region;
const struct cs_dsp_region *mem;
......@@ -1392,10 +1481,8 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
ret = -EINVAL;
pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
if (pos >= firmware->size) {
cs_dsp_err(dsp, "%s: file too short, %zu bytes\n",
file, firmware->size);
if (sizeof(*header) >= firmware->size) {
ret = -EOVERFLOW;
goto out_fw;
}
......@@ -1423,22 +1510,36 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
pos = sizeof(*header);
pos = dsp->ops->parse_sizes(dsp, file, pos, firmware);
if ((pos == 0) || (sizeof(*footer) > firmware->size - pos)) {
ret = -EOVERFLOW;
goto out_fw;
}
footer = (void *)&firmware->data[pos];
pos += sizeof(*footer);
if (le32_to_cpu(header->len) != pos) {
cs_dsp_err(dsp, "%s: unexpected header length %d\n",
file, le32_to_cpu(header->len));
ret = -EOVERFLOW;
goto out_fw;
}
cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file,
le64_to_cpu(footer->timestamp));
while (pos < firmware->size &&
sizeof(*region) < firmware->size - pos) {
while (pos < firmware->size) {
/* Is there enough data for a complete block header? */
if (sizeof(*region) > firmware->size - pos) {
ret = -EOVERFLOW;
goto out_fw;
}
region = (void *)&(firmware->data[pos]);
if (le32_to_cpu(region->len) > firmware->size - pos - sizeof(*region)) {
ret = -EOVERFLOW;
goto out_fw;
}
region_name = "Unknown";
reg = 0;
text = NULL;
......@@ -1495,16 +1596,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
regions, le32_to_cpu(region->len), offset,
region_name);
if (le32_to_cpu(region->len) >
firmware->size - pos - sizeof(*region)) {
cs_dsp_err(dsp,
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
file, regions, region_name,
le32_to_cpu(region->len), firmware->size);
ret = -EINVAL;
goto out_fw;
}
if (text) {
memcpy(text, region->data, le32_to_cpu(region->len));
cs_dsp_info(dsp, "%s: %s\n", file, text);
......@@ -1555,6 +1646,9 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
cs_dsp_buf_free(&buf_list);
kfree(text);
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
return ret;
}
......@@ -2122,10 +2216,20 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
pos = le32_to_cpu(hdr->len);
blocks = 0;
while (pos < firmware->size &&
sizeof(*blk) < firmware->size - pos) {
while (pos < firmware->size) {
/* Is there enough data for a complete block header? */
if (sizeof(*blk) > firmware->size - pos) {
ret = -EOVERFLOW;
goto out_fw;
}
blk = (void *)(&firmware->data[pos]);
if (le32_to_cpu(blk->len) > firmware->size - pos - sizeof(*blk)) {
ret = -EOVERFLOW;
goto out_fw;
}
type = le16_to_cpu(blk->type);
offset = le16_to_cpu(blk->offset);
version = le32_to_cpu(blk->ver) >> 8;
......@@ -2222,17 +2326,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
}
if (reg) {
if (le32_to_cpu(blk->len) >
firmware->size - pos - sizeof(*blk)) {
cs_dsp_err(dsp,
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
file, blocks, region_name,
le32_to_cpu(blk->len),
firmware->size);
ret = -EINVAL;
goto out_fw;
}
buf = cs_dsp_buf_alloc(blk->data,
le32_to_cpu(blk->len),
&buf_list);
......@@ -2272,6 +2365,10 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
regmap_async_complete(regmap);
cs_dsp_buf_free(&buf_list);
kfree(text);
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
return ret;
}
......
......@@ -128,8 +128,8 @@ static const struct cs35l41_config cs35l41_config_table[] = {
{ "17AA38B5", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
{ "17AA38B6", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
{ "17AA38B7", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
{ "17AA38C7", 4, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT }, 0, 2, -1, 1000, 4500, 24 },
{ "17AA38C8", 4, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT }, 0, 2, -1, 1000, 4500, 24 },
{ "17AA38C7", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
{ "17AA38C8", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
{ "17AA38F9", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
{ "17AA38FA", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
{}
......
......@@ -10053,6 +10053,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x103c, 0x841c, "HP Pavilion 15-CK0xx", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x103c, 0x84a6, "HP 250 G7 Notebook PC", ALC269_FIXUP_HP_LINE1_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x84ae, "HP 15-db0403ng", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN),
SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
......@@ -10383,6 +10384,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE),
SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE),
SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
......@@ -10480,6 +10482,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1558, 0xa600, "Clevo NL50NU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xa650, "Clevo NP[567]0SN[CD]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xa763, "Clevo V54x_6x_TU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xb022, "Clevo NH77D[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
......@@ -10655,6 +10658,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO),
SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME),
SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC),
......
......@@ -38,7 +38,9 @@ static bool rt711_readable_register(struct device *dev, unsigned int reg)
case 0x8300 ... 0x83ff:
case 0x9c00 ... 0x9cff:
case 0xb900 ... 0xb9ff:
case 0x752008:
case 0x752009:
case 0x75200b:
case 0x752011:
case 0x75201a:
case 0x752045:
......
......@@ -617,12 +617,6 @@ static int hda_dai_suspend(struct hdac_bus *bus)
sdai = swidget->private;
ops = sdai->platform_private;
ret = hda_link_dma_cleanup(hext_stream->link_substream,
hext_stream,
cpu_dai);
if (ret < 0)
return ret;
/* for consistency with TRIGGER_SUSPEND */
if (ops->post_trigger) {
ret = ops->post_trigger(sdev, cpu_dai,
......@@ -631,6 +625,12 @@ static int hda_dai_suspend(struct hdac_bus *bus)
if (ret < 0)
return ret;
}
ret = hda_link_dma_cleanup(hext_stream->link_substream,
hext_stream,
cpu_dai);
if (ret < 0)
return ret;
}
}
......
......@@ -258,6 +258,12 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
/* Limit the maximum number of periods to not exceed the BDL entries count */
if (runtime->hw.periods_max > HDA_DSP_MAX_BDL_ENTRIES)
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
runtime->hw.periods_min,
HDA_DSP_MAX_BDL_ENTRIES);
/* Only S16 and S32 supported by HDA hardware when used without DSP */
if (sdev->dspless_mode_selected)
snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment