Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Stefan Binding and committed by
Takashi Iwai
63f4b99f bb6eb621

+166 -9
+165 -9
sound/pci/hda/cs35l41_hda.c
··· 86 86 static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41, 87 87 const struct firmware **firmware, char **filename, 88 88 const char *dir, const char *ssid, const char *amp_name, 89 - const char *filetype) 89 + int spkid, const char *filetype) 90 90 { 91 91 const char * const dsp_name = cs35l41->cs_dsp.name; 92 92 char *s, c; 93 93 int ret = 0; 94 94 95 - if (ssid && amp_name) 95 + if (spkid > -1 && ssid && amp_name) 96 + *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d-%s.%s", dir, CS35L41_PART, 97 + dsp_name, "spk-prot", ssid, spkid, amp_name, filetype); 98 + else if (spkid > -1 && ssid) 99 + *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d.%s", dir, CS35L41_PART, 100 + dsp_name, "spk-prot", ssid, spkid, filetype); 101 + else if (ssid && amp_name) 96 102 *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, CS35L41_PART, 97 103 dsp_name, "spk-prot", ssid, amp_name, 98 104 filetype); ··· 136 130 return ret; 137 131 } 138 132 133 + static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, 134 + const struct firmware **wmfw_firmware, 135 + char **wmfw_filename, 136 + const struct firmware **coeff_firmware, 137 + char **coeff_filename) 138 + { 139 + int ret; 140 + 141 + /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.wmfw */ 142 + ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, 143 + CS35L41_FIRMWARE_ROOT, 144 + cs35l41->acpi_subsystem_id, cs35l41->amp_name, 145 + cs35l41->speaker_id, "wmfw"); 146 + if (!ret) { 147 + /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ 148 + cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 149 + CS35L41_FIRMWARE_ROOT, 150 + cs35l41->acpi_subsystem_id, cs35l41->amp_name, 151 + cs35l41->speaker_id, "bin"); 152 + return 0; 153 + } 154 + 155 + /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ 156 + ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, 157 + CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, 158 + cs35l41->amp_name, -1, "wmfw"); 159 + if (!ret) { 160 + /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ 161 + cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 162 + CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, 163 + cs35l41->amp_name, cs35l41->speaker_id, "bin"); 164 + return 0; 165 + } 166 + 167 + /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */ 168 + ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, 169 + CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, 170 + NULL, cs35l41->speaker_id, "wmfw"); 171 + if (!ret) { 172 + /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ 173 + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 174 + CS35L41_FIRMWARE_ROOT, 175 + cs35l41->acpi_subsystem_id, 176 + cs35l41->amp_name, cs35l41->speaker_id, "bin"); 177 + if (ret) 178 + /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ 179 + cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 180 + CS35L41_FIRMWARE_ROOT, 181 + cs35l41->acpi_subsystem_id, 182 + NULL, cs35l41->speaker_id, "bin"); 183 + return 0; 184 + } 185 + 186 + /* try cirrus/part-dspN-fwtype-sub.wmfw */ 187 + ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, 188 + CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, 189 + NULL, -1, "wmfw"); 190 + if (!ret) { 191 + /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ 192 + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 193 + CS35L41_FIRMWARE_ROOT, 194 + cs35l41->acpi_subsystem_id, 195 + cs35l41->amp_name, cs35l41->speaker_id, "bin"); 196 + if (ret) 197 + /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ 198 + cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 199 + CS35L41_FIRMWARE_ROOT, 200 + cs35l41->acpi_subsystem_id, 201 + NULL, cs35l41->speaker_id, "bin"); 202 + return 0; 203 + } 204 + 205 + /* fallback try cirrus/part-dspN-fwtype.wmfw */ 206 + ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, 207 + CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); 208 + if (!ret) { 209 + /* fallback try cirrus/part-dspN-fwtype.bin */ 210 + cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 211 + CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); 212 + return 0; 213 + } 214 + 215 + dev_warn(cs35l41->dev, "Failed to request firmware\n"); 216 + 217 + return ret; 218 + } 219 + 139 220 static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, 140 221 const struct firmware **wmfw_firmware, 141 222 char **wmfw_filename, ··· 231 138 { 232 139 int ret; 233 140 141 + if (cs35l41->speaker_id > -1) 142 + return cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename, 143 + coeff_firmware, coeff_filename); 144 + 234 145 /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ 235 146 ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, 236 147 CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, 237 - cs35l41->amp_name, "wmfw"); 148 + cs35l41->amp_name, -1, "wmfw"); 238 149 if (!ret) { 239 150 /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ 240 151 cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 241 152 CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, 242 - cs35l41->amp_name, "bin"); 153 + cs35l41->amp_name, -1, "bin"); 243 154 return 0; 244 155 } 245 156 246 157 /* try cirrus/part-dspN-fwtype-sub.wmfw */ 247 158 ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, 248 159 CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, 249 - NULL, "wmfw"); 160 + NULL, -1, "wmfw"); 250 161 if (!ret) { 251 162 /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ 252 163 ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 253 164 CS35L41_FIRMWARE_ROOT, 254 165 cs35l41->acpi_subsystem_id, 255 - cs35l41->amp_name, "bin"); 166 + cs35l41->amp_name, -1, "bin"); 256 167 if (ret) 257 168 /* try cirrus/part-dspN-fwtype-sub.bin */ 258 169 cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 259 170 CS35L41_FIRMWARE_ROOT, 260 - cs35l41->acpi_subsystem_id, NULL, "bin"); 171 + cs35l41->acpi_subsystem_id, 172 + NULL, -1, "bin"); 261 173 return 0; 262 174 } 263 175 264 176 /* fallback try cirrus/part-dspN-fwtype.wmfw */ 265 177 ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, 266 - CS35L41_FIRMWARE_ROOT, NULL, NULL, "wmfw"); 178 + CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); 267 179 if (!ret) { 268 180 /* fallback try cirrus/part-dspN-fwtype.bin */ 269 181 cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, 270 - CS35L41_FIRMWARE_ROOT, NULL, NULL, "bin"); 182 + CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); 271 183 return 0; 272 184 } 273 185 ··· 712 614 return ret; 713 615 } 714 616 617 + static int cs35l41_get_speaker_id(struct device *dev, int amp_index, 618 + int num_amps, int fixed_gpio_id) 619 + { 620 + struct gpio_desc *speaker_id_desc; 621 + int speaker_id = -ENODEV; 622 + 623 + if (fixed_gpio_id >= 0) { 624 + dev_dbg(dev, "Found Fixed Speaker ID GPIO (index = %d)\n", fixed_gpio_id); 625 + speaker_id_desc = gpiod_get_index(dev, NULL, fixed_gpio_id, GPIOD_IN); 626 + if (IS_ERR(speaker_id_desc)) { 627 + speaker_id = PTR_ERR(speaker_id_desc); 628 + return speaker_id; 629 + } 630 + speaker_id = gpiod_get_value_cansleep(speaker_id_desc); 631 + gpiod_put(speaker_id_desc); 632 + dev_dbg(dev, "Speaker ID = %d\n", speaker_id); 633 + } else { 634 + int base_index; 635 + int gpios_per_amp; 636 + int count; 637 + int tmp; 638 + int i; 639 + 640 + count = gpiod_count(dev, "spk-id"); 641 + if (count > 0) { 642 + speaker_id = 0; 643 + gpios_per_amp = count / num_amps; 644 + base_index = gpios_per_amp * amp_index; 645 + 646 + if (count % num_amps) 647 + return -EINVAL; 648 + 649 + dev_dbg(dev, "Found %d Speaker ID GPIOs per Amp\n", gpios_per_amp); 650 + 651 + for (i = 0; i < gpios_per_amp; i++) { 652 + speaker_id_desc = gpiod_get_index(dev, "spk-id", i + base_index, 653 + GPIOD_IN); 654 + if (IS_ERR(speaker_id_desc)) { 655 + speaker_id = PTR_ERR(speaker_id_desc); 656 + break; 657 + } 658 + tmp = gpiod_get_value_cansleep(speaker_id_desc); 659 + gpiod_put(speaker_id_desc); 660 + if (tmp < 0) { 661 + speaker_id = tmp; 662 + break; 663 + } 664 + speaker_id |= tmp << i; 665 + } 666 + dev_dbg(dev, "Speaker ID = %d\n", speaker_id); 667 + } 668 + } 669 + return speaker_id; 670 + } 671 + 715 672 static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id) 716 673 { 717 674 struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; ··· 872 719 else 873 720 hw_cfg->bst_cap = -1; 874 721 722 + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, cs35l41->index, nval, -1); 723 + 875 724 if (hw_cfg->bst_ind > 0 || hw_cfg->bst_cap > 0 || hw_cfg->bst_ipk > 0) 876 725 hw_cfg->bst_type = CS35L41_INT_BOOST; 877 726 else ··· 907 752 cs35l41->channel_index = 0; 908 753 cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); 909 754 cs35l41->hw_cfg.bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH; 755 + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); 910 756 hw_cfg->gpio2.func = CS35L41_GPIO2_INT_OPEN_DRAIN; 911 757 hw_cfg->gpio2.valid = true; 912 758 cs35l41->hw_cfg.valid = true;
+1
sound/pci/hda/cs35l41_hda.h
··· 43 43 unsigned volatile long irq_errors; 44 44 const char *amp_name; 45 45 const char *acpi_subsystem_id; 46 + int speaker_id; 46 47 struct mutex fw_mutex; 47 48 struct regmap_irq_chip_data *irq_data; 48 49 bool firmware_running;