brintos

brintos / linux-shallow public Read only

0
0
Text · 12.7 KiB · 365b1ac Raw
501 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// Bells audio support4//5// Copyright 2012 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/wm5102.h"14#include "../codecs/wm9081.h"15 16/* BCLK2 is fixed at this currently */17#define BCLK2_RATE (64 * 8000)18 19/*20 * Expect a 24.576MHz crystal if one is fitted (the driver will function21 * if this is not fitted).22 */23#define MCLK_RATE 2457600024 25#define SYS_AUDIO_RATE 4410026#define SYS_MCLK_RATE  (SYS_AUDIO_RATE * 512)27 28#define DAI_AP_DSP    029#define DAI_DSP_CODEC 130#define DAI_CODEC_CP  231#define DAI_CODEC_SUB 332 33struct bells_drvdata {34	int sysclk_rate;35	int asyncclk_rate;36};37 38static struct bells_drvdata wm2200_drvdata = {39	.sysclk_rate = 22579200,40};41 42static struct bells_drvdata wm5102_drvdata = {43	.sysclk_rate = 45158400,44	.asyncclk_rate = 49152000,45};46 47static struct bells_drvdata wm5110_drvdata = {48	.sysclk_rate = 135475200,49	.asyncclk_rate = 147456000,50};51 52static int bells_set_bias_level(struct snd_soc_card *card,53				struct snd_soc_dapm_context *dapm,54				enum snd_soc_bias_level level)55{56	struct snd_soc_pcm_runtime *rtd;57	struct snd_soc_dai *codec_dai;58	struct snd_soc_component *component;59	struct bells_drvdata *bells = card->drvdata;60	int ret;61 62	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);63	codec_dai = snd_soc_rtd_to_codec(rtd, 0);64	component = codec_dai->component;65 66	if (dapm->dev != codec_dai->dev)67		return 0;68 69	switch (level) {70	case SND_SOC_BIAS_PREPARE:71		if (dapm->bias_level != SND_SOC_BIAS_STANDBY)72			break;73 74		ret = snd_soc_component_set_pll(component, WM5102_FLL1,75					    ARIZONA_FLL_SRC_MCLK1,76					    MCLK_RATE,77					    bells->sysclk_rate);78		if (ret < 0)79			pr_err("Failed to start FLL: %d\n", ret);80 81		if (bells->asyncclk_rate) {82			ret = snd_soc_component_set_pll(component, WM5102_FLL2,83						    ARIZONA_FLL_SRC_AIF2BCLK,84						    BCLK2_RATE,85						    bells->asyncclk_rate);86			if (ret < 0)87				pr_err("Failed to start FLL: %d\n", ret);88		}89		break;90 91	default:92		break;93	}94 95	return 0;96}97 98static int bells_set_bias_level_post(struct snd_soc_card *card,99				     struct snd_soc_dapm_context *dapm,100				     enum snd_soc_bias_level level)101{102	struct snd_soc_pcm_runtime *rtd;103	struct snd_soc_dai *codec_dai;104	struct snd_soc_component *component;105	struct bells_drvdata *bells = card->drvdata;106	int ret;107 108	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);109	codec_dai = snd_soc_rtd_to_codec(rtd, 0);110	component = codec_dai->component;111 112	if (dapm->dev != codec_dai->dev)113		return 0;114 115	switch (level) {116	case SND_SOC_BIAS_STANDBY:117		ret = snd_soc_component_set_pll(component, WM5102_FLL1, 0, 0, 0);118		if (ret < 0) {119			pr_err("Failed to stop FLL: %d\n", ret);120			return ret;121		}122 123		if (bells->asyncclk_rate) {124			ret = snd_soc_component_set_pll(component, WM5102_FLL2,125						    0, 0, 0);126			if (ret < 0) {127				pr_err("Failed to stop FLL: %d\n", ret);128				return ret;129			}130		}131		break;132 133	default:134		break;135	}136 137	dapm->bias_level = level;138 139	return 0;140}141 142static int bells_late_probe(struct snd_soc_card *card)143{144	struct bells_drvdata *bells = card->drvdata;145	struct snd_soc_pcm_runtime *rtd;146	struct snd_soc_component *wm0010;147	struct snd_soc_component *component;148	struct snd_soc_dai *aif1_dai;149	struct snd_soc_dai *aif2_dai;150	struct snd_soc_dai *aif3_dai;151	struct snd_soc_dai *wm9081_dai;152	int ret;153 154	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_AP_DSP]);155	wm0010 = snd_soc_rtd_to_codec(rtd, 0)->component;156 157	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);158	component = snd_soc_rtd_to_codec(rtd, 0)->component;159	aif1_dai = snd_soc_rtd_to_codec(rtd, 0);160 161	ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK,162				       ARIZONA_CLK_SRC_FLL1,163				       bells->sysclk_rate,164				       SND_SOC_CLOCK_IN);165	if (ret != 0) {166		dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret);167		return ret;168	}169 170	ret = snd_soc_component_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);171	if (ret != 0) {172		dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret);173		return ret;174	}175 176	ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);177	if (ret != 0)178		dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);179 180	ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_OPCLK, 0,181				       SYS_MCLK_RATE, SND_SOC_CLOCK_OUT);182	if (ret != 0)183		dev_err(component->dev, "Failed to set OPCLK: %d\n", ret);184 185	if (card->num_rtd == DAI_CODEC_CP)186		return 0;187 188	ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_ASYNCCLK,189				       ARIZONA_CLK_SRC_FLL2,190				       bells->asyncclk_rate,191				       SND_SOC_CLOCK_IN);192	if (ret != 0) {193		dev_err(component->dev, "Failed to set ASYNCCLK: %d\n", ret);194		return ret;195	}196 197	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_CP]);198	aif2_dai = snd_soc_rtd_to_cpu(rtd, 0);199 200	ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);201	if (ret != 0) {202		dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);203		return ret;204	}205 206	if (card->num_rtd == DAI_CODEC_SUB)207		return 0;208 209	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_SUB]);210	aif3_dai = snd_soc_rtd_to_cpu(rtd, 0);211	wm9081_dai = snd_soc_rtd_to_codec(rtd, 0);212 213	ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);214	if (ret != 0) {215		dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);216		return ret;217	}218 219	ret = snd_soc_component_set_sysclk(wm9081_dai->component, WM9081_SYSCLK_MCLK,220				       0, SYS_MCLK_RATE, 0);221	if (ret != 0) {222		dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);223		return ret;224	}225 226	return 0;227}228 229static const struct snd_soc_pcm_stream baseband_params = {230	.formats = SNDRV_PCM_FMTBIT_S32_LE,231	.rate_min = 8000,232	.rate_max = 8000,233	.channels_min = 2,234	.channels_max = 2,235};236 237static const struct snd_soc_pcm_stream sub_params = {238	.formats = SNDRV_PCM_FMTBIT_S32_LE,239	.rate_min = SYS_AUDIO_RATE,240	.rate_max = SYS_AUDIO_RATE,241	.channels_min = 2,242	.channels_max = 2,243};244 245SND_SOC_DAILINK_DEFS(wm2200_cpu_dsp,246	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),247	DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),248	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));249 250SND_SOC_DAILINK_DEFS(wm2200_dsp_codec,251	DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),252	DAILINK_COMP_ARRAY(COMP_CODEC("wm2200.1-003a", "wm2200")));253 254static struct snd_soc_dai_link bells_dai_wm2200[] = {255	{256		.name = "CPU-DSP",257		.stream_name = "CPU-DSP",258		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF259				| SND_SOC_DAIFMT_CBM_CFM,260		SND_SOC_DAILINK_REG(wm2200_cpu_dsp),261	},262	{263		.name = "DSP-CODEC",264		.stream_name = "DSP-CODEC",265		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF266				| SND_SOC_DAIFMT_CBM_CFM,267		.c2c_params = &sub_params,268		.num_c2c_params = 1,269		.ignore_suspend = 1,270		SND_SOC_DAILINK_REG(wm2200_dsp_codec),271	},272};273 274SND_SOC_DAILINK_DEFS(wm5102_cpu_dsp,275	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),276	DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),277	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));278 279SND_SOC_DAILINK_DEFS(wm5102_dsp_codec,280	DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),281	DAILINK_COMP_ARRAY(COMP_CODEC("wm5102-codec", "wm5102-aif1")));282 283SND_SOC_DAILINK_DEFS(wm5102_baseband,284	DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif2")),285	DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));286 287SND_SOC_DAILINK_DEFS(wm5102_sub,288	DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif3")),289	DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));290 291static struct snd_soc_dai_link bells_dai_wm5102[] = {292	{293		.name = "CPU-DSP",294		.stream_name = "CPU-DSP",295		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF296				| SND_SOC_DAIFMT_CBM_CFM,297		SND_SOC_DAILINK_REG(wm5102_cpu_dsp),298	},299	{300		.name = "DSP-CODEC",301		.stream_name = "DSP-CODEC",302		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF303				| SND_SOC_DAIFMT_CBM_CFM,304		.c2c_params = &sub_params,305		.num_c2c_params = 1,306		.ignore_suspend = 1,307		SND_SOC_DAILINK_REG(wm5102_dsp_codec),308	},309	{310		.name = "Baseband",311		.stream_name = "Baseband",312		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF313				| SND_SOC_DAIFMT_CBM_CFM,314		.ignore_suspend = 1,315		.c2c_params = &baseband_params,316		.num_c2c_params = 1,317		SND_SOC_DAILINK_REG(wm5102_baseband),318	},319	{320		.name = "Sub",321		.stream_name = "Sub",322		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF323				| SND_SOC_DAIFMT_CBS_CFS,324		.ignore_suspend = 1,325		.c2c_params = &sub_params,326		.num_c2c_params = 1,327		SND_SOC_DAILINK_REG(wm5102_sub),328	},329};330 331SND_SOC_DAILINK_DEFS(wm5110_cpu_dsp,332	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),333	DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),334	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));335 336SND_SOC_DAILINK_DEFS(wm5110_dsp_codec,337	DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),338	DAILINK_COMP_ARRAY(COMP_CODEC("wm5110-codec", "wm5110-aif1")));339 340SND_SOC_DAILINK_DEFS(wm5110_baseband,341	DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif2")),342	DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));343 344 345SND_SOC_DAILINK_DEFS(wm5110_sub,346	DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif3")),347	DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));348 349static struct snd_soc_dai_link bells_dai_wm5110[] = {350	{351		.name = "CPU-DSP",352		.stream_name = "CPU-DSP",353		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF354				| SND_SOC_DAIFMT_CBM_CFM,355		SND_SOC_DAILINK_REG(wm5110_cpu_dsp),356	},357	{358		.name = "DSP-CODEC",359		.stream_name = "DSP-CODEC",360		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF361				| SND_SOC_DAIFMT_CBM_CFM,362		.c2c_params = &sub_params,363		.num_c2c_params = 1,364		.ignore_suspend = 1,365		SND_SOC_DAILINK_REG(wm5110_dsp_codec),366	},367	{368		.name = "Baseband",369		.stream_name = "Baseband",370		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF371				| SND_SOC_DAIFMT_CBM_CFM,372		.ignore_suspend = 1,373		.c2c_params = &baseband_params,374		.num_c2c_params = 1,375		SND_SOC_DAILINK_REG(wm5110_baseband),376	},377	{378		.name = "Sub",379		.stream_name = "Sub",380		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF381				| SND_SOC_DAIFMT_CBS_CFS,382		.ignore_suspend = 1,383		.c2c_params = &sub_params,384		.num_c2c_params = 1,385		SND_SOC_DAILINK_REG(wm5110_sub),386	},387};388 389static struct snd_soc_codec_conf bells_codec_conf[] = {390	{391		.dlc = COMP_CODEC_CONF("wm9081.1-006c"),392		.name_prefix = "Sub",393	},394};395 396static const struct snd_soc_dapm_widget bells_widgets[] = {397	SND_SOC_DAPM_MIC("DMIC", NULL),398};399 400static const struct snd_soc_dapm_route bells_routes[] = {401	{ "Sub CLK_SYS", NULL, "OPCLK" },402	{ "CLKIN", NULL, "OPCLK" },403 404	{ "DMIC", NULL, "MICBIAS2" },405	{ "IN2L", NULL, "DMIC" },406	{ "IN2R", NULL, "DMIC" },407};408 409static struct snd_soc_card bells_cards[] = {410	{411		.name = "Bells WM2200",412		.owner = THIS_MODULE,413		.dai_link = bells_dai_wm2200,414		.num_links = ARRAY_SIZE(bells_dai_wm2200),415		.codec_conf = bells_codec_conf,416		.num_configs = ARRAY_SIZE(bells_codec_conf),417 418		.late_probe = bells_late_probe,419 420		.dapm_widgets = bells_widgets,421		.num_dapm_widgets = ARRAY_SIZE(bells_widgets),422		.dapm_routes = bells_routes,423		.num_dapm_routes = ARRAY_SIZE(bells_routes),424 425		.set_bias_level = bells_set_bias_level,426		.set_bias_level_post = bells_set_bias_level_post,427 428		.drvdata = &wm2200_drvdata,429	},430	{431		.name = "Bells WM5102",432		.owner = THIS_MODULE,433		.dai_link = bells_dai_wm5102,434		.num_links = ARRAY_SIZE(bells_dai_wm5102),435		.codec_conf = bells_codec_conf,436		.num_configs = ARRAY_SIZE(bells_codec_conf),437 438		.late_probe = bells_late_probe,439 440		.dapm_widgets = bells_widgets,441		.num_dapm_widgets = ARRAY_SIZE(bells_widgets),442		.dapm_routes = bells_routes,443		.num_dapm_routes = ARRAY_SIZE(bells_routes),444 445		.set_bias_level = bells_set_bias_level,446		.set_bias_level_post = bells_set_bias_level_post,447 448		.drvdata = &wm5102_drvdata,449	},450	{451		.name = "Bells WM5110",452		.owner = THIS_MODULE,453		.dai_link = bells_dai_wm5110,454		.num_links = ARRAY_SIZE(bells_dai_wm5110),455		.codec_conf = bells_codec_conf,456		.num_configs = ARRAY_SIZE(bells_codec_conf),457 458		.late_probe = bells_late_probe,459 460		.dapm_widgets = bells_widgets,461		.num_dapm_widgets = ARRAY_SIZE(bells_widgets),462		.dapm_routes = bells_routes,463		.num_dapm_routes = ARRAY_SIZE(bells_routes),464 465		.set_bias_level = bells_set_bias_level,466		.set_bias_level_post = bells_set_bias_level_post,467 468		.drvdata = &wm5110_drvdata,469	},470};471 472static int bells_probe(struct platform_device *pdev)473{474	int ret;475 476	bells_cards[pdev->id].dev = &pdev->dev;477 478	ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]);479	if (ret)480		dev_err(&pdev->dev,481			"snd_soc_register_card(%s) failed: %d\n",482			bells_cards[pdev->id].name, ret);483 484	return ret;485}486 487static struct platform_driver bells_driver = {488	.driver = {489		.name = "bells",490		.pm = &snd_soc_pm_ops,491	},492	.probe = bells_probe,493};494 495module_platform_driver(bells_driver);496 497MODULE_DESCRIPTION("Bells audio support");498MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");499MODULE_LICENSE("GPL");500MODULE_ALIAS("platform:bells");501