lkml.org 
[lkml]   [2022]   [May]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v4 08/17] ALSA: hda: cs35l41: Support Speaker ID for laptops
    Date
    From: Stefan Binding <sbinding@opensource.cirrus.com>

    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>
    ---

    Changes since v2:
    - No change

    sound/pci/hda/cs35l41_hda.c | 174 ++++++++++++++++++++++++++++++++++--
    sound/pci/hda/cs35l41_hda.h | 1 +
    2 files changed, 166 insertions(+), 9 deletions(-)

    diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
    index 0957b4984143..1d62a41fbc75 100644
    --- a/sound/pci/hda/cs35l41_hda.c
    +++ b/sound/pci/hda/cs35l41_hda.c
    @@ -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;
    }

    @@ -602,6 +700,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;
    @@ -707,6 +860,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
    @@ -740,6 +895,7 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
    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;
    diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h
    index b8352088a5cb..717b30a2e33b 100644
    --- a/sound/pci/hda/cs35l41_hda.h
    +++ b/sound/pci/hda/cs35l41_hda.h
    @@ -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 regmap_irq_chip_data *irq_data;
    bool firmware_running;
    bool halo_initialized;
    --
    2.34.1
    \
     
     \ /
      Last update: 2022-05-25 15:19    [W:2.924 / U:0.048 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site