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

tobermory.c (5807B)


      1// SPDX-License-Identifier: GPL-2.0+
      2//
      3// Tobermory audio support
      4//
      5// Copyright 2011 Wolfson Microelectronics
      6
      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/wm8962.h"
     14
     15static int sample_rate = 44100;
     16
     17static int tobermory_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 *codec_dai;
     23	int ret;
     24
     25	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
     26	codec_dai = asoc_rtd_to_codec(rtd, 0);
     27
     28	if (dapm->dev != codec_dai->dev)
     29		return 0;
     30
     31	switch (level) {
     32	case SND_SOC_BIAS_PREPARE:
     33		if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
     34			ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
     35						  WM8962_FLL_MCLK, 32768,
     36						  sample_rate * 512);
     37			if (ret < 0)
     38				pr_err("Failed to start FLL: %d\n", ret);
     39
     40			ret = snd_soc_dai_set_sysclk(codec_dai,
     41						     WM8962_SYSCLK_FLL,
     42						     sample_rate * 512,
     43						     SND_SOC_CLOCK_IN);
     44			if (ret < 0) {
     45				pr_err("Failed to set SYSCLK: %d\n", ret);
     46				snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
     47						    0, 0, 0);
     48				return ret;
     49			}
     50		}
     51		break;
     52
     53	default:
     54		break;
     55	}
     56
     57	return 0;
     58}
     59
     60static int tobermory_set_bias_level_post(struct snd_soc_card *card,
     61					       struct snd_soc_dapm_context *dapm,
     62					       enum snd_soc_bias_level level)
     63{
     64	struct snd_soc_pcm_runtime *rtd;
     65	struct snd_soc_dai *codec_dai;
     66	int ret;
     67
     68	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
     69	codec_dai = asoc_rtd_to_codec(rtd, 0);
     70
     71	if (dapm->dev != codec_dai->dev)
     72		return 0;
     73
     74	switch (level) {
     75	case SND_SOC_BIAS_STANDBY:
     76		ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
     77					     32768, SND_SOC_CLOCK_IN);
     78		if (ret < 0) {
     79			pr_err("Failed to switch away from FLL: %d\n", ret);
     80			return ret;
     81		}
     82
     83		ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
     84					  0, 0, 0);
     85		if (ret < 0) {
     86			pr_err("Failed to stop FLL: %d\n", ret);
     87			return ret;
     88		}
     89		break;
     90
     91	default:
     92		break;
     93	}
     94
     95	dapm->bias_level = level;
     96
     97	return 0;
     98}
     99
    100static int tobermory_hw_params(struct snd_pcm_substream *substream,
    101			      struct snd_pcm_hw_params *params)
    102{
    103	sample_rate = params_rate(params);
    104
    105	return 0;
    106}
    107
    108static const struct snd_soc_ops tobermory_ops = {
    109	.hw_params = tobermory_hw_params,
    110};
    111
    112SND_SOC_DAILINK_DEFS(cpu,
    113	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
    114	DAILINK_COMP_ARRAY(COMP_CODEC("wm8962.1-001a", "wm8962")),
    115	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
    116
    117static struct snd_soc_dai_link tobermory_dai[] = {
    118	{
    119		.name = "CPU",
    120		.stream_name = "CPU",
    121		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
    122				| SND_SOC_DAIFMT_CBM_CFM,
    123		.ops = &tobermory_ops,
    124		SND_SOC_DAILINK_REG(cpu),
    125	},
    126};
    127
    128static const struct snd_kcontrol_new controls[] = {
    129	SOC_DAPM_PIN_SWITCH("Main Speaker"),
    130	SOC_DAPM_PIN_SWITCH("DMIC"),
    131};
    132
    133static const struct snd_soc_dapm_widget widgets[] = {
    134	SND_SOC_DAPM_HP("Headphone", NULL),
    135	SND_SOC_DAPM_MIC("Headset Mic", NULL),
    136
    137	SND_SOC_DAPM_MIC("DMIC", NULL),
    138	SND_SOC_DAPM_MIC("AMIC", NULL),
    139
    140	SND_SOC_DAPM_SPK("Main Speaker", NULL),
    141};
    142
    143static const struct snd_soc_dapm_route audio_paths[] = {
    144	{ "Headphone", NULL, "HPOUTL" },
    145	{ "Headphone", NULL, "HPOUTR" },
    146
    147	{ "Main Speaker", NULL, "SPKOUTL" },
    148	{ "Main Speaker", NULL, "SPKOUTR" },
    149
    150	{ "Headset Mic", NULL, "MICBIAS" },
    151	{ "IN4L", NULL, "Headset Mic" },
    152	{ "IN4R", NULL, "Headset Mic" },
    153
    154	{ "AMIC", NULL, "MICBIAS" },
    155	{ "IN1L", NULL, "AMIC" },
    156	{ "IN1R", NULL, "AMIC" },
    157
    158	{ "DMIC", NULL, "MICBIAS" },
    159	{ "DMICDAT", NULL, "DMIC" },
    160};
    161
    162static struct snd_soc_jack tobermory_headset;
    163
    164/* Headset jack detection DAPM pins */
    165static struct snd_soc_jack_pin tobermory_headset_pins[] = {
    166	{
    167		.pin = "Headset Mic",
    168		.mask = SND_JACK_MICROPHONE,
    169	},
    170	{
    171		.pin = "Headphone",
    172		.mask = SND_JACK_MICROPHONE,
    173	},
    174};
    175
    176static int tobermory_late_probe(struct snd_soc_card *card)
    177{
    178	struct snd_soc_pcm_runtime *rtd;
    179	struct snd_soc_component *component;
    180	struct snd_soc_dai *codec_dai;
    181	int ret;
    182
    183	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
    184	component = asoc_rtd_to_codec(rtd, 0)->component;
    185	codec_dai = asoc_rtd_to_codec(rtd, 0);
    186
    187	ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
    188				     32768, SND_SOC_CLOCK_IN);
    189	if (ret < 0)
    190		return ret;
    191
    192	ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET |
    193					 SND_JACK_BTN_0, &tobermory_headset,
    194					 tobermory_headset_pins,
    195					 ARRAY_SIZE(tobermory_headset_pins));
    196	if (ret)
    197		return ret;
    198
    199	wm8962_mic_detect(component, &tobermory_headset);
    200
    201	return 0;
    202}
    203
    204static struct snd_soc_card tobermory = {
    205	.name = "Tobermory",
    206	.owner = THIS_MODULE,
    207	.dai_link = tobermory_dai,
    208	.num_links = ARRAY_SIZE(tobermory_dai),
    209
    210	.set_bias_level = tobermory_set_bias_level,
    211	.set_bias_level_post = tobermory_set_bias_level_post,
    212
    213	.controls = controls,
    214	.num_controls = ARRAY_SIZE(controls),
    215	.dapm_widgets = widgets,
    216	.num_dapm_widgets = ARRAY_SIZE(widgets),
    217	.dapm_routes = audio_paths,
    218	.num_dapm_routes = ARRAY_SIZE(audio_paths),
    219	.fully_routed = true,
    220
    221	.late_probe = tobermory_late_probe,
    222};
    223
    224static int tobermory_probe(struct platform_device *pdev)
    225{
    226	struct snd_soc_card *card = &tobermory;
    227	int ret;
    228
    229	card->dev = &pdev->dev;
    230
    231	ret = devm_snd_soc_register_card(&pdev->dev, card);
    232	if (ret)
    233		dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
    234
    235	return ret;
    236}
    237
    238static struct platform_driver tobermory_driver = {
    239	.driver = {
    240		.name = "tobermory",
    241		.pm = &snd_soc_pm_ops,
    242	},
    243	.probe = tobermory_probe,
    244};
    245
    246module_platform_driver(tobermory_driver);
    247
    248MODULE_DESCRIPTION("Tobermory audio support");
    249MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
    250MODULE_LICENSE("GPL");
    251MODULE_ALIAS("platform:tobermory");