Message ID | 20211228191518.bb4fafff9439d42cf542375a@uvos.xyz (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [RFC,1/3] ASoC: simple-card-utils: add support for componants provideing jack events via set_jack | expand |
Implements an interrupt handler that fires when a headphone
is inserted. A jack must be provided to the codec via
snd_soc_component_driver .set_jack
Signed-off-by: Carl Philipp Klemm <philipp@uvos.xyz>
---
sound/soc/codecs/cpcap.c | 104 +++++++++++++++++++++++++++++++++++----
1 file changed, 94 insertions(+), 10 deletions(-)
diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c
index 3c2bc98031b5..ed418ac75eca 100644
--- a/sound/soc/codecs/cpcap.c
+++ b/sound/soc/codecs/cpcap.c
@@ -15,6 +15,7 @@
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/tlv.h>
+#include <sound/jack.h>
/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */
#define CPCAP_BIT_AUDIO_LOW_PWR 6
@@ -252,8 +253,14 @@ enum cpcap_dai {
};
struct cpcap_audio {
+ struct device *dev;
struct snd_soc_component *component;
struct regmap *regmap;
+ struct snd_soc_jack *hp_jack;
+
+ struct delayed_work jack_detect_work;
+
+ int hp_irq;
u16 vendor;
@@ -603,6 +610,21 @@ static int cpcap_input_left_mux_put_enum(struct snd_kcontrol *kcontrol,
return 0;
}
+static struct snd_soc_jack_pin headset_jack_pins[] = {
+ {
+ .pin = "Headset Right Playback Route",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Left Playback Route",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ }
+};
+
static const struct snd_kcontrol_new cpcap_input_left_mux =
SOC_DAPM_ENUM_EXT("Input Left", cpcap_input_left_mux_enum,
cpcap_input_left_mux_get_enum,
@@ -1561,8 +1583,6 @@ static int cpcap_dai_mux(struct cpcap_audio *cpcap, bool swap_dai_configuration)
u16 voice_mask = BIT(CPCAP_BIT_DIG_AUD_IN);
int err;
-
-
if (!swap_dai_configuration) {
/* Codec on DAI0, HiFi on DAI1 */
voice_val = 0;
@@ -1586,6 +1606,44 @@ static int cpcap_dai_mux(struct cpcap_audio *cpcap, bool swap_dai_configuration)
return 0;
}
+static irqreturn_t cpcap_hp_irq_thread(int irq, void *arg)
+{
+ struct cpcap_audio *cpcap = arg;
+ int val = -1;
+ bool plugged;
+
+ regmap_read(cpcap->regmap, CPCAP_REG_INTS1, &val);
+ plugged = val & (1<<9);
+
+ if (!cpcap->component) {
+ dev_warn(cpcap->dev, "%s called before component is ready.", __func__);
+ return IRQ_HANDLED;
+ }
+
+ if (!cpcap->hp_jack) {
+ dev_warn(cpcap->dev, "%s called before jack is ready.", __func__);
+ return IRQ_HANDLED;
+ }
+
+ snd_soc_jack_report(cpcap->hp_jack, plugged ? 0 : SND_JACK_HEADPHONE, SND_JACK_HEADPHONE);
+
+ return IRQ_HANDLED;
+}
+
+static int cpcap_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hp_jack, void *data)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ if (!cpcap->hp_jack) {
+ dev_info(cpcap->dev, "registering jack");
+ cpcap->hp_jack = hp_jack;
+ snd_soc_jack_add_pins(hp_jack, ARRAY_SIZE(headset_jack_pins), headset_jack_pins);
+ }
+
+ return 0;
+}
+
static int cpcap_audio_reset(struct snd_soc_component *component,
bool swap_dai_configuration)
{
@@ -1628,13 +1686,9 @@ static int cpcap_audio_reset(struct snd_soc_component *component,
static int cpcap_soc_probe(struct snd_soc_component *component)
{
- struct cpcap_audio *cpcap;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
int err;
- cpcap = devm_kzalloc(component->dev, sizeof(*cpcap), GFP_KERNEL);
- if (!cpcap)
- return -ENOMEM;
- snd_soc_component_set_drvdata(component, cpcap);
cpcap->component = component;
cpcap->regmap = dev_get_regmap(component->dev->parent, NULL);
@@ -1657,6 +1711,7 @@ static struct snd_soc_component_driver soc_codec_dev_cpcap = {
.num_dapm_widgets = ARRAY_SIZE(cpcap_dapm_widgets),
.dapm_routes = intercon,
.num_dapm_routes = ARRAY_SIZE(intercon),
+ .set_jack = cpcap_set_jack_detect,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
@@ -1665,13 +1720,42 @@ static struct snd_soc_component_driver soc_codec_dev_cpcap = {
static int cpcap_codec_probe(struct platform_device *pdev)
{
- struct device_node *codec_node =
- of_get_child_by_name(pdev->dev.parent->of_node, "audio-codec");
+ struct cpcap_audio *cpcap;
+ struct device_node *codec_node;
+ int ret;
+ codec_node = of_get_child_by_name(pdev->dev.parent->of_node, "audio-codec");
pdev->dev.of_node = codec_node;
- return devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_cpcap,
+ cpcap = devm_kzalloc(&pdev->dev, sizeof(*cpcap), GFP_KERNEL);
+ if (!cpcap)
+ return -ENOMEM;
+ dev_set_drvdata(&pdev->dev, cpcap);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_cpcap,
cpcap_dai, ARRAY_SIZE(cpcap_dai));
+ if (ret < 0)
+ return ret;
+
+ cpcap->hp_irq = platform_get_irq_byname(pdev, "hpplugged");
+ if (cpcap->hp_irq < 0)
+ return -ENODEV;
+
+ cpcap->dev = &pdev->dev;
+
+ ret = devm_request_threaded_irq(&pdev->dev, cpcap->hp_irq, NULL,
+ cpcap_hp_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-headphone", cpcap);
+ if (ret) {
+ dev_err(&pdev->dev, "could not get irq: %i\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
}
static struct platform_driver cpcap_codec_driver = {
diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c index 3c2bc98031b5..ed418ac75eca 100644 --- a/sound/soc/codecs/cpcap.c +++ b/sound/soc/codecs/cpcap.c @@ -15,6 +15,7 @@ #include <sound/core.h> #include <sound/soc.h> #include <sound/tlv.h> +#include <sound/jack.h> /* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */ #define CPCAP_BIT_AUDIO_LOW_PWR 6 @@ -252,8 +253,14 @@ enum cpcap_dai { }; struct cpcap_audio { + struct device *dev; struct snd_soc_component *component; struct regmap *regmap; + struct snd_soc_jack *hp_jack; + + struct delayed_work jack_detect_work; + + int hp_irq; u16 vendor; @@ -603,6 +610,21 @@ static int cpcap_input_left_mux_put_enum(struct snd_kcontrol *kcontrol, return 0; } +static struct snd_soc_jack_pin headset_jack_pins[] = { + { + .pin = "Headset Right Playback Route", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Left Playback Route", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + } +}; + static const struct snd_kcontrol_new cpcap_input_left_mux = SOC_DAPM_ENUM_EXT("Input Left", cpcap_input_left_mux_enum, cpcap_input_left_mux_get_enum, @@ -1561,8 +1583,6 @@ static int cpcap_dai_mux(struct cpcap_audio *cpcap, bool swap_dai_configuration) u16 voice_mask = BIT(CPCAP_BIT_DIG_AUD_IN); int err; - - if (!swap_dai_configuration) { /* Codec on DAI0, HiFi on DAI1 */ voice_val = 0; @@ -1586,6 +1606,44 @@ static int cpcap_dai_mux(struct cpcap_audio *cpcap, bool swap_dai_configuration) return 0; } +static irqreturn_t cpcap_hp_irq_thread(int irq, void *arg) +{ + struct cpcap_audio *cpcap = arg; + int val = -1; + bool plugged; + + regmap_read(cpcap->regmap, CPCAP_REG_INTS1, &val); + plugged = val & (1<<9); + + if (!cpcap->component) { + dev_warn(cpcap->dev, "%s called before component is ready.", __func__); + return IRQ_HANDLED; + } + + if (!cpcap->hp_jack) { + dev_warn(cpcap->dev, "%s called before jack is ready.", __func__); + return IRQ_HANDLED; + } + + snd_soc_jack_report(cpcap->hp_jack, plugged ? 0 : SND_JACK_HEADPHONE, SND_JACK_HEADPHONE); + + return IRQ_HANDLED; +} + +static int cpcap_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hp_jack, void *data) +{ + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + + if (!cpcap->hp_jack) { + dev_info(cpcap->dev, "registering jack"); + cpcap->hp_jack = hp_jack; + snd_soc_jack_add_pins(hp_jack, ARRAY_SIZE(headset_jack_pins), headset_jack_pins); + } + + return 0; +} +