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

smartq_wm8987.c (5490B)


      1// SPDX-License-Identifier: GPL-2.0+
      2//
      3// Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com>
      4//
      5// Based on smdk6410_wm8987.c
      6//     Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com
      7//     Graeme Gregory - graeme.gregory@wolfsonmicro.com
      8
      9#include <linux/gpio/consumer.h>
     10#include <linux/module.h>
     11
     12#include <sound/soc.h>
     13#include <sound/jack.h>
     14
     15#include "i2s.h"
     16#include "../codecs/wm8750.h"
     17
     18/*
     19 * WM8987 is register compatible with WM8750, so using that as base driver.
     20 */
     21
     22static struct snd_soc_card snd_soc_smartq;
     23
     24static int smartq_hifi_hw_params(struct snd_pcm_substream *substream,
     25	struct snd_pcm_hw_params *params)
     26{
     27	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     28	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
     29	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
     30	unsigned int clk = 0;
     31	int ret;
     32
     33	switch (params_rate(params)) {
     34	case 8000:
     35	case 16000:
     36	case 32000:
     37	case 48000:
     38	case 96000:
     39		clk = 12288000;
     40		break;
     41	case 11025:
     42	case 22050:
     43	case 44100:
     44	case 88200:
     45		clk = 11289600;
     46		break;
     47	}
     48
     49	/* Use PCLK for I2S signal generation */
     50	ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
     51					0, SND_SOC_CLOCK_IN);
     52	if (ret < 0)
     53		return ret;
     54
     55	/* Gate the RCLK output on PAD */
     56	ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
     57					0, SND_SOC_CLOCK_IN);
     58	if (ret < 0)
     59		return ret;
     60
     61	/* set the codec system clock for DAC and ADC */
     62	ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
     63				     SND_SOC_CLOCK_IN);
     64	if (ret < 0)
     65		return ret;
     66
     67	return 0;
     68}
     69
     70/*
     71 * SmartQ WM8987 HiFi DAI operations.
     72 */
     73static const struct snd_soc_ops smartq_hifi_ops = {
     74	.hw_params = smartq_hifi_hw_params,
     75};
     76
     77static struct snd_soc_jack smartq_jack;
     78
     79static struct snd_soc_jack_pin smartq_jack_pins[] = {
     80	/* Disable speaker when headphone is plugged in */
     81	{
     82		.pin	= "Internal Speaker",
     83		.mask	= SND_JACK_HEADPHONE,
     84	},
     85};
     86
     87static struct snd_soc_jack_gpio smartq_jack_gpios[] = {
     88	{
     89		.gpio		= -1,
     90		.name		= "headphone detect",
     91		.report		= SND_JACK_HEADPHONE,
     92		.debounce_time	= 200,
     93	},
     94};
     95
     96static const struct snd_kcontrol_new wm8987_smartq_controls[] = {
     97	SOC_DAPM_PIN_SWITCH("Internal Speaker"),
     98	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
     99	SOC_DAPM_PIN_SWITCH("Internal Mic"),
    100};
    101
    102static int smartq_speaker_event(struct snd_soc_dapm_widget *w,
    103				struct snd_kcontrol *k,
    104				int event)
    105{
    106	struct gpio_desc *gpio = snd_soc_card_get_drvdata(&snd_soc_smartq);
    107
    108	gpiod_set_value(gpio, SND_SOC_DAPM_EVENT_OFF(event));
    109
    110	return 0;
    111}
    112
    113static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = {
    114	SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event),
    115	SND_SOC_DAPM_HP("Headphone Jack", NULL),
    116	SND_SOC_DAPM_MIC("Internal Mic", NULL),
    117};
    118
    119static const struct snd_soc_dapm_route audio_map[] = {
    120	{"Headphone Jack", NULL, "LOUT2"},
    121	{"Headphone Jack", NULL, "ROUT2"},
    122
    123	{"Internal Speaker", NULL, "LOUT2"},
    124	{"Internal Speaker", NULL, "ROUT2"},
    125
    126	{"Mic Bias", NULL, "Internal Mic"},
    127	{"LINPUT2", NULL, "Mic Bias"},
    128};
    129
    130static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
    131{
    132	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
    133	int err = 0;
    134
    135	/* set endpoints to not connected */
    136	snd_soc_dapm_nc_pin(dapm, "LINPUT1");
    137	snd_soc_dapm_nc_pin(dapm, "RINPUT1");
    138	snd_soc_dapm_nc_pin(dapm, "OUT3");
    139	snd_soc_dapm_nc_pin(dapm, "ROUT1");
    140
    141	/* Headphone jack detection */
    142	err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
    143					 SND_JACK_HEADPHONE, &smartq_jack,
    144					 smartq_jack_pins,
    145					 ARRAY_SIZE(smartq_jack_pins));
    146	if (err)
    147		return err;
    148
    149	err = snd_soc_jack_add_gpios(&smartq_jack,
    150				     ARRAY_SIZE(smartq_jack_gpios),
    151				     smartq_jack_gpios);
    152
    153	return err;
    154}
    155
    156SND_SOC_DAILINK_DEFS(wm8987,
    157	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
    158	DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-0x1a", "wm8750-hifi")),
    159	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
    160
    161static struct snd_soc_dai_link smartq_dai[] = {
    162	{
    163		.name		= "wm8987",
    164		.stream_name	= "SmartQ Hi-Fi",
    165		.init		= smartq_wm8987_init,
    166		.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
    167				  SND_SOC_DAIFMT_CBS_CFS,
    168		.ops		= &smartq_hifi_ops,
    169		SND_SOC_DAILINK_REG(wm8987),
    170	},
    171};
    172
    173static struct snd_soc_card snd_soc_smartq = {
    174	.name = "SmartQ",
    175	.owner = THIS_MODULE,
    176	.dai_link = smartq_dai,
    177	.num_links = ARRAY_SIZE(smartq_dai),
    178
    179	.dapm_widgets = wm8987_dapm_widgets,
    180	.num_dapm_widgets = ARRAY_SIZE(wm8987_dapm_widgets),
    181	.dapm_routes = audio_map,
    182	.num_dapm_routes = ARRAY_SIZE(audio_map),
    183	.controls = wm8987_smartq_controls,
    184	.num_controls = ARRAY_SIZE(wm8987_smartq_controls),
    185};
    186
    187static int smartq_probe(struct platform_device *pdev)
    188{
    189	struct gpio_desc *gpio;
    190	int ret;
    191
    192	platform_set_drvdata(pdev, &snd_soc_smartq);
    193
    194	/* Initialise GPIOs used by amplifiers */
    195	gpio = devm_gpiod_get(&pdev->dev, "amplifiers shutdown",
    196			      GPIOD_OUT_HIGH);
    197	if (IS_ERR(gpio)) {
    198		dev_err(&pdev->dev, "Failed to register GPK12\n");
    199		ret = PTR_ERR(gpio);
    200		goto out;
    201	}
    202	snd_soc_card_set_drvdata(&snd_soc_smartq, gpio);
    203
    204	ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_smartq);
    205	if (ret)
    206		dev_err(&pdev->dev, "Failed to register card\n");
    207
    208out:
    209	return ret;
    210}
    211
    212static struct platform_driver smartq_driver = {
    213	.driver = {
    214		.name = "smartq-audio",
    215	},
    216	.probe = smartq_probe,
    217};
    218
    219module_platform_driver(smartq_driver);
    220
    221/* Module information */
    222MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>");
    223MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987");
    224MODULE_LICENSE("GPL");