Skip to content

Commit d110858

Browse files
Stefan Bindingtiwai
authored andcommitted
ALSA: hda: cs35l41: Prevent firmware load if SPI speed too low
Some laptops without _DSD have the SPI speed set very low in the BIOS. Since the SPI controller uses this speed as its max speed, the SPI transactions are very slow. Firmware download writes to many registers, and if the SPI speed is too slow, it can take a long time to download. For this reason, disable firmware loading if the maximum SPI speed is too low. Without Firmware, audio playback will work, but the volume will be low to ensure safe operation of the CS35L41. Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com> Cc: <stable@vger.kernel.org> # v6.7+ Link: https://lore.kernel.org/r/20231221132518.3213-3-sbinding@opensource.cirrus.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent ee694e7 commit d110858

5 files changed

Lines changed: 70 additions & 45 deletions

File tree

sound/pci/hda/cs35l41_hda.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <sound/hda_codec.h>
1313
#include <sound/soc.h>
1414
#include <linux/pm_runtime.h>
15+
#include <linux/spi/spi.h>
1516
#include "hda_local.h"
1617
#include "hda_auto_parser.h"
1718
#include "hda_jack.h"
@@ -996,6 +997,11 @@ static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
996997
__be32 halo_sts;
997998
int ret;
998999

1000+
if (cs35l41->bypass_fw) {
1001+
dev_warn(cs35l41->dev, "Bypassing Firmware.\n");
1002+
return 0;
1003+
}
1004+
9991005
ret = cs35l41_init_dsp(cs35l41);
10001006
if (ret) {
10011007
dev_warn(cs35l41->dev, "Cannot Initialize Firmware. Error: %d\n", ret);
@@ -1588,6 +1594,7 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
15881594
u32 values[HDA_MAX_COMPONENTS];
15891595
struct acpi_device *adev;
15901596
struct device *physdev;
1597+
struct spi_device *spi;
15911598
const char *sub;
15921599
char *property;
15931600
size_t nval;
@@ -1610,7 +1617,7 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
16101617
ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid);
16111618
if (!ret) {
16121619
dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n");
1613-
goto put_physdev;
1620+
goto out;
16141621
}
16151622

16161623
property = "cirrus,dev-index";
@@ -1701,8 +1708,20 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
17011708
hw_cfg->bst_type = CS35L41_EXT_BOOST;
17021709

17031710
hw_cfg->valid = true;
1711+
out:
17041712
put_device(physdev);
17051713

1714+
cs35l41->bypass_fw = false;
1715+
if (cs35l41->control_bus == SPI) {
1716+
spi = to_spi_device(cs35l41->dev);
1717+
if (spi->max_speed_hz < CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ) {
1718+
dev_warn(cs35l41->dev,
1719+
"SPI speed is too slow to support firmware download: %d Hz.\n",
1720+
spi->max_speed_hz);
1721+
cs35l41->bypass_fw = true;
1722+
}
1723+
}
1724+
17061725
return 0;
17071726

17081727
err:
@@ -1711,14 +1730,13 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
17111730
hw_cfg->gpio1.valid = false;
17121731
hw_cfg->gpio2.valid = false;
17131732
acpi_dev_put(cs35l41->dacpi);
1714-
put_physdev:
17151733
put_device(physdev);
17161734

17171735
return ret;
17181736
}
17191737

17201738
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
1721-
struct regmap *regmap)
1739+
struct regmap *regmap, enum control_bus control_bus)
17221740
{
17231741
unsigned int regid, reg_revid;
17241742
struct cs35l41_hda *cs35l41;
@@ -1737,6 +1755,7 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
17371755
cs35l41->dev = dev;
17381756
cs35l41->irq = irq;
17391757
cs35l41->regmap = regmap;
1758+
cs35l41->control_bus = control_bus;
17401759
dev_set_drvdata(dev, cs35l41);
17411760

17421761
ret = cs35l41_hda_read_acpi(cs35l41, device_name, id);

sound/pci/hda/cs35l41_hda.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <linux/firmware/cirrus/cs_dsp.h>
2121
#include <linux/firmware/cirrus/wmfw.h>
2222

23+
#define CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ 1000000
24+
2325
struct cs35l41_amp_cal_data {
2426
u32 calTarget[2];
2527
u32 calTime[2];
@@ -46,6 +48,11 @@ enum cs35l41_hda_gpio_function {
4648
CS35l41_SYNC,
4749
};
4850

51+
enum control_bus {
52+
I2C,
53+
SPI
54+
};
55+
4956
struct cs35l41_hda {
5057
struct device *dev;
5158
struct regmap *regmap;
@@ -74,6 +81,9 @@ struct cs35l41_hda {
7481
struct cs_dsp cs_dsp;
7582
struct acpi_device *dacpi;
7683
bool mute_override;
84+
enum control_bus control_bus;
85+
bool bypass_fw;
86+
7787
};
7888

7989
enum halo_state {
@@ -85,7 +95,7 @@ enum halo_state {
8595
extern const struct dev_pm_ops cs35l41_hda_pm_ops;
8696

8797
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
88-
struct regmap *regmap);
98+
struct regmap *regmap, enum control_bus control_bus);
8999
void cs35l41_hda_remove(struct device *dev);
90100
int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id);
91101

sound/pci/hda/cs35l41_hda_i2c.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ static int cs35l41_hda_i2c_probe(struct i2c_client *clt)
3030
return -ENODEV;
3131

3232
return cs35l41_hda_probe(&clt->dev, device_name, clt->addr, clt->irq,
33-
devm_regmap_init_i2c(clt, &cs35l41_regmap_i2c));
33+
devm_regmap_init_i2c(clt, &cs35l41_regmap_i2c), I2C);
3434
}
3535

3636
static void cs35l41_hda_i2c_remove(struct i2c_client *clt)

sound/pci/hda/cs35l41_hda_property.c

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616

1717
struct cs35l41_config {
1818
const char *ssid;
19-
enum {
20-
SPI,
21-
I2C
22-
} bus;
2319
int num_amps;
2420
enum {
2521
INTERNAL,
@@ -35,46 +31,46 @@ struct cs35l41_config {
3531
};
3632

3733
static const struct cs35l41_config cs35l41_config_table[] = {
38-
{ "10280B27", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
39-
{ "10280B28", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
40-
{ "10280BEB", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
41-
{ "10280C4D", I2C, 4, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT }, 0, 1, -1, 1000, 4500, 24 },
34+
{ "10280B27", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
35+
{ "10280B28", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
36+
{ "10280BEB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
37+
{ "10280C4D", 4, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT }, 0, 1, -1, 1000, 4500, 24 },
4238
/*
4339
* Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type.
4440
* We can override the _DSD to correct the boost type here.
4541
* Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists
4642
* in the ACPI. The Reset GPIO is also valid, so we can use the Reset defined in _DSD.
4743
*/
48-
{ "103C89C6", SPI, 2, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, -1, -1, -1, 1000, 4500, 24 },
49-
{ "104312AF", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
50-
{ "10431433", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
51-
{ "10431463", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
52-
{ "10431473", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
53-
{ "10431483", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
54-
{ "10431493", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
55-
{ "104314D3", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
56-
{ "104314E3", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
57-
{ "10431503", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
58-
{ "10431533", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
59-
{ "10431573", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
60-
{ "10431663", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
61-
{ "104316D3", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
62-
{ "104316F3", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
63-
{ "104317F3", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
64-
{ "10431863", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
65-
{ "104318D3", I2C, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
66-
{ "10431C9F", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
67-
{ "10431CAF", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
68-
{ "10431CCF", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
69-
{ "10431CDF", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
70-
{ "10431CEF", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
71-
{ "10431D1F", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
72-
{ "10431DA2", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
73-
{ "10431E02", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
74-
{ "10431EE2", I2C, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
75-
{ "10431F12", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
76-
{ "10431F1F", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
77-
{ "10431F62", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
44+
{ "103C89C6", 2, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, -1, -1, -1, 1000, 4500, 24 },
45+
{ "104312AF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
46+
{ "10431433", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
47+
{ "10431463", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
48+
{ "10431473", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
49+
{ "10431483", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
50+
{ "10431493", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
51+
{ "104314D3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
52+
{ "104314E3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
53+
{ "10431503", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
54+
{ "10431533", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
55+
{ "10431573", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
56+
{ "10431663", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
57+
{ "104316D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
58+
{ "104316F3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
59+
{ "104317F3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
60+
{ "10431863", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
61+
{ "104318D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
62+
{ "10431C9F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
63+
{ "10431CAF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
64+
{ "10431CCF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
65+
{ "10431CDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
66+
{ "10431CEF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
67+
{ "10431D1F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
68+
{ "10431DA2", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
69+
{ "10431E02", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
70+
{ "10431EE2", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
71+
{ "10431F12", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
72+
{ "10431F1F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
73+
{ "10431F62", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
7874
{}
7975
};
8076

@@ -212,7 +208,7 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde
212208
"_DSD already exists.\n");
213209
}
214210

215-
if (cfg->bus == SPI) {
211+
if (cs35l41->control_bus == SPI) {
216212
cs35l41->index = id;
217213

218214
#if IS_ENABLED(CONFIG_SPI)

sound/pci/hda/cs35l41_hda_spi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static int cs35l41_hda_spi_probe(struct spi_device *spi)
2626
return -ENODEV;
2727

2828
return cs35l41_hda_probe(&spi->dev, device_name, spi_get_chipselect(spi, 0), spi->irq,
29-
devm_regmap_init_spi(spi, &cs35l41_regmap_spi));
29+
devm_regmap_init_spi(spi, &cs35l41_regmap_spi), SPI);
3030
}
3131

3232
static void cs35l41_hda_spi_remove(struct spi_device *spi)

0 commit comments

Comments
 (0)