diff options
author | Irina Tirdea <irina.tirdea@intel.com> | 2016-08-12 17:27:57 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-08-15 10:14:57 -0400 |
commit | df1a2776a795848f4dbc7c0cb396158b43eb8aa3 (patch) | |
tree | ddcd5d5cf15b8702744bb61b2089776d80b2dbd1 /sound/soc/intel/boards | |
parent | 59e8b6520c6e2e867b35bc402d9a3f28aef3b2bc (diff) |
ASoC: Intel: bytcr_rt5640: add MCLK support
Use platform clocks "pmc_plt_clk_3" when MCLK quirk is defined.
By default always enable the 19.2 MHz PLL.
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/intel/boards')
-rw-r--r-- | sound/soc/intel/boards/bytcr_rt5640.c | 187 |
1 files changed, 172 insertions, 15 deletions
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 9b9d380d1cbb..11e11c6caa89 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <asm/cpu_device_id.h> | 27 | #include <asm/cpu_device_id.h> |
28 | #include <asm/platform_sst_audio.h> | 28 | #include <asm/platform_sst_audio.h> |
29 | #include <linux/clk.h> | ||
29 | #include <sound/pcm.h> | 30 | #include <sound/pcm.h> |
30 | #include <sound/pcm_params.h> | 31 | #include <sound/pcm_params.h> |
31 | #include <sound/soc.h> | 32 | #include <sound/soc.h> |
@@ -49,18 +50,104 @@ enum { | |||
49 | #define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */ | 50 | #define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */ |
50 | #define BYT_RT5640_SSP0_AIF1 BIT(20) | 51 | #define BYT_RT5640_SSP0_AIF1 BIT(20) |
51 | #define BYT_RT5640_SSP0_AIF2 BIT(21) | 52 | #define BYT_RT5640_SSP0_AIF2 BIT(21) |
53 | #define BYT_RT5640_MCLK_EN BIT(22) | ||
54 | #define BYT_RT5640_MCLK_25MHZ BIT(23) | ||
55 | |||
56 | struct byt_rt5640_private { | ||
57 | struct clk *mclk; | ||
58 | }; | ||
52 | 59 | ||
53 | static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | | 60 | static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | |
54 | BYT_RT5640_DMIC_EN; | 61 | BYT_RT5640_DMIC_EN | |
62 | BYT_RT5640_MCLK_EN; | ||
63 | |||
64 | #define BYT_CODEC_DAI1 "rt5640-aif1" | ||
65 | #define BYT_CODEC_DAI2 "rt5640-aif2" | ||
66 | |||
67 | static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card) | ||
68 | { | ||
69 | struct snd_soc_pcm_runtime *rtd; | ||
70 | |||
71 | list_for_each_entry(rtd, &card->rtd_list, list) { | ||
72 | if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1, | ||
73 | strlen(BYT_CODEC_DAI1))) | ||
74 | return rtd->codec_dai; | ||
75 | if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI2, | ||
76 | strlen(BYT_CODEC_DAI2))) | ||
77 | return rtd->codec_dai; | ||
78 | |||
79 | } | ||
80 | return NULL; | ||
81 | } | ||
82 | |||
83 | static int platform_clock_control(struct snd_soc_dapm_widget *w, | ||
84 | struct snd_kcontrol *k, int event) | ||
85 | { | ||
86 | struct snd_soc_dapm_context *dapm = w->dapm; | ||
87 | struct snd_soc_card *card = dapm->card; | ||
88 | struct snd_soc_dai *codec_dai; | ||
89 | struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); | ||
90 | int ret; | ||
91 | |||
92 | codec_dai = byt_get_codec_dai(card); | ||
93 | if (!codec_dai) { | ||
94 | dev_err(card->dev, | ||
95 | "Codec dai not found; Unable to set platform clock\n"); | ||
96 | return -EIO; | ||
97 | } | ||
98 | |||
99 | if (SND_SOC_DAPM_EVENT_ON(event)) { | ||
100 | if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { | ||
101 | ret = clk_prepare_enable(priv->mclk); | ||
102 | if (ret < 0) { | ||
103 | dev_err(card->dev, | ||
104 | "could not configure MCLK state"); | ||
105 | return ret; | ||
106 | } | ||
107 | } | ||
108 | ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, | ||
109 | 48000 * 512, | ||
110 | SND_SOC_CLOCK_IN); | ||
111 | } else { | ||
112 | /* | ||
113 | * Set codec clock source to internal clock before | ||
114 | * turning off the platform clock. Codec needs clock | ||
115 | * for Jack detection and button press | ||
116 | */ | ||
117 | ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_RCCLK, | ||
118 | 0, | ||
119 | SND_SOC_CLOCK_IN); | ||
120 | if (!ret) { | ||
121 | if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) | ||
122 | clk_disable_unprepare(priv->mclk); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | if (ret < 0) { | ||
127 | dev_err(card->dev, "can't set codec sysclk: %d\n", ret); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | return 0; | ||
132 | } | ||
55 | 133 | ||
56 | static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { | 134 | static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { |
57 | SND_SOC_DAPM_HP("Headphone", NULL), | 135 | SND_SOC_DAPM_HP("Headphone", NULL), |
58 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | 136 | SND_SOC_DAPM_MIC("Headset Mic", NULL), |
59 | SND_SOC_DAPM_MIC("Internal Mic", NULL), | 137 | SND_SOC_DAPM_MIC("Internal Mic", NULL), |
60 | SND_SOC_DAPM_SPK("Speaker", NULL), | 138 | SND_SOC_DAPM_SPK("Speaker", NULL), |
139 | SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, | ||
140 | platform_clock_control, SND_SOC_DAPM_PRE_PMU | | ||
141 | SND_SOC_DAPM_POST_PMD), | ||
142 | |||
61 | }; | 143 | }; |
62 | 144 | ||
63 | static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { | 145 | static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { |
146 | {"Headphone", NULL, "Platform Clock"}, | ||
147 | {"Headset Mic", NULL, "Platform Clock"}, | ||
148 | {"Internal Mic", NULL, "Platform Clock"}, | ||
149 | {"Speaker", NULL, "Platform Clock"}, | ||
150 | |||
64 | {"Headset Mic", NULL, "MICBIAS1"}, | 151 | {"Headset Mic", NULL, "MICBIAS1"}, |
65 | {"IN2P", NULL, "Headset Mic"}, | 152 | {"IN2P", NULL, "Headset Mic"}, |
66 | {"Headphone", NULL, "HPOL"}, | 153 | {"Headphone", NULL, "HPOL"}, |
@@ -150,21 +237,41 @@ static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream, | |||
150 | ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, | 237 | ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, |
151 | params_rate(params) * 512, | 238 | params_rate(params) * 512, |
152 | SND_SOC_CLOCK_IN); | 239 | SND_SOC_CLOCK_IN); |
240 | |||
153 | if (ret < 0) { | 241 | if (ret < 0) { |
154 | dev_err(rtd->dev, "can't set codec clock %d\n", ret); | 242 | dev_err(rtd->dev, "can't set codec clock %d\n", ret); |
155 | return ret; | 243 | return ret; |
156 | } | 244 | } |
157 | 245 | ||
158 | if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || | 246 | if (!(byt_rt5640_quirk & BYT_RT5640_MCLK_EN)) { |
159 | (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { | 247 | /* use bitclock as PLL input */ |
160 | 248 | if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || | |
161 | ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, | 249 | (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { |
162 | params_rate(params) * 32, /* FIXME */ | 250 | |
163 | params_rate(params) * 512); | 251 | /* 2x16 bit slots on SSP0 */ |
252 | ret = snd_soc_dai_set_pll(codec_dai, 0, | ||
253 | RT5640_PLL1_S_BCLK1, | ||
254 | params_rate(params) * 32, | ||
255 | params_rate(params) * 512); | ||
256 | } else { | ||
257 | /* 2x15 bit slots on SSP2 */ | ||
258 | ret = snd_soc_dai_set_pll(codec_dai, 0, | ||
259 | RT5640_PLL1_S_BCLK1, | ||
260 | params_rate(params) * 50, | ||
261 | params_rate(params) * 512); | ||
262 | } | ||
164 | } else { | 263 | } else { |
165 | ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, | 264 | if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) { |
166 | params_rate(params) * 50, | 265 | ret = snd_soc_dai_set_pll(codec_dai, 0, |
167 | params_rate(params) * 512); | 266 | RT5640_PLL1_S_MCLK, |
267 | 25000000, | ||
268 | params_rate(params) * 512); | ||
269 | } else { | ||
270 | ret = snd_soc_dai_set_pll(codec_dai, 0, | ||
271 | RT5640_PLL1_S_MCLK, | ||
272 | 19200000, | ||
273 | params_rate(params) * 512); | ||
274 | } | ||
168 | } | 275 | } |
169 | 276 | ||
170 | if (ret < 0) { | 277 | if (ret < 0) { |
@@ -188,7 +295,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { | |||
188 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | 295 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), |
189 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), | 296 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), |
190 | }, | 297 | }, |
191 | .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, | 298 | .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | |
299 | BYT_RT5640_MCLK_EN), | ||
192 | }, | 300 | }, |
193 | { | 301 | { |
194 | .callback = byt_rt5640_quirk_cb, | 302 | .callback = byt_rt5640_quirk_cb, |
@@ -199,7 +307,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { | |||
199 | .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | | 307 | .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | |
200 | BYT_RT5640_MONO_SPEAKER | | 308 | BYT_RT5640_MONO_SPEAKER | |
201 | BYT_RT5640_DIFF_MIC | | 309 | BYT_RT5640_DIFF_MIC | |
202 | BYT_RT5640_SSP0_AIF2 | 310 | BYT_RT5640_SSP0_AIF2 | |
311 | BYT_RT5640_MCLK_EN | ||
203 | ), | 312 | ), |
204 | }, | 313 | }, |
205 | { | 314 | { |
@@ -209,7 +318,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { | |||
209 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), | 318 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), |
210 | }, | 319 | }, |
211 | .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | | 320 | .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | |
212 | BYT_RT5640_DMIC_EN), | 321 | BYT_RT5640_DMIC_EN | |
322 | BYT_RT5640_MCLK_EN), | ||
213 | }, | 323 | }, |
214 | { | 324 | { |
215 | .callback = byt_rt5640_quirk_cb, | 325 | .callback = byt_rt5640_quirk_cb, |
@@ -217,7 +327,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { | |||
217 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | 327 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), |
218 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), | 328 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), |
219 | }, | 329 | }, |
220 | .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, | 330 | .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | |
331 | BYT_RT5640_MCLK_EN), | ||
221 | }, | 332 | }, |
222 | {} | 333 | {} |
223 | }; | 334 | }; |
@@ -228,13 +339,18 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) | |||
228 | struct snd_soc_codec *codec = runtime->codec; | 339 | struct snd_soc_codec *codec = runtime->codec; |
229 | struct snd_soc_card *card = runtime->card; | 340 | struct snd_soc_card *card = runtime->card; |
230 | const struct snd_soc_dapm_route *custom_map; | 341 | const struct snd_soc_dapm_route *custom_map; |
342 | struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); | ||
231 | int num_routes; | 343 | int num_routes; |
232 | 344 | ||
233 | card->dapm.idle_bias_off = true; | 345 | card->dapm.idle_bias_off = true; |
234 | 346 | ||
235 | rt5640_sel_asrc_clk_src(codec, | 347 | rt5640_sel_asrc_clk_src(codec, |
236 | RT5640_DA_STEREO_FILTER | | 348 | RT5640_DA_STEREO_FILTER | |
237 | RT5640_AD_STEREO_FILTER, | 349 | RT5640_DA_MONO_L_FILTER | |
350 | RT5640_DA_MONO_R_FILTER | | ||
351 | RT5640_AD_STEREO_FILTER | | ||
352 | RT5640_AD_MONO_L_FILTER | | ||
353 | RT5640_AD_MONO_R_FILTER, | ||
238 | RT5640_CLK_SEL_ASRC); | 354 | RT5640_CLK_SEL_ASRC); |
239 | 355 | ||
240 | ret = snd_soc_add_card_controls(card, byt_rt5640_controls, | 356 | ret = snd_soc_add_card_controls(card, byt_rt5640_controls, |
@@ -312,6 +428,30 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) | |||
312 | snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); | 428 | snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); |
313 | snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); | 429 | snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); |
314 | 430 | ||
431 | if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { | ||
432 | /* | ||
433 | * The firmware might enable the clock at | ||
434 | * boot (this information may or may not | ||
435 | * be reflected in the enable clock register). | ||
436 | * To change the rate we must disable the clock | ||
437 | * first to cover these cases. Due to common | ||
438 | * clock framework restrictions that do not allow | ||
439 | * to disable a clock that has not been enabled, | ||
440 | * we need to enable the clock first. | ||
441 | */ | ||
442 | ret = clk_prepare_enable(priv->mclk); | ||
443 | if (!ret) | ||
444 | clk_disable_unprepare(priv->mclk); | ||
445 | |||
446 | if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) | ||
447 | ret = clk_set_rate(priv->mclk, 25000000); | ||
448 | else | ||
449 | ret = clk_set_rate(priv->mclk, 19200000); | ||
450 | |||
451 | if (ret) | ||
452 | dev_err(card->dev, "unable to set MCLK rate\n"); | ||
453 | } | ||
454 | |||
315 | return ret; | 455 | return ret; |
316 | } | 456 | } |
317 | 457 | ||
@@ -490,6 +630,7 @@ static bool is_valleyview(void) | |||
490 | return true; | 630 | return true; |
491 | } | 631 | } |
492 | 632 | ||
633 | |||
493 | static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) | 634 | static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) |
494 | { | 635 | { |
495 | int ret_val = 0; | 636 | int ret_val = 0; |
@@ -497,10 +638,16 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) | |||
497 | const char *i2c_name = NULL; | 638 | const char *i2c_name = NULL; |
498 | int i; | 639 | int i; |
499 | int dai_index; | 640 | int dai_index; |
641 | struct byt_rt5640_private *priv; | ||
642 | |||
643 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); | ||
644 | if (!priv) | ||
645 | return -ENOMEM; | ||
500 | 646 | ||
501 | /* register the soc card */ | 647 | /* register the soc card */ |
502 | byt_rt5640_card.dev = &pdev->dev; | 648 | byt_rt5640_card.dev = &pdev->dev; |
503 | mach = byt_rt5640_card.dev->platform_data; | 649 | mach = byt_rt5640_card.dev->platform_data; |
650 | snd_soc_card_set_drvdata(&byt_rt5640_card, priv); | ||
504 | 651 | ||
505 | /* fix index of codec dai */ | 652 | /* fix index of codec dai */ |
506 | dai_index = MERR_DPCM_COMPR + 1; | 653 | dai_index = MERR_DPCM_COMPR + 1; |
@@ -561,6 +708,16 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) | |||
561 | byt_rt5640_cpu_dai_name; | 708 | byt_rt5640_cpu_dai_name; |
562 | } | 709 | } |
563 | 710 | ||
711 | if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && (is_valleyview())) { | ||
712 | priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); | ||
713 | if (IS_ERR(priv->mclk)) { | ||
714 | dev_err(&pdev->dev, | ||
715 | "Failed to get MCLK from pmc_plt_clk_3: %ld\n", | ||
716 | PTR_ERR(priv->mclk)); | ||
717 | return PTR_ERR(priv->mclk); | ||
718 | } | ||
719 | } | ||
720 | |||
564 | ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); | 721 | ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); |
565 | 722 | ||
566 | if (ret_val) { | 723 | if (ret_val) { |