1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * n810.c -- SoC audio for Nokia N810 4 * 5 * Copyright (C) 2008 Nokia Corporation 6 * 7 * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> 8 */ 9 10 #include <linux/clk.h> 11 #include <linux/i2c.h> 12 #include <linux/platform_device.h> 13 #include <sound/core.h> 14 #include <sound/pcm.h> 15 #include <sound/soc.h> 16 17 #include <asm/mach-types.h> 18 #include <linux/gpio/consumer.h> 19 #include <linux/module.h> 20 #include <linux/platform_data/asoc-ti-mcbsp.h> 21 22 #include "omap-mcbsp.h" 23 24 static struct gpio_desc *n810_headset_amp; 25 static struct gpio_desc *n810_speaker_amp; 26 27 enum { 28 N810_JACK_DISABLED, 29 N810_JACK_HP, 30 N810_JACK_HS, 31 N810_JACK_MIC, 32 }; 33 34 static struct clk *sys_clkout2; 35 static struct clk *sys_clkout2_src; 36 static struct clk *func96m_clk; 37 38 static int n810_spk_func; 39 static int n810_jack_func; 40 static int n810_dmic_func; 41 42 static void n810_ext_control(struct snd_soc_dapm_context *dapm) 43 { 44 int hp = 0, line1l = 0; 45 46 switch (n810_jack_func) { 47 case N810_JACK_HS: 48 line1l = 1; 49 fallthrough; 50 case N810_JACK_HP: 51 hp = 1; 52 break; 53 case N810_JACK_MIC: 54 line1l = 1; 55 break; 56 } 57 58 snd_soc_dapm_mutex_lock(dapm); 59 60 if (n810_spk_func) 61 snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); 62 else 63 snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); 64 65 if (hp) 66 snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); 67 else 68 snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 69 if (line1l) 70 snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic"); 71 else 72 snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic"); 73 74 if (n810_dmic_func) 75 snd_soc_dapm_enable_pin_unlocked(dapm, "DMic"); 76 else 77 snd_soc_dapm_disable_pin_unlocked(dapm, "DMic"); 78 79 snd_soc_dapm_sync_unlocked(dapm); 80 81 snd_soc_dapm_mutex_unlock(dapm); 82 } 83 84 static int n810_startup(struct snd_pcm_substream *substream) 85 { 86 struct snd_pcm_runtime *runtime = substream->runtime; 87 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 88 struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(rtd->card); 89 90 snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2); 91 92 n810_ext_control(dapm); 93 return clk_prepare_enable(sys_clkout2); 94 } 95 96 static void n810_shutdown(struct snd_pcm_substream *substream) 97 { 98 clk_disable_unprepare(sys_clkout2); 99 } 100 101 static int n810_hw_params(struct snd_pcm_substream *substream, 102 struct snd_pcm_hw_params *params) 103 { 104 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 105 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 106 int err; 107 108 /* Set the codec system clock for DAC and ADC */ 109 err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000, 110 SND_SOC_CLOCK_IN); 111 112 return err; 113 } 114 115 static const struct snd_soc_ops n810_ops = { 116 .startup = n810_startup, 117 .hw_params = n810_hw_params, 118 .shutdown = n810_shutdown, 119 }; 120 121 static int n810_get_spk(struct snd_kcontrol *kcontrol, 122 struct snd_ctl_elem_value *ucontrol) 123 { 124 ucontrol->value.enumerated.item[0] = n810_spk_func; 125 126 return 0; 127 } 128 129 static int n810_set_spk(struct snd_kcontrol *kcontrol, 130 struct snd_ctl_elem_value *ucontrol) 131 { 132 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 133 struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); 134 135 if (n810_spk_func == ucontrol->value.enumerated.item[0]) 136 return 0; 137 138 n810_spk_func = ucontrol->value.enumerated.item[0]; 139 n810_ext_control(dapm); 140 141 return 1; 142 } 143 144 static int n810_get_jack(struct snd_kcontrol *kcontrol, 145 struct snd_ctl_elem_value *ucontrol) 146 { 147 ucontrol->value.enumerated.item[0] = n810_jack_func; 148 149 return 0; 150 } 151 152 static int n810_set_jack(struct snd_kcontrol *kcontrol, 153 struct snd_ctl_elem_value *ucontrol) 154 { 155 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 156 struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); 157 158 if (n810_jack_func == ucontrol->value.enumerated.item[0]) 159 return 0; 160 161 n810_jack_func = ucontrol->value.enumerated.item[0]; 162 n810_ext_control(dapm); 163 164 return 1; 165 } 166 167 static int n810_get_input(struct snd_kcontrol *kcontrol, 168 struct snd_ctl_elem_value *ucontrol) 169 { 170 ucontrol->value.enumerated.item[0] = n810_dmic_func; 171 172 return 0; 173 } 174 175 static int n810_set_input(struct snd_kcontrol *kcontrol, 176 struct snd_ctl_elem_value *ucontrol) 177 { 178 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 179 struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); 180 181 if (n810_dmic_func == ucontrol->value.enumerated.item[0]) 182 return 0; 183 184 n810_dmic_func = ucontrol->value.enumerated.item[0]; 185 n810_ext_control(dapm); 186 187 return 1; 188 } 189 190 static int n810_spk_event(struct snd_soc_dapm_widget *w, 191 struct snd_kcontrol *k, int event) 192 { 193 if (SND_SOC_DAPM_EVENT_ON(event)) 194 gpiod_set_value(n810_speaker_amp, 1); 195 else 196 gpiod_set_value(n810_speaker_amp, 0); 197 198 return 0; 199 } 200 201 static int n810_jack_event(struct snd_soc_dapm_widget *w, 202 struct snd_kcontrol *k, int event) 203 { 204 if (SND_SOC_DAPM_EVENT_ON(event)) 205 gpiod_set_value(n810_headset_amp, 1); 206 else 207 gpiod_set_value(n810_headset_amp, 0); 208 209 return 0; 210 } 211 212 static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = { 213 SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event), 214 SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event), 215 SND_SOC_DAPM_MIC("DMic", NULL), 216 SND_SOC_DAPM_MIC("HS Mic", NULL), 217 }; 218 219 static const struct snd_soc_dapm_route audio_map[] = { 220 {"Headphone Jack", NULL, "HPLOUT"}, 221 {"Headphone Jack", NULL, "HPROUT"}, 222 223 {"Ext Spk", NULL, "LLOUT"}, 224 {"Ext Spk", NULL, "RLOUT"}, 225 226 {"DMic Rate 64", NULL, "DMic"}, 227 {"DMic", NULL, "Mic Bias"}, 228 229 /* 230 * Note that the mic bias is coming from Retu/Vilma and we don't have 231 * control over it atm. The analog HS mic is not working. <- TODO 232 */ 233 {"LINE1L", NULL, "HS Mic"}, 234 }; 235 236 static const char *spk_function[] = {"Off", "On"}; 237 static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"}; 238 static const char *input_function[] = {"ADC", "Digital Mic"}; 239 static const struct soc_enum n810_enum[] = { 240 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), 241 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function), 242 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function), 243 }; 244 245 static const struct snd_kcontrol_new aic33_n810_controls[] = { 246 SOC_ENUM_EXT("Speaker Function", n810_enum[0], 247 n810_get_spk, n810_set_spk), 248 SOC_ENUM_EXT("Jack Function", n810_enum[1], 249 n810_get_jack, n810_set_jack), 250 SOC_ENUM_EXT("Input Select", n810_enum[2], 251 n810_get_input, n810_set_input), 252 }; 253 254 /* Digital audio interface glue - connects codec <--> CPU */ 255 SND_SOC_DAILINK_DEFS(aic33, 256 DAILINK_COMP_ARRAY(COMP_CPU("48076000.mcbsp")), 257 DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic3x-codec.1-0018", 258 "tlv320aic3x-hifi")), 259 DAILINK_COMP_ARRAY(COMP_PLATFORM("48076000.mcbsp"))); 260 261 static struct snd_soc_dai_link n810_dai = { 262 .name = "TLV320AIC33", 263 .stream_name = "AIC33", 264 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 265 SND_SOC_DAIFMT_CBP_CFP, 266 .ops = &n810_ops, 267 SND_SOC_DAILINK_REG(aic33), 268 }; 269 270 /* Audio machine driver */ 271 static struct snd_soc_card snd_soc_n810 = { 272 .name = "N810", 273 .owner = THIS_MODULE, 274 .dai_link = &n810_dai, 275 .num_links = 1, 276 277 .controls = aic33_n810_controls, 278 .num_controls = ARRAY_SIZE(aic33_n810_controls), 279 .dapm_widgets = aic33_dapm_widgets, 280 .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets), 281 .dapm_routes = audio_map, 282 .num_dapm_routes = ARRAY_SIZE(audio_map), 283 .fully_routed = true, 284 }; 285 286 static struct platform_device *n810_snd_device; 287 288 static int __init n810_soc_init(void) 289 { 290 int err; 291 struct device *dev; 292 293 if (!of_have_populated_dt() || 294 (!of_machine_is_compatible("nokia,n810") && 295 !of_machine_is_compatible("nokia,n810-wimax"))) 296 return -ENODEV; 297 298 n810_snd_device = platform_device_alloc("soc-audio", -1); 299 if (!n810_snd_device) 300 return -ENOMEM; 301 302 platform_set_drvdata(n810_snd_device, &snd_soc_n810); 303 err = platform_device_add(n810_snd_device); 304 if (err) 305 goto err1; 306 307 dev = &n810_snd_device->dev; 308 309 sys_clkout2_src = clk_get(dev, "sys_clkout2_src"); 310 if (IS_ERR(sys_clkout2_src)) { 311 dev_err(dev, "Could not get sys_clkout2_src clock\n"); 312 err = PTR_ERR(sys_clkout2_src); 313 goto err2; 314 } 315 sys_clkout2 = clk_get(dev, "sys_clkout2"); 316 if (IS_ERR(sys_clkout2)) { 317 dev_err(dev, "Could not get sys_clkout2\n"); 318 err = PTR_ERR(sys_clkout2); 319 goto err3; 320 } 321 /* 322 * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use 323 * 96 MHz as its parent in order to get 12 MHz 324 */ 325 func96m_clk = clk_get(dev, "func_96m_ck"); 326 if (IS_ERR(func96m_clk)) { 327 dev_err(dev, "Could not get func 96M clock\n"); 328 err = PTR_ERR(func96m_clk); 329 goto err4; 330 } 331 clk_set_parent(sys_clkout2_src, func96m_clk); 332 clk_set_rate(sys_clkout2, 12000000); 333 334 n810_headset_amp = devm_gpiod_get(&n810_snd_device->dev, 335 "headphone", GPIOD_OUT_LOW); 336 if (IS_ERR(n810_headset_amp)) { 337 err = PTR_ERR(n810_headset_amp); 338 goto err4; 339 } 340 341 n810_speaker_amp = devm_gpiod_get(&n810_snd_device->dev, 342 "speaker", GPIOD_OUT_LOW); 343 if (IS_ERR(n810_speaker_amp)) { 344 err = PTR_ERR(n810_speaker_amp); 345 goto err4; 346 } 347 348 return 0; 349 err4: 350 clk_put(sys_clkout2); 351 err3: 352 clk_put(sys_clkout2_src); 353 err2: 354 platform_device_del(n810_snd_device); 355 err1: 356 platform_device_put(n810_snd_device); 357 358 return err; 359 } 360 361 static void __exit n810_soc_exit(void) 362 { 363 clk_put(sys_clkout2_src); 364 clk_put(sys_clkout2); 365 clk_put(func96m_clk); 366 367 platform_device_unregister(n810_snd_device); 368 } 369 370 module_init(n810_soc_init); 371 module_exit(n810_soc_exit); 372 373 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>"); 374 MODULE_DESCRIPTION("ALSA SoC Nokia N810"); 375 MODULE_LICENSE("GPL"); 376