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

corgi.c (9193B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * corgi.c  --  SoC audio for Corgi
      4 *
      5 * Copyright 2005 Wolfson Microelectronics PLC.
      6 * Copyright 2005 Openedhand Ltd.
      7 *
      8 * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
      9 *          Richard Purdie <richard@openedhand.com>
     10 */
     11
     12#include <linux/module.h>
     13#include <linux/moduleparam.h>
     14#include <linux/timer.h>
     15#include <linux/i2c.h>
     16#include <linux/interrupt.h>
     17#include <linux/platform_device.h>
     18#include <linux/gpio.h>
     19#include <sound/core.h>
     20#include <sound/pcm.h>
     21#include <sound/soc.h>
     22
     23#include <asm/mach-types.h>
     24#include <linux/platform_data/asoc-pxa.h>
     25
     26#include "../codecs/wm8731.h"
     27#include "pxa2xx-i2s.h"
     28
     29#define CORGI_HP        0
     30#define CORGI_MIC       1
     31#define CORGI_LINE      2
     32#define CORGI_HEADSET   3
     33#define CORGI_HP_OFF    4
     34#define CORGI_SPK_ON    0
     35#define CORGI_SPK_OFF   1
     36
     37 /* audio clock in Hz - rounded from 12.235MHz */
     38#define CORGI_AUDIO_CLOCK 12288000
     39
     40static int corgi_jack_func;
     41static int corgi_spk_func;
     42
     43static struct gpio_desc *gpiod_mute_l, *gpiod_mute_r,
     44			*gpiod_apm_on, *gpiod_mic_bias;
     45
     46static void corgi_ext_control(struct snd_soc_dapm_context *dapm)
     47{
     48	snd_soc_dapm_mutex_lock(dapm);
     49
     50	/* set up jack connection */
     51	switch (corgi_jack_func) {
     52	case CORGI_HP:
     53		/* set = unmute headphone */
     54		gpiod_set_value(gpiod_mute_l, 1);
     55		gpiod_set_value(gpiod_mute_r, 1);
     56		snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
     57		snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
     58		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
     59		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
     60		break;
     61	case CORGI_MIC:
     62		/* reset = mute headphone */
     63		gpiod_set_value(gpiod_mute_l, 0);
     64		gpiod_set_value(gpiod_mute_r, 0);
     65		snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
     66		snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
     67		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
     68		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
     69		break;
     70	case CORGI_LINE:
     71		gpiod_set_value(gpiod_mute_l, 0);
     72		gpiod_set_value(gpiod_mute_r, 0);
     73		snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
     74		snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
     75		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
     76		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
     77		break;
     78	case CORGI_HEADSET:
     79		gpiod_set_value(gpiod_mute_l, 0);
     80		gpiod_set_value(gpiod_mute_r, 1);
     81		snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
     82		snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
     83		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
     84		snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
     85		break;
     86	}
     87
     88	if (corgi_spk_func == CORGI_SPK_ON)
     89		snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
     90	else
     91		snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
     92
     93	/* signal a DAPM event */
     94	snd_soc_dapm_sync_unlocked(dapm);
     95
     96	snd_soc_dapm_mutex_unlock(dapm);
     97}
     98
     99static int corgi_startup(struct snd_pcm_substream *substream)
    100{
    101	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    102
    103	/* check the jack status at stream startup */
    104	corgi_ext_control(&rtd->card->dapm);
    105
    106	return 0;
    107}
    108
    109/* we need to unmute the HP at shutdown as the mute burns power on corgi */
    110static void corgi_shutdown(struct snd_pcm_substream *substream)
    111{
    112	/* set = unmute headphone */
    113	gpiod_set_value(gpiod_mute_l, 1);
    114	gpiod_set_value(gpiod_mute_r, 1);
    115}
    116
    117static int corgi_hw_params(struct snd_pcm_substream *substream,
    118	struct snd_pcm_hw_params *params)
    119{
    120	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    121	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
    122	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    123	unsigned int clk = 0;
    124	int ret = 0;
    125
    126	switch (params_rate(params)) {
    127	case 8000:
    128	case 16000:
    129	case 48000:
    130	case 96000:
    131		clk = 12288000;
    132		break;
    133	case 11025:
    134	case 22050:
    135	case 44100:
    136		clk = 11289600;
    137		break;
    138	}
    139
    140	/* set the codec system clock for DAC and ADC */
    141	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk,
    142		SND_SOC_CLOCK_IN);
    143	if (ret < 0)
    144		return ret;
    145
    146	/* set the I2S system clock as input (unused) */
    147	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
    148		SND_SOC_CLOCK_IN);
    149	if (ret < 0)
    150		return ret;
    151
    152	return 0;
    153}
    154
    155static const struct snd_soc_ops corgi_ops = {
    156	.startup = corgi_startup,
    157	.hw_params = corgi_hw_params,
    158	.shutdown = corgi_shutdown,
    159};
    160
    161static int corgi_get_jack(struct snd_kcontrol *kcontrol,
    162	struct snd_ctl_elem_value *ucontrol)
    163{
    164	ucontrol->value.enumerated.item[0] = corgi_jack_func;
    165	return 0;
    166}
    167
    168static int corgi_set_jack(struct snd_kcontrol *kcontrol,
    169	struct snd_ctl_elem_value *ucontrol)
    170{
    171	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
    172
    173	if (corgi_jack_func == ucontrol->value.enumerated.item[0])
    174		return 0;
    175
    176	corgi_jack_func = ucontrol->value.enumerated.item[0];
    177	corgi_ext_control(&card->dapm);
    178	return 1;
    179}
    180
    181static int corgi_get_spk(struct snd_kcontrol *kcontrol,
    182	struct snd_ctl_elem_value *ucontrol)
    183{
    184	ucontrol->value.enumerated.item[0] = corgi_spk_func;
    185	return 0;
    186}
    187
    188static int corgi_set_spk(struct snd_kcontrol *kcontrol,
    189	struct snd_ctl_elem_value *ucontrol)
    190{
    191	struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
    192
    193	if (corgi_spk_func == ucontrol->value.enumerated.item[0])
    194		return 0;
    195
    196	corgi_spk_func = ucontrol->value.enumerated.item[0];
    197	corgi_ext_control(&card->dapm);
    198	return 1;
    199}
    200
    201static int corgi_amp_event(struct snd_soc_dapm_widget *w,
    202	struct snd_kcontrol *k, int event)
    203{
    204	gpiod_set_value(gpiod_apm_on, SND_SOC_DAPM_EVENT_ON(event));
    205	return 0;
    206}
    207
    208static int corgi_mic_event(struct snd_soc_dapm_widget *w,
    209	struct snd_kcontrol *k, int event)
    210{
    211	gpiod_set_value(gpiod_mic_bias, SND_SOC_DAPM_EVENT_ON(event));
    212	return 0;
    213}
    214
    215/* corgi machine dapm widgets */
    216static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
    217SND_SOC_DAPM_HP("Headphone Jack", NULL),
    218SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
    219SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
    220SND_SOC_DAPM_LINE("Line Jack", NULL),
    221SND_SOC_DAPM_HP("Headset Jack", NULL),
    222};
    223
    224/* Corgi machine audio map (connections to the codec pins) */
    225static const struct snd_soc_dapm_route corgi_audio_map[] = {
    226
    227	/* headset Jack  - in = micin, out = LHPOUT*/
    228	{"Headset Jack", NULL, "LHPOUT"},
    229
    230	/* headphone connected to LHPOUT1, RHPOUT1 */
    231	{"Headphone Jack", NULL, "LHPOUT"},
    232	{"Headphone Jack", NULL, "RHPOUT"},
    233
    234	/* speaker connected to LOUT, ROUT */
    235	{"Ext Spk", NULL, "ROUT"},
    236	{"Ext Spk", NULL, "LOUT"},
    237
    238	/* mic is connected to MICIN (via right channel of headphone jack) */
    239	{"MICIN", NULL, "Mic Jack"},
    240
    241	/* Same as the above but no mic bias for line signals */
    242	{"MICIN", NULL, "Line Jack"},
    243};
    244
    245static const char * const jack_function[] = {"Headphone", "Mic", "Line",
    246	"Headset", "Off"};
    247static const char * const spk_function[] = {"On", "Off"};
    248static const struct soc_enum corgi_enum[] = {
    249	SOC_ENUM_SINGLE_EXT(5, jack_function),
    250	SOC_ENUM_SINGLE_EXT(2, spk_function),
    251};
    252
    253static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
    254	SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
    255		corgi_set_jack),
    256	SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
    257		corgi_set_spk),
    258};
    259
    260/* corgi digital audio interface glue - connects codec <--> CPU */
    261SND_SOC_DAILINK_DEFS(wm8731,
    262	DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
    263	DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
    264	DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
    265
    266static struct snd_soc_dai_link corgi_dai = {
    267	.name = "WM8731",
    268	.stream_name = "WM8731",
    269	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
    270		   SND_SOC_DAIFMT_CBS_CFS,
    271	.ops = &corgi_ops,
    272	SND_SOC_DAILINK_REG(wm8731),
    273};
    274
    275/* corgi audio machine driver */
    276static struct snd_soc_card corgi = {
    277	.name = "Corgi",
    278	.owner = THIS_MODULE,
    279	.dai_link = &corgi_dai,
    280	.num_links = 1,
    281
    282	.controls = wm8731_corgi_controls,
    283	.num_controls = ARRAY_SIZE(wm8731_corgi_controls),
    284	.dapm_widgets = wm8731_dapm_widgets,
    285	.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
    286	.dapm_routes = corgi_audio_map,
    287	.num_dapm_routes = ARRAY_SIZE(corgi_audio_map),
    288	.fully_routed = true,
    289};
    290
    291static int corgi_probe(struct platform_device *pdev)
    292{
    293	struct snd_soc_card *card = &corgi;
    294	int ret;
    295
    296	card->dev = &pdev->dev;
    297
    298	gpiod_mute_l = devm_gpiod_get(&pdev->dev, "mute-l", GPIOD_OUT_HIGH);
    299	if (IS_ERR(gpiod_mute_l))
    300		return PTR_ERR(gpiod_mute_l);
    301	gpiod_mute_r = devm_gpiod_get(&pdev->dev, "mute-r", GPIOD_OUT_HIGH);
    302	if (IS_ERR(gpiod_mute_r))
    303		return PTR_ERR(gpiod_mute_r);
    304	gpiod_apm_on = devm_gpiod_get(&pdev->dev, "apm-on", GPIOD_OUT_LOW);
    305	if (IS_ERR(gpiod_apm_on))
    306		return PTR_ERR(gpiod_apm_on);
    307	gpiod_mic_bias = devm_gpiod_get(&pdev->dev, "mic-bias", GPIOD_OUT_LOW);
    308	if (IS_ERR(gpiod_mic_bias))
    309		return PTR_ERR(gpiod_mic_bias);
    310
    311	ret = devm_snd_soc_register_card(&pdev->dev, card);
    312	if (ret)
    313		dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
    314			ret);
    315	return ret;
    316}
    317
    318static struct platform_driver corgi_driver = {
    319	.driver		= {
    320		.name	= "corgi-audio",
    321		.pm     = &snd_soc_pm_ops,
    322	},
    323	.probe		= corgi_probe,
    324};
    325
    326module_platform_driver(corgi_driver);
    327
    328/* Module information */
    329MODULE_AUTHOR("Richard Purdie");
    330MODULE_DESCRIPTION("ALSA SoC Corgi");
    331MODULE_LICENSE("GPL");
    332MODULE_ALIAS("platform:corgi-audio");