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

magician.c (10175B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * SoC audio for HTC Magician
      4 *
      5 * Copyright (c) 2006 Philipp Zabel <philipp.zabel@gmail.com>
      6 *
      7 * based on spitz.c,
      8 * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
      9 *          Richard Purdie <richard@openedhand.com>
     10 */
     11
     12#include <linux/module.h>
     13#include <linux/timer.h>
     14#include <linux/interrupt.h>
     15#include <linux/platform_device.h>
     16#include <linux/delay.h>
     17#include <linux/gpio/consumer.h>
     18#include <linux/i2c.h>
     19
     20#include <sound/core.h>
     21#include <sound/pcm.h>
     22#include <sound/pcm_params.h>
     23#include <sound/soc.h>
     24
     25#include <asm/mach-types.h>
     26#include "../codecs/uda1380.h"
     27#include "pxa2xx-i2s.h"
     28#include "pxa-ssp.h"
     29
     30#define MAGICIAN_MIC       0
     31#define MAGICIAN_MIC_EXT   1
     32
     33static int magician_hp_switch;
     34static int magician_spk_switch = 1;
     35static int magician_in_sel = MAGICIAN_MIC;
     36
     37static struct gpio_desc *gpiod_spk_power, *gpiod_ep_power, *gpiod_mic_power;
     38static struct gpio_desc *gpiod_in_sel0, *gpiod_in_sel1;
     39
     40static void magician_ext_control(struct snd_soc_dapm_context *dapm)
     41{
     42
     43	snd_soc_dapm_mutex_lock(dapm);
     44
     45	if (magician_spk_switch)
     46		snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
     47	else
     48		snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
     49	if (magician_hp_switch)
     50		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
     51	else
     52		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
     53
     54	switch (magician_in_sel) {
     55	case MAGICIAN_MIC:
     56		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic");
     57		snd_soc_dapm_enable_pin_unlocked(dapm, "Call Mic");
     58		break;
     59	case MAGICIAN_MIC_EXT:
     60		snd_soc_dapm_disable_pin_unlocked(dapm, "Call Mic");
     61		snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Mic");
     62		break;
     63	}
     64
     65	snd_soc_dapm_sync_unlocked(dapm);
     66
     67	snd_soc_dapm_mutex_unlock(dapm);
     68}
     69
     70static int magician_startup(struct snd_pcm_substream *substream)
     71{
     72	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     73
     74	/* check the jack status at stream startup */
     75	magician_ext_control(&rtd->card->dapm);
     76
     77	return 0;
     78}
     79
     80/*
     81 * Magician uses SSP port for playback.
     82 */
     83static int magician_playback_hw_params(struct snd_pcm_substream *substream,
     84				       struct snd_pcm_hw_params *params)
     85{
     86	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     87	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
     88	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
     89	unsigned int width;
     90	int ret = 0;
     91
     92	/* set codec DAI configuration */
     93	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
     94			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
     95	if (ret < 0)
     96		return ret;
     97
     98	/* set cpu DAI configuration */
     99	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
    100			SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_CBS_CFS);
    101	if (ret < 0)
    102		return ret;
    103
    104	width = snd_pcm_format_physical_width(params_format(params));
    105	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width);
    106	if (ret < 0)
    107		return ret;
    108
    109	/* set audio clock as clock source */
    110	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0,
    111			SND_SOC_CLOCK_OUT);
    112	if (ret < 0)
    113		return ret;
    114
    115	return 0;
    116}
    117
    118/*
    119 * Magician uses I2S for capture.
    120 */
    121static int magician_capture_hw_params(struct snd_pcm_substream *substream,
    122				      struct snd_pcm_hw_params *params)
    123{
    124	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    125	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
    126	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    127	int ret = 0;
    128
    129	/* set codec DAI configuration */
    130	ret = snd_soc_dai_set_fmt(codec_dai,
    131			SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
    132			SND_SOC_DAIFMT_CBS_CFS);
    133	if (ret < 0)
    134		return ret;
    135
    136	/* set cpu DAI configuration */
    137	ret = snd_soc_dai_set_fmt(cpu_dai,
    138			SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
    139			SND_SOC_DAIFMT_CBS_CFS);
    140	if (ret < 0)
    141		return ret;
    142
    143	/* set the I2S system clock as output */
    144	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
    145			SND_SOC_CLOCK_OUT);
    146	if (ret < 0)
    147		return ret;
    148
    149	return 0;
    150}
    151
    152static const struct snd_soc_ops magician_capture_ops = {
    153	.startup = magician_startup,
    154	.hw_params = magician_capture_hw_params,
    155};
    156
    157static const struct snd_soc_ops magician_playback_ops = {
    158	.startup = magician_startup,
    159	.hw_params = magician_playback_hw_params,
    160};
    161
    162static int magician_get_hp(struct snd_kcontrol *kcontrol,
    163			     struct snd_ctl_elem_value *ucontrol)
    164{
    165	ucontrol->value.integer.value[0] = magician_hp_switch;
    166	return 0;
    167}
    168
    169static int magician_set_hp(struct snd_kcontrol *kcontrol,
    170			     struct snd_ctl_elem_value *ucontrol)
    171{
    172	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
    173
    174	if (magician_hp_switch == ucontrol->value.integer.value[0])
    175		return 0;
    176
    177	magician_hp_switch = ucontrol->value.integer.value[0];
    178	magician_ext_control(&card->dapm);
    179	return 1;
    180}
    181
    182static int magician_get_spk(struct snd_kcontrol *kcontrol,
    183			    struct snd_ctl_elem_value *ucontrol)
    184{
    185	ucontrol->value.integer.value[0] = magician_spk_switch;
    186	return 0;
    187}
    188
    189static int magician_set_spk(struct snd_kcontrol *kcontrol,
    190			    struct snd_ctl_elem_value *ucontrol)
    191{
    192	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
    193
    194	if (magician_spk_switch == ucontrol->value.integer.value[0])
    195		return 0;
    196
    197	magician_spk_switch = ucontrol->value.integer.value[0];
    198	magician_ext_control(&card->dapm);
    199	return 1;
    200}
    201
    202static int magician_get_input(struct snd_kcontrol *kcontrol,
    203			      struct snd_ctl_elem_value *ucontrol)
    204{
    205	ucontrol->value.enumerated.item[0] = magician_in_sel;
    206	return 0;
    207}
    208
    209static int magician_set_input(struct snd_kcontrol *kcontrol,
    210			      struct snd_ctl_elem_value *ucontrol)
    211{
    212	if (magician_in_sel == ucontrol->value.enumerated.item[0])
    213		return 0;
    214
    215	magician_in_sel = ucontrol->value.enumerated.item[0];
    216
    217	switch (magician_in_sel) {
    218	case MAGICIAN_MIC:
    219		gpiod_set_value(gpiod_in_sel1, 1);
    220		break;
    221	case MAGICIAN_MIC_EXT:
    222		gpiod_set_value(gpiod_in_sel1, 0);
    223	}
    224
    225	return 1;
    226}
    227
    228static int magician_spk_power(struct snd_soc_dapm_widget *w,
    229				struct snd_kcontrol *k, int event)
    230{
    231	gpiod_set_value(gpiod_spk_power, SND_SOC_DAPM_EVENT_ON(event));
    232	return 0;
    233}
    234
    235static int magician_hp_power(struct snd_soc_dapm_widget *w,
    236				struct snd_kcontrol *k, int event)
    237{
    238	gpiod_set_value(gpiod_ep_power, SND_SOC_DAPM_EVENT_ON(event));
    239	return 0;
    240}
    241
    242static int magician_mic_bias(struct snd_soc_dapm_widget *w,
    243				struct snd_kcontrol *k, int event)
    244{
    245	gpiod_set_value(gpiod_mic_power, SND_SOC_DAPM_EVENT_ON(event));
    246	return 0;
    247}
    248
    249/* magician machine dapm widgets */
    250static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
    251	SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power),
    252	SND_SOC_DAPM_SPK("Speaker", magician_spk_power),
    253	SND_SOC_DAPM_MIC("Call Mic", magician_mic_bias),
    254	SND_SOC_DAPM_MIC("Headset Mic", magician_mic_bias),
    255};
    256
    257/* magician machine audio_map */
    258static const struct snd_soc_dapm_route audio_map[] = {
    259
    260	/* Headphone connected to VOUTL, VOUTR */
    261	{"Headphone Jack", NULL, "VOUTL"},
    262	{"Headphone Jack", NULL, "VOUTR"},
    263
    264	/* Speaker connected to VOUTL, VOUTR */
    265	{"Speaker", NULL, "VOUTL"},
    266	{"Speaker", NULL, "VOUTR"},
    267
    268	/* Mics are connected to VINM */
    269	{"VINM", NULL, "Headset Mic"},
    270	{"VINM", NULL, "Call Mic"},
    271};
    272
    273static const char * const input_select[] = {"Call Mic", "Headset Mic"};
    274static const struct soc_enum magician_in_sel_enum =
    275	SOC_ENUM_SINGLE_EXT(2, input_select);
    276
    277static const struct snd_kcontrol_new uda1380_magician_controls[] = {
    278	SOC_SINGLE_BOOL_EXT("Headphone Switch",
    279			(unsigned long)&magician_hp_switch,
    280			magician_get_hp, magician_set_hp),
    281	SOC_SINGLE_BOOL_EXT("Speaker Switch",
    282			(unsigned long)&magician_spk_switch,
    283			magician_get_spk, magician_set_spk),
    284	SOC_ENUM_EXT("Input Select", magician_in_sel_enum,
    285			magician_get_input, magician_set_input),
    286};
    287
    288/* magician digital audio interface glue - connects codec <--> CPU */
    289SND_SOC_DAILINK_DEFS(playback,
    290	DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.0")),
    291	DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018",
    292				      "uda1380-hifi-playback")),
    293	DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
    294
    295SND_SOC_DAILINK_DEFS(capture,
    296	DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
    297	DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018",
    298				      "uda1380-hifi-capture")),
    299	DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
    300
    301static struct snd_soc_dai_link magician_dai[] = {
    302{
    303	.name = "uda1380",
    304	.stream_name = "UDA1380 Playback",
    305	.ops = &magician_playback_ops,
    306	SND_SOC_DAILINK_REG(playback),
    307},
    308{
    309	.name = "uda1380",
    310	.stream_name = "UDA1380 Capture",
    311	.ops = &magician_capture_ops,
    312	SND_SOC_DAILINK_REG(capture),
    313}
    314};
    315
    316/* magician audio machine driver */
    317static struct snd_soc_card snd_soc_card_magician = {
    318	.name = "Magician",
    319	.owner = THIS_MODULE,
    320	.dai_link = magician_dai,
    321	.num_links = ARRAY_SIZE(magician_dai),
    322
    323	.controls = uda1380_magician_controls,
    324	.num_controls = ARRAY_SIZE(uda1380_magician_controls),
    325	.dapm_widgets = uda1380_dapm_widgets,
    326	.num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
    327	.dapm_routes = audio_map,
    328	.num_dapm_routes = ARRAY_SIZE(audio_map),
    329	.fully_routed = true,
    330};
    331
    332static int magician_audio_probe(struct platform_device *pdev)
    333{
    334	struct device *dev = &pdev->dev;
    335
    336	gpiod_spk_power = devm_gpiod_get(dev, "SPK_POWER", GPIOD_OUT_LOW);
    337	if (IS_ERR(gpiod_spk_power))
    338		return PTR_ERR(gpiod_spk_power);
    339	gpiod_ep_power = devm_gpiod_get(dev, "EP_POWER", GPIOD_OUT_LOW);
    340	if (IS_ERR(gpiod_ep_power))
    341		return PTR_ERR(gpiod_ep_power);
    342	gpiod_mic_power = devm_gpiod_get(dev, "MIC_POWER", GPIOD_OUT_LOW);
    343	if (IS_ERR(gpiod_mic_power))
    344		return PTR_ERR(gpiod_mic_power);
    345	gpiod_in_sel0 = devm_gpiod_get(dev, "IN_SEL0", GPIOD_OUT_HIGH);
    346	if (IS_ERR(gpiod_in_sel0))
    347		return PTR_ERR(gpiod_in_sel0);
    348	gpiod_in_sel1 = devm_gpiod_get(dev, "IN_SEL1", GPIOD_OUT_LOW);
    349	if (IS_ERR(gpiod_in_sel1))
    350		return PTR_ERR(gpiod_in_sel1);
    351
    352	snd_soc_card_magician.dev = &pdev->dev;
    353	return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_magician);
    354}
    355
    356static struct platform_driver magician_audio_driver = {
    357	.driver.name = "magician-audio",
    358	.driver.pm = &snd_soc_pm_ops,
    359	.probe = magician_audio_probe,
    360};
    361module_platform_driver(magician_audio_driver);
    362
    363MODULE_AUTHOR("Philipp Zabel");
    364MODULE_DESCRIPTION("ALSA SoC Magician");
    365MODULE_LICENSE("GPL");
    366MODULE_ALIAS("platform:magician-audio");