static int midas_card_resume(struct snd_soc_card *card) { struct snd_soc_codec *codec = card->rtd->codec; struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; int ret; midas_snd_set_mclk(true, false); #ifndef SND_USE_BIAS_LEVEL /* Switch the FLL */ ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, MIDAS_DEFAULT_MCLK1, MIDAS_DEFAULT_SYNC_CLK); if (ret < 0) dev_err(aif1_dai->dev, "Unable to start FLL1: %d\n", ret); /* Then switch AIF1CLK to it */ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1, MIDAS_DEFAULT_SYNC_CLK, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(aif1_dai->dev, "Unable to switch to FLL1: %d\n", ret); midas_micd_set_rate(codec); #endif return 0; }
static void midas_start_fll1(struct snd_soc_dai *aif1_dai) { int ret; if (midas_fll1_active) return; dev_info(aif1_dai->dev, "Moving to audio clocking settings\n"); /* Switch AIF1 to MCLK2 while we bring stuff up */ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, 32768, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(aif1_dai->dev, "Unable to switch to MCLK2: %d\n", ret); /* Start the 24MHz clock to provide a high frequency reference to * provide a high frequency reference for the FLL, giving improved * performance. */ midas_set_mclk(3); /* forced enable MCLK */ /* Switch the FLL */ ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, 24000000, 44100 * 256); if (ret < 0) dev_err(aif1_dai->dev, "Unable to start FLL1: %d\n", ret); #ifdef MANAGE_MCLK1 /* Now the FLL is running we can stop the reference clock, the * FLL will maintain frequency with no reference so this saves * power from the reference clock. */ midas_set_mclk(0); #endif /* Then switch AIF1CLK to it */ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1, 44100 * 256, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(aif1_dai->dev, "Unable to switch to FLL1: %d\n", ret); midas_micd_set_rate(aif1_dai->codec); midas_fll1_active = true; }
static int midas_card_suspend(struct snd_soc_card *card) { struct snd_soc_codec *codec = card->rtd->codec; struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; struct snd_soc_dai *aif2_dai = card->rtd[1].codec_dai; int ret; if (!codec->active) { #ifndef SND_USE_BIAS_LEVEL ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2, MIDAS_DEFAULT_MCLK2, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(codec->dev, "Unable to switch to MCLK2: %d\n", ret); ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2, 0, 0, 0); if (ret < 0) dev_err(codec->dev, "Unable to stop FLL2\n"); ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, MIDAS_DEFAULT_MCLK2, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(codec->dev, "Unable to switch to MCLK2\n"); ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0); if (ret < 0) dev_err(codec->dev, "Unable to stop FLL1\n"); midas_micd_set_rate(codec); #endif midas_snd_set_mclk(false, true); } #ifdef CONFIG_ARCH_EXYNOS5 exynos5_sys_powerdown_xxti_control(midas_snd_get_mclk() ? 1 : 0); #else /* for CONFIG_ARCH_EXYNOS5 */ exynos4_sys_powerdown_xusbxti_control(midas_snd_get_mclk() ? 1 : 0); #endif return 0; }
static void midas_start_fll1(struct snd_soc_dai *aif1_dai) { int ret; if (midas_fll1_active) return; dev_info(aif1_dai->dev, "Moving to audio clocking settings\n"); /* Switch AIF1 to MCLK2 while we bring stuff up */ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, MIDAS_DEFAULT_MCLK2, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(aif1_dai->dev, "Unable to switch to MCLK2: %d\n", ret); /* Start the 24MHz clock to provide a high frequency reference to * provide a high frequency reference for the FLL, giving improved * performance. */ midas_snd_set_mclk(true, true); /* Switch the FLL */ ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, MIDAS_DEFAULT_MCLK1, MIDAS_DEFAULT_SYNC_CLK); if (ret < 0) dev_err(aif1_dai->dev, "Unable to start FLL1: %d\n", ret); /* Then switch AIF1CLK to it */ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1, MIDAS_DEFAULT_SYNC_CLK, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(aif1_dai->dev, "Unable to switch to FLL1: %d\n", ret); midas_micd_set_rate(aif1_dai->codec); midas_fll1_active = true; }
static ssize_t earjack_select_jack_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct snd_soc_codec *codec = dev_get_drvdata(dev); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); wm8994->mic_detecting = false; wm8994->jack_mic = true; midas_micd_set_rate(codec); if ((!size) || (buf[0] != '1')) { snd_soc_jack_report(wm8994->micdet[0].jack, 0, SND_JACK_HEADSET); dev_info(codec->dev, "Forced remove microphone\n"); } else { snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET, SND_JACK_HEADSET); dev_info(codec->dev, "Forced detect microphone\n"); } return size; }
static void midas_micdet(u16 status, void *data) { struct wm1811_machine_priv *wm1811 = data; struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(wm1811->codec); int report; int reg; bool present; wake_lock_timeout(&wm1811->jackdet_wake_lock, 5 * HZ); /* Either nothing present or just starting detection */ if (!(status & WM8958_MICD_STS)) { if (!wm8994->jackdet) { /* If nothing present then clear our statuses */ dev_dbg(wm1811->codec->dev, "Detected open circuit\n"); wm8994->jack_mic = false; wm8994->mic_detecting = true; midas_micd_set_rate(wm1811->codec); snd_soc_jack_report(wm8994->micdet[0].jack, 0, wm8994->btn_mask | SND_JACK_HEADSET); } /*ToDo*/ /*return;*/ } /* If the measurement is showing a high impedence we've got a * microphone. */ if (wm8994->mic_detecting && (status & 0x400)) { dev_info(wm1811->codec->dev, "Detected microphone\n"); wm8994->mic_detecting = false; wm8994->jack_mic = true; midas_micd_set_rate(wm1811->codec); snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET, SND_JACK_HEADSET); } if (wm8994->mic_detecting && status & 0x4) { dev_info(wm1811->codec->dev, "Detected headphone\n"); wm8994->mic_detecting = false; midas_micd_set_rate(wm1811->codec); snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, SND_JACK_HEADSET); /* If we have jackdet that will detect removal */ if (wm8994->jackdet) { mutex_lock(&wm8994->accdet_lock); snd_soc_update_bits(wm1811->codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0); if (wm8994->active_refcount) { snd_soc_update_bits(wm1811->codec, WM8994_ANTIPOP_2, WM1811_JACKDET_MODE_MASK, WM1811_JACKDET_MODE_AUDIO); } mutex_unlock(&wm8994->accdet_lock); if (wm8994->pdata->jd_ext_cap) { mutex_lock(&wm1811->codec->mutex); snd_soc_dapm_disable_pin(&wm1811->codec->dapm, "MICBIAS2"); snd_soc_dapm_sync(&wm1811->codec->dapm); mutex_unlock(&wm1811->codec->mutex); } } } /* Report short circuit as a button */ if (wm8994->jack_mic) { report = 0; if (status & WM1811_JACKDET_BTN0) report |= SND_JACK_BTN_0; if (status & WM1811_JACKDET_BTN1) report |= SND_JACK_BTN_1; if (status & WM1811_JACKDET_BTN2) report |= SND_JACK_BTN_2; reg = snd_soc_read(wm1811->codec, WM1811_JACKDET_CTRL); if (reg < 0) { pr_err("%s: Failed to read jack status: %d\n", __func__, reg); return; } pr_err("%s: JACKDET %x\n", __func__, reg); present = reg & WM1811_JACKDET_LVL; if (!present) { pr_err("%s: button is ignored!!!\n", __func__); return; } dev_dbg(wm1811->codec->dev, "Detected Button: %08x (%08X)\n", report, status); snd_soc_jack_report(wm8994->micdet[0].jack, report, wm8994->btn_mask); } }
static int midas_wm1811_init_paiftx(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct wm1811_machine_priv *wm1811 = snd_soc_card_get_drvdata(codec->card); struct snd_soc_dai *aif1_dai = rtd->codec_dai; struct wm8994 *control = codec->control_data; struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int ret; #ifdef SND_USE_BIAS_LEVEL midas_aif1_dai = aif1_dai; #endif #ifdef CONFIG_MACH_GC1 wm1811_codec = codec; #endif midas_snd_set_mclk(true, false); rtd->codec_dai->driver->playback.channels_max = rtd->cpu_dai->driver->playback.channels_max; ret = snd_soc_add_controls(codec, midas_controls, ARRAY_SIZE(midas_controls)); ret = snd_soc_dapm_new_controls(&codec->dapm, midas_dapm_widgets, ARRAY_SIZE(midas_dapm_widgets)); if (ret != 0) dev_err(codec->dev, "Failed to add DAPM widgets: %d\n", ret); ret = snd_soc_dapm_add_routes(&codec->dapm, midas_dapm_routes, ARRAY_SIZE(midas_dapm_routes)); if (ret != 0) dev_err(codec->dev, "Failed to add DAPM routes: %d\n", ret); ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, MIDAS_DEFAULT_MCLK2, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(codec->dev, "Failed to boot clocking\n"); /* Force AIF1CLK on as it will be master for jack detection */ if (wm8994->revision > 1) { ret = snd_soc_dapm_force_enable_pin(&codec->dapm, "AIF1CLK"); if (ret < 0) dev_err(codec->dev, "Failed to enable AIF1CLK: %d\n", ret); } ret = snd_soc_dapm_disable_pin(&codec->dapm, "S5P RP"); if (ret < 0) dev_err(codec->dev, "Failed to disable S5P RP: %d\n", ret); snd_soc_dapm_ignore_suspend(&codec->dapm, "RCV"); snd_soc_dapm_ignore_suspend(&codec->dapm, "SPK"); snd_soc_dapm_ignore_suspend(&codec->dapm, "HP"); snd_soc_dapm_ignore_suspend(&codec->dapm, "Headset Mic"); snd_soc_dapm_ignore_suspend(&codec->dapm, "Sub Mic"); snd_soc_dapm_ignore_suspend(&codec->dapm, "Main Mic"); snd_soc_dapm_ignore_suspend(&codec->dapm, "AIF1DACDAT"); snd_soc_dapm_ignore_suspend(&codec->dapm, "AIF2DACDAT"); snd_soc_dapm_ignore_suspend(&codec->dapm, "AIF3DACDAT"); snd_soc_dapm_ignore_suspend(&codec->dapm, "AIF1ADCDAT"); snd_soc_dapm_ignore_suspend(&codec->dapm, "AIF2ADCDAT"); snd_soc_dapm_ignore_suspend(&codec->dapm, "AIF3ADCDAT"); snd_soc_dapm_ignore_suspend(&codec->dapm, "FM In"); snd_soc_dapm_ignore_suspend(&codec->dapm, "LINE"); snd_soc_dapm_ignore_suspend(&codec->dapm, "HDMI"); snd_soc_dapm_ignore_suspend(&codec->dapm, "Third Mic"); wm1811->codec = codec; midas_micd_set_rate(codec); #ifdef CONFIG_SEC_DEV_JACK /* By default use idle_bias_off, will override for WM8994 */ codec->dapm.idle_bias_off = 0; #else /* CONFIG_SEC_DEV_JACK */ wm1811->jack.status = 0; ret = snd_soc_jack_new(codec, "Midas Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, &wm1811->jack); if (ret < 0) dev_err(codec->dev, "Failed to create jack: %d\n", ret); ret = snd_jack_set_key(wm1811->jack.jack, SND_JACK_BTN_0, KEY_MEDIA); if (ret < 0) dev_err(codec->dev, "Failed to set KEY_MEDIA: %d\n", ret); ret = snd_jack_set_key(wm1811->jack.jack, SND_JACK_BTN_1, KEY_VOLUMEDOWN); if (ret < 0) dev_err(codec->dev, "Failed to set KEY_VOLUMEUP: %d\n", ret); ret = snd_jack_set_key(wm1811->jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); if (ret < 0) dev_err(codec->dev, "Failed to set KEY_VOLUMEDOWN: %d\n", ret); if (wm8994->revision > 1) { dev_info(codec->dev, "wm1811: Rev %c support mic detection\n", 'A' + wm8994->revision); ret = wm8958_mic_detect(codec, &wm1811->jack, midas_micdet, wm1811); if (ret < 0) dev_err(codec->dev, "Failed start detection: %d\n", ret); } else { dev_info(codec->dev, "wm1811: Rev %c doesn't support mic detection\n", 'A' + wm8994->revision); codec->dapm.idle_bias_off = 0; } /* To wakeup for earjack event in suspend mode */ enable_irq_wake(control->irq); wake_lock_init(&wm1811->jackdet_wake_lock, WAKE_LOCK_SUSPEND, "midas_jackdet"); /* To support PBA function test */ jack_class = class_create(THIS_MODULE, "audio"); if (IS_ERR(jack_class)) pr_err("Failed to create class\n"); jack_dev = device_create(jack_class, NULL, 0, codec, "earjack"); if (device_create_file(jack_dev, &dev_attr_select_jack) < 0) pr_err("Failed to create device file (%s)!\n", dev_attr_select_jack.attr.name); if (device_create_file(jack_dev, &dev_attr_key_state) < 0) pr_err("Failed to create device file (%s)!\n", dev_attr_key_state.attr.name); if (device_create_file(jack_dev, &dev_attr_state) < 0) pr_err("Failed to create device file (%s)!\n", dev_attr_state.attr.name); if (device_create_file(jack_dev, &dev_attr_reselect_jack) < 0) pr_err("Failed to create device file (%s)!\n", dev_attr_reselect_jack.attr.name); #endif /* CONFIG_SEC_DEV_JACK */ return snd_soc_dapm_sync(&codec->dapm); }
static void midas_micdet(u16 status, void *data) { struct snd_soc_codec *codec = data; struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int report; if (!check_rev00()) { /* Temporarily disable earjack event handling except PQ Rev00 */ pr_err("%s: jack status 0x%x", __func__, status); } else { /* Either nothing present or just starting detection */ if (!(status & WM8958_MICD_STS)) { if (!wm8994->jackdet) { /* If nothing present then clear our statuses */ dev_dbg(codec->dev, "Detected open circuit\n"); wm8994->jack_mic = false; wm8994->mic_detecting = true; midas_micd_set_rate(codec); snd_soc_jack_report(wm8994->micdet[0].jack, 0, wm8994->btn_mask | SND_JACK_HEADSET); /* For SLP platform */ jack_event_handler("earjack", 0); } return; } /* If the measurement is showing a high impedence we've got a * microphone. */ if (wm8994->mic_detecting && (status & 0x400)) { dev_info(codec->dev, "Detected microphone\n"); wm8994->mic_detecting = false; wm8994->jack_mic = true; midas_micd_set_rate(codec); snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET, SND_JACK_HEADSET); /* For SLP platform */ jack_event_handler("earjack", SND_JACK_HEADSET); } if (wm8994->mic_detecting && status & 0x4) { dev_info(codec->dev, "Detected headphone\n"); wm8994->mic_detecting = false; midas_micd_set_rate(codec); snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, SND_JACK_HEADSET); /* For SLP platform */ jack_event_handler("earjack", SND_JACK_HEADPHONE); /* If we have jackdet that will detect removal */ if (wm8994->jackdet) { snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0); if (wm8994->active_refcount) { snd_soc_update_bits(codec, WM8994_ANTIPOP_2, WM1811_JACKDET_MODE_MASK, WM1811_JACKDET_MODE_AUDIO); } else { snd_soc_update_bits(codec, WM8994_ANTIPOP_2, WM1811_JACKDET_MODE_MASK, WM1811_JACKDET_MODE_JACK); } } } /* Report short circuit as a button */ if (wm8994->jack_mic) { report = 0; if (status & WM1811_JACKDET_BTN0) { report |= SND_JACK_BTN_0; /* For SLP platform */ jack_event_handler("earkey", true); } else jack_event_handler("earkey", false); if (status & WM1811_JACKDET_BTN1) report |= SND_JACK_BTN_1; if (status & WM1811_JACKDET_BTN2) report |= SND_JACK_BTN_2; dev_dbg(codec->dev, "Detected Button: %08x (%08X)\n", report, status); snd_soc_jack_report(wm8994->micdet[0].jack, report, wm8994->btn_mask); } } }
static int midas_wm1811_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; unsigned int pll_out; int ret; dev_info(codec_dai->dev, "%s ++\n", __func__); /* AIF1CLK should be >=3MHz for optimal performance */ if (params_rate(params) == 8000 || params_rate(params) == 11025) pll_out = params_rate(params) * 512; else pll_out = params_rate(params) * 256; ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); if (ret < 0) return ret; /* Set the cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); if (ret < 0) return ret; #ifndef SND_USE_BIAS_LEVEL /* Switch the FLL */ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, MIDAS_DEFAULT_MCLK1, pll_out); if (ret < 0) dev_err(codec_dai->dev, "Unable to start FLL1: %d\n", ret); ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, pll_out, SND_SOC_CLOCK_IN); if (ret < 0) { dev_err(codec_dai->dev, "Unable to switch to FLL1: %d\n", ret); return ret; } ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_OPCLK, 0, MOD_OPCLK_PCLK); if (ret < 0) return ret; midas_micd_set_rate(rtd->codec); #else midas_start_fll1(codec_dai); #endif if (ret < 0) return ret; dev_info(codec_dai->dev, "%s --\n", __func__); return 0; }
static int midas_set_bias_level_post(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { struct snd_soc_codec *codec = card->rtd->codec; struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; struct snd_soc_dai *aif2_dai = card->rtd[1].codec_dai; int ret; if (dapm->dev != aif1_dai->dev) return 0; switch (level) { case SND_SOC_BIAS_STANDBY: /* When going idle stop FLL1 and revert to using MCLK2 * directly for minimum power consumptin for accessory * detection. */ if (card->dapm.bias_level == SND_SOC_BIAS_PREPARE) { dev_info(aif1_dai->dev, "Moving to STANDBY\n"); ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2, MIDAS_DEFAULT_MCLK2, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(codec->dev, "Failed to switch to MCLK2\n"); ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2, 0, 0, 0); if (ret < 0) dev_err(codec->dev, "Failed to change FLL2\n"); ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, MIDAS_DEFAULT_MCLK2, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(codec->dev, "Failed to switch to MCLK2\n"); ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0); if (ret < 0) dev_err(codec->dev, "Failed to stop FLL1\n"); midas_fll1_active = false; midas_micd_set_rate(codec); midas_snd_set_mclk(false, false); } break; default: break; } card->dapm.bias_level = level; return 0; }