From nobody Tue Dec 16 23:57:28 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 32C7DC3DA6F for ; Fri, 25 Aug 2023 12:06:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242192AbjHYMGF (ORCPT ); Fri, 25 Aug 2023 08:06:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55982 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240494AbjHYMFx (ORCPT ); Fri, 25 Aug 2023 08:05:53 -0400 Received: from mx0b-001ae601.pphosted.com (mx0a-001ae601.pphosted.com [67.231.149.25]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 241A6198E for ; Fri, 25 Aug 2023 05:05:51 -0700 (PDT) Received: from pps.filterd (m0077473.ppops.net [127.0.0.1]) by mx0a-001ae601.pphosted.com (8.17.1.22/8.17.1.22) with ESMTP id 37P7NERQ031680; Fri, 25 Aug 2023 07:05:35 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h= from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding:content-type; s=PODMain02222019; bh=r SouBPNdMZcZhCzIT7pAU6d/WOGmytOweVxRAv/Jk6s=; b=T5KNMLvuLkNtM0exv ImlsIsJZ95f6fMS0qRBePZKdSIFZsIMqw8R1sAmb2SfPCe/aaOXdXVuF2213bibh XJZ4zYUeks52if2YFZTG1wrt40vneuvk7OjMRjoMubMmBRQFzi5v4NORBAQczUYZ +zxEVuXyxRE92JAgGjUfdJHXdpDlTxxhuTfk5jWW+DUAXMtokWJpOssM3XniAX+p nDrGSnKD2NzuUxfQnLmBOAGmxEGPSLLpsz7PABMQHHymxFdXkuBRIUBtQwEydtPd yrR3qxjmHaITifHO2VcdPbu/IPibGzK9Imb3ONyFOe4wv9Enn5vfBOuwrzKROHtp 4++iA== Received: from ediex02.ad.cirrus.com ([84.19.233.68]) by mx0a-001ae601.pphosted.com (PPS) with ESMTPS id 3sp1rbj064-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 25 Aug 2023 07:05:35 -0500 (CDT) Received: from ediex02.ad.cirrus.com (198.61.84.81) by ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.30; Fri, 25 Aug 2023 13:05:32 +0100 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by anon-ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server id 15.2.1118.30 via Frontend Transport; Fri, 25 Aug 2023 13:05:32 +0100 Received: from sbinding-cirrus-dsktp2.ad.cirrus.com (unknown [198.90.238.85]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id A6FC711AB; Fri, 25 Aug 2023 12:05:32 +0000 (UTC) From: Stefan Binding To: Jaroslav Kysela , Takashi Iwai CC: , , , Vitaly Rodionov , Stefan Binding Subject: [PATCH v1] ALSA: hda: cs35l41: Support mute notifications for CS35L41 HDA Date: Fri, 25 Aug 2023 13:05:25 +0100 Message-ID: <20230825120525.1337417-1-sbinding@opensource.cirrus.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: 9Vnqo6I9vFOcLfrSXzvmbMqOZJVMQ8fN X-Proofpoint-GUID: 9Vnqo6I9vFOcLfrSXzvmbMqOZJVMQ8fN X-Proofpoint-Spam-Reason: safe Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Vitaly Rodionov Some laptops require a hardware based mute system, where when a hotkey is pressed, it forces the amp to be muted. For CS35L41, when the hotkey is pressed, an acpi notification is sent to the CS35L41 Device Node. The driver needs to handle this notification and call a _DSM function to retrieve the mute state. Since the amp is only muted during playback, the driver will only mute or unmute if playback is occurring, otherwise it will save the mute state for when playback starts. Only one handler can be registered for the acpi notification, but all amps need to receive that notification, we can register a single handler inside the Realtek HDA driver, so that it can then notify through the component framework. Signed-off-by: Vitaly Rodionov Signed-off-by: Stefan Binding --- sound/pci/hda/cs35l41_hda.c | 92 ++++++++++++++++++++++++++++++----- sound/pci/hda/cs35l41_hda.h | 3 ++ sound/pci/hda/hda_component.h | 3 ++ sound/pci/hda/patch_realtek.c | 48 +++++++++++++++++- 4 files changed, 132 insertions(+), 14 deletions(-) diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index f9b77353c266..609e63b34d6d 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -33,6 +33,9 @@ #define CAL_AMBIENT_DSP_CTL_NAME "CAL_AMBIENT" #define CAL_DSP_CTL_TYPE 5 #define CAL_DSP_CTL_ALG 205 +#define CS35L41_UUID "50d90cdc-3de4-4f18-b528-c7fe3b71f40d" +#define CS35L41_DSM_GET_MUTE 5 +#define CS35L41_NOTIFY_EVENT 0x91 =20 static bool firmware_autostart =3D 1; module_param(firmware_autostart, bool, 0444); @@ -520,6 +523,31 @@ static void cs35l41_hda_play_start(struct device *dev) =20 } =20 +static void cs35l41_mute(struct device *dev, bool mute) +{ + struct cs35l41_hda *cs35l41 =3D dev_get_drvdata(dev); + struct regmap *reg =3D cs35l41->regmap; + + dev_dbg(dev, "Mute(%d:%d) Playback Started: %d\n", mute, cs35l41->mute_ov= erride, + cs35l41->playback_started); + + if (cs35l41->playback_started) { + if (mute || cs35l41->mute_override) { + dev_dbg(dev, "Muting\n"); + regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mu= te)); + } else { + dev_dbg(dev, "Unmuting\n"); + if (cs35l41->firmware_running) { + regmap_multi_reg_write(reg, cs35l41_hda_unmute_dsp, + ARRAY_SIZE(cs35l41_hda_unmute_dsp)); + } else { + regmap_multi_reg_write(reg, cs35l41_hda_unmute, + ARRAY_SIZE(cs35l41_hda_unmute)); + } + } + } +} + static void cs35l41_hda_play_done(struct device *dev) { struct cs35l41_hda *cs35l41 =3D dev_get_drvdata(dev); @@ -529,13 +557,7 @@ static void cs35l41_hda_play_done(struct device *dev) =20 cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 1, NULL, cs35l41->firmware_running); - if (cs35l41->firmware_running) { - regmap_multi_reg_write(reg, cs35l41_hda_unmute_dsp, - ARRAY_SIZE(cs35l41_hda_unmute_dsp)); - } else { - regmap_multi_reg_write(reg, cs35l41_hda_unmute, - ARRAY_SIZE(cs35l41_hda_unmute)); - } + cs35l41_mute(dev, false); } =20 static void cs35l41_hda_pause_start(struct device *dev) @@ -545,7 +567,7 @@ static void cs35l41_hda_pause_start(struct device *dev) =20 dev_dbg(dev, "Pause (Start)\n"); =20 - regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute= )); + cs35l41_mute(dev, true); cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 0, NULL, cs35l41->firmware_running); } @@ -1073,6 +1095,44 @@ static int cs35l41_create_controls(struct cs35l41_hd= a *cs35l41) return 0; } =20 +static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_h= andle handle) +{ + guid_t guid; + union acpi_object *ret; + int mute =3D -ENODEV; + + guid_parse(CS35L41_UUID, &guid); + + if (acpi_check_dsm(handle, &guid, 0, BIT(CS35L41_DSM_GET_MUTE))) { + ret =3D acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL); + mute =3D *ret->buffer.pointer; + dev_dbg(cs35l41->dev, "CS35L41_DSM_GET_MUTE: %d\n", mute); + } + + dev_dbg(cs35l41->dev, "%s: %d\n", __func__, mute); + + return mute; +} + +static void cs35l41_acpi_device_notify(acpi_handle handle, u32 event, stru= ct device *dev) +{ + struct cs35l41_hda *cs35l41 =3D dev_get_drvdata(dev); + int mute; + + if (event !=3D CS35L41_NOTIFY_EVENT) + return; + + mute =3D cs35l41_get_acpi_mute_state(cs35l41, handle); + if (mute < 0) { + dev_warn(cs35l41->dev, "Unable to retrieve mute state: %d\n", mute); + return; + } + + dev_dbg(cs35l41->dev, "Requesting mute value: %d\n", mute); + cs35l41->mute_override =3D (mute > 0); + cs35l41_mute(cs35l41->dev, cs35l41->mute_override); +} + static int cs35l41_hda_bind(struct device *dev, struct device *master, voi= d *master_data) { struct cs35l41_hda *cs35l41 =3D dev_get_drvdata(dev); @@ -1114,6 +1174,11 @@ static int cs35l41_hda_bind(struct device *dev, stru= ct device *master, void *mas comps->playback_hook =3D cs35l41_hda_playback_hook; comps->pre_playback_hook =3D cs35l41_hda_pre_playback_hook; comps->post_playback_hook =3D cs35l41_hda_post_playback_hook; + comps->acpi_notify =3D cs35l41_acpi_device_notify; + comps->adev =3D cs35l41->dacpi; + + cs35l41->mute_override =3D cs35l41_get_acpi_mute_state(cs35l41, + acpi_device_handle(cs35l41->dacpi)) > 0; =20 mutex_unlock(&cs35l41->fw_mutex); =20 @@ -1387,8 +1452,8 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *= cs35l41, const char *hid, i return -ENODEV; } =20 + cs35l41->dacpi =3D adev; physdev =3D get_device(acpi_get_first_physical_node(adev)); - acpi_dev_put(adev); =20 sub =3D acpi_get_subsystem_id(ACPI_HANDLE(physdev)); if (IS_ERR(sub)) @@ -1498,6 +1563,7 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *= cs35l41, const char *hid, i hw_cfg->valid =3D false; hw_cfg->gpio1.valid =3D false; hw_cfg->gpio2.valid =3D false; + acpi_dev_put(cs35l41->dacpi); put_physdev: put_device(physdev); =20 @@ -1601,10 +1667,7 @@ int cs35l41_hda_probe(struct device *dev, const char= *device_name, int id, int i if (ret) goto err; =20 - ret =3D regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute, - ARRAY_SIZE(cs35l41_hda_mute)); - if (ret) - goto err; + cs35l41_mute(cs35l41->dev, true); =20 INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work); mutex_init(&cs35l41->fw_mutex); @@ -1641,6 +1704,7 @@ int cs35l41_hda_probe(struct device *dev, const char = *device_name, int id, int i if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type)) gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); gpiod_put(cs35l41->reset_gpio); + acpi_dev_put(cs35l41->dacpi); kfree(cs35l41->acpi_subsystem_id); =20 return ret; @@ -1659,6 +1723,8 @@ void cs35l41_hda_remove(struct device *dev) =20 component_del(cs35l41->dev, &cs35l41_hda_comp_ops); =20 + acpi_dev_put(cs35l41->dacpi); + pm_runtime_put_noidle(cs35l41->dev); =20 if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type)) diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h index b93bf762976e..ce3f2bb6ffd0 100644 --- a/sound/pci/hda/cs35l41_hda.h +++ b/sound/pci/hda/cs35l41_hda.h @@ -10,6 +10,7 @@ #ifndef __CS35L41_HDA_H__ #define __CS35L41_HDA_H__ =20 +#include #include #include #include @@ -70,6 +71,8 @@ struct cs35l41_hda { bool halo_initialized; bool playback_started; struct cs_dsp cs_dsp; + struct acpi_device *dacpi; + bool mute_override; }; =20 enum halo_state { diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h index f170aec967c1..c7a9b6a660e5 100644 --- a/sound/pci/hda/hda_component.h +++ b/sound/pci/hda/hda_component.h @@ -6,6 +6,7 @@ * Cirrus Logic International Semiconductor Ltd. */ =20 +#include #include =20 #define HDA_MAX_COMPONENTS 4 @@ -15,6 +16,8 @@ struct hda_component { struct device *dev; char name[HDA_MAX_NAME_SIZE]; struct hda_codec *codec; + struct acpi_device *adev; + void (*acpi_notify)(acpi_handle handle, u32 event, struct device *dev); void (*pre_playback_hook)(struct device *dev, int action); void (*playback_hook)(struct device *dev, int action); void (*post_playback_hook)(struct device *dev, int action); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a07df6f92960..fd3768e73c15 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6704,19 +6704,65 @@ static void alc287_fixup_legion_15imhg05_speakers(s= truct hda_codec *codec, } } =20 +static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *d= ata) +{ + struct hda_codec *cdc =3D data; + struct alc_spec *spec =3D cdc->spec; + int i; + + codec_info(cdc, "ACPI Notification %d\n", event); + + for (i =3D 0; i < HDA_MAX_COMPONENTS; i++) { + if (spec->comps[i].dev && spec->comps[i].acpi_notify) + spec->comps[i].acpi_notify(acpi_device_handle(spec->comps[i].adev), eve= nt, + spec->comps[i].dev); + } +} + static int comp_bind(struct device *dev) { struct hda_codec *cdc =3D dev_to_hda_codec(dev); struct alc_spec *spec =3D cdc->spec; + struct acpi_device *adev; + int ret; + + ret =3D component_bind_all(dev, spec->comps); + if (ret) + return ret; =20 - return component_bind_all(dev, spec->comps); + adev =3D spec->comps[0].adev; + if (!acpi_device_handle(adev)) + return 0; + + ret =3D acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, + comp_acpi_device_notify, cdc); + if (ret < 0) { + codec_warn(cdc, "Failed to install notify handler: %d\n", ret); + return 0; + } + + codec_dbg(cdc, "Notify handler installed\n"); + + return 0; } =20 static void comp_unbind(struct device *dev) { struct hda_codec *cdc =3D dev_to_hda_codec(dev); struct alc_spec *spec =3D cdc->spec; + struct acpi_device *adev; + int ret; + + adev =3D spec->comps[0].adev; + if (!acpi_device_handle(adev)) + goto unbind; + + ret =3D acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, + comp_acpi_device_notify); + if (ret < 0) + codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret); =20 +unbind: component_unbind_all(dev, spec->comps); } =20 --=20 2.34.1