1 // SPDX-License-Identifier: GPL-2.0-only 2 // 3 // Copyright (c) 2020, The Linux Foundation. All rights reserved. 4 // 5 // sc7180.c -- ALSA SoC Machine driver for SC7180 6 7 #include <dt-bindings/sound/sc7180-lpass.h> 8 #include <linux/gpio.h> 9 #include <linux/gpio/consumer.h> 10 #include <linux/module.h> 11 #include <linux/of_device.h> 12 #include <linux/platform_device.h> 13 #include <sound/core.h> 14 #include <sound/jack.h> 15 #include <sound/pcm.h> 16 #include <sound/soc.h> 17 #include <uapi/linux/input-event-codes.h> 18 19 #include "../codecs/rt5682.h" 20 #include "../codecs/rt5682s.h" 21 #include "common.h" 22 #include "lpass.h" 23 24 #define DEFAULT_MCLK_RATE 19200000 25 #define RT5682_PLL1_FREQ (48000 * 512) 26 27 #define DRIVER_NAME "SC7180" 28 29 struct sc7180_snd_data { 30 struct snd_soc_card card; 31 u32 pri_mi2s_clk_count; 32 struct snd_soc_jack hs_jack; 33 struct snd_soc_jack hdmi_jack; 34 struct gpio_desc *dmic_sel; 35 int dmic_switch; 36 }; 37 38 static void sc7180_jack_free(struct snd_jack *jack) 39 { 40 struct snd_soc_component *component = jack->private_data; 41 42 snd_soc_component_set_jack(component, NULL, NULL); 43 } 44 45 static struct snd_soc_jack_pin sc7180_jack_pins[] = { 46 { 47 .pin = "Headphone Jack", 48 .mask = SND_JACK_HEADPHONE, 49 }, 50 { 51 .pin = "Headset Mic", 52 .mask = SND_JACK_MICROPHONE, 53 }, 54 }; 55 56 static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd) 57 { 58 struct snd_soc_card *card = rtd->card; 59 struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card); 60 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 61 struct snd_soc_component *component = codec_dai->component; 62 struct snd_jack *jack; 63 int rval; 64 65 rval = snd_soc_card_jack_new_pins(card, "Headset Jack", 66 SND_JACK_HEADSET | 67 SND_JACK_HEADPHONE | 68 SND_JACK_BTN_0 | SND_JACK_BTN_1 | 69 SND_JACK_BTN_2 | SND_JACK_BTN_3, 70 &pdata->hs_jack, 71 sc7180_jack_pins, 72 ARRAY_SIZE(sc7180_jack_pins)); 73 74 if (rval < 0) { 75 dev_err(card->dev, "Unable to add Headset Jack\n"); 76 return rval; 77 } 78 79 jack = pdata->hs_jack.jack; 80 81 snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 82 snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); 83 snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP); 84 snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); 85 86 jack->private_data = component; 87 jack->private_free = sc7180_jack_free; 88 89 return snd_soc_component_set_jack(component, &pdata->hs_jack, NULL); 90 } 91 92 static int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd) 93 { 94 struct snd_soc_card *card = rtd->card; 95 struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card); 96 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 97 struct snd_soc_component *component = codec_dai->component; 98 struct snd_jack *jack; 99 int rval; 100 101 rval = snd_soc_card_jack_new( 102 card, "HDMI Jack", 103 SND_JACK_LINEOUT, 104 &pdata->hdmi_jack); 105 106 if (rval < 0) { 107 dev_err(card->dev, "Unable to add HDMI Jack\n"); 108 return rval; 109 } 110 111 jack = pdata->hdmi_jack.jack; 112 jack->private_data = component; 113 jack->private_free = sc7180_jack_free; 114 115 return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL); 116 } 117 118 static int sc7180_init(struct snd_soc_pcm_runtime *rtd) 119 { 120 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 121 122 switch (cpu_dai->id) { 123 case MI2S_PRIMARY: 124 return sc7180_headset_init(rtd); 125 case MI2S_SECONDARY: 126 return 0; 127 case LPASS_DP_RX: 128 return sc7180_hdmi_init(rtd); 129 default: 130 dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, 131 cpu_dai->id); 132 return -EINVAL; 133 } 134 return 0; 135 } 136 137 static int sc7180_snd_startup(struct snd_pcm_substream *substream) 138 { 139 struct snd_soc_pcm_runtime *rtd = substream->private_data; 140 struct snd_soc_card *card = rtd->card; 141 struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); 142 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 143 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 144 int pll_id, pll_source, pll_in, pll_out, clk_id, ret; 145 146 if (!strcmp(codec_dai->name, "rt5682-aif1")) { 147 pll_source = RT5682_PLL1_S_MCLK; 148 pll_id = 0; 149 clk_id = RT5682_SCLK_S_PLL1; 150 pll_out = RT5682_PLL1_FREQ; 151 pll_in = DEFAULT_MCLK_RATE; 152 } else if (!strcmp(codec_dai->name, "rt5682s-aif1")) { 153 pll_source = RT5682S_PLL_S_MCLK; 154 pll_id = RT5682S_PLL2; 155 clk_id = RT5682S_SCLK_S_PLL2; 156 pll_out = RT5682_PLL1_FREQ; 157 pll_in = DEFAULT_MCLK_RATE; 158 } 159 160 switch (cpu_dai->id) { 161 case MI2S_PRIMARY: 162 if (++data->pri_mi2s_clk_count == 1) { 163 snd_soc_dai_set_sysclk(cpu_dai, 164 LPASS_MCLK0, 165 DEFAULT_MCLK_RATE, 166 SNDRV_PCM_STREAM_PLAYBACK); 167 } 168 169 snd_soc_dai_set_fmt(codec_dai, 170 SND_SOC_DAIFMT_BC_FC | 171 SND_SOC_DAIFMT_NB_NF | 172 SND_SOC_DAIFMT_I2S); 173 174 /* Configure PLL1 for codec */ 175 ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, 176 pll_in, pll_out); 177 if (ret) { 178 dev_err(rtd->dev, "can't set codec pll: %d\n", ret); 179 return ret; 180 } 181 182 /* Configure sysclk for codec */ 183 ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out, 184 SND_SOC_CLOCK_IN); 185 if (ret) 186 dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", 187 ret); 188 189 break; 190 case MI2S_SECONDARY: 191 break; 192 case LPASS_DP_RX: 193 break; 194 default: 195 dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, 196 cpu_dai->id); 197 return -EINVAL; 198 } 199 return 0; 200 } 201 202 static int dmic_get(struct snd_kcontrol *kcontrol, 203 struct snd_ctl_elem_value *ucontrol) 204 { 205 struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 206 struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card); 207 208 ucontrol->value.integer.value[0] = data->dmic_switch; 209 return 0; 210 } 211 212 static int dmic_set(struct snd_kcontrol *kcontrol, 213 struct snd_ctl_elem_value *ucontrol) 214 { 215 struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 216 struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card); 217 218 data->dmic_switch = ucontrol->value.integer.value[0]; 219 gpiod_set_value(data->dmic_sel, data->dmic_switch); 220 return 0; 221 } 222 223 static void sc7180_snd_shutdown(struct snd_pcm_substream *substream) 224 { 225 struct snd_soc_pcm_runtime *rtd = substream->private_data; 226 struct snd_soc_card *card = rtd->card; 227 struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); 228 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 229 230 switch (cpu_dai->id) { 231 case MI2S_PRIMARY: 232 if (--data->pri_mi2s_clk_count == 0) { 233 snd_soc_dai_set_sysclk(cpu_dai, 234 LPASS_MCLK0, 235 0, 236 SNDRV_PCM_STREAM_PLAYBACK); 237 } 238 break; 239 case MI2S_SECONDARY: 240 break; 241 case LPASS_DP_RX: 242 break; 243 default: 244 dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, 245 cpu_dai->id); 246 break; 247 } 248 } 249 250 static int sc7180_adau7002_init(struct snd_soc_pcm_runtime *rtd) 251 { 252 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 253 254 switch (cpu_dai->id) { 255 case MI2S_PRIMARY: 256 return 0; 257 case MI2S_SECONDARY: 258 return 0; 259 case LPASS_DP_RX: 260 return sc7180_hdmi_init(rtd); 261 default: 262 dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, 263 cpu_dai->id); 264 return -EINVAL; 265 } 266 return 0; 267 } 268 269 static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream) 270 { 271 struct snd_soc_pcm_runtime *rtd = substream->private_data; 272 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 273 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 274 struct snd_pcm_runtime *runtime = substream->runtime; 275 276 switch (cpu_dai->id) { 277 case MI2S_PRIMARY: 278 snd_soc_dai_set_fmt(codec_dai, 279 SND_SOC_DAIFMT_CBS_CFS | 280 SND_SOC_DAIFMT_NB_NF | 281 SND_SOC_DAIFMT_I2S); 282 runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; 283 snd_pcm_hw_constraint_msbits(runtime, 0, 32, 32); 284 285 break; 286 case MI2S_SECONDARY: 287 break; 288 case LPASS_DP_RX: 289 break; 290 default: 291 dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, 292 cpu_dai->id); 293 return -EINVAL; 294 } 295 return 0; 296 } 297 298 static const struct snd_soc_ops sc7180_ops = { 299 .startup = sc7180_snd_startup, 300 .shutdown = sc7180_snd_shutdown, 301 }; 302 303 static const struct snd_soc_ops sc7180_adau7002_ops = { 304 .startup = sc7180_adau7002_snd_startup, 305 }; 306 307 static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = { 308 SND_SOC_DAPM_HP("Headphone Jack", NULL), 309 SND_SOC_DAPM_MIC("Headset Mic", NULL), 310 }; 311 312 static const struct snd_kcontrol_new sc7180_snd_controls[] = { 313 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 314 SOC_DAPM_PIN_SWITCH("Headset Mic"), 315 }; 316 317 static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = { 318 SND_SOC_DAPM_MIC("DMIC", NULL), 319 }; 320 321 static const char * const dmic_mux_text[] = { 322 "Front Mic", 323 "Rear Mic", 324 }; 325 326 static SOC_ENUM_SINGLE_DECL(sc7180_dmic_enum, 327 SND_SOC_NOPM, 0, dmic_mux_text); 328 329 static const struct snd_kcontrol_new sc7180_dmic_mux_control = 330 SOC_DAPM_ENUM_EXT("DMIC Select Mux", sc7180_dmic_enum, 331 dmic_get, dmic_set); 332 333 static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = { 334 SND_SOC_DAPM_HP("Headphone Jack", NULL), 335 SND_SOC_DAPM_MIC("Headset Mic", NULL), 336 SND_SOC_DAPM_MIC("DMIC", NULL), 337 SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control), 338 }; 339 340 static const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = { 341 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 342 SOC_DAPM_PIN_SWITCH("Headset Mic"), 343 }; 344 345 static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = { 346 {"Dmic Mux", "Front Mic", "DMIC"}, 347 {"Dmic Mux", "Rear Mic", "DMIC"}, 348 }; 349 350 static int sc7180_snd_platform_probe(struct platform_device *pdev) 351 { 352 struct snd_soc_card *card; 353 struct sc7180_snd_data *data; 354 struct device *dev = &pdev->dev; 355 struct snd_soc_dai_link *link; 356 int ret; 357 int i; 358 bool no_headphone = false; 359 360 /* Allocate the private data */ 361 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 362 if (!data) 363 return -ENOMEM; 364 365 card = &data->card; 366 snd_soc_card_set_drvdata(card, data); 367 368 card->owner = THIS_MODULE; 369 card->driver_name = DRIVER_NAME; 370 card->dev = dev; 371 card->dapm_widgets = sc7180_snd_widgets; 372 card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets); 373 card->controls = sc7180_snd_controls; 374 card->num_controls = ARRAY_SIZE(sc7180_snd_controls); 375 376 if (of_property_read_bool(dev->of_node, "dmic-gpios")) { 377 card->dapm_widgets = sc7180_snd_dual_mic_widgets, 378 card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets), 379 card->controls = sc7180_snd_dual_mic_controls, 380 card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls), 381 card->dapm_routes = sc7180_snd_dual_mic_audio_route, 382 card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route), 383 data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW); 384 if (IS_ERR(data->dmic_sel)) { 385 dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", PTR_ERR(data->dmic_sel)); 386 return PTR_ERR(data->dmic_sel); 387 } 388 } 389 390 if (of_device_is_compatible(dev->of_node, "google,sc7180-coachz")) { 391 no_headphone = true; 392 card->dapm_widgets = sc7180_adau7002_snd_widgets; 393 card->num_dapm_widgets = ARRAY_SIZE(sc7180_adau7002_snd_widgets); 394 } 395 396 ret = qcom_snd_parse_of(card); 397 if (ret) 398 return ret; 399 400 for_each_card_prelinks(card, i, link) { 401 if (no_headphone) { 402 link->ops = &sc7180_adau7002_ops; 403 link->init = sc7180_adau7002_init; 404 } else { 405 link->ops = &sc7180_ops; 406 link->init = sc7180_init; 407 } 408 } 409 410 return devm_snd_soc_register_card(dev, card); 411 } 412 413 static const struct of_device_id sc7180_snd_device_id[] = { 414 {.compatible = "google,sc7180-trogdor"}, 415 {.compatible = "google,sc7180-coachz"}, 416 {}, 417 }; 418 MODULE_DEVICE_TABLE(of, sc7180_snd_device_id); 419 420 static struct platform_driver sc7180_snd_driver = { 421 .probe = sc7180_snd_platform_probe, 422 .driver = { 423 .name = "msm-snd-sc7180", 424 .of_match_table = sc7180_snd_device_id, 425 .pm = &snd_soc_pm_ops, 426 }, 427 }; 428 module_platform_driver(sc7180_snd_driver); 429 430 MODULE_DESCRIPTION("sc7180 ASoC Machine Driver"); 431 MODULE_LICENSE("GPL v2"); 432