brintos

brintos / linux-shallow public Read only

0
0
Text · 13.9 KiB · 7878e06 Raw
497 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Machine driver for AMD Vangogh platform using either4 * NAU8821 & CS35L41 or NAU8821 & MAX98388 codecs.5 *6 * Copyright 2021 Advanced Micro Devices, Inc.7 */8 9#include <linux/acpi.h>10#include <linux/dmi.h>11#include <linux/gpio/consumer.h>12#include <linux/i2c.h>13#include <linux/input-event-codes.h>14#include <linux/module.h>15#include <sound/jack.h>16#include <sound/pcm_params.h>17#include <sound/soc.h>18#include <sound/soc-dapm.h>19 20#include "../../codecs/nau8821.h"21#include "acp5x.h"22 23#define DRV_NAME			"acp5x_mach"24#define DUAL_CHANNEL			225#define ACP5X_NAU8821_BCLK		307200026#define ACP5X_NAU8821_FREQ_OUT		1228800027#define ACP5X_NAU8821_COMP_NAME 	"i2c-NVTN2020:00"28#define ACP5X_NAU8821_DAI_NAME		"nau8821-hifi"29#define ACP5X_CS35L41_COMP_LNAME	"spi-VLV1776:00"30#define ACP5X_CS35L41_COMP_RNAME	"spi-VLV1776:01"31#define ACP5X_CS35L41_DAI_NAME		"cs35l41-pcm"32#define ACP5X_MAX98388_COMP_LNAME	"i2c-ADS8388:00"33#define ACP5X_MAX98388_COMP_RNAME	"i2c-ADS8388:01"34#define ACP5X_MAX98388_DAI_NAME		"max98388-aif1"35 36static struct snd_soc_jack vg_headset;37 38SND_SOC_DAILINK_DEF(platform,  DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0")));39SND_SOC_DAILINK_DEF(acp5x_i2s, DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.0")));40SND_SOC_DAILINK_DEF(acp5x_bt,  DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.1")));41SND_SOC_DAILINK_DEF(nau8821,   DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_NAU8821_COMP_NAME,42							     ACP5X_NAU8821_DAI_NAME)));43 44static struct snd_soc_jack_pin acp5x_nau8821_jack_pins[] = {45	{46		.pin	= "Headphone",47		.mask	= SND_JACK_HEADPHONE,48	},49	{50		.pin	= "Headset Mic",51		.mask	= SND_JACK_MICROPHONE,52	},53};54 55static const struct snd_kcontrol_new acp5x_8821_controls[] = {56	SOC_DAPM_PIN_SWITCH("Headphone"),57	SOC_DAPM_PIN_SWITCH("Headset Mic"),58	SOC_DAPM_PIN_SWITCH("Int Mic"),59};60 61static int platform_clock_control(struct snd_soc_dapm_widget *w,62				  struct snd_kcontrol *k, int event)63{64	struct snd_soc_dapm_context *dapm = w->dapm;65	struct snd_soc_card *card = dapm->card;66	struct snd_soc_dai *dai;67	int ret = 0;68 69	dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME);70	if (!dai) {71		dev_err(card->dev, "Codec dai not found\n");72		return -EIO;73	}74 75	if (SND_SOC_DAPM_EVENT_OFF(event)) {76		ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);77		if (ret < 0) {78			dev_err(card->dev, "set sysclk err = %d\n", ret);79			return -EIO;80		}81	} else {82		ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN);83		if (ret < 0)84			dev_err(dai->dev, "can't set BLK clock %d\n", ret);85		ret = snd_soc_dai_set_pll(dai, 0, 0, ACP5X_NAU8821_BCLK, ACP5X_NAU8821_FREQ_OUT);86		if (ret < 0)87			dev_err(dai->dev, "can't set FLL: %d\n", ret);88	}89 90	return ret;91}92 93static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd)94{95	struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;96	int ret;97 98	/*99	 * Headset buttons map to the google Reference headset.100	 * These can be configured by userspace.101	 */102	ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",103					 SND_JACK_HEADSET | SND_JACK_BTN_0,104					 &vg_headset, acp5x_nau8821_jack_pins,105					 ARRAY_SIZE(acp5x_nau8821_jack_pins));106	if (ret) {107		dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);108		return ret;109	}110 111	snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_0, KEY_MEDIA);112	nau8821_enable_jack_detect(component, &vg_headset);113 114	return ret;115}116 117static const unsigned int rates[] = {118	48000,119};120 121static const struct snd_pcm_hw_constraint_list constraints_rates = {122	.count = ARRAY_SIZE(rates),123	.list  = rates,124	.mask = 0,125};126 127static const unsigned int channels[] = {128	2,129};130 131static const struct snd_pcm_hw_constraint_list constraints_channels = {132	.count = ARRAY_SIZE(channels),133	.list = channels,134	.mask = 0,135};136 137static const unsigned int acp5x_nau8821_format[] = {32};138 139static struct snd_pcm_hw_constraint_list constraints_sample_bits = {140	.list = acp5x_nau8821_format,141	.count = ARRAY_SIZE(acp5x_nau8821_format),142};143 144static int acp5x_8821_startup(struct snd_pcm_substream *substream)145{146	struct snd_pcm_runtime *runtime = substream->runtime;147	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);148	struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);149 150	machine->play_i2s_instance = I2S_SP_INSTANCE;151	machine->cap_i2s_instance = I2S_SP_INSTANCE;152 153	runtime->hw.channels_max = DUAL_CHANNEL;154	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,155				   &constraints_channels);156	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,157				   &constraints_rates);158	snd_pcm_hw_constraint_list(substream->runtime, 0,159				   SNDRV_PCM_HW_PARAM_SAMPLE_BITS,160				   &constraints_sample_bits);161 162	return 0;163}164 165static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream,166				   struct snd_pcm_hw_params *params)167{168	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);169	struct snd_soc_card *card = rtd->card;170	struct snd_soc_dai *dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME);171	int ret, bclk;172 173	if (!dai)174		return -EINVAL;175 176	ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN);177	if (ret < 0)178		dev_err(card->dev, "can't set FS clock %d\n", ret);179 180	bclk = snd_soc_params_to_bclk(params);181	if (bclk < 0) {182		dev_err(dai->dev, "Fail to get BCLK rate: %d\n", bclk);183		return bclk;184	}185 186	ret = snd_soc_dai_set_pll(dai, 0, 0, bclk, params_rate(params) * 256);187	if (ret < 0)188		dev_err(card->dev, "can't set FLL: %d\n", ret);189 190	return ret;191}192 193static const struct snd_soc_ops acp5x_8821_ops = {194	.startup = acp5x_8821_startup,195	.hw_params = acp5x_nau8821_hw_params,196};197 198static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream)199{200	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);201	struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);202	struct snd_pcm_runtime *runtime = substream->runtime;203 204	machine->play_i2s_instance = I2S_HS_INSTANCE;205 206	runtime->hw.channels_max = DUAL_CHANNEL;207	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,208				   &constraints_channels);209	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,210				   &constraints_rates);211 212	return 0;213}214 215static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream,216				   struct snd_pcm_hw_params *params)217{218	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);219	unsigned int bclk, rate = params_rate(params);220	struct snd_soc_component *comp;221	int ret, i;222 223	switch (rate) {224	case 48000:225		bclk = 1536000;226		break;227	default:228		bclk = 0;229		break;230	}231 232	for_each_rtd_components(rtd, i, comp) {233		if (!(strcmp(comp->name, ACP5X_CS35L41_COMP_LNAME)) ||234		    !(strcmp(comp->name, ACP5X_CS35L41_COMP_RNAME))) {235			if (!bclk) {236				dev_err(comp->dev, "Invalid sample rate: 0x%x\n", rate);237				return -EINVAL;238			}239 240			ret = snd_soc_component_set_sysclk(comp, 0, 0, bclk, SND_SOC_CLOCK_IN);241			if (ret) {242				dev_err(comp->dev, "failed to set SYSCLK: %d\n", ret);243				return ret;244			}245		}246	}247 248	return 0;249}250 251static const struct snd_soc_ops acp5x_cs35l41_play_ops = {252	.startup = acp5x_cs35l41_startup,253	.hw_params = acp5x_cs35l41_hw_params,254};255 256static struct snd_soc_codec_conf acp5x_cs35l41_conf[] = {257	{258		.dlc = COMP_CODEC_CONF(ACP5X_CS35L41_COMP_LNAME),259		.name_prefix = "Left",260	},261	{262		.dlc = COMP_CODEC_CONF(ACP5X_CS35L41_COMP_RNAME),263		.name_prefix = "Right",264	},265};266 267SND_SOC_DAILINK_DEF(cs35l41, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_CS35L41_COMP_LNAME,268							   ACP5X_CS35L41_DAI_NAME),269						COMP_CODEC(ACP5X_CS35L41_COMP_RNAME,270							   ACP5X_CS35L41_DAI_NAME)));271 272static struct snd_soc_dai_link acp5x_8821_35l41_dai[] = {273	{274		.name = "acp5x-8821-play",275		.stream_name = "Playback/Capture",276		.dai_fmt = SND_SOC_DAIFMT_I2S |277			   SND_SOC_DAIFMT_NB_NF |278			   SND_SOC_DAIFMT_CBC_CFC,279		.dpcm_playback = 1,280		.dpcm_capture = 1,281		.ops = &acp5x_8821_ops,282		.init = acp5x_8821_init,283		SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),284	},285	{286		.name = "acp5x-CS35L41-Stereo",287		.stream_name = "CS35L41 Stereo Playback",288		.dai_fmt = SND_SOC_DAIFMT_I2S |289			   SND_SOC_DAIFMT_NB_NF |290			   SND_SOC_DAIFMT_CBC_CFC,291		.dpcm_playback = 1,292		.playback_only = 1,293		.ops = &acp5x_cs35l41_play_ops,294		SND_SOC_DAILINK_REG(acp5x_bt, cs35l41, platform),295	},296};297 298static const struct snd_soc_dapm_widget acp5x_8821_35l41_widgets[] = {299	SND_SOC_DAPM_HP("Headphone", NULL),300	SND_SOC_DAPM_MIC("Headset Mic", NULL),301	SND_SOC_DAPM_MIC("Int Mic", NULL),302	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,303			    platform_clock_control,304			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),305};306 307static const struct snd_soc_dapm_route acp5x_8821_35l41_audio_route[] = {308	/* HP jack connectors - unknown if we have jack detection */309	{ "Headphone", NULL, "HPOL" },310	{ "Headphone", NULL, "HPOR" },311	{ "MICL", NULL, "Headset Mic" },312	{ "MICR", NULL, "Headset Mic" },313	{ "DMIC", NULL, "Int Mic" },314 315	{ "Headphone", NULL, "Platform Clock" },316	{ "Headset Mic", NULL, "Platform Clock" },317	{ "Int Mic", NULL, "Platform Clock" },318};319 320static struct snd_soc_card acp5x_8821_35l41_card = {321	.name = "acp5x",322	.owner = THIS_MODULE,323	.dai_link = acp5x_8821_35l41_dai,324	.num_links = ARRAY_SIZE(acp5x_8821_35l41_dai),325	.dapm_widgets = acp5x_8821_35l41_widgets,326	.num_dapm_widgets = ARRAY_SIZE(acp5x_8821_35l41_widgets),327	.dapm_routes = acp5x_8821_35l41_audio_route,328	.num_dapm_routes = ARRAY_SIZE(acp5x_8821_35l41_audio_route),329	.codec_conf = acp5x_cs35l41_conf,330	.num_configs = ARRAY_SIZE(acp5x_cs35l41_conf),331	.controls = acp5x_8821_controls,332	.num_controls = ARRAY_SIZE(acp5x_8821_controls),333};334 335static int acp5x_max98388_startup(struct snd_pcm_substream *substream)336{337	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);338	struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);339	struct snd_pcm_runtime *runtime = substream->runtime;340 341	machine->play_i2s_instance = I2S_HS_INSTANCE;342 343	runtime->hw.channels_max = DUAL_CHANNEL;344	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,345				   &constraints_channels);346	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,347				   &constraints_rates);348	return 0;349}350 351static const struct snd_soc_ops acp5x_max98388_play_ops = {352	.startup = acp5x_max98388_startup,353};354 355static struct snd_soc_codec_conf acp5x_max98388_conf[] = {356	{357		.dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_LNAME),358		.name_prefix = "Left",359	},360	{361		.dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_RNAME),362		.name_prefix = "Right",363	},364};365 366SND_SOC_DAILINK_DEF(max98388, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_MAX98388_COMP_LNAME,367							    ACP5X_MAX98388_DAI_NAME),368						 COMP_CODEC(ACP5X_MAX98388_COMP_RNAME,369							    ACP5X_MAX98388_DAI_NAME)));370 371static struct snd_soc_dai_link acp5x_8821_98388_dai[] = {372	{373		.name = "acp5x-8821-play",374		.stream_name = "Playback/Capture",375		.dai_fmt = SND_SOC_DAIFMT_I2S |376			   SND_SOC_DAIFMT_NB_NF |377			   SND_SOC_DAIFMT_CBC_CFC,378		.dpcm_playback = 1,379		.dpcm_capture = 1,380		.ops = &acp5x_8821_ops,381		.init = acp5x_8821_init,382		SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),383	},384	{385		.name = "acp5x-max98388-play",386		.stream_name = "MAX98388 Playback",387		.dai_fmt = SND_SOC_DAIFMT_I2S |388			   SND_SOC_DAIFMT_NB_NF |389			   SND_SOC_DAIFMT_CBC_CFC,390		.dpcm_playback = 1,391		.playback_only = 1,392		.ops = &acp5x_max98388_play_ops,393		SND_SOC_DAILINK_REG(acp5x_bt, max98388, platform),394	},395};396 397static const struct snd_soc_dapm_widget acp5x_8821_98388_widgets[] = {398	SND_SOC_DAPM_HP("Headphone", NULL),399	SND_SOC_DAPM_MIC("Headset Mic", NULL),400	SND_SOC_DAPM_MIC("Int Mic", NULL),401	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,402			    platform_clock_control,403			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),404	SND_SOC_DAPM_SPK("SPK", NULL),405};406 407static const struct snd_soc_dapm_route acp5x_8821_98388_route[] = {408	{ "Headphone", NULL, "HPOL" },409	{ "Headphone", NULL, "HPOR" },410	{ "MICL", NULL, "Headset Mic" },411	{ "MICR", NULL, "Headset Mic" },412	{ "DMIC", NULL, "Int Mic" },413 414	{ "Headphone", NULL, "Platform Clock" },415	{ "Headset Mic", NULL, "Platform Clock" },416	{ "Int Mic", NULL, "Platform Clock" },417 418	{ "SPK", NULL, "Left BE_OUT" },419	{ "SPK", NULL, "Right BE_OUT" },420};421 422static struct snd_soc_card acp5x_8821_98388_card = {423	.name = "acp5x-max98388",424	.owner = THIS_MODULE,425	.dai_link = acp5x_8821_98388_dai,426	.num_links = ARRAY_SIZE(acp5x_8821_98388_dai),427	.dapm_widgets = acp5x_8821_98388_widgets,428	.num_dapm_widgets = ARRAY_SIZE(acp5x_8821_98388_widgets),429	.dapm_routes = acp5x_8821_98388_route,430	.num_dapm_routes = ARRAY_SIZE(acp5x_8821_98388_route),431	.codec_conf = acp5x_max98388_conf,432	.num_configs = ARRAY_SIZE(acp5x_max98388_conf),433	.controls = acp5x_8821_controls,434	.num_controls = ARRAY_SIZE(acp5x_8821_controls),435};436 437static const struct dmi_system_id acp5x_vg_quirk_table[] = {438	{439		.matches = {440			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),441			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),442		},443		.driver_data = (void *)&acp5x_8821_35l41_card,444	},445	{446		.matches = {447			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),448			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galileo"),449		},450		.driver_data = (void *)&acp5x_8821_98388_card,451	},452	{}453};454 455static int acp5x_probe(struct platform_device *pdev)456{457	const struct dmi_system_id *dmi_id;458	struct acp5x_platform_info *machine;459	struct device *dev = &pdev->dev;460	struct snd_soc_card *card;461	int ret;462 463	dmi_id = dmi_first_match(acp5x_vg_quirk_table);464	if (!dmi_id || !dmi_id->driver_data)465		return -ENODEV;466 467	machine = devm_kzalloc(dev, sizeof(*machine), GFP_KERNEL);468	if (!machine)469		return -ENOMEM;470 471	card = dmi_id->driver_data;472	card->dev = dev;473	platform_set_drvdata(pdev, card);474	snd_soc_card_set_drvdata(card, machine);475 476	ret = devm_snd_soc_register_card(dev, card);477	if (ret)478		return dev_err_probe(dev, ret, "Register card (%s) failed\n", card->name);479 480	return 0;481}482 483static struct platform_driver acp5x_mach_driver = {484	.driver = {485		.name = DRV_NAME,486		.pm = &snd_soc_pm_ops,487	},488	.probe = acp5x_probe,489};490 491module_platform_driver(acp5x_mach_driver);492 493MODULE_AUTHOR("Vijendar.Mukunda@amd.com");494MODULE_DESCRIPTION("NAU8821/CS35L41 & NAU8821/MAX98388 audio support");495MODULE_LICENSE("GPL v2");496MODULE_ALIAS("platform:" DRV_NAME);497