brintos

brintos / linux-shallow public Read only

0
0
Text · 8.6 KiB · c5260e1 Raw
364 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// Littlemill audio support4//5// Copyright 2011 Wolfson Microelectronics6 7#include <sound/soc.h>8#include <sound/soc-dapm.h>9#include <sound/jack.h>10#include <linux/gpio.h>11#include <linux/module.h>12 13#include "../codecs/wm8994.h"14 15static int sample_rate = 44100;16 17static int littlemill_set_bias_level(struct snd_soc_card *card,18					  struct snd_soc_dapm_context *dapm,19					  enum snd_soc_bias_level level)20{21	struct snd_soc_pcm_runtime *rtd;22	struct snd_soc_dai *aif1_dai;23	int ret;24 25	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);26	aif1_dai = snd_soc_rtd_to_codec(rtd, 0);27 28	if (dapm->dev != aif1_dai->dev)29		return 0;30 31	switch (level) {32	case SND_SOC_BIAS_PREPARE:33		/*34		 * If we've not already clocked things via hw_params()35		 * then do so now, otherwise these are noops.36		 */37		if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {38			ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,39						  WM8994_FLL_SRC_MCLK2, 32768,40						  sample_rate * 512);41			if (ret < 0) {42				pr_err("Failed to start FLL: %d\n", ret);43				return ret;44			}45 46			ret = snd_soc_dai_set_sysclk(aif1_dai,47						     WM8994_SYSCLK_FLL1,48						     sample_rate * 512,49						     SND_SOC_CLOCK_IN);50			if (ret < 0) {51				pr_err("Failed to set SYSCLK: %d\n", ret);52				return ret;53			}54		}55		break;56 57	default:58		break;59	}60 61	return 0;62}63 64static int littlemill_set_bias_level_post(struct snd_soc_card *card,65					       struct snd_soc_dapm_context *dapm,66					       enum snd_soc_bias_level level)67{68	struct snd_soc_pcm_runtime *rtd;69	struct snd_soc_dai *aif1_dai;70	int ret;71 72	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);73	aif1_dai = snd_soc_rtd_to_codec(rtd, 0);74 75	if (dapm->dev != aif1_dai->dev)76		return 0;77 78	switch (level) {79	case SND_SOC_BIAS_STANDBY:80		ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,81					     32768, SND_SOC_CLOCK_IN);82		if (ret < 0) {83			pr_err("Failed to switch away from FLL1: %d\n", ret);84			return ret;85		}86 87		ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,88					  0, 0, 0);89		if (ret < 0) {90			pr_err("Failed to stop FLL1: %d\n", ret);91			return ret;92		}93		break;94 95	default:96		break;97	}98 99	dapm->bias_level = level;100 101	return 0;102}103 104static int littlemill_hw_params(struct snd_pcm_substream *substream,105				struct snd_pcm_hw_params *params)106{107	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);108	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);109	int ret;110 111	sample_rate = params_rate(params);112 113	ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1,114				  WM8994_FLL_SRC_MCLK2, 32768,115				  sample_rate * 512);116	if (ret < 0) {117		pr_err("Failed to start FLL: %d\n", ret);118		return ret;119	}120 121	ret = snd_soc_dai_set_sysclk(codec_dai,122				     WM8994_SYSCLK_FLL1,123				     sample_rate * 512,124				     SND_SOC_CLOCK_IN);125	if (ret < 0) {126		pr_err("Failed to set SYSCLK: %d\n", ret);127		return ret;128	}129 130	return 0;131}132 133static const struct snd_soc_ops littlemill_ops = {134	.hw_params = littlemill_hw_params,135};136 137static const struct snd_soc_pcm_stream baseband_params = {138	.formats = SNDRV_PCM_FMTBIT_S32_LE,139	.rate_min = 8000,140	.rate_max = 8000,141	.channels_min = 2,142	.channels_max = 2,143};144 145SND_SOC_DAILINK_DEFS(cpu,146	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),147	DAILINK_COMP_ARRAY(COMP_CODEC("wm8994-codec", "wm8994-aif1")),148	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));149 150SND_SOC_DAILINK_DEFS(baseband,151	DAILINK_COMP_ARRAY(COMP_CPU("wm8994-aif2")),152	DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027",153				      "wm1250-ev1")));154 155static struct snd_soc_dai_link littlemill_dai[] = {156	{157		.name = "CPU",158		.stream_name = "CPU",159		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF160				| SND_SOC_DAIFMT_CBM_CFM,161		.ops = &littlemill_ops,162		SND_SOC_DAILINK_REG(cpu),163	},164	{165		.name = "Baseband",166		.stream_name = "Baseband",167		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF168				| SND_SOC_DAIFMT_CBM_CFM,169		.ignore_suspend = 1,170		.c2c_params = &baseband_params,171		.num_c2c_params = 1,172		SND_SOC_DAILINK_REG(baseband),173	},174};175 176static int bbclk_ev(struct snd_soc_dapm_widget *w,177		    struct snd_kcontrol *kcontrol, int event)178{179	struct snd_soc_card *card = w->dapm->card;180	struct snd_soc_pcm_runtime *rtd;181	struct snd_soc_dai *aif2_dai;182	int ret;183 184	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);185	aif2_dai = snd_soc_rtd_to_cpu(rtd, 0);186 187	switch (event) {188	case SND_SOC_DAPM_PRE_PMU:189		ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,190					  WM8994_FLL_SRC_BCLK, 64 * 8000,191					  8000 * 256);192		if (ret < 0) {193			pr_err("Failed to start FLL: %d\n", ret);194			return ret;195		}196 197		ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_FLL2,198					     8000 * 256,199					     SND_SOC_CLOCK_IN);200		if (ret < 0) {201			pr_err("Failed to set SYSCLK: %d\n", ret);202			return ret;203		}204		break;205	case SND_SOC_DAPM_POST_PMD:206		ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2,207					     32768, SND_SOC_CLOCK_IN);208		if (ret < 0) {209			pr_err("Failed to switch away from FLL2: %d\n", ret);210			return ret;211		}212 213		ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,214					  0, 0, 0);215		if (ret < 0) {216			pr_err("Failed to stop FLL2: %d\n", ret);217			return ret;218		}219		break;220	default:221		return -EINVAL;222	}223 224	return 0;225}226 227static const struct snd_kcontrol_new controls[] = {228	SOC_DAPM_PIN_SWITCH("Headphone"),229	SOC_DAPM_PIN_SWITCH("Headset Mic"),230	SOC_DAPM_PIN_SWITCH("WM1250 Input"),231	SOC_DAPM_PIN_SWITCH("WM1250 Output"),232};233 234static const struct snd_soc_dapm_widget widgets[] = {235	SND_SOC_DAPM_HP("Headphone", NULL),236	SND_SOC_DAPM_HP("Headset Mic", NULL),237 238	SND_SOC_DAPM_MIC("AMIC", NULL),239	SND_SOC_DAPM_MIC("DMIC", NULL),240 241	SND_SOC_DAPM_SUPPLY_S("Baseband Clock", -1, SND_SOC_NOPM, 0, 0,242			      bbclk_ev,243			      SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),244};245 246static const struct snd_soc_dapm_route audio_paths[] = {247	{ "Headphone", NULL, "HPOUT1L" },248	{ "Headphone", NULL, "HPOUT1R" },249 250	{ "AMIC", NULL, "MICBIAS1" },   /* Default for AMICBIAS jumper */251	{ "IN1LN", NULL, "AMIC" },252 253	{ "DMIC", NULL, "MICBIAS2" },   /* Default for DMICBIAS jumper */254	{ "DMIC1DAT", NULL, "DMIC" },255	{ "DMIC2DAT", NULL, "DMIC" },256 257	{ "AIF2CLK", NULL, "Baseband Clock" },258};259 260static struct snd_soc_jack littlemill_headset;261static struct snd_soc_jack_pin littlemill_headset_pins[] = {262	{263		.pin = "Headphone",264		.mask = SND_JACK_HEADPHONE,265	},266	{267		.pin = "Headset Mic",268		.mask = SND_JACK_MICROPHONE,269	},270};271 272static int littlemill_late_probe(struct snd_soc_card *card)273{274	struct snd_soc_pcm_runtime *rtd;275	struct snd_soc_component *component;276	struct snd_soc_dai *aif1_dai;277	struct snd_soc_dai *aif2_dai;278	int ret;279 280	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);281	component = snd_soc_rtd_to_codec(rtd, 0)->component;282	aif1_dai = snd_soc_rtd_to_codec(rtd, 0);283 284	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);285	aif2_dai = snd_soc_rtd_to_cpu(rtd, 0);286 287	ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,288				     32768, SND_SOC_CLOCK_IN);289	if (ret < 0)290		return ret;291 292	ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2,293				     32768, SND_SOC_CLOCK_IN);294	if (ret < 0)295		return ret;296 297	ret = snd_soc_card_jack_new_pins(card, "Headset",298					 SND_JACK_HEADSET | SND_JACK_MECHANICAL |299					 SND_JACK_BTN_0 | SND_JACK_BTN_1 |300					 SND_JACK_BTN_2 | SND_JACK_BTN_3 |301					 SND_JACK_BTN_4 | SND_JACK_BTN_5,302					 &littlemill_headset,303					 littlemill_headset_pins,304					 ARRAY_SIZE(littlemill_headset_pins));305	if (ret)306		return ret;307 308	/* This will check device compatibility itself */309	wm8958_mic_detect(component, &littlemill_headset, NULL, NULL, NULL, NULL);310 311	/* As will this */312	wm8994_mic_detect(component, &littlemill_headset, 1);313 314	return 0;315}316 317static struct snd_soc_card littlemill = {318	.name = "Littlemill",319	.owner = THIS_MODULE,320	.dai_link = littlemill_dai,321	.num_links = ARRAY_SIZE(littlemill_dai),322 323	.set_bias_level = littlemill_set_bias_level,324	.set_bias_level_post = littlemill_set_bias_level_post,325 326	.controls = controls,327	.num_controls = ARRAY_SIZE(controls),328	.dapm_widgets = widgets,329	.num_dapm_widgets = ARRAY_SIZE(widgets),330	.dapm_routes = audio_paths,331	.num_dapm_routes = ARRAY_SIZE(audio_paths),332 333	.late_probe = littlemill_late_probe,334};335 336static int littlemill_probe(struct platform_device *pdev)337{338	struct snd_soc_card *card = &littlemill;339	int ret;340 341	card->dev = &pdev->dev;342 343	ret = devm_snd_soc_register_card(&pdev->dev, card);344	if (ret)345		dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");346 347	return ret;348}349 350static struct platform_driver littlemill_driver = {351	.driver = {352		.name = "littlemill",353		.pm = &snd_soc_pm_ops,354	},355	.probe = littlemill_probe,356};357 358module_platform_driver(littlemill_driver);359 360MODULE_DESCRIPTION("Littlemill audio support");361MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");362MODULE_LICENSE("GPL");363MODULE_ALIAS("platform:littlemill");364