ALSA: hda: cs35l41: Support Speaker ID for laptops
Some Laptops use a number of gpios to define which vendor is used for a particular laptop. Different coefficient files are used for different vendors. Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com> Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com> Link: https://lore.kernel.org/r/20220630002335.366545-9-vitalyr@opensource.cirrus.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
bb6eb621f5
commit
63f4b99f00
@ -86,13 +86,19 @@ static const struct cs_dsp_client_ops client_ops = {
|
||||
static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41,
|
||||
const struct firmware **firmware, char **filename,
|
||||
const char *dir, const char *ssid, const char *amp_name,
|
||||
const char *filetype)
|
||||
int spkid, const char *filetype)
|
||||
{
|
||||
const char * const dsp_name = cs35l41->cs_dsp.name;
|
||||
char *s, c;
|
||||
int ret = 0;
|
||||
|
||||
if (ssid && amp_name)
|
||||
if (spkid > -1 && ssid && amp_name)
|
||||
*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d-%s.%s", dir, CS35L41_PART,
|
||||
dsp_name, "spk-prot", ssid, spkid, amp_name, filetype);
|
||||
else if (spkid > -1 && ssid)
|
||||
*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d.%s", dir, CS35L41_PART,
|
||||
dsp_name, "spk-prot", ssid, spkid, filetype);
|
||||
else if (ssid && amp_name)
|
||||
*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, CS35L41_PART,
|
||||
dsp_name, "spk-prot", ssid, amp_name,
|
||||
filetype);
|
||||
@ -130,6 +136,93 @@ static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
|
||||
const struct firmware **wmfw_firmware,
|
||||
char **wmfw_filename,
|
||||
const struct firmware **coeff_firmware,
|
||||
char **coeff_filename)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.wmfw */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
|
||||
CS35L41_FIRMWARE_ROOT,
|
||||
cs35l41->acpi_subsystem_id, cs35l41->amp_name,
|
||||
cs35l41->speaker_id, "wmfw");
|
||||
if (!ret) {
|
||||
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
|
||||
cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT,
|
||||
cs35l41->acpi_subsystem_id, cs35l41->amp_name,
|
||||
cs35l41->speaker_id, "bin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
|
||||
CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
|
||||
cs35l41->amp_name, -1, "wmfw");
|
||||
if (!ret) {
|
||||
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
|
||||
cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
|
||||
cs35l41->amp_name, cs35l41->speaker_id, "bin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
|
||||
CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
|
||||
NULL, cs35l41->speaker_id, "wmfw");
|
||||
if (!ret) {
|
||||
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT,
|
||||
cs35l41->acpi_subsystem_id,
|
||||
cs35l41->amp_name, cs35l41->speaker_id, "bin");
|
||||
if (ret)
|
||||
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
|
||||
cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT,
|
||||
cs35l41->acpi_subsystem_id,
|
||||
NULL, cs35l41->speaker_id, "bin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try cirrus/part-dspN-fwtype-sub.wmfw */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
|
||||
CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
|
||||
NULL, -1, "wmfw");
|
||||
if (!ret) {
|
||||
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT,
|
||||
cs35l41->acpi_subsystem_id,
|
||||
cs35l41->amp_name, cs35l41->speaker_id, "bin");
|
||||
if (ret)
|
||||
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
|
||||
cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT,
|
||||
cs35l41->acpi_subsystem_id,
|
||||
NULL, cs35l41->speaker_id, "bin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fallback try cirrus/part-dspN-fwtype.wmfw */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
|
||||
CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw");
|
||||
if (!ret) {
|
||||
/* fallback try cirrus/part-dspN-fwtype.bin */
|
||||
cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_warn(cs35l41->dev, "Failed to request firmware\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
|
||||
const struct firmware **wmfw_firmware,
|
||||
char **wmfw_filename,
|
||||
@ -138,43 +231,48 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (cs35l41->speaker_id > -1)
|
||||
return cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename,
|
||||
coeff_firmware, coeff_filename);
|
||||
|
||||
/* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
|
||||
CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
|
||||
cs35l41->amp_name, "wmfw");
|
||||
cs35l41->amp_name, -1, "wmfw");
|
||||
if (!ret) {
|
||||
/* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
|
||||
cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
|
||||
cs35l41->amp_name, "bin");
|
||||
cs35l41->amp_name, -1, "bin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try cirrus/part-dspN-fwtype-sub.wmfw */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
|
||||
CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
|
||||
NULL, "wmfw");
|
||||
NULL, -1, "wmfw");
|
||||
if (!ret) {
|
||||
/* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT,
|
||||
cs35l41->acpi_subsystem_id,
|
||||
cs35l41->amp_name, "bin");
|
||||
cs35l41->amp_name, -1, "bin");
|
||||
if (ret)
|
||||
/* try cirrus/part-dspN-fwtype-sub.bin */
|
||||
cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT,
|
||||
cs35l41->acpi_subsystem_id, NULL, "bin");
|
||||
cs35l41->acpi_subsystem_id,
|
||||
NULL, -1, "bin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fallback try cirrus/part-dspN-fwtype.wmfw */
|
||||
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
|
||||
CS35L41_FIRMWARE_ROOT, NULL, NULL, "wmfw");
|
||||
CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw");
|
||||
if (!ret) {
|
||||
/* fallback try cirrus/part-dspN-fwtype.bin */
|
||||
cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
|
||||
CS35L41_FIRMWARE_ROOT, NULL, NULL, "bin");
|
||||
CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -614,6 +712,61 @@ static int cs35l41_get_acpi_sub_string(struct device *dev, struct acpi_device *a
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cs35l41_get_speaker_id(struct device *dev, int amp_index,
|
||||
int num_amps, int fixed_gpio_id)
|
||||
{
|
||||
struct gpio_desc *speaker_id_desc;
|
||||
int speaker_id = -ENODEV;
|
||||
|
||||
if (fixed_gpio_id >= 0) {
|
||||
dev_dbg(dev, "Found Fixed Speaker ID GPIO (index = %d)\n", fixed_gpio_id);
|
||||
speaker_id_desc = gpiod_get_index(dev, NULL, fixed_gpio_id, GPIOD_IN);
|
||||
if (IS_ERR(speaker_id_desc)) {
|
||||
speaker_id = PTR_ERR(speaker_id_desc);
|
||||
return speaker_id;
|
||||
}
|
||||
speaker_id = gpiod_get_value_cansleep(speaker_id_desc);
|
||||
gpiod_put(speaker_id_desc);
|
||||
dev_dbg(dev, "Speaker ID = %d\n", speaker_id);
|
||||
} else {
|
||||
int base_index;
|
||||
int gpios_per_amp;
|
||||
int count;
|
||||
int tmp;
|
||||
int i;
|
||||
|
||||
count = gpiod_count(dev, "spk-id");
|
||||
if (count > 0) {
|
||||
speaker_id = 0;
|
||||
gpios_per_amp = count / num_amps;
|
||||
base_index = gpios_per_amp * amp_index;
|
||||
|
||||
if (count % num_amps)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(dev, "Found %d Speaker ID GPIOs per Amp\n", gpios_per_amp);
|
||||
|
||||
for (i = 0; i < gpios_per_amp; i++) {
|
||||
speaker_id_desc = gpiod_get_index(dev, "spk-id", i + base_index,
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(speaker_id_desc)) {
|
||||
speaker_id = PTR_ERR(speaker_id_desc);
|
||||
break;
|
||||
}
|
||||
tmp = gpiod_get_value_cansleep(speaker_id_desc);
|
||||
gpiod_put(speaker_id_desc);
|
||||
if (tmp < 0) {
|
||||
speaker_id = tmp;
|
||||
break;
|
||||
}
|
||||
speaker_id |= tmp << i;
|
||||
}
|
||||
dev_dbg(dev, "Speaker ID = %d\n", speaker_id);
|
||||
}
|
||||
}
|
||||
return speaker_id;
|
||||
}
|
||||
|
||||
static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id)
|
||||
{
|
||||
struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
|
||||
@ -719,6 +872,8 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
|
||||
else
|
||||
hw_cfg->bst_cap = -1;
|
||||
|
||||
cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, cs35l41->index, nval, -1);
|
||||
|
||||
if (hw_cfg->bst_ind > 0 || hw_cfg->bst_cap > 0 || hw_cfg->bst_ipk > 0)
|
||||
hw_cfg->bst_type = CS35L41_INT_BOOST;
|
||||
else
|
||||
@ -752,6 +907,7 @@ no_acpi_dsd:
|
||||
cs35l41->channel_index = 0;
|
||||
cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
|
||||
cs35l41->hw_cfg.bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH;
|
||||
cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2);
|
||||
hw_cfg->gpio2.func = CS35L41_GPIO2_INT_OPEN_DRAIN;
|
||||
hw_cfg->gpio2.valid = true;
|
||||
cs35l41->hw_cfg.valid = true;
|
||||
|
@ -43,6 +43,7 @@ struct cs35l41_hda {
|
||||
unsigned volatile long irq_errors;
|
||||
const char *amp_name;
|
||||
const char *acpi_subsystem_id;
|
||||
int speaker_id;
|
||||
struct mutex fw_mutex;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
bool firmware_running;
|
||||
|
Loading…
Reference in New Issue
Block a user