1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * cht-bsw-nau8824.c - ASoc Machine driver for Intel Cherryview-based 4 * platforms Cherrytrail and Braswell, with nau8824 codec. 5 * 6 * Copyright (C) 2018 Intel Corp 7 * Copyright (C) 2018 Nuvoton Technology Corp 8 * 9 * Author: Wang, Joseph C <joequant@gmail.com> 10 * Co-author: John Hsu <KCHSU0@nuvoton.com> 11 * This file is based on cht_bsw_rt5672.c and cht-bsw-max98090.c 12 */ 13 14 #include <linux/module.h> 15 #include <linux/platform_device.h> 16 #include <linux/slab.h> 17 #include <sound/pcm.h> 18 #include <sound/pcm_params.h> 19 #include <sound/soc.h> 20 #include <sound/soc-acpi.h> 21 #include <sound/jack.h> 22 #include <linux/input.h> 23 #include "../atom/sst-atom-controls.h" 24 #include "../../codecs/nau8824.h" 25 26 struct cht_mc_private { 27 struct snd_soc_jack jack; 28 }; 29 30 static struct snd_soc_jack_pin cht_bsw_jack_pins[] = { 31 { 32 .pin = "Headphone", 33 .mask = SND_JACK_HEADPHONE, 34 }, 35 { 36 .pin = "Headset Mic", 37 .mask = SND_JACK_MICROPHONE, 38 }, 39 }; 40 41 static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { 42 SND_SOC_DAPM_HP("Headphone", NULL), 43 SND_SOC_DAPM_MIC("Headset Mic", NULL), 44 SND_SOC_DAPM_MIC("Int Mic", NULL), 45 SND_SOC_DAPM_SPK("Ext Spk", NULL), 46 }; 47 48 static const struct snd_soc_dapm_route cht_audio_map[] = { 49 {"Ext Spk", NULL, "SPKOUTL"}, 50 {"Ext Spk", NULL, "SPKOUTR"}, 51 {"Headphone", NULL, "HPOL"}, 52 {"Headphone", NULL, "HPOR"}, 53 {"MIC1", NULL, "Int Mic"}, 54 {"MIC2", NULL, "Int Mic"}, 55 {"HSMIC1", NULL, "Headset Mic"}, 56 {"HSMIC2", NULL, "Headset Mic"}, 57 {"Playback", NULL, "ssp2 Tx"}, 58 {"ssp2 Tx", NULL, "codec_out0"}, 59 {"ssp2 Tx", NULL, "codec_out1"}, 60 {"codec_in0", NULL, "ssp2 Rx" }, 61 {"codec_in1", NULL, "ssp2 Rx" }, 62 {"ssp2 Rx", NULL, "Capture"}, 63 }; 64 65 static const struct snd_kcontrol_new cht_mc_controls[] = { 66 SOC_DAPM_PIN_SWITCH("Headphone"), 67 SOC_DAPM_PIN_SWITCH("Headset Mic"), 68 SOC_DAPM_PIN_SWITCH("Int Mic"), 69 SOC_DAPM_PIN_SWITCH("Ext Spk"), 70 }; 71 72 static int cht_aif1_hw_params(struct snd_pcm_substream *substream, 73 struct snd_pcm_hw_params *params) 74 { 75 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 76 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 77 int ret; 78 79 ret = snd_soc_dai_set_sysclk(codec_dai, NAU8824_CLK_FLL_FS, 0, 80 SND_SOC_CLOCK_IN); 81 if (ret < 0) { 82 dev_err(codec_dai->dev, "can't set FS clock %d\n", ret); 83 return ret; 84 } 85 ret = snd_soc_dai_set_pll(codec_dai, 0, 0, params_rate(params), 86 params_rate(params) * 256); 87 if (ret < 0) { 88 dev_err(codec_dai->dev, "can't set FLL: %d\n", ret); 89 return ret; 90 } 91 92 return 0; 93 } 94 95 static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) 96 { 97 struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); 98 struct snd_soc_jack *jack = &ctx->jack; 99 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); 100 struct snd_soc_component *component = codec_dai->component; 101 int ret, jack_type; 102 103 /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ 104 ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xf, 0x1, 4, 24); 105 if (ret < 0) { 106 dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret); 107 return ret; 108 } 109 110 /* NAU88L24 supports 4 butons headset detection 111 * KEY_PLAYPAUSE 112 * KEY_VOICECOMMAND 113 * KEY_VOLUMEUP 114 * KEY_VOLUMEDOWN 115 */ 116 jack_type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | 117 SND_JACK_BTN_2 | SND_JACK_BTN_3; 118 ret = snd_soc_card_jack_new(runtime->card, "Headset", jack_type, jack, 119 cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins)); 120 if (ret) { 121 dev_err(runtime->dev, 122 "Headset Jack creation failed %d\n", ret); 123 return ret; 124 } 125 snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 126 snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); 127 snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); 128 snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); 129 130 nau8824_enable_jack_detect(component, jack); 131 132 return ret; 133 } 134 135 static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, 136 struct snd_pcm_hw_params *params) 137 { 138 struct snd_interval *rate = hw_param_interval(params, 139 SNDRV_PCM_HW_PARAM_RATE); 140 struct snd_interval *channels = hw_param_interval(params, 141 SNDRV_PCM_HW_PARAM_CHANNELS); 142 struct snd_mask *fmt = 143 hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 144 145 /* The DSP will covert the FE rate to 48k, stereo, 24bits */ 146 rate->min = rate->max = 48000; 147 channels->min = channels->max = 2; 148 149 /* set SSP2 to 24-bit */ 150 snd_mask_none(fmt); 151 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); 152 153 return 0; 154 } 155 156 static int cht_aif1_startup(struct snd_pcm_substream *substream) 157 { 158 return snd_pcm_hw_constraint_single(substream->runtime, 159 SNDRV_PCM_HW_PARAM_RATE, 48000); 160 } 161 162 static const struct snd_soc_ops cht_aif1_ops = { 163 .startup = cht_aif1_startup, 164 }; 165 166 static const struct snd_soc_ops cht_be_ssp2_ops = { 167 .hw_params = cht_aif1_hw_params, 168 }; 169 170 SND_SOC_DAILINK_DEF(dummy, 171 DAILINK_COMP_ARRAY(COMP_DUMMY())); 172 173 SND_SOC_DAILINK_DEF(media, 174 DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai"))); 175 176 SND_SOC_DAILINK_DEF(deepbuffer, 177 DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai"))); 178 179 SND_SOC_DAILINK_DEF(compress, 180 DAILINK_COMP_ARRAY(COMP_CPU("compress-cpu-dai"))); 181 182 SND_SOC_DAILINK_DEF(ssp2_port, 183 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); 184 SND_SOC_DAILINK_DEF(ssp2_codec, 185 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508824:00", 186 NAU8824_CODEC_DAI))); 187 188 SND_SOC_DAILINK_DEF(platform, 189 DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform"))); 190 191 static struct snd_soc_dai_link cht_dailink[] = { 192 /* Front End DAI links */ 193 [MERR_DPCM_AUDIO] = { 194 .name = "Audio Port", 195 .stream_name = "Audio", 196 .nonatomic = true, 197 .dynamic = 1, 198 .dpcm_playback = 1, 199 .dpcm_capture = 1, 200 .ops = &cht_aif1_ops, 201 SND_SOC_DAILINK_REG(media, dummy, platform), 202 }, 203 [MERR_DPCM_DEEP_BUFFER] = { 204 .name = "Deep-Buffer Audio Port", 205 .stream_name = "Deep-Buffer Audio", 206 .nonatomic = true, 207 .dynamic = 1, 208 .dpcm_playback = 1, 209 .ops = &cht_aif1_ops, 210 SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), 211 }, 212 [MERR_DPCM_COMPR] = { 213 .name = "Compressed Port", 214 .stream_name = "Compress", 215 SND_SOC_DAILINK_REG(compress, dummy, platform), 216 }, 217 /* Back End DAI links */ 218 { 219 /* SSP2 - Codec */ 220 .name = "SSP2-Codec", 221 .id = 1, 222 .no_pcm = 1, 223 .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF 224 | SND_SOC_DAIFMT_CBS_CFS, 225 .init = cht_codec_init, 226 .be_hw_params_fixup = cht_codec_fixup, 227 .dpcm_playback = 1, 228 .dpcm_capture = 1, 229 .ops = &cht_be_ssp2_ops, 230 SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform), 231 }, 232 }; 233 234 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 235 /* use space before codec name to simplify card ID, and simplify driver name */ 236 #define CARD_NAME "bytcht nau8824" /* card name will be 'sof-bytcht nau8824 */ 237 #define DRIVER_NAME "SOF" 238 #else 239 #define CARD_NAME "chtnau8824" 240 #define DRIVER_NAME NULL /* card name will be used for driver name */ 241 #endif 242 243 /* SoC card */ 244 static struct snd_soc_card snd_soc_card_cht = { 245 .name = CARD_NAME, 246 .driver_name = DRIVER_NAME, 247 .owner = THIS_MODULE, 248 .dai_link = cht_dailink, 249 .num_links = ARRAY_SIZE(cht_dailink), 250 .dapm_widgets = cht_dapm_widgets, 251 .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), 252 .dapm_routes = cht_audio_map, 253 .num_dapm_routes = ARRAY_SIZE(cht_audio_map), 254 .controls = cht_mc_controls, 255 .num_controls = ARRAY_SIZE(cht_mc_controls), 256 }; 257 258 static int snd_cht_mc_probe(struct platform_device *pdev) 259 { 260 struct cht_mc_private *drv; 261 struct snd_soc_acpi_mach *mach; 262 const char *platform_name; 263 int ret_val; 264 265 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); 266 if (!drv) 267 return -ENOMEM; 268 snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); 269 270 /* override plaform name, if required */ 271 snd_soc_card_cht.dev = &pdev->dev; 272 mach = pdev->dev.platform_data; 273 platform_name = mach->mach_params.platform; 274 275 ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht, 276 platform_name); 277 if (ret_val) 278 return ret_val; 279 280 /* register the soc card */ 281 ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); 282 if (ret_val) { 283 dev_err(&pdev->dev, 284 "snd_soc_register_card failed %d\n", ret_val); 285 return ret_val; 286 } 287 platform_set_drvdata(pdev, &snd_soc_card_cht); 288 289 return ret_val; 290 } 291 292 static struct platform_driver snd_cht_mc_driver = { 293 .driver = { 294 .name = "cht-bsw-nau8824", 295 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 296 .pm = &snd_soc_pm_ops, 297 #endif 298 }, 299 .probe = snd_cht_mc_probe, 300 }; 301 302 module_platform_driver(snd_cht_mc_driver); 303 304 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); 305 MODULE_AUTHOR("Wang, Joseph C <joequant@gmail.com>"); 306 MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>"); 307 MODULE_LICENSE("GPL v2"); 308 MODULE_ALIAS("platform:cht-bsw-nau8824"); 309