From: Maciej Strozek <mstrozek@opensource.cirrus.com>
To: linusw@kernel.org
Cc: brgl@kernel.org, patches@opensource.cirrus.com,
linux-gpio@vger.kernel.org, linux-sound@vger.kernel.org,
linux-kernel@vger.kernel.org,
Maciej Strozek <mstrozek@opensource.cirrus.com>
Subject: [PATCH] pinctrl: cs42l43: Add GPIO controlled mic/spk mute LEDs
Date: Tue, 10 Nov 2026 14:20:58 +0000 [thread overview]
Message-ID: <20261110142058.354518-1-mstrozek@opensource.cirrus.com> (raw)
Add ability to use cs42l43's GPIOs to drive speaker and microphone mute
LEDs. The LEDs must be described using
01fa-global-mute-led-(spk|mic)-mute-gpio ACPI property, which tells
which of the 3 available cs42l43's GPIOs to use for the respective
function.
Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
---
drivers/pinctrl/cirrus/pinctrl-cs42l43.c | 156 +++++++++++++++++++++++
1 file changed, 156 insertions(+)
diff --git a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c
index 8990fab0446cf..7e4e43783ef14 100644
--- a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c
+++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c
@@ -5,11 +5,15 @@
// Copyright (c) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
+#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/build_bug.h>
#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
+#include <linux/gpio/machine.h>
+#include <linux/leds.h>
#include <linux/mfd/cs42l43.h>
#include <linux/mfd/cs42l43-regs.h>
#include <linux/module.h>
@@ -29,9 +33,18 @@
#define CS42L43_NUM_GPIOS 3
+struct cs42l43_mute_led {
+ struct led_classdev cdev;
+ struct gpio_desc *gpio;
+ u32 gpio_num;
+};
+
struct cs42l43_pin {
struct gpio_chip gpio_chip;
+ struct cs42l43_mute_led spk_mute_led;
+ struct cs42l43_mute_led mic_mute_led;
+
struct device *dev;
struct regmap *regmap;
bool shutters_locked;
@@ -535,6 +548,143 @@ static void cs42l43_fwnode_put(void *data)
fwnode_handle_put(data);
}
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+static int cs42l43_find_mute_gpios(struct cs42l43_pin *priv, struct fwnode_handle *fwnode)
+{
+ struct cs42l43_mute_led *mic = &priv->mic_mute_led;
+ struct cs42l43_mute_led *spk = &priv->spk_mute_led;
+ int ret;
+
+ fwnode_for_each_child_node_scoped(fwnode, child_fwnode) {
+ struct fwnode_handle *ext_fwnode __free(fwnode_handle) =
+ fwnode_get_named_child_node(child_fwnode,
+ "mipi-sdca-function-expansion-subproperties");
+ bool mic_present, spk_present;
+
+ if (!ext_fwnode)
+ continue;
+
+ mic_present = fwnode_property_present(ext_fwnode,
+ "01fa-global-mute-led-mic-mute-gpio");
+ spk_present = fwnode_property_present(ext_fwnode,
+ "01fa-global-mute-led-spk-mute-gpio");
+ if (!mic_present && !spk_present)
+ continue;
+
+ if (mic_present) {
+ ret = fwnode_property_read_u32(ext_fwnode,
+ "01fa-global-mute-led-mic-mute-gpio",
+ &mic->gpio_num);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "Invalid mic-mute LED GPIO property\n");
+ }
+
+ if (spk_present) {
+ ret = fwnode_property_read_u32(ext_fwnode,
+ "01fa-global-mute-led-spk-mute-gpio",
+ &spk->gpio_num);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "Invalid spk-mute LED GPIO property\n");
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static int cs42l43_mute_led_set(struct led_classdev *cdev, enum led_brightness value)
+{
+ struct cs42l43_mute_led *led = container_of(cdev, struct cs42l43_mute_led, cdev);
+
+ return gpiod_set_value_cansleep(led->gpio, value != LED_OFF);
+}
+
+static void cs42l43_mute_led_gpio_free(void *data)
+{
+ struct cs42l43_mute_led *led = data;
+
+ gpiochip_free_own_desc(led->gpio);
+}
+
+static int cs42l43_mute_led_register(struct cs42l43_pin *priv,
+ struct cs42l43_mute_led *led,
+ const char *function, const char *trigger)
+{
+ int ret;
+
+ led->gpio = gpiochip_request_own_desc(&priv->gpio_chip, led->gpio_num - 1,
+ trigger, GPIO_ACTIVE_HIGH,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(led->gpio))
+ return dev_err_probe(priv->dev, PTR_ERR(led->gpio),
+ "Failed to request GPIO%u for %s LED\n",
+ led->gpio_num, trigger);
+
+ ret = devm_add_action_or_reset(priv->dev, cs42l43_mute_led_gpio_free, led);
+ if (ret)
+ return ret;
+
+ led->cdev.name = devm_kasprintf(priv->dev, GFP_KERNEL, "%s::%s",
+ dev_name(priv->dev), function);
+ if (!led->cdev.name)
+ return -ENOMEM;
+
+ led->cdev.default_trigger = trigger;
+ led->cdev.brightness_set_blocking = cs42l43_mute_led_set;
+ led->cdev.max_brightness = 1;
+
+ ret = devm_led_classdev_register(priv->dev, &led->cdev);
+ if (ret)
+ return dev_err_probe(priv->dev, ret, "Failed to register %s LED\n",
+ trigger);
+
+ dev_dbg(priv->dev, "%s LED uses codec GPIO%u\n", trigger, led->gpio_num);
+
+ return 0;
+}
+
+static int cs42l43_mute_leds_register(struct cs42l43_pin *priv,
+ struct fwnode_handle *fwnode)
+{
+ struct cs42l43_mute_led *mic = &priv->mic_mute_led;
+ struct cs42l43_mute_led *spk = &priv->spk_mute_led;
+ int ret;
+
+ if (!is_acpi_node(fwnode))
+ return 0;
+
+ ret = cs42l43_find_mute_gpios(priv, fwnode);
+ if (ret)
+ return ret;
+
+ if (mic->gpio_num > CS42L43_NUM_GPIOS ||
+ spk->gpio_num > CS42L43_NUM_GPIOS ||
+ (mic->gpio_num && mic->gpio_num == spk->gpio_num))
+ return dev_err_probe(priv->dev, -EINVAL,
+ "Invalid or shared mute LED GPIOs: mic=%u, spk=%u\n",
+ mic->gpio_num, spk->gpio_num);
+
+ if (mic->gpio_num) {
+ ret = cs42l43_mute_led_register(priv, mic, LED_FUNCTION_MICMUTE,
+ "audio-micmute");
+ if (ret)
+ return ret;
+ }
+
+ if (spk->gpio_num) {
+ ret = cs42l43_mute_led_register(priv, spk, LED_FUNCTION_MUTE,
+ "audio-mute");
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
static int cs42l43_pin_probe(struct platform_device *pdev)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
@@ -598,6 +748,12 @@ static int cs42l43_pin_probe(struct platform_device *pdev)
return dev_err_probe(priv->dev, ret,
"Failed to register gpiochip\n");
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ ret = cs42l43_mute_leds_register(priv, dev_fwnode(cs42l43->dev));
+ if (ret)
+ return ret;
+#endif
+
return 0;
}
--
2.47.3
next reply other threads:[~2026-11-10 14:21 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 14:20 Maciej Strozek [this message]
2026-11-10 14:40 ` [PATCH] pinctrl: cs42l43: Add GPIO controlled mic/spk mute LEDs sashiko-bot
2026-11-11 11:01 ` Charles Keepax
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261110142058.354518-1-mstrozek@opensource.cirrus.com \
--to=mstrozek@opensource.cirrus.com \
--cc=brgl@kernel.org \
--cc=linusw@kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=patches@opensource.cirrus.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.