From 1e28c6392f1898a56d93830c46b5d650e7bfc038 Mon Sep 17 00:00:00 2001 From: Gary Zhang Date: Tue, 18 Dec 2012 15:15:25 +0800 Subject: [PATCH] ENGR00236910 wm8962: use alsa jack mechanism to handle pin detection use alsa jack mechanism to handle pin detection Signed-off-by: Gary Zhang --- sound/soc/imx/imx-wm8962.c | 299 +++++++++++++++++-------------------- 1 file changed, 140 insertions(+), 159 deletions(-) diff --git a/sound/soc/imx/imx-wm8962.c b/sound/soc/imx/imx-wm8962.c index 9abc4a7424dd..e7b60df13180 100644 --- a/sound/soc/imx/imx-wm8962.c +++ b/sound/soc/imx/imx-wm8962.c @@ -58,10 +58,34 @@ unsigned int sample_format = SNDRV_PCM_FMTBIT_S16_LE; static struct imx_priv card_priv; static struct snd_soc_card snd_soc_card_imx; static struct snd_soc_codec *gcodec; -static int suspend_hp_flag; -static int suspend_mic_flag; -static int hp_irq; -static int mic_irq; + +static struct snd_soc_jack imx_hp_jack; +static struct snd_soc_jack_pin imx_hp_jack_pins[] = { + { + .pin = "Ext Spk", + .mask = SND_JACK_HEADPHONE, + }, +}; +static struct snd_soc_jack_gpio imx_hp_jack_gpio = { + .name = "headphone detect", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, + .invert = 0, +}; + +static struct snd_soc_jack imx_mic_jack; +static struct snd_soc_jack_pin imx_mic_jack_pins[] = { + { + .pin = "DMIC", + .mask = SND_JACK_MICROPHONE, + }, +}; +static struct snd_soc_jack_gpio imx_mic_jack_gpio = { + .name = "micphone detect", + .report = SND_JACK_MICROPHONE, + .debounce_time = 150, + .invert = 0, +}; static int imx_hifi_startup(struct snd_pcm_substream *substream) { @@ -150,35 +174,50 @@ static int imx_hifi_hw_params(struct snd_pcm_substream *substream, return 0; } -static const struct snd_kcontrol_new controls[] = { - SOC_DAPM_PIN_SWITCH("Ext Spk"), -}; +static void imx_resume_event(struct work_struct *wor) +{ + struct imx_priv *priv = &card_priv; + struct platform_device *pdev = priv->pdev; + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + struct snd_soc_jack *jack; + int enable; + int report; -/* imx card dapm widgets */ -static const struct snd_soc_dapm_widget imx_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_SPK("Ext Spk", NULL), - SND_SOC_DAPM_MIC("AMIC", NULL), - SND_SOC_DAPM_MIC("DMIC", NULL), -}; + if (plat->hp_gpio != -1) { + jack = imx_hp_jack_gpio.jack; -/* imx machine connections to the codec pins */ -static const struct snd_soc_dapm_route audio_map[] = { - { "Headphone Jack", NULL, "HPOUTL" }, - { "Headphone Jack", NULL, "HPOUTR" }, + enable = gpio_get_value_cansleep(imx_hp_jack_gpio.gpio); + if (imx_hp_jack_gpio.invert) + enable = !enable; - { "Ext Spk", NULL, "SPKOUTL" }, - { "Ext Spk", NULL, "SPKOUTR" }, + if (enable) + report = imx_hp_jack_gpio.report; + else + report = 0; - { "MICBIAS", NULL, "AMIC" }, - { "IN3R", NULL, "MICBIAS" }, + snd_soc_jack_report(jack, report, imx_hp_jack_gpio.report); + } - { "DMIC", NULL, "MICBIAS" }, - { "DMICDAT", NULL, "DMIC" }, + if (plat->mic_gpio != -1) { + jack = imx_mic_jack_gpio.jack; -}; + enable = gpio_get_value_cansleep(imx_mic_jack_gpio.gpio); + if (imx_mic_jack_gpio.invert) + enable = !enable; + + if (enable) + report = imx_mic_jack_gpio.report; + else + report = 0; + + snd_soc_jack_report(jack, report, imx_mic_jack_gpio.report); + } + + return; +} -static void headphone_detect_handler(struct work_struct *wor) +static int imx_event_hp(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { struct imx_priv *priv = &card_priv; struct platform_device *pdev = priv->pdev; @@ -186,23 +225,12 @@ static void headphone_detect_handler(struct work_struct *wor) char *envp[3]; char *buf; - /*sysfs_notify(&pdev->dev.kobj, NULL, "headphone");*/ - priv->hp_status = gpio_get_value(plat->hp_gpio); - - /* if headphone is inserted, disable speaker */ - if (priv->hp_status != plat->hp_active_low) - snd_soc_dapm_nc_pin(&gcodec->dapm, "Ext Spk"); - else - snd_soc_dapm_enable_pin(&gcodec->dapm, "Ext Spk"); - - snd_soc_dapm_sync(&gcodec->dapm); - - /* setup a message for userspace headphone in */ buf = kmalloc(32, GFP_ATOMIC); if (!buf) { pr_err("%s kmalloc failed\n", __func__); - return; + return -ENOMEM; } + priv->hp_status = gpio_get_value(plat->hp_gpio); if (priv->hp_status != plat->hp_active_low) snprintf(buf, 32, "STATE=%d", 2); @@ -215,49 +243,11 @@ static void headphone_detect_handler(struct work_struct *wor) kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp); kfree(buf); - if ((suspend_hp_flag == 1) && (hp_irq == 0)) { - suspend_hp_flag = 0; - return; - } - hp_irq = 0; - enable_irq(priv->hp_irq); - - return; -} - -static DECLARE_DELAYED_WORK(hp_event, headphone_detect_handler); - -static irqreturn_t imx_headphone_detect_handler(int irq, void *data) -{ - if (suspend_hp_flag == 1) - return IRQ_HANDLED; - - hp_irq = 1; - disable_irq_nosync(irq); - schedule_delayed_work(&hp_event, msecs_to_jiffies(200)); - return IRQ_HANDLED; -} - -static ssize_t show_headphone(struct device_driver *dev, char *buf) -{ - struct imx_priv *priv = &card_priv; - struct platform_device *pdev = priv->pdev; - struct mxc_audio_platform_data *plat = pdev->dev.platform_data; - - /* determine whether hp is plugged in */ - priv->hp_status = gpio_get_value(plat->hp_gpio); - - if (priv->hp_status != plat->hp_active_low) - strcpy(buf, "headphone\n"); - else - strcpy(buf, "speaker\n"); - - return strlen(buf); + return 0; } -static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL); - -static void amic_detect_handler(struct work_struct *work) +static int imx_event_mic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { struct imx_priv *priv = &card_priv; struct platform_device *pdev = priv->pdev; @@ -265,22 +255,12 @@ static void amic_detect_handler(struct work_struct *work) char *envp[3]; char *buf; - /* sysfs_notify(&pdev->dev.kobj, NULL, "amic"); */ priv->amic_status = gpio_get_value(plat->mic_gpio); - /* if amic is inserted, disable dmic */ - if (priv->amic_status != plat->mic_active_low) - snd_soc_dapm_nc_pin(&gcodec->dapm, "DMIC"); - else - snd_soc_dapm_enable_pin(&gcodec->dapm, "DMIC"); - - snd_soc_dapm_sync(&gcodec->dapm); - - /* setup a message for userspace headphone in */ buf = kmalloc(32, GFP_ATOMIC); if (!buf) { pr_err("%s kmalloc failed\n", __func__); - return; + return -ENOMEM; } if (priv->amic_status == 0) @@ -294,27 +274,57 @@ static void amic_detect_handler(struct work_struct *work) kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp); kfree(buf); - if ((suspend_mic_flag == 1) && (mic_irq == 0)) { - suspend_mic_flag = 0; - return; - } - mic_irq = 0; - enable_irq(priv->amic_irq); + return 0; } -static DECLARE_DELAYED_WORK(amic_event, amic_detect_handler); -static irqreturn_t imx_amic_detect_handler(int irq, void *data) +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +/* imx card dapm widgets */ +static const struct snd_soc_dapm_widget imx_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Ext Spk", imx_event_hp), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("DMIC", imx_event_mic), +}; + +/* imx machine connections to the codec pins */ +static const struct snd_soc_dapm_route audio_map[] = { + { "Headphone Jack", NULL, "HPOUTL" }, + { "Headphone Jack", NULL, "HPOUTR" }, + + { "Ext Spk", NULL, "SPKOUTL" }, + { "Ext Spk", NULL, "SPKOUTR" }, + + { "MICBIAS", NULL, "AMIC" }, + { "IN3R", NULL, "MICBIAS" }, + + { "DMIC", NULL, "MICBIAS" }, + { "DMICDAT", NULL, "DMIC" }, + +}; + +static ssize_t show_headphone(struct device_driver *dev, char *buf) { - if (suspend_mic_flag == 1) - return IRQ_HANDLED; + struct imx_priv *priv = &card_priv; + struct platform_device *pdev = priv->pdev; + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + + /* determine whether hp is plugged in */ + priv->hp_status = gpio_get_value(plat->hp_gpio); + + if (priv->hp_status != plat->hp_active_low) + strcpy(buf, "headphone\n"); + else + strcpy(buf, "speaker\n"); - mic_irq = 1; - disable_irq_nosync(irq); - schedule_delayed_work(&amic_event, msecs_to_jiffies(200)); - return IRQ_HANDLED; + return strlen(buf); } +static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL); + static ssize_t show_amic(struct device_driver *dev, char *buf) { struct imx_priv *priv = &card_priv; @@ -334,9 +344,7 @@ static ssize_t show_amic(struct device_driver *dev, char *buf) static DRIVER_ATTR(amic, S_IRUGO | S_IWUSR, show_amic, NULL); -static DECLARE_DELAYED_WORK(resume_hp_event, headphone_detect_handler); -static DECLARE_DELAYED_WORK(resume_mic_event, amic_detect_handler); - +static DECLARE_DELAYED_WORK(resume_hp_event, imx_resume_event); int imx_hifi_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -344,17 +352,10 @@ int imx_hifi_trigger(struct snd_pcm_substream *substream, int cmd) struct platform_device *pdev = priv->pdev; struct mxc_audio_platform_data *plat = pdev->dev.platform_data; - if (SNDRV_PCM_TRIGGER_SUSPEND == cmd) { - suspend_hp_flag = 1; - suspend_mic_flag = 1; - } if (SNDRV_PCM_TRIGGER_RESUME == cmd) { - if (plat->hp_gpio != -1) + if ((plat->hp_gpio != -1) || (plat->mic_gpio != -1)) schedule_delayed_work(&resume_hp_event, - msecs_to_jiffies(200)); - if (plat->mic_gpio != -1) - schedule_delayed_work(&resume_mic_event, - msecs_to_jiffies(200)); + msecs_to_jiffies(200)); } return 0; @@ -380,63 +381,43 @@ static int imx_wm8962_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack"); snd_soc_dapm_enable_pin(&codec->dapm, "AMIC"); - if (plat->hp_gpio != -1) { - priv->hp_irq = gpio_to_irq(plat->hp_gpio); - - ret = request_irq(priv->hp_irq, - imx_headphone_detect_handler, - IRQ_TYPE_EDGE_BOTH, pdev->name, priv); + snd_soc_dapm_sync(&codec->dapm); - if (ret < 0) { - ret = -EINVAL; - return ret; - } + if (plat->hp_gpio != -1) { + imx_hp_jack_gpio.gpio = plat->hp_gpio; + snd_soc_jack_new(codec, "Ext Spk", SND_JACK_LINEOUT, + &imx_hp_jack); + snd_soc_jack_add_pins(&imx_hp_jack, + ARRAY_SIZE(imx_hp_jack_pins), + imx_hp_jack_pins); + snd_soc_jack_add_gpios(&imx_hp_jack, + 1, &imx_hp_jack_gpio); ret = driver_create_file(pdev->dev.driver, - &driver_attr_headphone); + &driver_attr_headphone); if (ret < 0) { ret = -EINVAL; return ret; } - - priv->hp_status = gpio_get_value(plat->hp_gpio); - - /* if headphone is inserted, disable speaker */ - if (priv->hp_status != plat->hp_active_low) - snd_soc_dapm_nc_pin(&codec->dapm, "Ext Spk"); - else - snd_soc_dapm_enable_pin(&codec->dapm, "Ext Spk"); } if (plat->mic_gpio != -1) { - priv->amic_irq = gpio_to_irq(plat->mic_gpio); - - ret = request_irq(priv->amic_irq, - imx_amic_detect_handler, - IRQ_TYPE_EDGE_BOTH, pdev->name, priv); + imx_mic_jack_gpio.gpio = plat->mic_gpio; + snd_soc_jack_new(codec, "DMIC", SND_JACK_MICROPHONE, + &imx_mic_jack); + snd_soc_jack_add_pins(&imx_mic_jack, + ARRAY_SIZE(imx_mic_jack_pins), + imx_mic_jack_pins); + snd_soc_jack_add_gpios(&imx_mic_jack, + 1, &imx_mic_jack_gpio); + ret = driver_create_file(pdev->dev.driver, + &driver_attr_amic); if (ret < 0) { ret = -EINVAL; return ret; } - - ret = driver_create_file(pdev->dev.driver, &driver_attr_amic); - if (ret < 0) { - ret = -EINVAL; - return ret; - } - - priv->amic_status = gpio_get_value(plat->mic_gpio); - - /* if amic is inserted, disable DMIC */ - if (priv->amic_status != plat->mic_active_low) - snd_soc_dapm_nc_pin(&codec->dapm, "DMIC"); - else - snd_soc_dapm_enable_pin(&codec->dapm, "DMIC"); - } else if (!snd_soc_dapm_get_pin_status(&codec->dapm, "DMICDAT")) - snd_soc_dapm_nc_pin(&codec->dapm, "DMIC"); - - snd_soc_dapm_sync(&codec->dapm); + } return 0; } -- 2.39.5