@@ -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
static bool firmware_autostart = 1;
module_param(firmware_autostart, bool, 0444);
@@ -520,6 +523,31 @@ static void cs35l41_hda_play_start(struct device *dev)
}
+static void cs35l41_mute(struct device *dev, bool mute)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Mute(%d:%d) Playback Started: %d\n", mute, cs35l41->mute_override,
+ 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_mute));
+ } 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 = dev_get_drvdata(dev);
@@ -529,13 +557,7 @@ static void cs35l41_hda_play_done(struct device *dev)
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);
}
static void cs35l41_hda_pause_start(struct device *dev)
@@ -545,7 +567,7 @@ static void cs35l41_hda_pause_start(struct device *dev)
dev_dbg(dev, "Pause (Start)\n");
- 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,53 @@ static int cs35l41_create_controls(struct cs35l41_hda *cs35l41)
return 0;
}
+static bool cs35l41_dsm_supported(acpi_handle handle, unsigned int commands)
+{
+ guid_t guid;
+
+ guid_parse(CS35L41_UUID, &guid);
+
+ return acpi_check_dsm(handle, &guid, 0, BIT(commands));
+}
+
+static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_handle handle)
+{
+ guid_t guid;
+ union acpi_object *ret;
+ int mute = -ENODEV;
+
+ guid_parse(CS35L41_UUID, &guid);
+
+ if (cs35l41_dsm_supported(handle, CS35L41_DSM_GET_MUTE)) {
+ ret = acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL);
+ mute = *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, struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ int mute;
+
+ if (event != CS35L41_NOTIFY_EVENT)
+ return;
+
+ mute = 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 = (mute > 0);
+ cs35l41_mute(cs35l41->dev, cs35l41->mute_override);
+}
+
static int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
@@ -1114,6 +1183,14 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
comps->playback_hook = cs35l41_hda_playback_hook;
comps->pre_playback_hook = cs35l41_hda_pre_playback_hook;
comps->post_playback_hook = cs35l41_hda_post_playback_hook;
+ comps->acpi_notify = cs35l41_acpi_device_notify;
+ comps->adev = cs35l41->dacpi;
+
+ comps->acpi_notifications_supported = cs35l41_dsm_supported(acpi_device_handle(comps->adev),
+ CS35L41_DSM_GET_MUTE);
+
+ cs35l41->mute_override = cs35l41_get_acpi_mute_state(cs35l41,
+ acpi_device_handle(cs35l41->dacpi)) > 0;
mutex_unlock(&cs35l41->fw_mutex);
@@ -1387,8 +1464,8 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
return -ENODEV;
}
+ cs35l41->dacpi = adev;
physdev = get_device(acpi_get_first_physical_node(adev));
- acpi_dev_put(adev);
sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
if (IS_ERR(sub))
@@ -1498,6 +1575,7 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
hw_cfg->valid = false;
hw_cfg->gpio1.valid = false;
hw_cfg->gpio2.valid = false;
+ acpi_dev_put(cs35l41->dacpi);
put_physdev:
put_device(physdev);
@@ -1601,10 +1679,7 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
if (ret)
goto err;
- ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute,
- ARRAY_SIZE(cs35l41_hda_mute));
- if (ret)
- goto err;
+ cs35l41_mute(cs35l41->dev, true);
INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work);
mutex_init(&cs35l41->fw_mutex);
@@ -1641,6 +1716,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);
return ret;
@@ -1659,6 +1735,8 @@ void cs35l41_hda_remove(struct device *dev)
component_del(cs35l41->dev, &cs35l41_hda_comp_ops);
+ acpi_dev_put(cs35l41->dacpi);
+
pm_runtime_put_noidle(cs35l41->dev);
if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
@@ -10,6 +10,7 @@
#ifndef __CS35L41_HDA_H__
#define __CS35L41_HDA_H__
+#include <linux/acpi.h>
#include <linux/efi.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
@@ -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;
};
enum halo_state {
@@ -6,6 +6,7 @@
* Cirrus Logic International Semiconductor Ltd.
*/
+#include <linux/acpi.h>
#include <linux/component.h>
#define HDA_MAX_COMPONENTS 4
@@ -15,6 +16,9 @@ struct hda_component {
struct device *dev;
char name[HDA_MAX_NAME_SIZE];
struct hda_codec *codec;
+ struct acpi_device *adev;
+ bool acpi_notifications_supported;
+ 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);
@@ -10,6 +10,7 @@
* Jonathan Woithe <jwoithe@just42.net>
*/
+#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -6704,19 +6705,73 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
}
}
+static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct hda_codec *cdc = data;
+ struct alc_spec *spec = cdc->spec;
+ int i;
+
+ codec_info(cdc, "ACPI Notification %d\n", event);
+
+ for (i = 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), event,
+ spec->comps[i].dev);
+ }
+}
+
static int comp_bind(struct device *dev)
{
struct hda_codec *cdc = dev_to_hda_codec(dev);
struct alc_spec *spec = cdc->spec;
+ struct acpi_device *adev;
+ bool support_notifications = false;
+ int ret;
+ int i;
+
+ ret = component_bind_all(dev, spec->comps);
+ if (ret)
+ return ret;
- return component_bind_all(dev, spec->comps);
+ adev = spec->comps[0].adev;
+ if (!acpi_device_handle(adev))
+ return 0;
+
+ for (i = 0; i < HDA_MAX_COMPONENTS; i++)
+ support_notifications = support_notifications ||
+ spec->comps[i].acpi_notifications_supported;
+
+ if (support_notifications) {
+ ret = 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;
}
static void comp_unbind(struct device *dev)
{
struct hda_codec *cdc = dev_to_hda_codec(dev);
struct alc_spec *spec = cdc->spec;
+ struct acpi_device *adev;
+ int ret;
+
+ adev = spec->comps[0].adev;
+ if (!acpi_device_handle(adev))
+ goto unbind;
+
+ ret = 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);
+unbind:
component_unbind_all(dev, spec->comps);
}