1 /* 2 * bytcht_es8316.c - ASoc Machine driver for Intel Baytrail/Cherrytrail 3 * platforms with Everest ES8316 SoC 4 * 5 * Copyright (C) 2017 Endless Mobile, Inc. 6 * Authors: David Yang <yangxiaohua@everest-semi.com>, 7 * Daniel Drake <drake@endlessm.com> 8 * 9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; version 2 of the License. 14 * 15 * This program is distributed in the hope that it will be useful, but 16 * WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * General Public License for more details. 19 * 20 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 21 */ 22 #include <linux/init.h> 23 #include <linux/module.h> 24 #include <linux/platform_device.h> 25 #include <linux/device.h> 26 #include <linux/slab.h> 27 #include <asm/platform_sst_audio.h> 28 #include <linux/clk.h> 29 #include <sound/pcm.h> 30 #include <sound/pcm_params.h> 31 #include <sound/soc.h> 32 #include "../atom/sst-atom-controls.h" 33 #include "../common/sst-acpi.h" 34 #include "../common/sst-dsp.h" 35 36 struct byt_cht_es8316_private { 37 struct clk *mclk; 38 }; 39 40 #define CODEC_DAI1 "ES8316 HiFi" 41 42 static inline struct snd_soc_dai *get_codec_dai(struct snd_soc_card *card) 43 { 44 struct snd_soc_pcm_runtime *rtd; 45 46 list_for_each_entry(rtd, &card->rtd_list, list) { 47 if (!strncmp(rtd->codec_dai->name, CODEC_DAI1, 48 strlen(CODEC_DAI1))) 49 return rtd->codec_dai; 50 } 51 return NULL; 52 } 53 54 static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = { 55 SND_SOC_DAPM_HP("Headphone", NULL), 56 57 /* 58 * The codec supports two analog microphone inputs. I have only 59 * tested MIC1. A DMIC route could also potentially be added 60 * if such functionality is found on another platform. 61 */ 62 SND_SOC_DAPM_MIC("Microphone 1", NULL), 63 SND_SOC_DAPM_MIC("Microphone 2", NULL), 64 }; 65 66 static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = { 67 {"MIC1", NULL, "Microphone 1"}, 68 {"MIC2", NULL, "Microphone 2"}, 69 70 {"Headphone", NULL, "HPOL"}, 71 {"Headphone", NULL, "HPOR"}, 72 73 {"Playback", NULL, "ssp2 Tx"}, 74 {"ssp2 Tx", NULL, "codec_out0"}, 75 {"ssp2 Tx", NULL, "codec_out1"}, 76 {"codec_in0", NULL, "ssp2 Rx" }, 77 {"codec_in1", NULL, "ssp2 Rx" }, 78 {"ssp2 Rx", NULL, "Capture"}, 79 }; 80 81 static const struct snd_kcontrol_new byt_cht_es8316_controls[] = { 82 SOC_DAPM_PIN_SWITCH("Headphone"), 83 SOC_DAPM_PIN_SWITCH("Microphone 1"), 84 SOC_DAPM_PIN_SWITCH("Microphone 2"), 85 }; 86 87 static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) 88 { 89 struct snd_soc_card *card = runtime->card; 90 struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); 91 int ret; 92 93 card->dapm.idle_bias_off = true; 94 95 /* 96 * The firmware might enable the clock at boot (this information 97 * may or may not be reflected in the enable clock register). 98 * To change the rate we must disable the clock first to cover these 99 * cases. Due to common clock framework restrictions that do not allow 100 * to disable a clock that has not been enabled, we need to enable 101 * the clock first. 102 */ 103 ret = clk_prepare_enable(priv->mclk); 104 if (!ret) 105 clk_disable_unprepare(priv->mclk); 106 107 ret = clk_set_rate(priv->mclk, 19200000); 108 if (ret) 109 dev_err(card->dev, "unable to set MCLK rate\n"); 110 111 ret = clk_prepare_enable(priv->mclk); 112 if (ret) 113 dev_err(card->dev, "unable to enable MCLK\n"); 114 115 ret = snd_soc_dai_set_sysclk(runtime->codec_dai, 0, 19200000, 116 SND_SOC_CLOCK_IN); 117 if (ret < 0) { 118 dev_err(card->dev, "can't set codec clock %d\n", ret); 119 return ret; 120 } 121 122 return 0; 123 } 124 125 static const struct snd_soc_pcm_stream byt_cht_es8316_dai_params = { 126 .formats = SNDRV_PCM_FMTBIT_S24_LE, 127 .rate_min = 48000, 128 .rate_max = 48000, 129 .channels_min = 2, 130 .channels_max = 2, 131 }; 132 133 static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd, 134 struct snd_pcm_hw_params *params) 135 { 136 struct snd_interval *rate = hw_param_interval(params, 137 SNDRV_PCM_HW_PARAM_RATE); 138 struct snd_interval *channels = hw_param_interval(params, 139 SNDRV_PCM_HW_PARAM_CHANNELS); 140 int ret; 141 142 /* The DSP will covert the FE rate to 48k, stereo */ 143 rate->min = rate->max = 48000; 144 channels->min = channels->max = 2; 145 146 /* set SSP2 to 24-bit */ 147 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); 148 149 /* 150 * Default mode for SSP configuration is TDM 4 slot, override config 151 * with explicit setting to I2S 2ch 24-bit. The word length is set with 152 * dai_set_tdm_slot() since there is no other API exposed 153 */ 154 ret = snd_soc_dai_set_fmt(rtd->cpu_dai, 155 SND_SOC_DAIFMT_I2S | 156 SND_SOC_DAIFMT_NB_NF | 157 SND_SOC_DAIFMT_CBS_CFS 158 ); 159 if (ret < 0) { 160 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); 161 return ret; 162 } 163 164 ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); 165 if (ret < 0) { 166 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); 167 return ret; 168 } 169 170 return 0; 171 } 172 173 static int byt_cht_es8316_aif1_startup(struct snd_pcm_substream *substream) 174 { 175 return snd_pcm_hw_constraint_single(substream->runtime, 176 SNDRV_PCM_HW_PARAM_RATE, 48000); 177 } 178 179 static const struct snd_soc_ops byt_cht_es8316_aif1_ops = { 180 .startup = byt_cht_es8316_aif1_startup, 181 }; 182 183 static struct snd_soc_dai_link byt_cht_es8316_dais[] = { 184 [MERR_DPCM_AUDIO] = { 185 .name = "Audio Port", 186 .stream_name = "Audio", 187 .cpu_dai_name = "media-cpu-dai", 188 .codec_dai_name = "snd-soc-dummy-dai", 189 .codec_name = "snd-soc-dummy", 190 .platform_name = "sst-mfld-platform", 191 .nonatomic = true, 192 .dynamic = 1, 193 .dpcm_playback = 1, 194 .dpcm_capture = 1, 195 .ops = &byt_cht_es8316_aif1_ops, 196 }, 197 198 [MERR_DPCM_DEEP_BUFFER] = { 199 .name = "Deep-Buffer Audio Port", 200 .stream_name = "Deep-Buffer Audio", 201 .cpu_dai_name = "deepbuffer-cpu-dai", 202 .codec_dai_name = "snd-soc-dummy-dai", 203 .codec_name = "snd-soc-dummy", 204 .platform_name = "sst-mfld-platform", 205 .nonatomic = true, 206 .dynamic = 1, 207 .dpcm_playback = 1, 208 .ops = &byt_cht_es8316_aif1_ops, 209 }, 210 211 [MERR_DPCM_COMPR] = { 212 .name = "Compressed Port", 213 .stream_name = "Compress", 214 .cpu_dai_name = "compress-cpu-dai", 215 .codec_dai_name = "snd-soc-dummy-dai", 216 .codec_name = "snd-soc-dummy", 217 .platform_name = "sst-mfld-platform", 218 }, 219 220 /* back ends */ 221 { 222 /* Only SSP2 has been tested here, so BYT-CR platforms that 223 * require SSP0 will not work. 224 */ 225 .name = "SSP2-Codec", 226 .id = 1, 227 .cpu_dai_name = "ssp2-port", 228 .platform_name = "sst-mfld-platform", 229 .no_pcm = 1, 230 .codec_dai_name = "ES8316 HiFi", 231 .codec_name = "i2c-ESSX8316:00", 232 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 233 | SND_SOC_DAIFMT_CBS_CFS, 234 .be_hw_params_fixup = byt_cht_es8316_codec_fixup, 235 .nonatomic = true, 236 .dpcm_playback = 1, 237 .dpcm_capture = 1, 238 .init = byt_cht_es8316_init, 239 }, 240 }; 241 242 243 /* SoC card */ 244 static struct snd_soc_card byt_cht_es8316_card = { 245 .name = "bytcht-es8316", 246 .owner = THIS_MODULE, 247 .dai_link = byt_cht_es8316_dais, 248 .num_links = ARRAY_SIZE(byt_cht_es8316_dais), 249 .dapm_widgets = byt_cht_es8316_widgets, 250 .num_dapm_widgets = ARRAY_SIZE(byt_cht_es8316_widgets), 251 .dapm_routes = byt_cht_es8316_audio_map, 252 .num_dapm_routes = ARRAY_SIZE(byt_cht_es8316_audio_map), 253 .controls = byt_cht_es8316_controls, 254 .num_controls = ARRAY_SIZE(byt_cht_es8316_controls), 255 .fully_routed = true, 256 }; 257 258 static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) 259 { 260 int ret = 0; 261 struct byt_cht_es8316_private *priv; 262 263 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); 264 if (!priv) 265 return -ENOMEM; 266 267 /* register the soc card */ 268 byt_cht_es8316_card.dev = &pdev->dev; 269 snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv); 270 271 priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); 272 if (IS_ERR(priv->mclk)) { 273 ret = PTR_ERR(priv->mclk); 274 dev_err(&pdev->dev, 275 "Failed to get MCLK from pmc_plt_clk_3: %d\n", 276 ret); 277 return ret; 278 } 279 280 ret = devm_snd_soc_register_card(&pdev->dev, &byt_cht_es8316_card); 281 if (ret) { 282 dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); 283 return ret; 284 } 285 platform_set_drvdata(pdev, &byt_cht_es8316_card); 286 return ret; 287 } 288 289 static struct platform_driver snd_byt_cht_es8316_mc_driver = { 290 .driver = { 291 .name = "bytcht_es8316", 292 }, 293 .probe = snd_byt_cht_es8316_mc_probe, 294 }; 295 296 module_platform_driver(snd_byt_cht_es8316_mc_driver); 297 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver"); 298 MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>"); 299 MODULE_LICENSE("GPL v2"); 300 MODULE_ALIAS("platform:bytcht_es8316"); 301