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(ssp2_port, 180 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); 181 SND_SOC_DAILINK_DEF(ssp2_codec, 182 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508824:00", 183 NAU8824_CODEC_DAI))); 184 185 SND_SOC_DAILINK_DEF(platform, 186 DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform"))); 187 188 static struct snd_soc_dai_link cht_dailink[] = { 189 /* Front End DAI links */ 190 [MERR_DPCM_AUDIO] = { 191 .name = "Audio Port", 192 .stream_name = "Audio", 193 .nonatomic = true, 194 .dynamic = 1, 195 .dpcm_playback = 1, 196 .dpcm_capture = 1, 197 .ops = &cht_aif1_ops, 198 SND_SOC_DAILINK_REG(media, dummy, platform), 199 }, 200 [MERR_DPCM_DEEP_BUFFER] = { 201 .name = "Deep-Buffer Audio Port", 202 .stream_name = "Deep-Buffer Audio", 203 .nonatomic = true, 204 .dynamic = 1, 205 .dpcm_playback = 1, 206 .ops = &cht_aif1_ops, 207 SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), 208 }, 209 /* Back End DAI links */ 210 { 211 /* SSP2 - Codec */ 212 .name = "SSP2-Codec", 213 .id = 0, 214 .no_pcm = 1, 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 SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform), 223 }, 224 }; 225 226 /* use space before codec name to simplify card ID, and simplify driver name */ 227 #define SOF_CARD_NAME "bytcht nau8824" /* card name will be 'sof-bytcht nau8824 */ 228 #define SOF_DRIVER_NAME "SOF" 229 230 #define CARD_NAME "chtnau8824" 231 #define DRIVER_NAME NULL /* card name will be used for driver name */ 232 233 /* SoC card */ 234 static struct snd_soc_card snd_soc_card_cht = { 235 .owner = THIS_MODULE, 236 .dai_link = cht_dailink, 237 .num_links = ARRAY_SIZE(cht_dailink), 238 .dapm_widgets = cht_dapm_widgets, 239 .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), 240 .dapm_routes = cht_audio_map, 241 .num_dapm_routes = ARRAY_SIZE(cht_audio_map), 242 .controls = cht_mc_controls, 243 .num_controls = ARRAY_SIZE(cht_mc_controls), 244 }; 245 246 static int snd_cht_mc_probe(struct platform_device *pdev) 247 { 248 struct cht_mc_private *drv; 249 struct snd_soc_acpi_mach *mach; 250 const char *platform_name; 251 bool sof_parent; 252 int ret_val; 253 254 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); 255 if (!drv) 256 return -ENOMEM; 257 snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); 258 259 /* override plaform name, if required */ 260 snd_soc_card_cht.dev = &pdev->dev; 261 mach = pdev->dev.platform_data; 262 platform_name = mach->mach_params.platform; 263 264 ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht, 265 platform_name); 266 if (ret_val) 267 return ret_val; 268 269 sof_parent = snd_soc_acpi_sof_parent(&pdev->dev); 270 271 /* set card and driver name */ 272 if (sof_parent) { 273 snd_soc_card_cht.name = SOF_CARD_NAME; 274 snd_soc_card_cht.driver_name = SOF_DRIVER_NAME; 275 } else { 276 snd_soc_card_cht.name = CARD_NAME; 277 snd_soc_card_cht.driver_name = DRIVER_NAME; 278 } 279 280 /* set pm ops */ 281 if (sof_parent) 282 pdev->dev.driver->pm = &snd_soc_pm_ops; 283 284 /* register the soc card */ 285 ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); 286 if (ret_val) { 287 dev_err(&pdev->dev, 288 "snd_soc_register_card failed %d\n", ret_val); 289 return ret_val; 290 } 291 platform_set_drvdata(pdev, &snd_soc_card_cht); 292 293 return ret_val; 294 } 295 296 static struct platform_driver snd_cht_mc_driver = { 297 .driver = { 298 .name = "cht-bsw-nau8824", 299 }, 300 .probe = snd_cht_mc_probe, 301 }; 302 303 module_platform_driver(snd_cht_mc_driver); 304 305 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); 306 MODULE_AUTHOR("Wang, Joseph C <joequant@gmail.com>"); 307 MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>"); 308 MODULE_LICENSE("GPL v2"); 309 MODULE_ALIAS("platform:cht-bsw-nau8824"); 310