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 = substream->private_data; 76 struct snd_soc_dai *codec_dai = rtd->codec_dai; 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 = runtime->codec_dai; 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_MEDIA 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_MEDIA); 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 static struct snd_soc_dai_link cht_dailink[] = { 171 /* Front End DAI links */ 172 [MERR_DPCM_AUDIO] = { 173 .name = "Audio Port", 174 .stream_name = "Audio", 175 .cpu_dai_name = "media-cpu-dai", 176 .codec_dai_name = "snd-soc-dummy-dai", 177 .codec_name = "snd-soc-dummy", 178 .platform_name = "sst-mfld-platform", 179 .nonatomic = true, 180 .dynamic = 1, 181 .dpcm_playback = 1, 182 .dpcm_capture = 1, 183 .ops = &cht_aif1_ops, 184 }, 185 [MERR_DPCM_DEEP_BUFFER] = { 186 .name = "Deep-Buffer Audio Port", 187 .stream_name = "Deep-Buffer Audio", 188 .cpu_dai_name = "deepbuffer-cpu-dai", 189 .codec_dai_name = "snd-soc-dummy-dai", 190 .codec_name = "snd-soc-dummy", 191 .platform_name = "sst-mfld-platform", 192 .nonatomic = true, 193 .dynamic = 1, 194 .dpcm_playback = 1, 195 .ops = &cht_aif1_ops, 196 }, 197 [MERR_DPCM_COMPR] = { 198 .name = "Compressed Port", 199 .stream_name = "Compress", 200 .cpu_dai_name = "compress-cpu-dai", 201 .codec_dai_name = "snd-soc-dummy-dai", 202 .codec_name = "snd-soc-dummy", 203 .platform_name = "sst-mfld-platform", 204 }, 205 /* Back End DAI links */ 206 { 207 /* SSP2 - Codec */ 208 .name = "SSP2-Codec", 209 .id = 1, 210 .cpu_dai_name = "ssp2-port", 211 .platform_name = "sst-mfld-platform", 212 .no_pcm = 1, 213 .codec_dai_name = NAU8824_CODEC_DAI, 214 .codec_name = "i2c-10508824:00", 215 .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF 216 | SND_SOC_DAIFMT_CBS_CFS, 217 .init = cht_codec_init, 218 .be_hw_params_fixup = cht_codec_fixup, 219 .dpcm_playback = 1, 220 .dpcm_capture = 1, 221 .ops = &cht_be_ssp2_ops, 222 }, 223 }; 224 225 /* SoC card */ 226 static struct snd_soc_card snd_soc_card_cht = { 227 .name = "chtnau8824", 228 .owner = THIS_MODULE, 229 .dai_link = cht_dailink, 230 .num_links = ARRAY_SIZE(cht_dailink), 231 .dapm_widgets = cht_dapm_widgets, 232 .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), 233 .dapm_routes = cht_audio_map, 234 .num_dapm_routes = ARRAY_SIZE(cht_audio_map), 235 .controls = cht_mc_controls, 236 .num_controls = ARRAY_SIZE(cht_mc_controls), 237 }; 238 239 static int snd_cht_mc_probe(struct platform_device *pdev) 240 { 241 struct cht_mc_private *drv; 242 struct snd_soc_acpi_mach *mach; 243 const char *platform_name; 244 int ret_val; 245 246 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); 247 if (!drv) 248 return -ENOMEM; 249 snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); 250 251 /* override plaform name, if required */ 252 snd_soc_card_cht.dev = &pdev->dev; 253 mach = (&pdev->dev)->platform_data; 254 platform_name = mach->mach_params.platform; 255 256 ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht, 257 platform_name); 258 if (ret_val) 259 return ret_val; 260 261 /* register the soc card */ 262 ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); 263 if (ret_val) { 264 dev_err(&pdev->dev, 265 "snd_soc_register_card failed %d\n", ret_val); 266 return ret_val; 267 } 268 platform_set_drvdata(pdev, &snd_soc_card_cht); 269 270 return ret_val; 271 } 272 273 static struct platform_driver snd_cht_mc_driver = { 274 .driver = { 275 .name = "cht-bsw-nau8824", 276 }, 277 .probe = snd_cht_mc_probe, 278 }; 279 280 module_platform_driver(snd_cht_mc_driver); 281 282 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); 283 MODULE_AUTHOR("Wang, Joseph C <joequant@gmail.com>"); 284 MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>"); 285 MODULE_LICENSE("GPL v2"); 286 MODULE_ALIAS("platform:cht-bsw-nau8824"); 287