Skip to content

Commit

Permalink
ASoC: cpcap: add headphone jack plug detection support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
IMbackK committed Jul 2, 2022
1 parent 3e25932 commit 0db22f6
Showing 1 changed file with 97 additions and 11 deletions.
108 changes: 97 additions & 11 deletions sound/soc/codecs/cpcap.c
Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -1586,6 +1606,45 @@ 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;
}

dev_info(cpcap->dev, "%s jack state: %i", __func__, plugged ? 0 : SND_JACK_HEADPHONE);
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)
{
Expand Down Expand Up @@ -1628,13 +1687,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);
Expand All @@ -1657,6 +1712,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,
Expand All @@ -1665,15 +1721,45 @@ 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;

if (!codec_node)
return -ENODEV;

pdev->dev.of_node = codec_node;
cpcap = devm_kzalloc(&pdev->dev, sizeof(*cpcap), GFP_KERNEL);
if (!cpcap)
return -ENOMEM;
dev_set_drvdata(&pdev->dev, cpcap);

return devm_snd_soc_register_component(&pdev->dev, &soc_codec_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 = {
Expand Down

0 comments on commit 0db22f6

Please sign in to comment.