cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

acp5x-mach.c (10992B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Machine driver for AMD Vangogh platform using NAU8821 & CS35L41
      4 * codecs.
      5 *
      6 * Copyright 2021 Advanced Micro Devices, Inc.
      7 */
      8
      9#include <sound/soc.h>
     10#include <sound/soc-dapm.h>
     11#include <linux/module.h>
     12#include <linux/io.h>
     13#include <sound/pcm.h>
     14#include <sound/pcm_params.h>
     15
     16#include <sound/jack.h>
     17#include <linux/clk.h>
     18#include <linux/gpio.h>
     19#include <linux/gpio/consumer.h>
     20#include <linux/i2c.h>
     21#include <linux/input.h>
     22#include <linux/acpi.h>
     23#include <linux/dmi.h>
     24
     25#include "../../codecs/nau8821.h"
     26#include "../../codecs/cs35l41.h"
     27
     28#include "acp5x.h"
     29
     30#define DRV_NAME "acp5x_mach"
     31#define DUAL_CHANNEL		2
     32#define ACP5X_NUVOTON_CODEC_DAI	"nau8821-hifi"
     33#define VG_JUPITER 1
     34#define ACP5X_NUVOTON_BCLK 3072000
     35#define ACP5X_NAU8821_FREQ_OUT 12288000
     36
     37static unsigned long acp5x_machine_id;
     38static struct snd_soc_jack vg_headset;
     39
     40static struct snd_soc_jack_pin acp5x_nau8821_jack_pins[] = {
     41	{
     42		.pin	= "Headphone",
     43		.mask	= SND_JACK_HEADPHONE,
     44	},
     45	{
     46		.pin	= "Headset Mic",
     47		.mask	= SND_JACK_MICROPHONE,
     48	},
     49};
     50
     51static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd)
     52{
     53	int ret;
     54	struct snd_soc_card *card = rtd->card;
     55	struct snd_soc_component *component =
     56					asoc_rtd_to_codec(rtd, 0)->component;
     57
     58	/*
     59	 * Headset buttons map to the google Reference headset.
     60	 * These can be configured by userspace.
     61	 */
     62	ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
     63					 SND_JACK_HEADSET | SND_JACK_BTN_0,
     64					 &vg_headset, acp5x_nau8821_jack_pins,
     65					 ARRAY_SIZE(acp5x_nau8821_jack_pins));
     66	if (ret) {
     67		dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
     68		return ret;
     69	}
     70
     71	snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_0, KEY_MEDIA);
     72	nau8821_enable_jack_detect(component, &vg_headset);
     73	return ret;
     74}
     75
     76static int acp5x_cs35l41_init(struct snd_soc_pcm_runtime *rtd)
     77{
     78	return 0;
     79}
     80
     81static const unsigned int rates[] = {
     82	48000,
     83};
     84
     85static const struct snd_pcm_hw_constraint_list constraints_rates = {
     86	.count = ARRAY_SIZE(rates),
     87	.list  = rates,
     88	.mask = 0,
     89};
     90
     91static const unsigned int channels[] = {
     92	2,
     93};
     94
     95static const struct snd_pcm_hw_constraint_list constraints_channels = {
     96	.count = ARRAY_SIZE(channels),
     97	.list = channels,
     98	.mask = 0,
     99};
    100
    101static const unsigned int acp5x_nau8821_format[] = {32};
    102
    103static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
    104	.list = acp5x_nau8821_format,
    105	.count = ARRAY_SIZE(acp5x_nau8821_format),
    106};
    107
    108static int acp5x_8821_startup(struct snd_pcm_substream *substream)
    109{
    110	struct snd_pcm_runtime *runtime = substream->runtime;
    111	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    112	struct snd_soc_card *card = rtd->card;
    113	struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(card);
    114
    115	machine->play_i2s_instance = I2S_SP_INSTANCE;
    116	machine->cap_i2s_instance = I2S_SP_INSTANCE;
    117
    118	runtime->hw.channels_max = DUAL_CHANNEL;
    119	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
    120				   &constraints_channels);
    121	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
    122				   &constraints_rates);
    123	snd_pcm_hw_constraint_list(substream->runtime, 0,
    124				   SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
    125				   &constraints_sample_bits);
    126	return 0;
    127}
    128
    129static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream,
    130				   struct snd_pcm_hw_params *params)
    131{
    132	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    133	struct snd_soc_card *card = rtd->card;
    134	struct snd_soc_dai *codec_dai =
    135			snd_soc_card_get_codec_dai(card,
    136						   ACP5X_NUVOTON_CODEC_DAI);
    137	int ret;
    138
    139	ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
    140				     SND_SOC_CLOCK_IN);
    141	if (ret < 0)
    142		dev_err(card->dev, "can't set FS clock %d\n", ret);
    143	ret = snd_soc_dai_set_pll(codec_dai, 0, 0, snd_soc_params_to_bclk(params),
    144				  params_rate(params) * 256);
    145	if (ret < 0)
    146		dev_err(card->dev, "can't set FLL: %d\n", ret);
    147
    148	return ret;
    149}
    150
    151static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream)
    152{
    153	struct snd_pcm_runtime *runtime = substream->runtime;
    154	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    155	struct snd_soc_card *card = rtd->card;
    156	struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(card);
    157
    158	machine->play_i2s_instance = I2S_HS_INSTANCE;
    159
    160	runtime->hw.channels_max = DUAL_CHANNEL;
    161	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
    162				   &constraints_channels);
    163	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
    164				   &constraints_rates);
    165	return 0;
    166}
    167
    168static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream,
    169				   struct snd_pcm_hw_params *params)
    170{
    171	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    172	struct snd_soc_card *card = rtd->card;
    173	struct snd_soc_dai *codec_dai;
    174	int ret, i;
    175	unsigned int num_codecs = rtd->num_codecs;
    176	unsigned int bclk_val;
    177
    178	ret = 0;
    179	for (i = 0; i < num_codecs; i++) {
    180		codec_dai = asoc_rtd_to_codec(rtd, i);
    181		if ((strcmp(codec_dai->name, "spi-VLV1776:00") == 0) ||
    182		    (strcmp(codec_dai->name, "spi-VLV1776:01") == 0)) {
    183			switch (params_rate(params)) {
    184			case 48000:
    185				bclk_val = 1536000;
    186				break;
    187			default:
    188				dev_err(card->dev, "Invalid Samplerate:0x%x\n",
    189					params_rate(params));
    190				return -EINVAL;
    191			}
    192			ret = snd_soc_component_set_sysclk(codec_dai->component,
    193							   0, 0, bclk_val, SND_SOC_CLOCK_IN);
    194			if (ret < 0) {
    195				dev_err(card->dev, "failed to set sysclk for CS35l41 dai\n");
    196				return ret;
    197			}
    198		}
    199	}
    200
    201	return ret;
    202}
    203
    204static const struct snd_soc_ops acp5x_8821_ops = {
    205	.startup = acp5x_8821_startup,
    206	.hw_params = acp5x_nau8821_hw_params,
    207};
    208
    209static const struct snd_soc_ops acp5x_cs35l41_play_ops = {
    210	.startup = acp5x_cs35l41_startup,
    211	.hw_params = acp5x_cs35l41_hw_params,
    212};
    213
    214static struct snd_soc_codec_conf cs35l41_conf[] = {
    215	{
    216		.dlc = COMP_CODEC_CONF("spi-VLV1776:00"),
    217		.name_prefix = "Left",
    218	},
    219	{
    220		.dlc = COMP_CODEC_CONF("spi-VLV1776:01"),
    221		.name_prefix = "Right",
    222	},
    223};
    224
    225SND_SOC_DAILINK_DEF(acp5x_i2s,
    226		    DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.0")));
    227
    228SND_SOC_DAILINK_DEF(acp5x_bt,
    229		    DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.1")));
    230
    231SND_SOC_DAILINK_DEF(nau8821,
    232		    DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00",
    233						  "nau8821-hifi")));
    234
    235SND_SOC_DAILINK_DEF(cs35l41,
    236		    DAILINK_COMP_ARRAY(COMP_CODEC("spi-VLV1776:00", "cs35l41-pcm"),
    237				       COMP_CODEC("spi-VLV1776:01", "cs35l41-pcm")));
    238
    239SND_SOC_DAILINK_DEF(platform,
    240		    DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0")));
    241
    242static struct snd_soc_dai_link acp5x_dai[] = {
    243	{
    244		.name = "acp5x-8821-play",
    245		.stream_name = "Playback/Capture",
    246		.dai_fmt = SND_SOC_DAIFMT_I2S  | SND_SOC_DAIFMT_NB_NF |
    247			   SND_SOC_DAIFMT_CBC_CFC,
    248		.dpcm_playback = 1,
    249		.dpcm_capture = 1,
    250		.ops = &acp5x_8821_ops,
    251		.init = acp5x_8821_init,
    252		SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),
    253	},
    254	{
    255		.name = "acp5x-CS35L41-Stereo",
    256		.stream_name = "CS35L41 Stereo Playback",
    257		.dai_fmt = SND_SOC_DAIFMT_I2S  | SND_SOC_DAIFMT_NB_NF |
    258			   SND_SOC_DAIFMT_CBC_CFC,
    259		.dpcm_playback = 1,
    260		.playback_only = 1,
    261		.ops = &acp5x_cs35l41_play_ops,
    262		.init = acp5x_cs35l41_init,
    263		SND_SOC_DAILINK_REG(acp5x_bt, cs35l41, platform),
    264	},
    265};
    266
    267static int platform_clock_control(struct snd_soc_dapm_widget *w,
    268				  struct snd_kcontrol *k, int  event)
    269{
    270	struct snd_soc_dapm_context *dapm = w->dapm;
    271	struct snd_soc_card *card = dapm->card;
    272	struct snd_soc_dai *codec_dai;
    273	int ret = 0;
    274
    275	codec_dai = snd_soc_card_get_codec_dai(card, ACP5X_NUVOTON_CODEC_DAI);
    276	if (!codec_dai) {
    277		dev_err(card->dev, "Codec dai not found\n");
    278		return -EIO;
    279	}
    280
    281	if (SND_SOC_DAPM_EVENT_OFF(event)) {
    282		ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_INTERNAL,
    283					     0, SND_SOC_CLOCK_IN);
    284		if (ret < 0) {
    285			dev_err(card->dev, "set sysclk err = %d\n", ret);
    286			return -EIO;
    287		}
    288	} else {
    289		ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
    290					     SND_SOC_CLOCK_IN);
    291		if (ret < 0)
    292			dev_err(codec_dai->dev, "can't set BLK clock %d\n", ret);
    293		ret = snd_soc_dai_set_pll(codec_dai, 0, 0, ACP5X_NUVOTON_BCLK,
    294					  ACP5X_NAU8821_FREQ_OUT);
    295		if (ret < 0)
    296			dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
    297	}
    298	return ret;
    299}
    300
    301static const struct snd_kcontrol_new acp5x_8821_controls[] = {
    302	SOC_DAPM_PIN_SWITCH("Headphone"),
    303	SOC_DAPM_PIN_SWITCH("Headset Mic"),
    304	SOC_DAPM_PIN_SWITCH("Int Mic"),
    305};
    306
    307static const struct snd_soc_dapm_widget acp5x_8821_widgets[] = {
    308	SND_SOC_DAPM_HP("Headphone", NULL),
    309	SND_SOC_DAPM_MIC("Headset Mic", NULL),
    310	SND_SOC_DAPM_MIC("Int Mic", NULL),
    311	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
    312			    platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
    313};
    314
    315static const struct snd_soc_dapm_route acp5x_8821_audio_route[] = {
    316	/* HP jack connectors - unknown if we have jack detection */
    317	{ "Headphone", NULL, "HPOL" },
    318	{ "Headphone", NULL, "HPOR" },
    319	{ "MICL", NULL, "Headset Mic" },
    320	{ "MICR", NULL, "Headset Mic" },
    321	{ "DMIC", NULL, "Int Mic" },
    322
    323	{ "Headphone", NULL, "Platform Clock" },
    324	{ "Headset Mic", NULL, "Platform Clock" },
    325	{ "Int Mic", NULL, "Platform Clock" },
    326};
    327
    328static struct snd_soc_card acp5x_card = {
    329	.name = "acp5x",
    330	.owner = THIS_MODULE,
    331	.dai_link = acp5x_dai,
    332	.num_links = ARRAY_SIZE(acp5x_dai),
    333	.dapm_widgets = acp5x_8821_widgets,
    334	.num_dapm_widgets = ARRAY_SIZE(acp5x_8821_widgets),
    335	.dapm_routes = acp5x_8821_audio_route,
    336	.num_dapm_routes = ARRAY_SIZE(acp5x_8821_audio_route),
    337	.codec_conf = cs35l41_conf,
    338	.num_configs = ARRAY_SIZE(cs35l41_conf),
    339	.controls = acp5x_8821_controls,
    340	.num_controls = ARRAY_SIZE(acp5x_8821_controls),
    341};
    342
    343static int acp5x_vg_quirk_cb(const struct dmi_system_id *id)
    344{
    345	acp5x_machine_id = VG_JUPITER;
    346	return 1;
    347}
    348
    349static const struct dmi_system_id acp5x_vg_quirk_table[] = {
    350	{
    351		.callback = acp5x_vg_quirk_cb,
    352		.matches = {
    353			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
    354			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
    355		}
    356	},
    357	{}
    358};
    359
    360static int acp5x_probe(struct platform_device *pdev)
    361{
    362	int ret;
    363	struct acp5x_platform_info *machine;
    364	struct snd_soc_card *card;
    365
    366	machine = devm_kzalloc(&pdev->dev, sizeof(struct acp5x_platform_info),
    367			       GFP_KERNEL);
    368	if (!machine)
    369		return -ENOMEM;
    370
    371	dmi_check_system(acp5x_vg_quirk_table);
    372	switch (acp5x_machine_id) {
    373	case VG_JUPITER:
    374		card = &acp5x_card;
    375		acp5x_card.dev = &pdev->dev;
    376		break;
    377	default:
    378		return -ENODEV;
    379	}
    380	platform_set_drvdata(pdev, card);
    381	snd_soc_card_set_drvdata(card, machine);
    382
    383	ret = devm_snd_soc_register_card(&pdev->dev, card);
    384	if (ret) {
    385		return dev_err_probe(&pdev->dev, ret,
    386				     "snd_soc_register_card(%s) failed\n",
    387				     acp5x_card.name);
    388	}
    389	return 0;
    390}
    391
    392static struct platform_driver acp5x_mach_driver = {
    393	.driver = {
    394		.name = "acp5x_mach",
    395		.pm = &snd_soc_pm_ops,
    396	},
    397	.probe = acp5x_probe,
    398};
    399
    400module_platform_driver(acp5x_mach_driver);
    401
    402MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
    403MODULE_DESCRIPTION("NAU8821 & CS35L41 audio support");
    404MODULE_LICENSE("GPL v2");
    405MODULE_ALIAS("platform:" DRV_NAME);