1 /* 2 * Intel Broxton-P I2S Machine Driver 3 * 4 * Copyright (C) 2014-2016, Intel Corporation. All rights reserved. 5 * 6 * Modified from: 7 * Intel Skylake I2S Machine driver 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License version 11 * 2 as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 */ 18 19 #include <linux/module.h> 20 #include <linux/platform_device.h> 21 #include <sound/core.h> 22 #include <sound/pcm.h> 23 #include <sound/soc.h> 24 #include <sound/jack.h> 25 #include <sound/pcm_params.h> 26 #include "../../codecs/hdac_hdmi.h" 27 #include "../../codecs/rt298.h" 28 29 static struct snd_soc_jack broxton_headset; 30 /* Headset jack detection DAPM pins */ 31 32 enum { 33 BXT_DPCM_AUDIO_PB = 0, 34 BXT_DPCM_AUDIO_CP, 35 BXT_DPCM_AUDIO_REF_CP, 36 BXT_DPCM_AUDIO_HDMI1_PB, 37 BXT_DPCM_AUDIO_HDMI2_PB, 38 BXT_DPCM_AUDIO_HDMI3_PB, 39 }; 40 41 static struct snd_soc_jack_pin broxton_headset_pins[] = { 42 { 43 .pin = "Mic Jack", 44 .mask = SND_JACK_MICROPHONE, 45 }, 46 { 47 .pin = "Headphone Jack", 48 .mask = SND_JACK_HEADPHONE, 49 }, 50 }; 51 52 static const struct snd_kcontrol_new broxton_controls[] = { 53 SOC_DAPM_PIN_SWITCH("Speaker"), 54 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 55 SOC_DAPM_PIN_SWITCH("Mic Jack"), 56 }; 57 58 static const struct snd_soc_dapm_widget broxton_widgets[] = { 59 SND_SOC_DAPM_HP("Headphone Jack", NULL), 60 SND_SOC_DAPM_SPK("Speaker", NULL), 61 SND_SOC_DAPM_MIC("Mic Jack", NULL), 62 SND_SOC_DAPM_MIC("DMIC2", NULL), 63 SND_SOC_DAPM_MIC("SoC DMIC", NULL), 64 SND_SOC_DAPM_SPK("HDMI1", NULL), 65 SND_SOC_DAPM_SPK("HDMI2", NULL), 66 SND_SOC_DAPM_SPK("HDMI3", NULL), 67 }; 68 69 static const struct snd_soc_dapm_route broxton_rt298_map[] = { 70 /* speaker */ 71 {"Speaker", NULL, "SPOR"}, 72 {"Speaker", NULL, "SPOL"}, 73 74 /* HP jack connectors - unknown if we have jack detect */ 75 {"Headphone Jack", NULL, "HPO Pin"}, 76 77 /* other jacks */ 78 {"MIC1", NULL, "Mic Jack"}, 79 80 /* digital mics */ 81 {"DMIC1 Pin", NULL, "DMIC2"}, 82 {"DMic", NULL, "SoC DMIC"}, 83 84 {"HDMI1", NULL, "hif5 Output"}, 85 {"HDMI2", NULL, "hif6 Output"}, 86 {"HDMI3", NULL, "hif7 Output"}, 87 88 /* CODEC BE connections */ 89 { "AIF1 Playback", NULL, "ssp5 Tx"}, 90 { "ssp5 Tx", NULL, "codec0_out"}, 91 92 { "codec0_in", NULL, "ssp5 Rx" }, 93 { "ssp5 Rx", NULL, "AIF1 Capture" }, 94 95 { "dmic01_hifi", NULL, "DMIC01 Rx" }, 96 { "DMIC01 Rx", NULL, "Capture" }, 97 98 { "hifi3", NULL, "iDisp3 Tx"}, 99 { "iDisp3 Tx", NULL, "iDisp3_out"}, 100 { "hifi2", NULL, "iDisp2 Tx"}, 101 { "iDisp2 Tx", NULL, "iDisp2_out"}, 102 { "hifi1", NULL, "iDisp1 Tx"}, 103 { "iDisp1 Tx", NULL, "iDisp1_out"}, 104 105 }; 106 107 static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd) 108 { 109 struct snd_soc_codec *codec = rtd->codec; 110 int ret = 0; 111 112 ret = snd_soc_card_jack_new(rtd->card, "Headset", 113 SND_JACK_HEADSET | SND_JACK_BTN_0, 114 &broxton_headset, 115 broxton_headset_pins, ARRAY_SIZE(broxton_headset_pins)); 116 117 if (ret) 118 return ret; 119 120 rt298_mic_detect(codec, &broxton_headset); 121 return 0; 122 } 123 124 static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd) 125 { 126 struct snd_soc_dai *dai = rtd->codec_dai; 127 128 return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id); 129 } 130 131 static int broxton_ssp5_fixup(struct snd_soc_pcm_runtime *rtd, 132 struct snd_pcm_hw_params *params) 133 { 134 struct snd_interval *rate = hw_param_interval(params, 135 SNDRV_PCM_HW_PARAM_RATE); 136 struct snd_interval *channels = hw_param_interval(params, 137 SNDRV_PCM_HW_PARAM_CHANNELS); 138 struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 139 140 /* The ADSP will covert the FE rate to 48k, stereo */ 141 rate->min = rate->max = 48000; 142 channels->min = channels->max = 2; 143 144 /* set SSP5 to 24 bit */ 145 snd_mask_none(fmt); 146 snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); 147 148 return 0; 149 } 150 151 static int broxton_rt298_hw_params(struct snd_pcm_substream *substream, 152 struct snd_pcm_hw_params *params) 153 { 154 struct snd_soc_pcm_runtime *rtd = substream->private_data; 155 struct snd_soc_dai *codec_dai = rtd->codec_dai; 156 int ret; 157 158 ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL, 159 19200000, SND_SOC_CLOCK_IN); 160 if (ret < 0) { 161 dev_err(rtd->dev, "can't set codec sysclk configuration\n"); 162 return ret; 163 } 164 165 return ret; 166 } 167 168 static struct snd_soc_ops broxton_rt298_ops = { 169 .hw_params = broxton_rt298_hw_params, 170 }; 171 172 /* broxton digital audio interface glue - connects codec <--> CPU */ 173 static struct snd_soc_dai_link broxton_rt298_dais[] = { 174 /* Front End DAI links */ 175 [BXT_DPCM_AUDIO_PB] 176 { 177 .name = "Bxt Audio Port", 178 .stream_name = "Audio", 179 .cpu_dai_name = "System Pin", 180 .platform_name = "0000:00:0e.0", 181 .nonatomic = 1, 182 .dynamic = 1, 183 .codec_name = "snd-soc-dummy", 184 .codec_dai_name = "snd-soc-dummy-dai", 185 .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, 186 .dpcm_playback = 1, 187 }, 188 [BXT_DPCM_AUDIO_CP] 189 { 190 .name = "Bxt Audio Capture Port", 191 .stream_name = "Audio Record", 192 .cpu_dai_name = "System Pin", 193 .platform_name = "0000:00:0e.0", 194 .nonatomic = 1, 195 .dynamic = 1, 196 .codec_name = "snd-soc-dummy", 197 .codec_dai_name = "snd-soc-dummy-dai", 198 .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, 199 .dpcm_capture = 1, 200 }, 201 [BXT_DPCM_AUDIO_REF_CP] 202 { 203 .name = "Bxt Audio Reference cap", 204 .stream_name = "refcap", 205 .cpu_dai_name = "Reference Pin", 206 .codec_name = "snd-soc-dummy", 207 .codec_dai_name = "snd-soc-dummy-dai", 208 .platform_name = "0000:00:0e.0", 209 .init = NULL, 210 .dpcm_capture = 1, 211 .nonatomic = 1, 212 .dynamic = 1, 213 }, 214 [BXT_DPCM_AUDIO_HDMI1_PB] 215 { 216 .name = "Bxt HDMI Port1", 217 .stream_name = "Hdmi1", 218 .cpu_dai_name = "HDMI1 Pin", 219 .codec_name = "snd-soc-dummy", 220 .codec_dai_name = "snd-soc-dummy-dai", 221 .platform_name = "0000:00:0e.0", 222 .dpcm_playback = 1, 223 .init = NULL, 224 .nonatomic = 1, 225 .dynamic = 1, 226 }, 227 [BXT_DPCM_AUDIO_HDMI2_PB] 228 { 229 .name = "Bxt HDMI Port2", 230 .stream_name = "Hdmi2", 231 .cpu_dai_name = "HDMI2 Pin", 232 .codec_name = "snd-soc-dummy", 233 .codec_dai_name = "snd-soc-dummy-dai", 234 .platform_name = "0000:00:0e.0", 235 .dpcm_playback = 1, 236 .init = NULL, 237 .nonatomic = 1, 238 .dynamic = 1, 239 }, 240 [BXT_DPCM_AUDIO_HDMI3_PB] 241 { 242 .name = "Bxt HDMI Port3", 243 .stream_name = "Hdmi3", 244 .cpu_dai_name = "HDMI3 Pin", 245 .codec_name = "snd-soc-dummy", 246 .codec_dai_name = "snd-soc-dummy-dai", 247 .platform_name = "0000:00:0e.0", 248 .dpcm_playback = 1, 249 .init = NULL, 250 .nonatomic = 1, 251 .dynamic = 1, 252 }, 253 /* Back End DAI links */ 254 { 255 /* SSP5 - Codec */ 256 .name = "SSP5-Codec", 257 .id = 0, 258 .cpu_dai_name = "SSP5 Pin", 259 .platform_name = "0000:00:0e.0", 260 .no_pcm = 1, 261 .codec_name = "i2c-INT343A:00", 262 .codec_dai_name = "rt298-aif1", 263 .init = broxton_rt298_codec_init, 264 .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | 265 SND_SOC_DAIFMT_CBS_CFS, 266 .ignore_pmdown_time = 1, 267 .be_hw_params_fixup = broxton_ssp5_fixup, 268 .ops = &broxton_rt298_ops, 269 .dpcm_playback = 1, 270 .dpcm_capture = 1, 271 }, 272 { 273 .name = "dmic01", 274 .id = 1, 275 .cpu_dai_name = "DMIC01 Pin", 276 .codec_name = "dmic-codec", 277 .codec_dai_name = "dmic-hifi", 278 .platform_name = "0000:00:0e.0", 279 .ignore_suspend = 1, 280 .dpcm_capture = 1, 281 .no_pcm = 1, 282 }, 283 { 284 .name = "iDisp1", 285 .id = 3, 286 .cpu_dai_name = "iDisp1 Pin", 287 .codec_name = "ehdaudio0D2", 288 .codec_dai_name = "intel-hdmi-hifi1", 289 .platform_name = "0000:00:0e.0", 290 .init = broxton_hdmi_init, 291 .dpcm_playback = 1, 292 .no_pcm = 1, 293 }, 294 { 295 .name = "iDisp2", 296 .id = 4, 297 .cpu_dai_name = "iDisp2 Pin", 298 .codec_name = "ehdaudio0D2", 299 .codec_dai_name = "intel-hdmi-hifi2", 300 .platform_name = "0000:00:0e.0", 301 .init = broxton_hdmi_init, 302 .dpcm_playback = 1, 303 .no_pcm = 1, 304 }, 305 { 306 .name = "iDisp3", 307 .id = 5, 308 .cpu_dai_name = "iDisp3 Pin", 309 .codec_name = "ehdaudio0D2", 310 .codec_dai_name = "intel-hdmi-hifi3", 311 .platform_name = "0000:00:0e.0", 312 .init = broxton_hdmi_init, 313 .dpcm_playback = 1, 314 .no_pcm = 1, 315 }, 316 }; 317 318 /* broxton audio machine driver for SPT + RT298S */ 319 static struct snd_soc_card broxton_rt298 = { 320 .name = "broxton-rt298", 321 .owner = THIS_MODULE, 322 .dai_link = broxton_rt298_dais, 323 .num_links = ARRAY_SIZE(broxton_rt298_dais), 324 .controls = broxton_controls, 325 .num_controls = ARRAY_SIZE(broxton_controls), 326 .dapm_widgets = broxton_widgets, 327 .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), 328 .dapm_routes = broxton_rt298_map, 329 .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), 330 .fully_routed = true, 331 }; 332 333 static int broxton_audio_probe(struct platform_device *pdev) 334 { 335 broxton_rt298.dev = &pdev->dev; 336 337 return devm_snd_soc_register_card(&pdev->dev, &broxton_rt298); 338 } 339 340 static struct platform_driver broxton_audio = { 341 .probe = broxton_audio_probe, 342 .driver = { 343 .name = "bxt_alc298s_i2s", 344 }, 345 }; 346 module_platform_driver(broxton_audio) 347 348 /* Module information */ 349 MODULE_AUTHOR("Ramesh Babu <Ramesh.Babu@intel.com>"); 350 MODULE_AUTHOR("Senthilnathan Veppur <senthilnathanx.veppur@intel.com>"); 351 MODULE_DESCRIPTION("Intel SST Audio for Broxton"); 352 MODULE_LICENSE("GPL v2"); 353 MODULE_ALIAS("platform:bxt_alc298s_i2s"); 354