summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorZidan Wang <zidan.wang@freescale.com>2015-12-22 14:04:04 +0800
committerNitin Garg <nitin.garg@nxp.com>2016-01-14 11:02:48 -0600
commit7524715475f8ec563dd60cf43caff662f3b3985c (patch)
tree03950aea9d7c7af26de928af9d47d1c563bd22d2 /sound
parentd8ae370033f93becb3ca93fc36b6819496ebba4b (diff)
MLK-12057 ASoC: imx-wm8960: add headphone/micphone/headset jack support
Add headphone/micphone/headset jack support for different board. There are headphone detect gpio and microphone detect gpio which can be configured from device tree. If headphone and microphone using the same gpio for jack detect, it suppose to be a headset and will register a headphone jack for it. If headphone and microphone using different gpio for jack detect, it suppose not to be a headset, and will register headphone jack and microphone jack respectively. Is't not appropriate to set the adc data output in machine driver. It will not be compatibly when we change hardware connection. wm8960 codec driver has added "ADC Data Output Select" kcontrol, so that we can set the ADC data output from user space. Signed-off-by: Zidan Wang <zidan.wang@freescale.com> (cherry picked from commit 92f65b4bee51fabdfa3a3c191f511c2ec7cb18a1)
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/fsl/imx-wm8960.c288
1 files changed, 148 insertions, 140 deletions
diff --git a/sound/soc/fsl/imx-wm8960.c b/sound/soc/fsl/imx-wm8960.c
index c12f5bea733c..bbf3424483ca 100644
--- a/sound/soc/fsl/imx-wm8960.c
+++ b/sound/soc/fsl/imx-wm8960.c
@@ -40,10 +40,10 @@ struct imx_wm8960_data {
};
struct imx_priv {
- int hp_set_gpio;
- int hp_active_low;
- struct snd_kcontrol *headset_kctl;
- struct snd_soc_codec *codec;
+ enum of_gpio_flags hp_active_low;
+ enum of_gpio_flags mic_active_low;
+ bool is_headset_jack;
+ struct snd_kcontrol *headphone_kctl;
struct platform_device *pdev;
struct platform_device *asrc_pdev;
struct snd_card *snd_card;
@@ -51,104 +51,102 @@ struct imx_priv {
static struct imx_priv card_priv;
-static struct snd_soc_jack imx_hp_set;
-static struct snd_soc_jack_pin imx_hp_set_pins[] = {
- {
- .pin = "Headset Jack",
- .mask = SND_JACK_HEADSET,
- },
+static struct snd_soc_jack imx_hp_jack;
+static struct snd_soc_jack_pin imx_hp_jack_pin = {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
};
-static struct snd_soc_jack_gpio imx_hp_set_gpio = {
- .name = "headset detect",
- .report = SND_JACK_HEADSET,
+static struct snd_soc_jack_gpio imx_hp_jack_gpio = {
+ .name = "headphone detect",
+ .report = SND_JACK_HEADPHONE,
.debounce_time = 250,
- .invert = 1,
+ .invert = 0,
};
-static int hp_set_status_check(void *data)
+static struct snd_soc_jack imx_mic_jack;
+static struct snd_soc_jack_pin imx_mic_jack_pins = {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+};
+static struct snd_soc_jack_gpio imx_mic_jack_gpio = {
+ .name = "mic detect",
+ .report = SND_JACK_MICROPHONE,
+ .debounce_time = 250,
+ .invert = 0,
+};
+
+static int hp_jack_status_check(void *data)
{
struct imx_priv *priv = &card_priv;
- struct platform_device *pdev = priv->pdev;
- char *envp[3], *buf;
+ struct snd_soc_jack *jack = data;
+ struct snd_soc_dapm_context *dapm = &jack->card->dapm;
int hp_status, ret;
- if (!gpio_is_valid(priv->hp_set_gpio))
- return 0;
-
- hp_status = gpio_get_value(priv->hp_set_gpio);
- buf = kmalloc(32, GFP_ATOMIC);
- if (!buf) {
- dev_err(&pdev->dev, "%s kmalloc failed\n", __func__);
- return -ENOMEM;
- }
+ hp_status = gpio_get_value(imx_hp_jack_gpio.gpio);
if (hp_status != priv->hp_active_low) {
- snprintf(buf, 32, "STATE=%d", 2);
- snd_soc_dapm_disable_pin(&priv->codec->dapm, "Ext Spk");
- snd_soc_dapm_disable_pin(&priv->codec->dapm, "Main MIC");
- ret = imx_hp_set_gpio.report;
-
- /*
- * As the hp MIC only connect the input for left channel, we
- * need to route it for right channel.
- */
- snd_soc_update_bits(priv->codec, WM8960_ADDCTL1, 3<<2, 1<<2);
-
- snd_kctl_jack_report(priv->snd_card, priv->headset_kctl, 1);
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk");
+ if (priv->is_headset_jack) {
+ snd_soc_dapm_enable_pin(dapm, "Mic Jack");
+ snd_soc_dapm_disable_pin(dapm, "Main MIC");
+ }
+ ret = imx_hp_jack_gpio.report;
+ snd_kctl_jack_report(priv->snd_card, priv->headphone_kctl, 1);
} else {
- snprintf(buf, 32, "STATE=%d", 0);
- snd_soc_dapm_enable_pin(&priv->codec->dapm, "Ext Spk");
- snd_soc_dapm_enable_pin(&priv->codec->dapm, "Main MIC");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+ if (priv->is_headset_jack) {
+ snd_soc_dapm_disable_pin(dapm, "Mic Jack");
+ snd_soc_dapm_enable_pin(dapm, "Main MIC");
+ }
ret = 0;
+ snd_kctl_jack_report(priv->snd_card, priv->headphone_kctl, 0);
+ }
- /*
- * As the Main MIC only connect the input for right channel,
- * we need to route it for left channel.
- */
- snd_soc_update_bits(priv->codec, WM8960_ADDCTL1, 3<<2, 2<<2);
+ return ret;
+}
- snd_kctl_jack_report(priv->snd_card, priv->headset_kctl, 0);
- }
+static int mic_jack_status_check(void *data)
+{
+ struct imx_priv *priv = &card_priv;
+ struct snd_soc_jack *jack = data;
+ struct snd_soc_dapm_context *dapm = &jack->card->dapm;
+ int mic_status, ret;
+
+ mic_status = gpio_get_value(imx_mic_jack_gpio.gpio);
- envp[0] = "NAME=headset";
- envp[1] = buf;
- envp[2] = NULL;
- kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);
- kfree(buf);
+ if (mic_status != priv->mic_active_low) {
+ snd_soc_dapm_disable_pin(dapm, "Main MIC");
+ ret = imx_mic_jack_gpio.report;
+ } else {
+ snd_soc_dapm_enable_pin(dapm, "Main MIC");
+ ret = 0;
+ }
return ret;
}
static const struct snd_soc_dapm_widget imx_wm8960_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headset Jack", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
- SND_SOC_DAPM_MIC("Hp MIC", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_MIC("Main MIC", NULL),
};
-static int imx_wm8960_gpio_init(struct snd_soc_card *card)
+static int imx_wm8960_jack_init(struct snd_soc_card *card,
+ struct snd_soc_jack *jack, struct snd_soc_jack_pin *pin,
+ struct snd_soc_jack_gpio *gpio)
{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
- struct imx_priv *priv = &card_priv;
int ret;
- priv->codec = codec;
- if (gpio_is_valid(priv->hp_set_gpio)) {
- imx_hp_set_gpio.gpio = priv->hp_set_gpio;
- imx_hp_set_gpio.jack_status_check = hp_set_status_check;
-
- ret = snd_soc_card_jack_new(card, "Headset Jack",
- SND_JACK_HEADSET, &imx_hp_set,
- imx_hp_set_pins, ARRAY_SIZE(imx_hp_set_pins));
- if (ret)
- return ret;
- ret = snd_soc_jack_add_gpios(&imx_hp_set, 1,
- &imx_hp_set_gpio);
- if (ret)
- return ret;
+ ret = snd_soc_card_jack_new(card, pin->pin, pin->mask, jack, pin, 1);
+ if (ret) {
+ return ret;
}
+ ret = snd_soc_jack_add_gpios(jack, 1, gpio);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -157,13 +155,8 @@ static ssize_t show_headphone(struct device_driver *dev, char *buf)
struct imx_priv *priv = &card_priv;
int hp_status;
- if (!gpio_is_valid(priv->hp_set_gpio)) {
- strcpy(buf, "no detect gpio connected\n");
- return strlen(buf);
- }
-
/* Check if headphone is plugged in */
- hp_status = gpio_get_value(priv->hp_set_gpio);
+ hp_status = gpio_get_value(imx_hp_jack_gpio.gpio);
if (hp_status != priv->hp_active_low)
strcpy(buf, "Headphone\n");
@@ -176,18 +169,13 @@ static ssize_t show_headphone(struct device_driver *dev, char *buf)
static ssize_t show_micphone(struct device_driver *dev, char *buf)
{
struct imx_priv *priv = &card_priv;
- int hp_status;
-
- if (!gpio_is_valid(priv->hp_set_gpio)) {
- strcpy(buf, "no detect gpio connected\n");
- return strlen(buf);
- }
+ int mic_status;
/* Check if headphone is plugged in */
- hp_status = gpio_get_value(priv->hp_set_gpio);
+ mic_status = gpio_get_value(imx_mic_jack_gpio.gpio);
- if (hp_status != priv->hp_active_low)
- strcpy(buf, "Hp MIC\n");
+ if (mic_status != priv->mic_active_low)
+ strcpy(buf, "Mic Jack\n");
else
strcpy(buf, "Main MIC\n");
@@ -196,37 +184,6 @@ static ssize_t show_micphone(struct device_driver *dev, char *buf)
static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
static DRIVER_ATTR(micphone, S_IRUGO | S_IWUSR, show_micphone, NULL);
-static void wm8960_init(struct snd_soc_card *card)
-{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
- struct imx_wm8960_data *data = snd_soc_card_get_drvdata(card);
-
- /*
- * codec ADCLRC pin configured as GPIO, DACLRC pin is used as a frame
- * clock for ADCs and DACs
- */
- snd_soc_update_bits(codec, WM8960_IFACE2, 1<<6, 1<<6);
-
- /*
- * GPIO1 used as headphone detect output
- */
- snd_soc_update_bits(codec, WM8960_ADDCTL4, 7<<4, 3<<4);
-
- /*
- * Enable headphone jack detect
- */
- snd_soc_update_bits(codec, WM8960_ADDCTL2, 1<<6, 1<<6);
- snd_soc_update_bits(codec, WM8960_ADDCTL2, 1<<5, data->hp_det[1]<<5);
- snd_soc_update_bits(codec, WM8960_ADDCTL4, 3<<2, data->hp_det[0]<<2);
- snd_soc_update_bits(codec, WM8960_ADDCTL1, 3, 3);
-
- /*
- * route left channel to right channel in default.
- */
- snd_soc_update_bits(codec, WM8960_ADDCTL1, 3<<2, 1<<2);
-}
-
static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -387,15 +344,25 @@ static struct snd_soc_ops imx_hifi_ops = {
static int imx_wm8960_late_probe(struct snd_soc_card *card)
{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
struct imx_wm8960_data *data = snd_soc_card_get_drvdata(card);
/*
- * set SAI2_MCLK_DIR to enable codec MCLK
+ * codec ADCLRC pin configured as GPIO, DACLRC pin is used as a frame
+ * clock for ADCs and DACs
*/
- if (data->gpr)
- regmap_update_bits(data->gpr, 4, 1<<20, 1<<20);
+ snd_soc_update_bits(codec, WM8960_IFACE2, 1<<6, 1<<6);
+
+ /* GPIO1 used as headphone detect output */
+ snd_soc_update_bits(codec, WM8960_ADDCTL4, 7<<4, 3<<4);
+
+ /* Enable headphone jack detect */
+ snd_soc_update_bits(codec, WM8960_ADDCTL2, 1<<6, 1<<6);
+ snd_soc_update_bits(codec, WM8960_ADDCTL2, 1<<5, data->hp_det[1]<<5);
+ snd_soc_update_bits(codec, WM8960_ADDCTL4, 3<<2, data->hp_det[0]<<2);
+ snd_soc_update_bits(codec, WM8960_ADDCTL1, 3, 3);
- wm8960_init(card);
return 0;
}
@@ -519,6 +486,9 @@ static int imx_wm8960_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get gpr regmap\n");
goto fail;
}
+
+ /* set SAI2_MCLK_DIR to enable codec MCLK for imx7d */
+ regmap_update_bits(data->gpr, 4, 1<<20, 1<<20);
}
of_property_read_u32_array(pdev->dev.of_node, "hp-det", data->hp_det, 2);
@@ -588,30 +558,59 @@ static int imx_wm8960_probe(struct platform_device *pdev)
priv->snd_card = data->card.snd_card;
- priv->hp_set_gpio = of_get_named_gpio_flags(pdev->dev.of_node, "hp-det-gpios", 0,
- (enum of_gpio_flags *)&priv->hp_active_low);
- if (IS_ERR(ERR_PTR(priv->hp_set_gpio)))
- goto fail;
+ imx_hp_jack_gpio.gpio = of_get_named_gpio_flags(pdev->dev.of_node,
+ "hp-det-gpios", 0, &priv->hp_active_low);
- priv->headset_kctl = snd_kctl_jack_new("Headset", 0, priv->snd_card);
- ret = snd_ctl_add(data->card.snd_card, priv->headset_kctl);
- if (ret)
- goto fail;
+ imx_mic_jack_gpio.gpio = of_get_named_gpio_flags(pdev->dev.of_node,
+ "mic-det-gpios", 0, &priv->mic_active_low);
- ret = imx_wm8960_gpio_init(&data->card);
+ if (gpio_is_valid(imx_hp_jack_gpio.gpio) &&
+ gpio_is_valid(imx_mic_jack_gpio.gpio) &&
+ imx_hp_jack_gpio.gpio == imx_mic_jack_gpio.gpio)
+ priv->is_headset_jack = true;
- if (gpio_is_valid(priv->hp_set_gpio)) {
- ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);
- if (ret) {
- dev_err(&pdev->dev, "create hp attr failed (%d)\n", ret);
- goto fail;
+ if (gpio_is_valid(imx_hp_jack_gpio.gpio)) {
+ priv->headphone_kctl = snd_kctl_jack_new("Headphone", 0, NULL);
+ ret = snd_ctl_add(priv->snd_card, priv->headphone_kctl);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to create headphone jack kctl\n");
+
+ if (priv->is_headset_jack) {
+ imx_hp_jack_pin.mask |= SND_JACK_MICROPHONE;
+ imx_hp_jack_gpio.report |= SND_JACK_MICROPHONE;
}
- ret = driver_create_file(pdev->dev.driver, &driver_attr_micphone);
+ imx_hp_jack_gpio.jack_status_check = hp_jack_status_check;
+ imx_hp_jack_gpio.data = &imx_hp_jack;
+ ret = imx_wm8960_jack_init(&data->card, &imx_hp_jack,
+ &imx_hp_jack_pin, &imx_hp_jack_gpio);
if (ret) {
- dev_err(&pdev->dev, "create mic attr failed (%d)\n", ret);
- goto fail;
+ dev_warn(&pdev->dev, "hp jack init failed (%d)\n", ret);
+ goto out;
+ }
+
+ ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);
+ if (ret)
+ dev_warn(&pdev->dev, "create hp attr failed (%d)\n", ret);
+ }
+
+ if (gpio_is_valid(imx_mic_jack_gpio.gpio)) {
+ if (!priv->is_headset_jack) {
+ imx_mic_jack_gpio.jack_status_check = mic_jack_status_check;
+ imx_mic_jack_gpio.data = &imx_mic_jack;
+ ret = imx_wm8960_jack_init(&data->card, &imx_mic_jack,
+ &imx_mic_jack_pins, &imx_mic_jack_gpio);
+ if (ret) {
+ dev_warn(&pdev->dev, "mic jack init failed (%d)\n", ret);
+ goto out;
+ }
}
+ ret = driver_create_file(pdev->dev.driver, &driver_attr_micphone);
+ if (ret)
+ dev_warn(&pdev->dev, "create mic attr failed (%d)\n", ret);
}
+
+out:
+ ret = 0;
fail:
if (cpu_np)
of_node_put(cpu_np);
@@ -621,6 +620,14 @@ fail:
return ret;
}
+static int imx_wm8960_remove(struct platform_device *pdev)
+{
+ driver_remove_file(pdev->dev.driver, &driver_attr_micphone);
+ driver_remove_file(pdev->dev.driver, &driver_attr_headphone);
+
+ return 0;
+}
+
static const struct of_device_id imx_wm8960_dt_ids[] = {
{ .compatible = "fsl,imx-audio-wm8960", },
{ /* sentinel */ }
@@ -634,6 +641,7 @@ static struct platform_driver imx_wm8960_driver = {
.of_match_table = imx_wm8960_dt_ids,
},
.probe = imx_wm8960_probe,
+ .remove = imx_wm8960_remove,
};
module_platform_driver(imx_wm8960_driver);